diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-10-26 23:57:58 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-10-26 23:57:58 +0000 |
commit | d8f412571f8768df2d3239e72392dfeabbad1559 (patch) | |
tree | 19d182df05ead7ff8ba7ee00a7d57555e1383fdf /libgo/go/path | |
parent | e0c39d66d4f0607177b1cf8995dda56a667e07b3 (diff) | |
download | gcc-d8f412571f8768df2d3239e72392dfeabbad1559.zip gcc-d8f412571f8768df2d3239e72392dfeabbad1559.tar.gz gcc-d8f412571f8768df2d3239e72392dfeabbad1559.tar.bz2 |
Update Go library to last weekly.
From-SVN: r180552
Diffstat (limited to 'libgo/go/path')
-rw-r--r-- | libgo/go/path/filepath/match.go | 2 | ||||
-rw-r--r-- | libgo/go/path/filepath/match_test.go | 10 | ||||
-rw-r--r-- | libgo/go/path/filepath/path.go | 154 | ||||
-rw-r--r-- | libgo/go/path/filepath/path_test.go | 264 | ||||
-rw-r--r-- | libgo/go/path/filepath/path_unix.go | 2 | ||||
-rw-r--r-- | libgo/go/path/filepath/path_windows.go | 35 |
6 files changed, 369 insertions, 98 deletions
diff --git a/libgo/go/path/filepath/match.go b/libgo/go/path/filepath/match.go index 7fcc214..0ccc87e 100644 --- a/libgo/go/path/filepath/match.go +++ b/libgo/go/path/filepath/match.go @@ -215,7 +215,7 @@ func getEsc(chunk string) (r int, nchunk string, err os.Error) { func Glob(pattern string) (matches []string, err os.Error) { if !hasMeta(pattern) { if _, err = os.Stat(pattern); err != nil { - return + return nil, nil } return []string{pattern}, nil } diff --git a/libgo/go/path/filepath/match_test.go b/libgo/go/path/filepath/match_test.go index 5fc7b9c..fa7aad9 100644 --- a/libgo/go/path/filepath/match_test.go +++ b/libgo/go/path/filepath/match_test.go @@ -125,6 +125,16 @@ func TestGlob(t *testing.T) { t.Errorf("Glob(%#q) = %#v want %v", tt.pattern, matches, tt.result) } } + for _, pattern := range []string{"no_match", "../*/no_match"} { + matches, err := Glob(pattern) + if err != nil { + t.Errorf("Glob error for %q: %s", pattern, err) + continue + } + if len(matches) != 0 { + t.Errorf("Glob(%#q) = %#v want []", pattern, matches) + } + } } func TestGlobError(t *testing.T) { diff --git a/libgo/go/path/filepath/path.go b/libgo/go/path/filepath/path.go index 3d5b915..afb8f10 100644 --- a/libgo/go/path/filepath/path.go +++ b/libgo/go/path/filepath/path.go @@ -41,9 +41,12 @@ func Clean(path string) string { vol := VolumeName(path) path = path[len(vol):] if path == "" { + if len(vol) > 1 && vol[1] != ':' { + // should be UNC + return FromSlash(vol) + } return vol + "." } - rooted := os.IsPathSeparator(path[0]) // Invariants: @@ -144,8 +147,9 @@ func SplitList(path string) []string { // If there is no Separator in path, Split returns an empty dir // and file set to path. func Split(path string) (dir, file string) { + vol := VolumeName(path) i := len(path) - 1 - for i >= 0 && !os.IsPathSeparator(path[i]) { + for i >= len(vol) && !os.IsPathSeparator(path[i]) { i-- } return path[:i+1], path[i+1:] @@ -254,38 +258,132 @@ func Abs(path string) (string, os.Error) { return Join(wd, path), nil } -// Visitor methods are invoked for corresponding file tree entries -// visited by Walk. The parameter path is the full path of f relative -// to root. -type Visitor interface { - VisitDir(path string, f *os.FileInfo) bool - VisitFile(path string, f *os.FileInfo) +// Rel returns a relative path that is lexically equivalent to targpath when +// joined to basepath with an intervening separator. That is, +// Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself. +// An error is returned if targpath can't be made relative to basepath or if +// knowing the current working directory would be necessary to compute it. +func Rel(basepath, targpath string) (string, os.Error) { + baseVol := VolumeName(basepath) + targVol := VolumeName(targpath) + base := Clean(basepath) + targ := Clean(targpath) + if targ == base { + return ".", nil + } + base = base[len(baseVol):] + targ = targ[len(targVol):] + if base == "." { + base = "" + } + // Can't use IsAbs - `\a` and `a` are both relative in Windows. + baseSlashed := len(base) > 0 && base[0] == Separator + targSlashed := len(targ) > 0 && targ[0] == Separator + if baseSlashed != targSlashed || baseVol != targVol { + return "", os.NewError("Rel: can't make " + targ + " relative to " + base) + } + // Position base[b0:bi] and targ[t0:ti] at the first differing elements. + bl := len(base) + tl := len(targ) + var b0, bi, t0, ti int + for { + for bi < bl && base[bi] != Separator { + bi++ + } + for ti < tl && targ[ti] != Separator { + ti++ + } + if targ[t0:ti] != base[b0:bi] { + break + } + if bi < bl { + bi++ + } + if ti < tl { + ti++ + } + b0 = bi + t0 = ti + } + if base[b0:bi] == ".." { + return "", os.NewError("Rel: can't make " + targ + " relative to " + base) + } + if b0 != bl { + // Base elements left. Must go up before going down. + seps := strings.Count(base[b0:bl], string(Separator)) + buf := make([]byte, 3+seps*3+tl-t0) + n := copy(buf, "..") + for i := 0; i < seps; i++ { + buf[n] = Separator + copy(buf[n+1:], "..") + n += 3 + } + if t0 != tl { + buf[n] = Separator + copy(buf[n+1:], targ[t0:]) + } + return string(buf), nil + } + return targ[t0:], nil } -func walk(path string, f *os.FileInfo, v Visitor, errors chan<- os.Error) { - if !f.IsDirectory() { - v.VisitFile(path, f) - return +// SkipDir is used as a return value from WalkFuncs to indicate that +// the directory named in the call is to be skipped. It is not returned +// as an error by any function. +var SkipDir = os.NewError("skip this directory") + +// WalkFunc is the type of the function called for each file or directory +// visited by Walk. If there was a problem walking to the file or directory +// named by path, the incoming error will describe the problem and the +// function can decide how to handle that error (and Walk will not descend +// into that directory). If an error is returned, processing stops. The +// sole exception is that if path is a directory and the function returns the +// special value SkipDir, the contents of the directory are skipped +// and processing continues as usual on the next file. +type WalkFunc func(path string, info *os.FileInfo, err os.Error) os.Error + +// walk recursively descends path, calling w. +func walk(path string, info *os.FileInfo, walkFn WalkFunc) os.Error { + err := walkFn(path, info, nil) + if err != nil { + if info.IsDirectory() && err == SkipDir { + return nil + } + return err } - if !v.VisitDir(path, f) { - return // skip directory entries + if !info.IsDirectory() { + return nil } list, err := readDir(path) if err != nil { - if errors != nil { - errors <- err + return walkFn(path, info, err) + } + + for _, fileInfo := range list { + if err = walk(Join(path, fileInfo.Name), fileInfo, walkFn); err != nil { + return err } } + return nil +} - for _, e := range list { - walk(Join(path, e.Name), e, v, errors) +// Walk walks the file tree rooted at root, calling walkFn for each file or +// directory in the tree, including root. All errors that arise visiting files +// and directories are filtered by walkFn. The files are walked in lexical +// order, which makes the output deterministic but means that for very +// large directories Walk can be inefficient. +func Walk(root string, walkFn WalkFunc) os.Error { + info, err := os.Lstat(root) + if err != nil { + return walkFn(root, nil, err) } + return walk(root, info, walkFn) } // readDir reads the directory named by dirname and returns -// a list of sorted directory entries. +// a sorted list of directory entries. // Copied from io/ioutil to avoid the circular import. func readDir(dirname string) ([]*os.FileInfo, os.Error) { f, err := os.Open(dirname) @@ -312,24 +410,6 @@ func (f fileInfoList) Len() int { return len(f) } func (f fileInfoList) Less(i, j int) bool { return f[i].Name < f[j].Name } func (f fileInfoList) Swap(i, j int) { f[i], f[j] = f[j], f[i] } -// Walk walks the file tree rooted at root, calling v.VisitDir or -// v.VisitFile for each directory or file in the tree, including root. -// If v.VisitDir returns false, Walk skips the directory's entries; -// otherwise it invokes itself for each directory entry in sorted order. -// An error reading a directory does not abort the Walk. -// If errors != nil, Walk sends each directory read error -// to the channel. Otherwise Walk discards the error. -func Walk(root string, v Visitor, errors chan<- os.Error) { - f, err := os.Lstat(root) - if err != nil { - if errors != nil { - errors <- err - } - return // can't progress - } - walk(root, f, v, errors) -} - // Base returns the last element of path. // Trailing path separators are removed before extracting the last element. // If the path is empty, Base returns ".". diff --git a/libgo/go/path/filepath/path_test.go b/libgo/go/path/filepath/path_test.go index e944bf4..f8e055b 100644 --- a/libgo/go/path/filepath/path_test.go +++ b/libgo/go/path/filepath/path_test.go @@ -72,15 +72,23 @@ var wincleantests = []PathTest{ {`c:\abc`, `c:\abc`}, {`c:abc\..\..\.\.\..\def`, `c:..\..\def`}, {`c:\abc\def\..\..`, `c:\`}, + {`c:\..\abc`, `c:\abc`}, {`c:..\abc`, `c:..\abc`}, {`\`, `\`}, {`/`, `\`}, + {`\\i\..\c$`, `\c$`}, + {`\\i\..\i\c$`, `\i\c$`}, + {`\\i\..\I\c$`, `\I\c$`}, + {`\\host\share\foo\..\bar`, `\\host\share\bar`}, + {`//host/share/foo/../baz`, `\\host\share\baz`}, + {`\\a\b\..\c`, `\\a\b\c`}, + {`\\a\b`, `\\a\b`}, } func TestClean(t *testing.T) { tests := cleantests if runtime.GOOS == "windows" { - for i, _ := range tests { + for i := range tests { tests[i].result = filepath.FromSlash(tests[i].result) } tests = append(tests, wincleantests...) @@ -145,9 +153,25 @@ var unixsplittests = []SplitTest{ {"/", "/", ""}, } +var winsplittests = []SplitTest{ + {`c:`, `c:`, ``}, + {`c:/`, `c:/`, ``}, + {`c:/foo`, `c:/`, `foo`}, + {`c:/foo/bar`, `c:/foo/`, `bar`}, + {`//host/share`, `//host/share`, ``}, + {`//host/share/`, `//host/share/`, ``}, + {`//host/share/foo`, `//host/share/`, `foo`}, + {`\\host\share`, `\\host\share`, ``}, + {`\\host\share\`, `\\host\share\`, ``}, + {`\\host\share\foo`, `\\host\share\`, `foo`}, +} + func TestSplit(t *testing.T) { var splittests []SplitTest splittests = unixsplittests + if runtime.GOOS == "windows" { + splittests = append(splittests, winsplittests...) + } for _, test := range splittests { if d, f := filepath.Split(test.path); d != test.dir || f != test.file { t.Errorf("Split(%q) = %q, %q, want %q, %q", test.path, d, f, test.dir, test.file) @@ -185,6 +209,8 @@ var winjointests = []JoinTest{ {[]string{`C:\Windows\`, ``}, `C:\Windows`}, {[]string{`C:\`, `Windows`}, `C:\Windows`}, {[]string{`C:`, `Windows`}, `C:\Windows`}, + {[]string{`\\host\share`, `foo`}, `\\host\share\foo`}, + {[]string{`//host/share`, `foo/bar`}, `\\host\share\foo\bar`}, } // join takes a []string and passes it to Join. @@ -279,9 +305,9 @@ func makeTree(t *testing.T) { func markTree(n *Node) { walkTree(n, "", func(path string, n *Node) { n.mark++ }) } -func checkMarks(t *testing.T) { +func checkMarks(t *testing.T, report bool) { walkTree(tree, tree.name, func(path string, n *Node) { - if n.mark != 1 { + if n.mark != 1 && report { t.Errorf("node %s mark = %d; expected 1", path, n.mark) } n.mark = 0 @@ -289,44 +315,41 @@ func checkMarks(t *testing.T) { } // Assumes that each node name is unique. Good enough for a test. -func mark(name string) { - name = filepath.ToSlash(name) +// If clear is true, any incoming error is cleared before return. The errors +// are always accumulated, though. +func mark(path string, info *os.FileInfo, err os.Error, errors *[]os.Error, clear bool) os.Error { + if err != nil { + *errors = append(*errors, err) + if clear { + return nil + } + return err + } walkTree(tree, tree.name, func(path string, n *Node) { - if n.name == name { + if n.name == info.Name { n.mark++ } }) -} - -type TestVisitor struct{} - -func (v *TestVisitor) VisitDir(path string, f *os.FileInfo) bool { - mark(f.Name) - return true -} - -func (v *TestVisitor) VisitFile(path string, f *os.FileInfo) { - mark(f.Name) + return nil } func TestWalk(t *testing.T) { makeTree(t) - - // 1) ignore error handling, expect none - v := &TestVisitor{} - filepath.Walk(tree.name, v, nil) - checkMarks(t) - - // 2) handle errors, expect none - errors := make(chan os.Error, 64) - filepath.Walk(tree.name, v, errors) - select { - case err := <-errors: + errors := make([]os.Error, 0, 10) + clear := true + markFn := func(path string, info *os.FileInfo, err os.Error) os.Error { + return mark(path, info, err, &errors, clear) + } + // Expect no errors. + err := filepath.Walk(tree.name, markFn) + if err != nil { t.Errorf("no error expected, found: %s", err) - default: - // ok } - checkMarks(t) + if len(errors) != 0 { + t.Errorf("unexpected errors: %s", errors) + } + checkMarks(t, true) + errors = errors[0:0] // Test permission errors. Only possible if we're not root // and only on some file systems (AFS, FAT). To avoid errors during @@ -335,40 +358,50 @@ func TestWalk(t *testing.T) { // introduce 2 errors: chmod top-level directories to 0 os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0) os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0) + + // 3) capture errors, expect two. // mark respective subtrees manually markTree(tree.entries[1]) markTree(tree.entries[3]) // correct double-marking of directory itself tree.entries[1].mark-- tree.entries[3].mark-- + err := filepath.Walk(tree.name, markFn) + if err != nil { + t.Errorf("expected no error return from Walk, %s", err) + } + if len(errors) != 2 { + t.Errorf("expected 2 errors, got %d: %s", len(errors), errors) + } + // the inaccessible subtrees were marked manually + checkMarks(t, true) + errors = errors[0:0] - // 3) handle errors, expect two - errors = make(chan os.Error, 64) - os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0) - filepath.Walk(tree.name, v, errors) - Loop: - for i := 1; i <= 2; i++ { - select { - case <-errors: - // ok - default: - t.Errorf("%d. error expected, none found", i) - break Loop - } + // 4) capture errors, stop after first error. + // mark respective subtrees manually + markTree(tree.entries[1]) + markTree(tree.entries[3]) + // correct double-marking of directory itself + tree.entries[1].mark-- + tree.entries[3].mark-- + clear = false // error will stop processing + err = filepath.Walk(tree.name, markFn) + if err == nil { + t.Errorf("expected error return from Walk") } - select { - case err := <-errors: - t.Errorf("only two errors expected, found 3rd: %v", err) - default: - // ok + if len(errors) != 1 { + t.Errorf("expected 1 error, got %d: %s", len(errors), errors) } // the inaccessible subtrees were marked manually - checkMarks(t) + checkMarks(t, false) + errors = errors[0:0] + + // restore permissions + os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770) + os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770) } // cleanup - os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770) - os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770) if err := os.RemoveAll(tree.name); err != nil { t.Errorf("removeTree: %v", err) } @@ -421,6 +454,8 @@ var winisabstests = []IsAbsTest{ {`\`, false}, {`\Windows`, false}, {`c:a\b`, false}, + {`\\host\share\foo`, true}, + {`//host/share/foo/bar`, true}, } func TestIsAbs(t *testing.T) { @@ -540,10 +575,11 @@ var abstests = []string{ "pkg/../../AUTHORS", "Make.pkg", "pkg/Makefile", - - // Already absolute + ".", "$GOROOT/src/Make.pkg", "$GOROOT/src/../src/Make.pkg", + "$GOROOT/misc/cgo", + "$GOROOT", } func TestAbs(t *testing.T) { @@ -557,14 +593,15 @@ func TestAbs(t *testing.T) { os.Chdir(cwd) for _, path := range abstests { path = strings.Replace(path, "$GOROOT", goroot, -1) - abspath, err := filepath.Abs(path) - if err != nil { - t.Errorf("Abs(%q) error: %v", path, err) - } info, err := os.Stat(path) if err != nil { t.Errorf("%s: %s", path, err) } + + abspath, err := filepath.Abs(path) + if err != nil { + t.Errorf("Abs(%q) error: %v", path, err) + } absinfo, err := os.Stat(abspath) if err != nil || absinfo.Ino != info.Ino { t.Errorf("Abs(%q)=%q, not the same file", path, abspath) @@ -579,3 +616,114 @@ func TestAbs(t *testing.T) { } */ + +type RelTests struct { + root, path, want string +} + +var reltests = []RelTests{ + {"a/b", "a/b", "."}, + {"a/b/.", "a/b", "."}, + {"a/b", "a/b/.", "."}, + {"./a/b", "a/b", "."}, + {"a/b", "./a/b", "."}, + {"ab/cd", "ab/cde", "../cde"}, + {"ab/cd", "ab/c", "../c"}, + {"a/b", "a/b/c/d", "c/d"}, + {"a/b", "a/b/../c", "../c"}, + {"a/b/../c", "a/b", "../b"}, + {"a/b/c", "a/c/d", "../../c/d"}, + {"a/b", "c/d", "../../c/d"}, + {"../../a/b", "../../a/b/c/d", "c/d"}, + {"/a/b", "/a/b", "."}, + {"/a/b/.", "/a/b", "."}, + {"/a/b", "/a/b/.", "."}, + {"/ab/cd", "/ab/cde", "../cde"}, + {"/ab/cd", "/ab/c", "../c"}, + {"/a/b", "/a/b/c/d", "c/d"}, + {"/a/b", "/a/b/../c", "../c"}, + {"/a/b/../c", "/a/b", "../b"}, + {"/a/b/c", "/a/c/d", "../../c/d"}, + {"/a/b", "/c/d", "../../c/d"}, + {"/../../a/b", "/../../a/b/c/d", "c/d"}, + {".", "a/b", "a/b"}, + {".", "..", ".."}, + + // can't do purely lexically + {"..", ".", "err"}, + {"..", "a", "err"}, + {"../..", "..", "err"}, + {"a", "/a", "err"}, + {"/a", "a", "err"}, +} + +var winreltests = []RelTests{ + {`C:a\b\c`, `C:a/b/d`, `..\d`}, + {`C:\`, `D:\`, `err`}, + {`C:`, `D:`, `err`}, +} + +func TestRel(t *testing.T) { + tests := append([]RelTests{}, reltests...) + if runtime.GOOS == "windows" { + for i := range tests { + tests[i].want = filepath.FromSlash(tests[i].want) + } + tests = append(tests, winreltests...) + } + for _, test := range tests { + got, err := filepath.Rel(test.root, test.path) + if test.want == "err" { + if err == nil { + t.Errorf("Rel(%q, %q)=%q, want error", test.root, test.path, got) + } + continue + } + if err != nil { + t.Errorf("Rel(%q, %q): want %q, got error: %s", test.root, test.path, test.want, err) + } + if got != test.want { + t.Errorf("Rel(%q, %q)=%q, want %q", test.root, test.path, got, test.want) + } + } +} + +type VolumeNameTest struct { + path string + vol string +} + +var volumenametests = []VolumeNameTest{ + {`c:/foo/bar`, `c:`}, + {`c:`, `c:`}, + {``, ``}, + {`\\\host`, ``}, + {`\\\host\`, ``}, + {`\\\host\share`, ``}, + {`\\\host\\share`, ``}, + {`\\host`, ``}, + {`//host`, ``}, + {`\\host\`, ``}, + {`//host/`, ``}, + {`\\host\share`, `\\host\share`}, + {`//host/share`, `//host/share`}, + {`\\host\share\`, `\\host\share`}, + {`//host/share/`, `//host/share`}, + {`\\host\share\foo`, `\\host\share`}, + {`//host/share/foo`, `//host/share`}, + {`\\host\share\\foo\\\bar\\\\baz`, `\\host\share`}, + {`//host/share//foo///bar////baz`, `//host/share`}, + {`\\host\share\foo\..\bar`, `\\host\share`}, + {`//host/share/foo/../bar`, `//host/share`}, +} + +func TestVolumeName(t *testing.T) { + if runtime.GOOS != "windows" { + return + } + for _, v := range volumenametests { + if vol := filepath.VolumeName(v.path); vol != v.vol { + t.Errorf("VolumeName(%q)=%q, want %q", v.path, vol, v.vol) + } + } +} diff --git a/libgo/go/path/filepath/path_unix.go b/libgo/go/path/filepath/path_unix.go index b2a4151..daf0eb2 100644 --- a/libgo/go/path/filepath/path_unix.go +++ b/libgo/go/path/filepath/path_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux openbsd + package filepath import "strings" diff --git a/libgo/go/path/filepath/path_windows.go b/libgo/go/path/filepath/path_windows.go index 2535697..9692fd9 100644 --- a/libgo/go/path/filepath/path_windows.go +++ b/libgo/go/path/filepath/path_windows.go @@ -4,7 +4,13 @@ package filepath -import "strings" +import ( + "strings" +) + +func isSlash(c uint8) bool { + return c == '\\' || c == '/' +} // IsAbs returns true if the path is absolute. func IsAbs(path string) (b bool) { @@ -16,11 +22,12 @@ func IsAbs(path string) (b bool) { if path == "" { return false } - return path[0] == '/' || path[0] == '\\' + return isSlash(path[0]) } // VolumeName returns leading volume name. // Given "C:\foo\bar" it returns "C:" under windows. +// Given "\\host\share\foo" it returns "\\host\share". // On other platforms it returns "". func VolumeName(path string) (v string) { if len(path) < 2 { @@ -33,6 +40,30 @@ func VolumeName(path string) (v string) { 'A' <= c && c <= 'Z') { return path[:2] } + // is it UNC + if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) && + !isSlash(path[2]) && path[2] != '.' { + // first, leading `\\` and next shouldn't be `\`. its server name. + for n := 3; n < l-1; n++ { + // second, next '\' shouldn't be repeated. + if isSlash(path[n]) { + n++ + // third, following something characters. its share name. + if !isSlash(path[n]) { + if path[n] == '.' { + break + } + for ; n < l; n++ { + if isSlash(path[n]) { + break + } + } + return path[:n] + } + break + } + } + } return "" } |