this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

pkg/path: activate OS-dependent version

This also fixes a bad interaction between validators
and variable arguments: IsAbs qualifies as a validator,
but it is ambiguous to allow both. In the futurre we
could introduce an AbsDir function, or alike, that
functions only as a validator. Also `must` would help
here.

The fix has been left in this CL, as it isn't a problem
before this CL and it makes the context clear.

Overall, this doesn't seem to be too big of a deal.

Change-Id: I4f28ad507cd4d8a42c3d02bde28be53b7db3ad63
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7846
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>

+1324 -545
+35 -15
internal/core/adt/expr.go
··· 980 980 Value Value // Could become Value later, using disjunctions for defaults. 981 981 } 982 982 983 + // Kind returns the kind mask of this parameter. 983 984 func (p Param) Kind() Kind { 984 985 return p.Value.Kind() 985 986 } 986 987 988 + // Default reports the default value for this Param or nil if there is none. 987 989 func (p Param) Default() Value { 988 990 d, ok := p.Value.(*Disjunction) 989 991 if !ok || d.NumDefaults != 1 { ··· 1009 1011 return &BuiltinValidator{Builtin: x} 1010 1012 } 1011 1013 1012 - func (x *Builtin) IsValidator(numArgs int) bool { 1013 - return len(x.Params)-1 == numArgs && x.Result&^BoolKind == 0 1014 + // IsValidator reports whether b should be interpreted as a Validator for the 1015 + // given number of arguments. 1016 + func (b *Builtin) IsValidator(numArgs int) bool { 1017 + return numArgs == len(b.Params)-1 && 1018 + b.Result&^BoolKind == 0 && 1019 + b.Params[numArgs].Default() == nil 1014 1020 } 1015 1021 1016 1022 func bottom(v Value) *Bottom { ··· 1040 1046 args = append(args, v) 1041 1047 } 1042 1048 for i, a := range args { 1043 - if x.Params[i].Kind() != BottomKind { 1044 - if b := bottom(a); b != nil { 1045 - return b 1049 + if x.Params[i].Kind() == BottomKind { 1050 + continue 1051 + } 1052 + if b := bottom(a); b != nil { 1053 + return b 1054 + } 1055 + if k := kind(a); x.Params[i].Kind()&k == BottomKind { 1056 + code := EvalError 1057 + b, _ := args[i].(*Bottom) 1058 + if b != nil { 1059 + code = b.Code 1046 1060 } 1047 - if k := kind(a); x.Params[i].Kind()&k == BottomKind { 1048 - code := EvalError 1049 - b, _ := args[i].(*Bottom) 1050 - if b != nil { 1051 - code = b.Code 1052 - } 1053 - c.addErrf(code, pos(a), 1054 - "cannot use %s (type %s) as %s in argument %d to %s", 1055 - a, k, x.Params[i].Kind(), i+1, fun) 1056 - return nil 1061 + c.addErrf(code, pos(a), 1062 + "cannot use %s (type %s) as %s in argument %d to %s", 1063 + a, k, x.Params[i].Kind(), i+1, fun) 1064 + return nil 1065 + } 1066 + v := x.Params[i].Value 1067 + if _, ok := v.(*BasicType); !ok { 1068 + env := c.Env(0) 1069 + x := &BinaryExpr{Op: AndOp, X: v, Y: a} 1070 + n := &Vertex{Conjuncts: []Conjunct{{env, x, 0}}} 1071 + c.Unifier.Unify(c, n, Finalized) 1072 + if _, ok := n.BaseValue.(*Bottom); ok { 1073 + c.addErrf(0, pos(a), 1074 + "cannot use %s as %s in argument %d to %s", 1075 + a, v, i+1, fun) 1057 1076 } 1077 + args[i] = n 1058 1078 } 1059 1079 } 1060 1080 return x.Func(c, args)
+5 -13
pkg/internal/builtin.go
··· 55 55 } 56 56 57 57 type Param struct { 58 - Kind adt.Kind 59 - Value adt.Value // input constraint 60 - Default adt.Value // may be nil 58 + Kind adt.Kind 59 + Value adt.Value // input constraint (may be nil) 61 60 } 62 61 63 62 type Package struct { ··· 112 111 func toBuiltin(ctx *adt.OpContext, b *Builtin) *adt.Builtin { 113 112 params := make([]adt.Param, len(b.Params)) 114 113 for i, p := range b.Params { 115 - // TODO: use Value. 116 - params[i].Value = &adt.BasicType{K: p.Kind} 117 - if p.Default != nil { 118 - params[i].Value = &adt.Disjunction{ 119 - NumDefaults: 1, 120 - Values: []*adt.Vertex{ 121 - adt.ToVertex(p.Default), 122 - adt.ToVertex(params[i].Value), 123 - }, 124 - } 114 + params[i].Value = p.Value 115 + if params[i].Value == nil { 116 + params[i].Value = &adt.BasicType{K: p.Kind} 125 117 } 126 118 } 127 119
-28
pkg/path/manual.go
··· 1 - // Copyright 2018 The CUE Authors 2 - // 3 - // Licensed under the Apache License, Version 2.0 (the "License"); 4 - // you may not use this file except in compliance with the License. 5 - // You may obtain a copy of the License at 6 - // 7 - // http://www.apache.org/licenses/LICENSE-2.0 8 - // 9 - // Unless required by applicable law or agreed to in writing, software 10 - // distributed under the License is distributed on an "AS IS" BASIS, 11 - // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 - // See the License for the specific language governing permissions and 13 - // limitations under the License. 14 - 15 - package path 16 - 17 - import "path" 18 - 19 - var split = path.Split 20 - 21 - // Split splits path immediately following the final slash and returns them as 22 - // the list [dir, file], separating it into a directory and file name component. 23 - // If there is no slash in path, Split returns an empty dir and file set to 24 - // path. The returned values have the property that path = dir+file. 25 - func Split(path string) []string { 26 - file, dir := split(path) 27 - return []string{file, dir} 28 - }
+362 -55
pkg/path/path.go
··· 1 - // Copyright 2020 The CUE Authors 1 + // Copyright 2020 CUE Authors 2 2 // 3 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 4 // you may not use this file except in compliance with the License. ··· 12 12 // See the License for the specific language governing permissions and 13 13 // limitations under the License. 14 14 15 - // Copyright 2018 The Go Authors. All rights reserved. 15 + // Copyright 2009 The Go Authors. All rights reserved. 16 16 // Use of this source code is governed by a BSD-style 17 17 // license that can be found in the LICENSE file. 18 18 19 - //go:generate go run cuelang.org/go/internal/cmd/qgo -exclude=Split,Join extract path 19 + // Package filepath implements utility routines for manipulating filename paths 20 + // in a way compatible with the target operating system-defined file paths. 21 + // 22 + // The filepath package uses either forward slashes or backslashes, 23 + // depending on the operating system. To process paths such as URLs 24 + // that always use forward slashes regardless of the operating 25 + // system, see the path package. 26 + package path 27 + 28 + import ( 29 + "errors" 30 + "strings" 31 + ) 32 + 33 + // A lazybuf is a lazily constructed path buffer. 34 + // It supports append, reading previously appended bytes, 35 + // and retrieving the final string. It does not allocate a buffer 36 + // to hold the output until that output diverges. 37 + type lazybuf struct { 38 + path string 39 + buf []byte 40 + w int 41 + volAndPath string 42 + volLen int 43 + } 20 44 21 - package path 45 + func (b *lazybuf) index(i int) byte { 46 + if b.buf != nil { 47 + return b.buf[i] 48 + } 49 + return b.path[i] 50 + } 22 51 23 - import "path" 52 + func (b *lazybuf) append(c byte) { 53 + if b.buf == nil { 54 + if b.w < len(b.path) && b.path[b.w] == c { 55 + b.w++ 56 + return 57 + } 58 + b.buf = make([]byte, len(b.path)) 59 + copy(b.buf, b.path[:b.w]) 60 + } 61 + b.buf[b.w] = c 62 + b.w++ 63 + } 24 64 25 - // Match reports whether name matches the shell pattern. 26 - // The pattern syntax is: 27 - // 28 - // pattern: 29 - // { term } 30 - // term: 31 - // '*' matches any sequence of non-/ characters 32 - // '?' matches any single non-/ character 33 - // '[' [ '^' ] { character-range } ']' 34 - // character class (must be non-empty) 35 - // c matches character c (c != '*', '?', '\\', '[') 36 - // '\\' c matches character c 37 - // 38 - // character-range: 39 - // c matches character c (c != '\\', '-', ']') 40 - // '\\' c matches character c 41 - // lo '-' hi matches character c for lo <= c <= hi 42 - // 43 - // Match requires pattern to match all of name, not just a substring. 44 - // The only possible returned error is ErrBadPattern, when pattern 45 - // is malformed. 46 - // 47 - func Match(pattern, name string) (matched bool, err error) { 48 - return path.Match(pattern, name) 65 + func (b *lazybuf) string() string { 66 + if b.buf == nil { 67 + return b.volAndPath[:b.volLen+b.w] 68 + } 69 + return b.volAndPath[:b.volLen] + string(b.buf[:b.w]) 49 70 } 50 71 51 72 // Clean returns the shortest path name equivalent to path 52 - // by purely lexical processing. It applies the following rules 73 + // by purely lexical processing. The default value for os is Unix. 74 + // It applies the following rules 53 75 // iteratively until no further processing can be done: 54 76 // 55 - // 1. Replace multiple slashes with a single slash. 77 + // 1. Replace multiple Separator elements with a single one. 56 78 // 2. Eliminate each . path name element (the current directory). 57 79 // 3. Eliminate each inner .. path name element (the parent directory) 58 80 // along with the non-.. element that precedes it. 59 81 // 4. Eliminate .. elements that begin a rooted path: 60 - // that is, replace "/.." by "/" at the beginning of a path. 82 + // that is, replace "/.." by "/" at the beginning of a path, 83 + // assuming Separator is '/'. 61 84 // 62 - // The returned path ends in a slash only if it is the root "/". 85 + // The returned path ends in a slash only if it represents a root directory, 86 + // such as "/" on Unix or `C:\` on Windows. 87 + // 88 + // Finally, any occurrences of slash are replaced by Separator. 63 89 // 64 90 // If the result of this process is an empty string, Clean 65 91 // returns the string ".". ··· 67 93 // See also Rob Pike, ``Lexical File Names in Plan 9 or 68 94 // Getting Dot-Dot Right,'' 69 95 // https://9p.io/sys/doc/lexnames.html 70 - func Clean(path string) string { return pathClean(path) } 96 + func Clean(path string, os OS) string { 97 + return clean(path, getOS(os)) 98 + } 99 + 100 + func clean(path string, os os) string { 101 + originalPath := path 102 + volLen := os.volumeNameLen(path) 103 + path = path[volLen:] 104 + if path == "" { 105 + if volLen > 1 && originalPath[1] != ':' { 106 + // should be UNC 107 + return fromSlash(originalPath, os) 108 + } 109 + return originalPath + "." 110 + } 111 + rooted := os.IsPathSeparator(path[0]) 112 + 113 + // Invariants: 114 + // reading from path; r is index of next byte to process. 115 + // writing to buf; w is index of next byte to write. 116 + // dotdot is index in buf where .. must stop, either because 117 + // it is the leading slash or it is a leading ../../.. prefix. 118 + n := len(path) 119 + out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen} 120 + r, dotdot := 0, 0 121 + if rooted { 122 + out.append(os.Separator) 123 + r, dotdot = 1, 1 124 + } 125 + 126 + for r < n { 127 + switch { 128 + case os.IsPathSeparator(path[r]): 129 + // empty path element 130 + r++ 131 + case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])): 132 + // . element 133 + r++ 134 + case path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])): 135 + // .. element: remove to last separator 136 + r += 2 137 + switch { 138 + case out.w > dotdot: 139 + // can backtrack 140 + out.w-- 141 + for out.w > dotdot && !os.IsPathSeparator(out.index(out.w)) { 142 + out.w-- 143 + } 144 + case !rooted: 145 + // cannot backtrack, but not rooted, so append .. element. 146 + if out.w > 0 { 147 + out.append(os.Separator) 148 + } 149 + out.append('.') 150 + out.append('.') 151 + dotdot = out.w 152 + } 153 + default: 154 + // real path element. 155 + // add slash if needed 156 + if rooted && out.w != 1 || !rooted && out.w != 0 { 157 + out.append(os.Separator) 158 + } 159 + // copy element 160 + for ; r < n && !os.IsPathSeparator(path[r]); r++ { 161 + out.append(path[r]) 162 + } 163 + } 164 + } 165 + 166 + // Turn empty string into "." 167 + if out.w == 0 { 168 + out.append('.') 169 + } 170 + 171 + return fromSlash(out.string(), os) 172 + } 173 + 174 + // ToSlash returns the result of replacing each separator character 175 + // in path with a slash ('/') character. Multiple separators are 176 + // replaced by multiple slashes. 177 + func ToSlash(path string, os OS) string { 178 + return toSlash(path, getOS(os)) 179 + } 180 + 181 + func toSlash(path string, os os) string { 182 + if os.Separator == '/' { 183 + return path 184 + } 185 + return strings.ReplaceAll(path, string(os.Separator), "/") 186 + } 187 + 188 + // FromSlash returns the result of replacing each slash ('/') character 189 + // in path with a separator character. Multiple slashes are replaced 190 + // by multiple separators. 191 + func FromSlash(path string, os OS) string { 192 + return fromSlash(path, getOS(os)) 193 + } 194 + 195 + func fromSlash(path string, os os) string { 196 + if os.Separator == '/' { 197 + return path 198 + } 199 + return strings.ReplaceAll(path, "/", string(os.Separator)) 200 + } 201 + 202 + // SplitList splits a list of paths joined by the OS-specific ListSeparator, 203 + // usually found in PATH or GOPATH environment variables. 204 + // Unlike strings.Split, SplitList returns an empty slice when passed an empty 205 + // string. 206 + func SplitList(path string, os OS) []string { 207 + return getOS(os).splitList(path) 208 + } 209 + 210 + // Split splits path immediately following the final slash and returns them as 211 + // the list [dir, file], separating it into a directory and file name component. 212 + // If there is no slash in path, Split returns an empty dir and file set to 213 + // path. The returned values have the property that path = dir+file. 214 + // The default value for os is Unix. 215 + func Split(path string, os OS) []string { 216 + x := getOS(os) 217 + vol := volumeName(path, x) 218 + i := len(path) - 1 219 + for i >= len(vol) && !x.IsPathSeparator(path[i]) { 220 + i-- 221 + } 222 + return []string{path[:i+1], path[i+1:]} 223 + } 71 224 72 - var pathClean = path.Clean 225 + // Join joins any number of path elements into a single path, 226 + // separating them with an OS specific Separator. Empty elements 227 + // are ignored. The result is Cleaned. However, if the argument 228 + // list is empty or all its elements are empty, Join returns 229 + // an empty string. 230 + // On Windows, the result will only be a UNC path if the first 231 + // non-empty element is a UNC path. 232 + // The default value for os is Unix. 233 + func Join(elem []string, os OS) string { 234 + return getOS(os).join(elem) 235 + } 73 236 74 237 // Ext returns the file name extension used by path. 75 238 // The extension is the suffix beginning at the final dot 76 - // in the final slash-separated element of path; 77 - // it is empty if there is no dot. 78 - func Ext(path string) string { return pathExt(path) } 239 + // in the final element of path; it is empty if there is 240 + // no dot. The default value for os is Unix. 241 + func Ext(path string, os OS) string { 242 + x := getOS(os) 243 + for i := len(path) - 1; i >= 0 && !x.IsPathSeparator(path[i]); i-- { 244 + if path[i] == '.' { 245 + return path[i:] 246 + } 247 + } 248 + return "" 249 + } 79 250 80 - var pathExt = path.Ext 251 + // Resolve reports the path of sub relative to dir. If sub is an absolute path, 252 + // or if dir is empty, it will return sub. If sub is empty, it will return dir. 253 + // Resolve calls Clean on the result. The default value for os is Unix. 254 + func Resolve(dir, sub string, os OS) string { 255 + x := getOS(os) 256 + if x.IsAbs(sub) { 257 + return clean(sub, x) 258 + } 259 + dir = clean(dir, x) 260 + return x.join([]string{dir, sub}) 261 + } 262 + 263 + // Rel returns a relative path that is lexically equivalent to targpath when 264 + // joined to basepath with an intervening separator. That is, 265 + // Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself. 266 + // On success, the returned path will always be relative to basepath, 267 + // even if basepath and targpath share no elements. 268 + // An error is returned if targpath can't be made relative to basepath or if 269 + // knowing the current working directory would be necessary to compute it. 270 + // Rel calls Clean on the result. The default value for os is Unix. 271 + func Rel(basepath, targpath string, os OS) (string, error) { 272 + x := getOS(os) 273 + baseVol := volumeName(basepath, x) 274 + targVol := volumeName(targpath, x) 275 + base := clean(basepath, x) 276 + targ := clean(targpath, x) 277 + if x.sameWord(targ, base) { 278 + return ".", nil 279 + } 280 + base = base[len(baseVol):] 281 + targ = targ[len(targVol):] 282 + if base == "." { 283 + base = "" 284 + } 285 + // Can't use IsAbs - `\a` and `a` are both relative in Windows. 286 + baseSlashed := len(base) > 0 && base[0] == x.Separator 287 + targSlashed := len(targ) > 0 && targ[0] == x.Separator 288 + if baseSlashed != targSlashed || !x.sameWord(baseVol, targVol) { 289 + return "", errors.New("Rel: can't make " + targpath + " relative to " + basepath) 290 + } 291 + // Position base[b0:bi] and targ[t0:ti] at the first differing elements. 292 + bl := len(base) 293 + tl := len(targ) 294 + var b0, bi, t0, ti int 295 + for { 296 + for bi < bl && base[bi] != x.Separator { 297 + bi++ 298 + } 299 + for ti < tl && targ[ti] != x.Separator { 300 + ti++ 301 + } 302 + if !x.sameWord(targ[t0:ti], base[b0:bi]) { 303 + break 304 + } 305 + if bi < bl { 306 + bi++ 307 + } 308 + if ti < tl { 309 + ti++ 310 + } 311 + b0 = bi 312 + t0 = ti 313 + } 314 + if base[b0:bi] == ".." { 315 + return "", errors.New("Rel: can't make " + targpath + " relative to " + basepath) 316 + } 317 + if b0 != bl { 318 + // Base elements left. Must go up before going down. 319 + seps := strings.Count(base[b0:bl], string(x.Separator)) 320 + size := 2 + seps*3 321 + if tl != t0 { 322 + size += 1 + tl - t0 323 + } 324 + buf := make([]byte, size) 325 + n := copy(buf, "..") 326 + for i := 0; i < seps; i++ { 327 + buf[n] = x.Separator 328 + copy(buf[n+1:], "..") 329 + n += 3 330 + } 331 + if t0 != tl { 332 + buf[n] = x.Separator 333 + copy(buf[n+1:], targ[t0:]) 334 + } 335 + return string(buf), nil 336 + } 337 + return targ[t0:], nil 338 + } 81 339 82 340 // Base returns the last element of path. 83 - // Trailing slashes are removed before extracting the last element. 341 + // Trailing path separators are removed before extracting the last element. 84 342 // If the path is empty, Base returns ".". 85 - // If the path consists entirely of slashes, Base returns "/". 86 - func Base(path string) string { return pathBase(path) } 87 - 88 - var pathBase = path.Base 89 - 90 - // IsAbs reports whether the path is absolute. 91 - func IsAbs(path string) bool { return pathIsAbs(path) } 92 - 93 - var pathIsAbs = path.IsAbs 343 + // If the path consists entirely of separators, Base returns a single separator. 344 + // The default value for os is Unix. 345 + func Base(path string, os OS) string { 346 + x := getOS(os) 347 + if path == "" { 348 + return "." 349 + } 350 + // Strip trailing slashes. 351 + for len(path) > 0 && x.IsPathSeparator(path[len(path)-1]) { 352 + path = path[0 : len(path)-1] 353 + } 354 + // Throw away volume name 355 + path = path[x.volumeNameLen(path):] 356 + // Find the last element 357 + i := len(path) - 1 358 + for i >= 0 && !x.IsPathSeparator(path[i]) { 359 + i-- 360 + } 361 + if i >= 0 { 362 + path = path[i+1:] 363 + } 364 + // If empty now, it had only slashes. 365 + if path == "" { 366 + return string(x.Separator) 367 + } 368 + return path 369 + } 94 370 95 371 // Dir returns all but the last element of path, typically the path's directory. 96 - // After dropping the final element using Split, the path is Cleaned and trailing 372 + // After dropping the final element, Dir calls Clean on the path and trailing 97 373 // slashes are removed. 98 374 // If the path is empty, Dir returns ".". 99 - // If the path consists entirely of slashes followed by non-slash bytes, Dir 100 - // returns a single slash. In any other case, the returned path does not end in a 101 - // slash. 102 - func Dir(path string) string { return pathDir(path) } 375 + // If the path consists entirely of separators, Dir returns a single separator. 376 + // The returned path does not end in a separator unless it is the root directory. 377 + // The default value for os is Unix. 378 + func Dir(path string, os OS) string { 379 + x := getOS(os) 380 + vol := volumeName(path, x) 381 + i := len(path) - 1 382 + for i >= len(vol) && !x.IsPathSeparator(path[i]) { 383 + i-- 384 + } 385 + dir := clean(path[len(vol):i+1], x) 386 + if dir == "." && len(vol) > 2 { 387 + // must be UNC 388 + return vol 389 + } 390 + return vol + dir 391 + } 103 392 104 - var pathDir = path.Dir 393 + // IsAbs reports whether the path is absolute. The default value for os is Unix. 394 + // Note that because IsAbs has a default value, it cannot be used as 395 + // a validator. 396 + func IsAbs(path string, os OS) bool { 397 + return getOS(os).IsAbs(path) 398 + } 399 + 400 + // VolumeName returns leading volume name. 401 + // Given "C:\foo\bar" it returns "C:" on Windows. 402 + // Given "\\host\share\foo" it returns "\\host\share". 403 + // On other platforms it returns "". 404 + // The default value for os is Windows. 405 + func VolumeName(path string, os OS) string { 406 + return volumeName(path, getOS(os)) 407 + } 408 + 409 + func volumeName(path string, os os) string { 410 + return path[:os.volumeNameLen(path)] 411 + }
+184 -20
pkg/path/pkg.go
··· 1 - // Code generated by go generate. DO NOT EDIT. 2 - 3 - //go:generate rm pkg.go 4 - //go:generate go run ../gen/gen.go 1 + // Copyright 2020 CUE Authors 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // http://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 5 14 6 15 package path 7 16 ··· 16 25 17 26 var _ = adt.TopKind // in case the adt package isn't used 18 27 28 + var ( 29 + osRequired = &adt.Disjunction{ 30 + Values: allOS, 31 + } 32 + 33 + unixDefault = &adt.Disjunction{ 34 + NumDefaults: 1, 35 + Values: allOS, 36 + } 37 + 38 + // windowsDefault is the default for VolumeName. 39 + windowsDefault = &adt.Disjunction{ 40 + NumDefaults: 1, 41 + Values: append([]*adt.Vertex{ 42 + newStr("windows"), 43 + newStr("unix"), 44 + newStr("plan9")}, unixOS...), 45 + } 46 + 47 + allOS = append([]*adt.Vertex{ 48 + newStr("unix"), 49 + newStr("windows"), 50 + newStr("plan9"), 51 + }, unixOS...) 52 + 53 + // These all fall back to unix 54 + unixOS = []*adt.Vertex{ 55 + newStr("aix"), 56 + newStr("android"), 57 + newStr("darwin"), 58 + newStr("dragonfly"), 59 + newStr("freebsd"), 60 + newStr("hurd"), 61 + newStr("illumos"), 62 + newStr("ios"), 63 + newStr("js"), 64 + newStr("linux"), 65 + newStr("nacl"), 66 + newStr("netbsd"), 67 + newStr("openbsd"), 68 + newStr("solaris"), 69 + newStr("zos"), 70 + } 71 + ) 72 + 73 + func newStr(s string) *adt.Vertex { 74 + v := &adt.Vertex{} 75 + v.SetValue(nil, adt.Finalized, &adt.String{Str: s}) 76 + return v 77 + } 78 + 19 79 var pkg = &internal.Package{ 80 + CUE: `{ 81 + Unix: "unix" 82 + Windows: "windows" 83 + Plan9: "plan9" 84 + }`, 20 85 Native: []*internal.Builtin{{ 21 86 Name: "Split", 22 87 Params: []internal.Param{ 23 88 {Kind: adt.StringKind}, 89 + {Kind: adt.StringKind, Value: unixDefault}, 24 90 }, 25 91 Result: adt.ListKind, 26 92 Func: func(c *internal.CallCtxt) { 27 - path := c.String(0) 93 + path, os := c.String(0), c.String(1) 28 94 if c.Do() { 29 - c.Ret = Split(path) 95 + c.Ret = Split(path, OS(os)) 30 96 } 31 97 }, 32 98 }, { 33 - Name: "Match", 99 + Name: "SplitList", 34 100 Params: []internal.Param{ 35 101 {Kind: adt.StringKind}, 102 + {Kind: adt.StringKind, Value: osRequired}, 103 + }, 104 + Result: adt.ListKind, 105 + Func: func(c *internal.CallCtxt) { 106 + path, os := c.String(0), c.String(1) 107 + if c.Do() { 108 + c.Ret = SplitList(path, OS(os)) 109 + } 110 + }, 111 + }, { 112 + Name: "Join", 113 + Params: []internal.Param{ 114 + {Kind: adt.ListKind}, 115 + {Kind: adt.StringKind, Value: unixDefault}, 116 + }, 117 + Result: adt.StringKind, 118 + Func: func(c *internal.CallCtxt) { 119 + list, os := c.StringList(0), c.String(1) 120 + if c.Do() { 121 + c.Ret = Join(list, OS(os)) 122 + } 123 + }, 124 + }, { 125 + Name: "Match", 126 + Params: []internal.Param{ 36 127 {Kind: adt.StringKind}, 128 + {Kind: adt.StringKind, Value: unixDefault}, 37 129 }, 38 130 Result: adt.BoolKind, 39 131 Func: func(c *internal.CallCtxt) { 40 - pattern, name := c.String(0), c.String(1) 132 + pattern, name, os := c.String(0), c.String(1), c.String(2) 41 133 if c.Do() { 42 - c.Ret, c.Err = Match(pattern, name) 134 + c.Ret, c.Err = Match(pattern, name, OS(os)) 43 135 } 44 136 }, 45 137 }, { 46 138 Name: "Clean", 47 139 Params: []internal.Param{ 48 140 {Kind: adt.StringKind}, 141 + {Kind: adt.StringKind, Value: unixDefault}, 49 142 }, 50 143 Result: adt.StringKind, 51 144 Func: func(c *internal.CallCtxt) { 52 - path := c.String(0) 145 + path, os := c.String(0), c.String(1) 146 + if c.Do() { 147 + c.Ret = Clean(path, OS(os)) 148 + } 149 + }, 150 + }, { 151 + Name: "ToSlash", 152 + Params: []internal.Param{ 153 + {Kind: adt.StringKind}, 154 + {Kind: adt.StringKind, Value: osRequired}, 155 + }, 156 + Result: adt.StringKind, 157 + Func: func(c *internal.CallCtxt) { 158 + path, os := c.String(0), c.String(1) 159 + if c.Do() { 160 + c.Ret = ToSlash(path, OS(os)) 161 + } 162 + }, 163 + }, { 164 + Name: "FromSlash", 165 + Params: []internal.Param{ 166 + {Kind: adt.StringKind}, 167 + {Kind: adt.StringKind, Value: osRequired}, 168 + }, 169 + Result: adt.StringKind, 170 + Func: func(c *internal.CallCtxt) { 171 + path, os := c.String(0), c.String(1) 53 172 if c.Do() { 54 - c.Ret = Clean(path) 173 + c.Ret = FromSlash(path, OS(os)) 55 174 } 56 175 }, 57 176 }, { 58 177 Name: "Ext", 59 178 Params: []internal.Param{ 60 179 {Kind: adt.StringKind}, 180 + {Kind: adt.StringKind, Value: unixDefault}, 61 181 }, 62 182 Result: adt.StringKind, 63 183 Func: func(c *internal.CallCtxt) { 64 - path := c.String(0) 184 + path, os := c.String(0), c.String(1) 65 185 if c.Do() { 66 - c.Ret = Ext(path) 186 + c.Ret = Ext(path, OS(os)) 187 + } 188 + }, 189 + }, { 190 + Name: "Resolve", 191 + Params: []internal.Param{ 192 + {Kind: adt.StringKind}, 193 + {Kind: adt.StringKind}, 194 + {Kind: adt.StringKind, Value: unixDefault}, 195 + }, 196 + Result: adt.StringKind, 197 + Func: func(c *internal.CallCtxt) { 198 + dir, sub, os := c.String(0), c.String(1), c.String(2) 199 + if c.Do() { 200 + c.Ret = Resolve(dir, sub, OS(os)) 201 + } 202 + }, 203 + }, { 204 + Name: "Rel", 205 + Params: []internal.Param{ 206 + {Kind: adt.StringKind}, 207 + {Kind: adt.StringKind}, 208 + {Kind: adt.StringKind, Value: unixDefault}, 209 + }, 210 + Result: adt.StringKind, 211 + Func: func(c *internal.CallCtxt) { 212 + base, target, os := c.String(0), c.String(1), c.String(2) 213 + if c.Do() { 214 + c.Ret, c.Err = Rel(base, target, OS(os)) 67 215 } 68 216 }, 69 217 }, { 70 218 Name: "Base", 71 219 Params: []internal.Param{ 72 220 {Kind: adt.StringKind}, 221 + {Kind: adt.StringKind, Value: unixDefault}, 73 222 }, 74 223 Result: adt.StringKind, 75 224 Func: func(c *internal.CallCtxt) { 76 - path := c.String(0) 225 + path, os := c.String(0), c.String(1) 226 + if c.Do() { 227 + c.Ret = Base(path, OS(os)) 228 + } 229 + }, 230 + }, { 231 + Name: "Dir", 232 + Params: []internal.Param{ 233 + {Kind: adt.StringKind}, 234 + {Kind: adt.StringKind, Value: unixDefault}, 235 + }, 236 + Result: adt.StringKind, 237 + Func: func(c *internal.CallCtxt) { 238 + path, os := c.String(0), c.String(1) 77 239 if c.Do() { 78 - c.Ret = Base(path) 240 + c.Ret = Dir(path, OS(os)) 79 241 } 80 242 }, 81 243 }, { 82 244 Name: "IsAbs", 83 245 Params: []internal.Param{ 84 246 {Kind: adt.StringKind}, 247 + {Kind: adt.StringKind, Value: unixDefault}, 85 248 }, 86 249 Result: adt.BoolKind, 87 250 Func: func(c *internal.CallCtxt) { 88 - path := c.String(0) 251 + path, os := c.String(0), c.String(1) 89 252 if c.Do() { 90 - c.Ret = IsAbs(path) 253 + c.Ret = IsAbs(path, OS(os)) 91 254 } 92 255 }, 93 256 }, { 94 - Name: "Dir", 257 + Name: "VolumeName", 95 258 Params: []internal.Param{ 96 259 {Kind: adt.StringKind}, 260 + {Kind: adt.StringKind, Value: windowsDefault}, 97 261 }, 98 262 Result: adt.StringKind, 99 263 Func: func(c *internal.CallCtxt) { 100 - path := c.String(0) 264 + path, os := c.String(0), c.String(1) 101 265 if c.Do() { 102 - c.Ret = Dir(path) 266 + c.Ret = VolumeName(path, OS(os)) 103 267 } 104 268 }, 105 269 }},
+28
pkg/path/testdata/error.txtar
··· 1 + // Copyright 2020 CUE Authors 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // http://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 14 + 15 + -- in.cue -- 16 + import "path" 17 + 18 + joinOK: path.Join(["a", "b"], "aix") 19 + joinErr: path.Join(["a", "b"], "foo") 20 + -- out/path -- 21 + Errors: 22 + joinErr: cannot use "foo" as *"unix" | "windows" | "plan9" | "aix" | "android" | "darwin" | "dragonfly" | "freebsd" | "hurd" | "illumos" | "ios" | "js" | "linux" | "nacl" | "netbsd" | "openbsd" | "solaris" | "zos" in argument 2 to path.Join: 23 + ./in.cue:4:32 24 + 25 + Result: 26 + joinOK: "a/b" 27 + joinErr: _|_ // joinErr: cannot use "foo" as *"unix" | "windows" | "plan9" | "aix" | "android" | "darwin" | "dragonfly" | "freebsd" | "hurd" | "illumos" | "ios" | "js" | "linux" | "nacl" | "netbsd" | "openbsd" | "solaris" | "zos" in argument 2 to path.Join (and 1 more errors) 28 +
pkg/path/testdata/example_nix_test.go pkg/path/example_nix_test.go
pkg/path/testdata/example_test.go pkg/path/example_test.go
+205
pkg/path/testdata/join.txtar
··· 1 + // Copyright 2020 CUE Authors 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // http://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 14 + 15 + 16 + -- in.cue -- 17 + import "path" 18 + 19 + joinSingle: path.Join(["a", "b"]) 20 + joinSingle: "a/b" 21 + 22 + Join: unix: _ 23 + Join: windows: _ 24 + 25 + Join: [OS=string]: [...{ 26 + arg: [...string] 27 + 28 + out: path.Join(arg, OS) 29 + }] 30 + 31 + Join: [_]: [ 32 + {arg: ["a", "b"]}, 33 + {arg: ["a/b", "c/d"]}, 34 + 35 + {arg: ["/"]}, 36 + {arg: ["a"]}, 37 + 38 + {arg: ["a", "b"]}, 39 + {arg: ["a", ""]}, 40 + {arg: ["", "b"]}, 41 + {arg: ["/", "a"]}, 42 + {arg: ["/", "a/b"]}, 43 + {arg: ["/", ""]}, 44 + {arg: ["//", "a"]}, 45 + 46 + {arg: ["directory", "file"]}, 47 + 48 + {arg: [#"C:\Windows\"#, #"System32"#]}, 49 + {arg: [#"C:\Windows\"#, #""#]}, 50 + {arg: [#"C:\"#, #"Windows"#]}, 51 + {arg: [#"C:"#, #"a\b"#]}, 52 + {arg: [#"C:"#, #"a"#, #"b"#]}, 53 + {arg: [#"C:"#, #""#, #""#, #"b"#]}, 54 + {arg: [#"C:"#, #""#]}, 55 + {arg: [#"C:"#, #""#, #""#]}, 56 + {arg: [#"C:."#, #"a"#]}, 57 + {arg: [#"C:a"#, #"b"#]}, 58 + {arg: [#"\\host\share"#, #"foo"#]}, 59 + ] 60 + 61 + -- out/path -- 62 + joinSingle: "a/b" 63 + Join: { 64 + unix: [{ 65 + arg: ["a", "b"] 66 + out: "a/b" 67 + }, { 68 + arg: ["a/b", "c/d"] 69 + out: "a/b/c/d" 70 + }, { 71 + arg: ["/"] 72 + out: "/" 73 + }, { 74 + arg: ["a"] 75 + out: "a" 76 + }, { 77 + arg: ["a", "b"] 78 + out: "a/b" 79 + }, { 80 + arg: ["a", ""] 81 + out: "a" 82 + }, { 83 + arg: ["", "b"] 84 + out: "b" 85 + }, { 86 + arg: ["/", "a"] 87 + out: "/a" 88 + }, { 89 + arg: ["/", "a/b"] 90 + out: "/a/b" 91 + }, { 92 + arg: ["/", ""] 93 + out: "/" 94 + }, { 95 + arg: ["//", "a"] 96 + out: "/a" 97 + }, { 98 + arg: ["directory", "file"] 99 + out: "directory/file" 100 + }, { 101 + arg: [#"C:\Windows\"#, #"System32"#] 102 + out: "C:\\Windows\\/System32" 103 + }, { 104 + arg: [#"C:\Windows\"#, #""#] 105 + out: "C:\\Windows\\" 106 + }, { 107 + arg: [#"C:\"#, #"Windows"#] 108 + out: "C:\\/Windows" 109 + }, { 110 + arg: [#"C:"#, #"a\b"#] 111 + out: "C:/a\\b" 112 + }, { 113 + arg: [#"C:"#, #"a"#, #"b"#] 114 + out: "C:/a/b" 115 + }, { 116 + arg: [#"C:"#, #""#, #""#, #"b"#] 117 + out: "C:/b" 118 + }, { 119 + arg: [#"C:"#, #""#] 120 + out: "C:" 121 + }, { 122 + arg: [#"C:"#, #""#, #""#] 123 + out: "C:" 124 + }, { 125 + arg: [#"C:."#, #"a"#] 126 + out: "C:./a" 127 + }, { 128 + arg: [#"C:a"#, #"b"#] 129 + out: "C:a/b" 130 + }, { 131 + arg: [#"\\host\share"#, #"foo"#] 132 + out: "\\\\host\\share/foo" 133 + }] 134 + windows: [{ 135 + arg: ["a", "b"] 136 + out: "a\\b" 137 + }, { 138 + arg: ["a/b", "c/d"] 139 + out: "a\\b\\c\\d" 140 + }, { 141 + arg: ["/"] 142 + out: "\\" 143 + }, { 144 + arg: ["a"] 145 + out: "a" 146 + }, { 147 + arg: ["a", "b"] 148 + out: "a\\b" 149 + }, { 150 + arg: ["a", ""] 151 + out: "a" 152 + }, { 153 + arg: ["", "b"] 154 + out: "b" 155 + }, { 156 + arg: ["/", "a"] 157 + out: "\\a" 158 + }, { 159 + arg: ["/", "a/b"] 160 + out: "\\a\\b" 161 + }, { 162 + arg: ["/", ""] 163 + out: "\\" 164 + }, { 165 + arg: ["//", "a"] 166 + out: "\\a" 167 + }, { 168 + arg: ["directory", "file"] 169 + out: "directory\\file" 170 + }, { 171 + arg: [#"C:\Windows\"#, #"System32"#] 172 + out: "C:\\Windows\\System32" 173 + }, { 174 + arg: [#"C:\Windows\"#, #""#] 175 + out: "C:\\Windows" 176 + }, { 177 + arg: [#"C:\"#, #"Windows"#] 178 + out: "C:\\Windows" 179 + }, { 180 + arg: [#"C:"#, #"a\b"#] 181 + out: "C:a\\b" 182 + }, { 183 + arg: [#"C:"#, #"a"#, #"b"#] 184 + out: "C:a\\b" 185 + }, { 186 + arg: [#"C:"#, #""#, #""#, #"b"#] 187 + out: "C:b" 188 + }, { 189 + arg: [#"C:"#, #""#] 190 + out: "C:." 191 + }, { 192 + arg: [#"C:"#, #""#, #""#] 193 + out: "C:." 194 + }, { 195 + arg: [#"C:."#, #"a"#] 196 + out: "C:a" 197 + }, { 198 + arg: [#"C:a"#, #"b"#] 199 + out: "C:a\\b" 200 + }, { 201 + arg: [#"\\host\share"#, #"foo"#] 202 + out: "\\\\host\\share\\foo" 203 + }] 204 + } 205 +
pkg/path/testdata/match.go pkg/path/match.go
pkg/path/testdata/match_test.go pkg/path/match_test.go
+1
pkg/path/testdata/os.go pkg/path/os.go
··· 14 14 15 15 package path 16 16 17 + // OS must be a valid runtime.GOOS value or "unix". 17 18 type OS string 18 19 19 20 const (
+504
pkg/path/testdata/os.txtar
··· 1 + // Copyright 2020 CUE Authors 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // http://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 14 + 15 + 16 + -- in.cue -- 17 + import "path" 18 + 19 + #OSes: [path.Unix, path.Windows, path.Plan9] 20 + #AnyOS: or(#OSes) 21 + 22 + // Test these OSes for all tests below. 23 + { 24 + [string]: { 25 + unix: _ 26 + plan9: _ 27 + windows: _ 28 + } 29 + } 30 + 31 + Split: [OS=#AnyOS]: [ARG=string]: path.Split(ARG, OS) 32 + Split: default: [ARG=string]: path.Split(ARG) 33 + Split: default: Split.unix 34 + Split: [_]: { 35 + "/foo/bar/baz": _ 36 + "a/b": _ 37 + "//host/share/foo": _ 38 + #"\\host\share\foo"#: _ 39 + "c:/foo/bar": _ 40 + #"c:\foo\bar"#: _ 41 + } 42 + 43 + SplitList: [OS=string]: [ARG=string]: path.SplitList(ARG, OS) 44 + SplitList: [_]: { 45 + "a:b": _ 46 + "a\u0000b": _ 47 + "a;b": _ 48 + } 49 + 50 + Clean: [OS=#AnyOS]: [ARG=string]: path.Clean(ARG, OS) 51 + Clean: default: [ARG=string]: path.Clean(ARG) 52 + Clean: default: Clean.unix 53 + Clean: [_]: { 54 + "abc//def//ghi": _ 55 + #"c:\abc\def\..\.."#: _ 56 + } 57 + 58 + Slash: [OS=string]: [ARG=string]: { 59 + to: path.ToSlash(ARG, OS) 60 + from: path.FromSlash(ARG, OS) 61 + 62 + // should roundtrip 63 + to: path.ToSlash(from, OS) 64 + from: path.FromSlash(to, OS) 65 + } 66 + Slash: [_]: { 67 + "": _ 68 + "/": _ 69 + "/a/b": _ 70 + "/a//b": _ 71 + } 72 + 73 + Ext: [OS=#AnyOS]: [ARG=string]: path.Ext(ARG, OS) 74 + Ext: default: [ARG=string]: path.Ext(ARG) 75 + Ext: default: Ext.unix 76 + Ext: [_]: { 77 + // Same for all OS-es 78 + "path.go": ".go" 79 + "path.pb.go": ".go" 80 + "a.dir/b": "" 81 + "a.dir/b.go": ".go" 82 + "a.dir/": "" 83 + 84 + // Differs on Windows. 85 + "a.dir\\foo": _ 86 + } 87 + 88 + Resolve: [OS=#AnyOS]: [A1=_]: [A2=_]: path.Resolve(A1, A2, OS) 89 + Resolve: default: [A1=_]: [A2=_]: path.Resolve(A1, A2) 90 + Resolve: default: Resolve.unix 91 + Resolve: [_]: { 92 + "a/b/c": "d/e": _ 93 + "/a/b": "/c/d": _ 94 + "c:/a": #"d:\"#: _ 95 + 96 + "//home/user/foo": "bar": _ 97 + "//home/user/foo": "//other/abs/foo": _ 98 + } 99 + 100 + IsAbs: [OS=#AnyOS]: [ARG=string]: path.IsAbs(ARG, OS) 101 + IsAbs: default: [ARG=string]: path.IsAbs(ARG) 102 + IsAbs: default: IsAbs.unix 103 + IsAbs: [_]: { 104 + "": _ 105 + "/a": _ 106 + "a": _ 107 + "c:": _ 108 + "c:/": _ 109 + "c:\\": _ 110 + 111 + "//home/user/foo": _ 112 + } 113 + 114 + 115 + Volume: [OS=string]: [ARG=string]: path.VolumeName(ARG, OS) 116 + Volume: [!="windows"]: [string]: "" // non-windows is always "" 117 + Volume: [_]: { 118 + "c:/foo/bar": _ 119 + "c:": _ 120 + "2:": _ 121 + "": _ 122 + 123 + #"\\\host"#: _ 124 + #"\\\host\"#: _ 125 + #"\\\host\share"#: _ 126 + #"\\\host\\share"#: _ 127 + #"\\host"#: _ 128 + #"//host"#: _ 129 + #"\\host\"#: _ 130 + #"//host/"#: _ 131 + #"\\host\share"#: _ 132 + #"//host/share"#: _ 133 + #"\\host\share\"#: _ 134 + #"//host/share/"#: _ 135 + #"\\host\share\foo"#: _ 136 + #"//host/share/foo"#: _ 137 + 138 + #"\\host\share\\foo\\\bar\\\\baz"#: _ 139 + #"//host/share//foo///bar////baz"#: _ 140 + #"\\host\share\foo\..\bar"#: _ 141 + #"//host/share/foo/../bar"#: _ 142 + } 143 + 144 + -- out/path -- 145 + #OSes: ["unix", "windows", "plan9"] 146 + #AnyOS: "unix" | "windows" | "plan9" 147 + Split: { 148 + unix: { 149 + "/foo/bar/baz": ["/foo/bar/", "baz"] 150 + "a/b": ["a/", "b"] 151 + "//host/share/foo": ["//host/share/", "foo"] 152 + "\\\\host\\share\\foo": ["", "\\\\host\\share\\foo"] 153 + "c:/foo/bar": ["c:/foo/", "bar"] 154 + "c:\\foo\\bar": ["", "c:\\foo\\bar"] 155 + } 156 + plan9: { 157 + "/foo/bar/baz": ["/foo/bar/", "baz"] 158 + "a/b": ["a/", "b"] 159 + "//host/share/foo": ["//host/share/", "foo"] 160 + "\\\\host\\share\\foo": ["", "\\\\host\\share\\foo"] 161 + "c:/foo/bar": ["c:/foo/", "bar"] 162 + "c:\\foo\\bar": ["", "c:\\foo\\bar"] 163 + } 164 + default: { 165 + "/foo/bar/baz": ["/foo/bar/", "baz"] 166 + "a/b": ["a/", "b"] 167 + "//host/share/foo": ["//host/share/", "foo"] 168 + "\\\\host\\share\\foo": ["", "\\\\host\\share\\foo"] 169 + "c:/foo/bar": ["c:/foo/", "bar"] 170 + "c:\\foo\\bar": ["", "c:\\foo\\bar"] 171 + } 172 + windows: { 173 + "/foo/bar/baz": ["/foo/bar/", "baz"] 174 + "a/b": ["a/", "b"] 175 + "//host/share/foo": ["//host/share/", "foo"] 176 + "\\\\host\\share\\foo": ["\\\\host\\share\\", "foo"] 177 + "c:/foo/bar": ["c:/foo/", "bar"] 178 + "c:\\foo\\bar": ["c:\\foo\\", "bar"] 179 + } 180 + } 181 + SplitList: { 182 + unix: { 183 + "a:b": ["a", "b"] 184 + "a\u0000b": ["a\u0000b"] 185 + "a;b": ["a;b"] 186 + } 187 + plan9: { 188 + "a:b": ["a:b"] 189 + "a\u0000b": ["a", "b"] 190 + "a;b": ["a;b"] 191 + } 192 + windows: { 193 + "a:b": ["a:b"] 194 + "a\u0000b": ["a\u0000b"] 195 + "a;b": ["a", "b"] 196 + } 197 + } 198 + Clean: { 199 + unix: { 200 + "abc//def//ghi": "abc/def/ghi" 201 + "c:\\abc\\def\\..\\..": "c:\\abc\\def\\..\\.." 202 + } 203 + plan9: { 204 + "abc//def//ghi": "abc/def/ghi" 205 + "c:\\abc\\def\\..\\..": "c:\\abc\\def\\..\\.." 206 + } 207 + default: { 208 + "abc//def//ghi": "abc/def/ghi" 209 + "c:\\abc\\def\\..\\..": "c:\\abc\\def\\..\\.." 210 + } 211 + windows: { 212 + "abc//def//ghi": "abc\\def\\ghi" 213 + "c:\\abc\\def\\..\\..": "c:\\" 214 + } 215 + } 216 + Slash: { 217 + unix: { 218 + "": { 219 + // should roundtrip 220 + to: "" 221 + from: "" 222 + } 223 + "/": { 224 + // should roundtrip 225 + to: "/" 226 + from: "/" 227 + } 228 + "/a/b": { 229 + // should roundtrip 230 + to: "/a/b" 231 + from: "/a/b" 232 + } 233 + "/a//b": { 234 + // should roundtrip 235 + to: "/a//b" 236 + from: "/a//b" 237 + } 238 + } 239 + plan9: { 240 + "": { 241 + // should roundtrip 242 + to: "" 243 + from: "" 244 + } 245 + "/": { 246 + // should roundtrip 247 + to: "/" 248 + from: "/" 249 + } 250 + "/a/b": { 251 + // should roundtrip 252 + to: "/a/b" 253 + from: "/a/b" 254 + } 255 + "/a//b": { 256 + // should roundtrip 257 + to: "/a//b" 258 + from: "/a//b" 259 + } 260 + } 261 + windows: { 262 + "": { 263 + // should roundtrip 264 + to: "" 265 + from: "" 266 + } 267 + "/": { 268 + // should roundtrip 269 + to: "/" 270 + from: "\\" 271 + } 272 + "/a/b": { 273 + // should roundtrip 274 + to: "/a/b" 275 + from: "\\a\\b" 276 + } 277 + "/a//b": { 278 + // should roundtrip 279 + to: "/a//b" 280 + from: "\\a\\\\b" 281 + } 282 + } 283 + } 284 + Ext: { 285 + unix: { 286 + // Same for all OS-es 287 + "path.go": ".go" 288 + "path.pb.go": ".go" 289 + "a.dir/b": "" 290 + "a.dir/b.go": ".go" 291 + "a.dir/": "" 292 + 293 + // Differs on Windows. 294 + "a.dir\\foo": ".dir\\foo" 295 + } 296 + plan9: { 297 + // Same for all OS-es 298 + "path.go": ".go" 299 + "path.pb.go": ".go" 300 + "a.dir/b": "" 301 + "a.dir/b.go": ".go" 302 + "a.dir/": "" 303 + 304 + // Differs on Windows. 305 + "a.dir\\foo": ".dir\\foo" 306 + } 307 + default: { 308 + // Same for all OS-es 309 + "path.go": ".go" 310 + "path.pb.go": ".go" 311 + "a.dir/b": "" 312 + "a.dir/b.go": ".go" 313 + "a.dir/": "" 314 + 315 + // Differs on Windows. 316 + "a.dir\\foo": ".dir\\foo" 317 + } 318 + windows: { 319 + // Same for all OS-es 320 + "path.go": ".go" 321 + "path.pb.go": ".go" 322 + "a.dir/b": "" 323 + "a.dir/b.go": ".go" 324 + "a.dir/": "" 325 + 326 + // Differs on Windows. 327 + "a.dir\\foo": "" 328 + } 329 + } 330 + Resolve: { 331 + unix: { 332 + "a/b/c": { 333 + "d/e": "a/b/c/d/e" 334 + } 335 + "/a/b": { 336 + "/c/d": "/c/d" 337 + } 338 + "c:/a": { 339 + "d:\\": "c:/a/d:\\" 340 + } 341 + "//home/user/foo": { 342 + bar: "/home/user/foo/bar" 343 + "//other/abs/foo": "/other/abs/foo" 344 + } 345 + } 346 + plan9: { 347 + "a/b/c": { 348 + "d/e": "a/b/c/d/e" 349 + } 350 + "/a/b": { 351 + "/c/d": "/c/d" 352 + } 353 + "c:/a": { 354 + "d:\\": "c:/a/d:\\" 355 + } 356 + "//home/user/foo": { 357 + bar: "/home/user/foo/bar" 358 + "//other/abs/foo": "/other/abs/foo" 359 + } 360 + } 361 + default: { 362 + "a/b/c": { 363 + "d/e": "a/b/c/d/e" 364 + } 365 + "/a/b": { 366 + "/c/d": "/c/d" 367 + } 368 + "c:/a": { 369 + "d:\\": "c:/a/d:\\" 370 + } 371 + "//home/user/foo": { 372 + bar: "/home/user/foo/bar" 373 + "//other/abs/foo": "/other/abs/foo" 374 + } 375 + } 376 + windows: { 377 + "a/b/c": { 378 + "d/e": "a\\b\\c\\d\\e" 379 + } 380 + "/a/b": { 381 + "/c/d": "\\a\\b\\c\\d" 382 + } 383 + "c:/a": { 384 + "d:\\": "d:\\" 385 + } 386 + "//home/user/foo": { 387 + bar: "\\\\home\\user\\foo\\bar" 388 + "//other/abs/foo": "\\\\other\\abs\\foo" 389 + } 390 + } 391 + } 392 + IsAbs: { 393 + unix: { 394 + "": false 395 + "/a": true 396 + a: false 397 + "c:": false 398 + "c:/": false 399 + "c:\\": false 400 + "//home/user/foo": true 401 + } 402 + plan9: { 403 + "": false 404 + "/a": true 405 + a: false 406 + "c:": false 407 + "c:/": false 408 + "c:\\": false 409 + "//home/user/foo": true 410 + } 411 + default: { 412 + "": false 413 + "/a": true 414 + a: false 415 + "c:": false 416 + "c:/": false 417 + "c:\\": false 418 + "//home/user/foo": true 419 + } 420 + windows: { 421 + "": false 422 + "/a": false 423 + a: false 424 + "c:": false 425 + "c:/": true 426 + "c:\\": true 427 + "//home/user/foo": true 428 + } 429 + } 430 + Volume: { 431 + unix: { 432 + "c:/foo/bar": "" 433 + "c:": "" 434 + "2:": "" 435 + "": "" 436 + "\\\\\\host": "" 437 + "\\\\\\host\\": "" 438 + "\\\\\\host\\share": "" 439 + "\\\\\\host\\\\share": "" 440 + "\\\\host": "" 441 + "//host": "" 442 + "\\\\host\\": "" 443 + "//host/": "" 444 + "\\\\host\\share": "" 445 + "//host/share": "" 446 + "\\\\host\\share\\": "" 447 + "//host/share/": "" 448 + "\\\\host\\share\\foo": "" 449 + "//host/share/foo": "" 450 + "\\\\host\\share\\\\foo\\\\\\bar\\\\\\\\baz": "" 451 + "//host/share//foo///bar////baz": "" 452 + "\\\\host\\share\\foo\\..\\bar": "" 453 + "//host/share/foo/../bar": "" 454 + } 455 + plan9: { 456 + "c:/foo/bar": "" 457 + "c:": "" 458 + "2:": "" 459 + "": "" 460 + "\\\\\\host": "" 461 + "\\\\\\host\\": "" 462 + "\\\\\\host\\share": "" 463 + "\\\\\\host\\\\share": "" 464 + "\\\\host": "" 465 + "//host": "" 466 + "\\\\host\\": "" 467 + "//host/": "" 468 + "\\\\host\\share": "" 469 + "//host/share": "" 470 + "\\\\host\\share\\": "" 471 + "//host/share/": "" 472 + "\\\\host\\share\\foo": "" 473 + "//host/share/foo": "" 474 + "\\\\host\\share\\\\foo\\\\\\bar\\\\\\\\baz": "" 475 + "//host/share//foo///bar////baz": "" 476 + "\\\\host\\share\\foo\\..\\bar": "" 477 + "//host/share/foo/../bar": "" 478 + } 479 + windows: { 480 + "c:/foo/bar": "c:" 481 + "c:": "c:" 482 + "2:": "" 483 + "": "" 484 + "\\\\\\host": "" 485 + "\\\\\\host\\": "" 486 + "\\\\\\host\\share": "" 487 + "\\\\\\host\\\\share": "" 488 + "\\\\host": "" 489 + "//host": "" 490 + "\\\\host\\": "" 491 + "//host/": "" 492 + "\\\\host\\share": "\\\\host\\share" 493 + "//host/share": "//host/share" 494 + "\\\\host\\share\\": "\\\\host\\share" 495 + "//host/share/": "//host/share" 496 + "\\\\host\\share\\foo": "\\\\host\\share" 497 + "//host/share/foo": "//host/share" 498 + "\\\\host\\share\\\\foo\\\\\\bar\\\\\\\\baz": "\\\\host\\share" 499 + "//host/share//foo///bar////baz": "//host/share" 500 + "\\\\host\\share\\foo\\..\\bar": "\\\\host\\share" 501 + "//host/share/foo/../bar": "//host/share" 502 + } 503 + } 504 +
-414
pkg/path/testdata/path.go
··· 1 - // Copyright 2020 CUE Authors 2 - // 3 - // Licensed under the Apache License, Version 2.0 (the "License"); 4 - // you may not use this file except in compliance with the License. 5 - // You may obtain a copy of the License at 6 - // 7 - // http://www.apache.org/licenses/LICENSE-2.0 8 - // 9 - // Unless required by applicable law or agreed to in writing, software 10 - // distributed under the License is distributed on an "AS IS" BASIS, 11 - // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 - // See the License for the specific language governing permissions and 13 - // limitations under the License. 14 - 15 - // Copyright 2009 The Go Authors. All rights reserved. 16 - // Use of this source code is governed by a BSD-style 17 - // license that can be found in the LICENSE file. 18 - 19 - // Package filepath implements utility routines for manipulating filename paths 20 - // in a way compatible with the target operating system-defined file paths. 21 - // 22 - // The filepath package uses either forward slashes or backslashes, 23 - // depending on the operating system. To process paths such as URLs 24 - // that always use forward slashes regardless of the operating 25 - // system, see the path package. 26 - package path 27 - 28 - import ( 29 - "errors" 30 - "strings" 31 - ) 32 - 33 - // A lazybuf is a lazily constructed path buffer. 34 - // It supports append, reading previously appended bytes, 35 - // and retrieving the final string. It does not allocate a buffer 36 - // to hold the output until that output diverges from s. 37 - type lazybuf struct { 38 - path string 39 - buf []byte 40 - w int 41 - volAndPath string 42 - volLen int 43 - } 44 - 45 - func (b *lazybuf) index(i int) byte { 46 - if b.buf != nil { 47 - return b.buf[i] 48 - } 49 - return b.path[i] 50 - } 51 - 52 - func (b *lazybuf) append(c byte) { 53 - if b.buf == nil { 54 - if b.w < len(b.path) && b.path[b.w] == c { 55 - b.w++ 56 - return 57 - } 58 - b.buf = make([]byte, len(b.path)) 59 - copy(b.buf, b.path[:b.w]) 60 - } 61 - b.buf[b.w] = c 62 - b.w++ 63 - } 64 - 65 - func (b *lazybuf) string() string { 66 - if b.buf == nil { 67 - return b.volAndPath[:b.volLen+b.w] 68 - } 69 - return b.volAndPath[:b.volLen] + string(b.buf[:b.w]) 70 - } 71 - 72 - // const ( 73 - // Separator = os.PathSeparator 74 - // ListSeparator = os.PathListSeparator 75 - // ) 76 - 77 - // Clean returns the shortest path name equivalent to path 78 - // by purely lexical processing. The default value for os is Unix. 79 - // It applies the following rules 80 - // iteratively until no further processing can be done: 81 - // 82 - // 1. Replace multiple Separator elements with a single one. 83 - // 2. Eliminate each . path name element (the current directory). 84 - // 3. Eliminate each inner .. path name element (the parent directory) 85 - // along with the non-.. element that precedes it. 86 - // 4. Eliminate .. elements that begin a rooted path: 87 - // that is, replace "/.." by "/" at the beginning of a path, 88 - // assuming Separator is '/'. 89 - // 90 - // The returned path ends in a slash only if it represents a root directory, 91 - // such as "/" on Unix or `C:\` on Windows. 92 - // 93 - // Finally, any occurrences of slash are replaced by Separator. 94 - // 95 - // If the result of this process is an empty string, Clean 96 - // returns the string ".". 97 - // 98 - // See also Rob Pike, ``Lexical File Names in Plan 9 or 99 - // Getting Dot-Dot Right,'' 100 - // https://9p.io/sys/doc/lexnames.html 101 - func Clean(path string, os OS) string { 102 - return clean(path, getOS(os)) 103 - } 104 - 105 - func clean(path string, os os) string { 106 - originalPath := path 107 - volLen := os.volumeNameLen(path) 108 - path = path[volLen:] 109 - if path == "" { 110 - if volLen > 1 && originalPath[1] != ':' { 111 - // should be UNC 112 - return fromSlash(originalPath, os) 113 - } 114 - return originalPath + "." 115 - } 116 - rooted := os.IsPathSeparator(path[0]) 117 - 118 - // Invariants: 119 - // reading from path; r is index of next byte to process. 120 - // writing to buf; w is index of next byte to write. 121 - // dotdot is index in buf where .. must stop, either because 122 - // it is the leading slash or it is a leading ../../.. prefix. 123 - n := len(path) 124 - out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen} 125 - r, dotdot := 0, 0 126 - if rooted { 127 - out.append(os.Separator) 128 - r, dotdot = 1, 1 129 - } 130 - 131 - for r < n { 132 - switch { 133 - case os.IsPathSeparator(path[r]): 134 - // empty path element 135 - r++ 136 - case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])): 137 - // . element 138 - r++ 139 - case path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])): 140 - // .. element: remove to last separator 141 - r += 2 142 - switch { 143 - case out.w > dotdot: 144 - // can backtrack 145 - out.w-- 146 - for out.w > dotdot && !os.IsPathSeparator(out.index(out.w)) { 147 - out.w-- 148 - } 149 - case !rooted: 150 - // cannot backtrack, but not rooted, so append .. element. 151 - if out.w > 0 { 152 - out.append(os.Separator) 153 - } 154 - out.append('.') 155 - out.append('.') 156 - dotdot = out.w 157 - } 158 - default: 159 - // real path element. 160 - // add slash if needed 161 - if rooted && out.w != 1 || !rooted && out.w != 0 { 162 - out.append(os.Separator) 163 - } 164 - // copy element 165 - for ; r < n && !os.IsPathSeparator(path[r]); r++ { 166 - out.append(path[r]) 167 - } 168 - } 169 - } 170 - 171 - // Turn empty string into "." 172 - if out.w == 0 { 173 - out.append('.') 174 - } 175 - 176 - return fromSlash(out.string(), os) 177 - } 178 - 179 - // ToSlash returns the result of replacing each separator character 180 - // in path with a slash ('/') character. Multiple separators are 181 - // replaced by multiple slashes. 182 - func ToSlash(path string, os OS) string { 183 - return toSlash(path, getOS(os)) 184 - } 185 - 186 - func toSlash(path string, os os) string { 187 - if os.Separator == '/' { 188 - return path 189 - } 190 - return strings.ReplaceAll(path, string(os.Separator), "/") 191 - } 192 - 193 - // FromSlash returns the result of replacing each slash ('/') character 194 - // in path with a separator character. Multiple slashes are replaced 195 - // by multiple separators. 196 - func FromSlash(path string, os OS) string { 197 - return fromSlash(path, getOS(os)) 198 - } 199 - 200 - func fromSlash(path string, os os) string { 201 - if os.Separator == '/' { 202 - return path 203 - } 204 - return strings.ReplaceAll(path, "/", string(os.Separator)) 205 - } 206 - 207 - // SplitList splits a list of paths joined by the OS-specific ListSeparator, 208 - // usually found in PATH or GOPATH environment variables. 209 - // Unlike strings.Split, SplitList returns an empty slice when passed an empty 210 - // string. 211 - func SplitList(path string, os OS) []string { 212 - return getOS(os).splitList(path) 213 - } 214 - 215 - // Split splits path immediately following the final slash and returns them as 216 - // the list [dir, file], separating it into a directory and file name component. 217 - // If there is no slash in path, Split returns an empty dir and file set to 218 - // path. The returned values have the property that path = dir+file. 219 - // The default value for os is Unix. 220 - func Split(path string, os OS) []string { 221 - x := getOS(os) 222 - vol := volumeName(path, x) 223 - i := len(path) - 1 224 - for i >= len(vol) && !x.IsPathSeparator(path[i]) { 225 - i-- 226 - } 227 - return []string{path[:i+1], path[i+1:]} 228 - } 229 - 230 - // Join joins any number of path elements into a single path, 231 - // separating them with an OS specific Separator. Empty elements 232 - // are ignored. The result is Cleaned. However, if the argument 233 - // list is empty or all its elements are empty, Join returns 234 - // an empty string. 235 - // On Windows, the result will only be a UNC path if the first 236 - // non-empty element is a UNC path. 237 - // The default value for os is Unix. 238 - func Join(elem []string, os OS) string { 239 - return getOS(os).join(elem) 240 - } 241 - 242 - // Ext returns the file name extension used by path. 243 - // The extension is the suffix beginning at the final dot 244 - // in the final element of path; it is empty if there is 245 - // no dot. The default value for os is Unix. 246 - func Ext(path string, os OS) string { 247 - x := getOS(os) 248 - for i := len(path) - 1; i >= 0 && !x.IsPathSeparator(path[i]); i-- { 249 - if path[i] == '.' { 250 - return path[i:] 251 - } 252 - } 253 - return "" 254 - } 255 - 256 - // Resolve reports the path of sub relative to dir. If sub is an absolute path, 257 - // or if dir is empty, it will return sub. If sub is empty, it will return dir. 258 - // Resolve calls Clean on the result. The default value for os is Unix. 259 - func Resolve(dir, sub string, os OS) string { 260 - x := getOS(os) 261 - if x.IsAbs(sub) { 262 - return clean(sub, x) 263 - } 264 - dir = clean(dir, x) 265 - return x.join([]string{dir, sub}) 266 - } 267 - 268 - // Rel returns a relative path that is lexically equivalent to targpath when 269 - // joined to basepath with an intervening separator. That is, 270 - // Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself. 271 - // On success, the returned path will always be relative to basepath, 272 - // even if basepath and targpath share no elements. 273 - // An error is returned if targpath can't be made relative to basepath or if 274 - // knowing the current working directory would be necessary to compute it. 275 - // Rel calls Clean on the result. The default value for os is Unix. 276 - func Rel(basepath, targpath string, os OS) (string, error) { 277 - x := getOS(os) 278 - baseVol := volumeName(basepath, x) 279 - targVol := volumeName(targpath, x) 280 - base := clean(basepath, x) 281 - targ := clean(targpath, x) 282 - if x.sameWord(targ, base) { 283 - return ".", nil 284 - } 285 - base = base[len(baseVol):] 286 - targ = targ[len(targVol):] 287 - if base == "." { 288 - base = "" 289 - } 290 - // Can't use IsAbs - `\a` and `a` are both relative in Windows. 291 - baseSlashed := len(base) > 0 && base[0] == x.Separator 292 - targSlashed := len(targ) > 0 && targ[0] == x.Separator 293 - if baseSlashed != targSlashed || !x.sameWord(baseVol, targVol) { 294 - return "", errors.New("Rel: can't make " + targpath + " relative to " + basepath) 295 - } 296 - // Position base[b0:bi] and targ[t0:ti] at the first differing elements. 297 - bl := len(base) 298 - tl := len(targ) 299 - var b0, bi, t0, ti int 300 - for { 301 - for bi < bl && base[bi] != x.Separator { 302 - bi++ 303 - } 304 - for ti < tl && targ[ti] != x.Separator { 305 - ti++ 306 - } 307 - if !x.sameWord(targ[t0:ti], base[b0:bi]) { 308 - break 309 - } 310 - if bi < bl { 311 - bi++ 312 - } 313 - if ti < tl { 314 - ti++ 315 - } 316 - b0 = bi 317 - t0 = ti 318 - } 319 - if base[b0:bi] == ".." { 320 - return "", errors.New("Rel: can't make " + targpath + " relative to " + basepath) 321 - } 322 - if b0 != bl { 323 - // Base elements left. Must go up before going down. 324 - seps := strings.Count(base[b0:bl], string(x.Separator)) 325 - size := 2 + seps*3 326 - if tl != t0 { 327 - size += 1 + tl - t0 328 - } 329 - buf := make([]byte, size) 330 - n := copy(buf, "..") 331 - for i := 0; i < seps; i++ { 332 - buf[n] = x.Separator 333 - copy(buf[n+1:], "..") 334 - n += 3 335 - } 336 - if t0 != tl { 337 - buf[n] = x.Separator 338 - copy(buf[n+1:], targ[t0:]) 339 - } 340 - return string(buf), nil 341 - } 342 - return targ[t0:], nil 343 - } 344 - 345 - // Base returns the last element of path. 346 - // Trailing path separators are removed before extracting the last element. 347 - // If the path is empty, Base returns ".". 348 - // If the path consists entirely of separators, Base returns a single separator. 349 - // The default value for os is Unix. 350 - func Base(path string, os OS) string { 351 - x := getOS(os) 352 - if path == "" { 353 - return "." 354 - } 355 - // Strip trailing slashes. 356 - for len(path) > 0 && x.IsPathSeparator(path[len(path)-1]) { 357 - path = path[0 : len(path)-1] 358 - } 359 - // Throw away volume name 360 - path = path[x.volumeNameLen(path):] 361 - // Find the last element 362 - i := len(path) - 1 363 - for i >= 0 && !x.IsPathSeparator(path[i]) { 364 - i-- 365 - } 366 - if i >= 0 { 367 - path = path[i+1:] 368 - } 369 - // If empty now, it had only slashes. 370 - if path == "" { 371 - return string(x.Separator) 372 - } 373 - return path 374 - } 375 - 376 - // Dir returns all but the last element of path, typically the path's directory. 377 - // After dropping the final element, Dir calls Clean on the path and trailing 378 - // slashes are removed. 379 - // If the path is empty, Dir returns ".". 380 - // If the path consists entirely of separators, Dir returns a single separator. 381 - // The returned path does not end in a separator unless it is the root directory. 382 - // The default value for os is Unix. 383 - func Dir(path string, os OS) string { 384 - x := getOS(os) 385 - vol := volumeName(path, x) 386 - i := len(path) - 1 387 - for i >= len(vol) && !x.IsPathSeparator(path[i]) { 388 - i-- 389 - } 390 - dir := clean(path[len(vol):i+1], x) 391 - if dir == "." && len(vol) > 2 { 392 - // must be UNC 393 - return vol 394 - } 395 - return vol + dir 396 - } 397 - 398 - // IsAbs reports whether the path is absolute. The default value for os is Unix. 399 - func IsAbs(path string, os OS) bool { 400 - return getOS(os).IsAbs(path) 401 - } 402 - 403 - // VolumeName returns leading volume name. 404 - // Given "C:\foo\bar" it returns "C:" on Windows. 405 - // Given "\\host\share\foo" it returns "\\host\share". 406 - // On other platforms it returns "". 407 - // The default value for os is Windows. 408 - func VolumeName(path string, os OS) string { 409 - return volumeName(path, getOS(os)) 410 - } 411 - 412 - func volumeName(path string, os os) string { 413 - return path[:os.volumeNameLen(path)] 414 - }
pkg/path/testdata/path_nix.go pkg/path/path_nix.go
pkg/path/testdata/path_p9.go pkg/path/path_p9.go
pkg/path/testdata/path_test.go pkg/path/path_test.go
pkg/path/testdata/path_win.go pkg/path/path_win.go
pkg/path/testdata/path_windows_test.go pkg/path/path_windows_test.go
pkg/path/testdata/pathtxtar_test.go pkg/path/pathtxtar_test.go