diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2016-07-22 18:15:38 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2016-07-22 18:15:38 +0000 |
commit | 22b955cca564a9a3a5b8c9d9dd1e295b7943c128 (patch) | |
tree | abdbd898676e1f853fca2d7e031d105d7ebcf676 /libgo/go/path | |
parent | 9d04a3af4c6491536badf6bde9707c907e4d196b (diff) | |
download | gcc-22b955cca564a9a3a5b8c9d9dd1e295b7943c128.zip gcc-22b955cca564a9a3a5b8c9d9dd1e295b7943c128.tar.gz gcc-22b955cca564a9a3a5b8c9d9dd1e295b7943c128.tar.bz2 |
libgo: update to go1.7rc3
Reviewed-on: https://go-review.googlesource.com/25150
From-SVN: r238662
Diffstat (limited to 'libgo/go/path')
-rw-r--r-- | libgo/go/path/example_test.go | 9 | ||||
-rw-r--r-- | libgo/go/path/filepath/example_unix_test.go | 14 | ||||
-rw-r--r-- | libgo/go/path/filepath/export_windows_test.go | 7 | ||||
-rw-r--r-- | libgo/go/path/filepath/match.go | 49 | ||||
-rw-r--r-- | libgo/go/path/filepath/match_test.go | 171 | ||||
-rw-r--r-- | libgo/go/path/filepath/path.go | 18 | ||||
-rw-r--r-- | libgo/go/path/filepath/path_test.go | 4 | ||||
-rw-r--r-- | libgo/go/path/filepath/path_windows.go | 2 | ||||
-rw-r--r-- | libgo/go/path/filepath/symlink.go | 2 | ||||
-rw-r--r-- | libgo/go/path/filepath/symlink_windows.go | 100 | ||||
-rw-r--r-- | libgo/go/path/match.go | 2 | ||||
-rw-r--r-- | libgo/go/path/path.go | 4 | ||||
-rw-r--r-- | libgo/go/path/path_test.go | 8 |
13 files changed, 318 insertions, 72 deletions
diff --git a/libgo/go/path/example_test.go b/libgo/go/path/example_test.go index ca18b32..88b7655 100644 --- a/libgo/go/path/example_test.go +++ b/libgo/go/path/example_test.go @@ -56,7 +56,14 @@ func ExampleIsAbs() { func ExampleJoin() { fmt.Println(path.Join("a", "b", "c")) - // Output: a/b/c + fmt.Println(path.Join("a", "b/c")) + fmt.Println(path.Join("a/b", "c")) + fmt.Println(path.Join("a/b", "/c")) + // Output: + // a/b/c + // a/b/c + // a/b/c + // a/b/c } func ExampleSplit() { diff --git a/libgo/go/path/filepath/example_unix_test.go b/libgo/go/path/filepath/example_unix_test.go index 893be1b..cd8233c 100644 --- a/libgo/go/path/filepath/example_unix_test.go +++ b/libgo/go/path/filepath/example_unix_test.go @@ -65,3 +65,17 @@ func ExampleSplit() { // dir: "/usr/local//" // file: "go" } + +func ExampleJoin() { + fmt.Println("On Unix:") + fmt.Println(filepath.Join("a", "b", "c")) + fmt.Println(filepath.Join("a", "b/c")) + fmt.Println(filepath.Join("a/b", "c")) + fmt.Println(filepath.Join("a/b", "/c")) + // Output: + // On Unix: + // a/b/c + // a/b/c + // a/b/c + // a/b/c +} diff --git a/libgo/go/path/filepath/export_windows_test.go b/libgo/go/path/filepath/export_windows_test.go new file mode 100644 index 0000000..8ca007f --- /dev/null +++ b/libgo/go/path/filepath/export_windows_test.go @@ -0,0 +1,7 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package filepath + +var ToNorm = toNorm diff --git a/libgo/go/path/filepath/match.go b/libgo/go/path/filepath/match.go index 89f16de..9fa68f5 100644 --- a/libgo/go/path/filepath/match.go +++ b/libgo/go/path/filepath/match.go @@ -49,7 +49,7 @@ Pattern: star, chunk, pattern = scanChunk(pattern) if star && chunk == "" { // Trailing * matches rest of string unless it has a /. - return strings.Index(name, string(Separator)) < 0, nil + return !strings.Contains(name, string(Separator)), nil } // Look for match at current position. t, ok, err := matchChunk(chunk, name) @@ -240,19 +240,21 @@ func Glob(pattern string) (matches []string, err error) { } dir, file := Split(pattern) - switch dir { - case "": - dir = "." - case string(Separator): - // nothing - default: - dir = dir[0 : len(dir)-1] // chop off trailing separator + if runtime.GOOS == "windows" { + dir = cleanGlobPathWindows(dir) + } else { + dir = cleanGlobPath(dir) } if !hasMeta(dir) { return glob(dir, file, nil) } + // Prevent infinite recursion. See issue 15879. + if dir == pattern { + return nil, ErrBadPattern + } + var m []string m, err = Glob(dir) if err != nil { @@ -267,6 +269,35 @@ func Glob(pattern string) (matches []string, err error) { return } +// cleanGlobPath prepares path for glob matching. +func cleanGlobPath(path string) string { + switch path { + case "": + return "." + case string(Separator): + // do nothing to the path + return path + default: + return path[0 : len(path)-1] // chop off trailing separator + } +} + +// cleanGlobPathWindows is windows version of cleanGlobPath. +func cleanGlobPathWindows(path string) string { + vollen := volumeNameLen(path) + switch { + case path == "": + return "." + case vollen+1 == len(path) && os.IsPathSeparator(path[len(path)-1]): // /, \, C:\ and C:/ + // do nothing to the path + return path + case vollen == len(path) && len(path) == 2: // C: + return path + "." // convert C: into C:. + default: + return path[0 : len(path)-1] // chop off trailing separator + } +} + // glob searches for files matching pattern in the directory dir // and appends them to matches. If the directory cannot be // opened, it returns the existing matches. New matches are @@ -305,5 +336,5 @@ func glob(dir, pattern string, matches []string) (m []string, e error) { // recognized by Match. func hasMeta(path string) bool { // TODO(niemeyer): Should other magic characters be added here? - return strings.IndexAny(path, "*?[") >= 0 + return strings.ContainsAny(path, "*?[") } diff --git a/libgo/go/path/filepath/match_test.go b/libgo/go/path/filepath/match_test.go index 209c67f..102eb1ce0 100644 --- a/libgo/go/path/filepath/match_test.go +++ b/libgo/go/path/filepath/match_test.go @@ -5,10 +5,12 @@ package filepath_test import ( + "fmt" "io/ioutil" "os" . "path/filepath" "runtime" + "sort" "strings" "testing" ) @@ -88,7 +90,7 @@ func TestMatch(t *testing.T) { pattern := tt.pattern s := tt.s if runtime.GOOS == "windows" { - if strings.Index(pattern, "\\") >= 0 { + if strings.Contains(pattern, "\\") { // no escape allowed on windows. continue } @@ -158,6 +160,12 @@ func TestGlobError(t *testing.T) { } } +func TestGlobUNC(t *testing.T) { + // Just make sure this runs without crashing for now. + // See issue 15879. + Glob(`\\?\C:\*`) +} + var globSymlinkTests = []struct { path, dest string brokenLink bool @@ -210,3 +218,164 @@ func TestGlobSymlink(t *testing.T) { } } } + +type globTest struct { + pattern string + matches []string +} + +func (test *globTest) buildWant(root string) []string { + want := make([]string, 0) + for _, m := range test.matches { + want = append(want, root+FromSlash(m)) + } + sort.Strings(want) + return want +} + +func (test *globTest) globAbs(root, rootPattern string) error { + p := FromSlash(rootPattern + `\` + test.pattern) + have, err := Glob(p) + if err != nil { + return err + } + sort.Strings(have) + want := test.buildWant(root + `\`) + if strings.Join(want, "_") == strings.Join(have, "_") { + return nil + } + return fmt.Errorf("Glob(%q) returns %q, but %q expected", p, have, want) +} + +func (test *globTest) globRel(root string) error { + p := root + FromSlash(test.pattern) + have, err := Glob(p) + if err != nil { + return err + } + sort.Strings(have) + want := test.buildWant(root) + if strings.Join(want, "_") == strings.Join(have, "_") { + return nil + } + // try also matching version without root prefix + wantWithNoRoot := test.buildWant("") + if strings.Join(wantWithNoRoot, "_") == strings.Join(have, "_") { + return nil + } + return fmt.Errorf("Glob(%q) returns %q, but %q expected", p, have, want) +} + +func TestWindowsGlob(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skipf("skipping windows specific test") + } + + tmpDir, err := ioutil.TempDir("", "TestWindowsGlob") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + // /tmp may itself be a symlink + tmpDir, err = EvalSymlinks(tmpDir) + if err != nil { + t.Fatal("eval symlink for tmp dir:", err) + } + + if len(tmpDir) < 3 { + t.Fatalf("tmpDir path %q is too short", tmpDir) + } + if tmpDir[1] != ':' { + t.Fatalf("tmpDir path %q must have drive letter in it", tmpDir) + } + + dirs := []string{ + "a", + "b", + "dir/d/bin", + } + files := []string{ + "dir/d/bin/git.exe", + } + for _, dir := range dirs { + err := os.MkdirAll(Join(tmpDir, dir), 0777) + if err != nil { + t.Fatal(err) + } + } + for _, file := range files { + err := ioutil.WriteFile(Join(tmpDir, file), nil, 0666) + if err != nil { + t.Fatal(err) + } + } + + tests := []globTest{ + {"a", []string{"a"}}, + {"b", []string{"b"}}, + {"c", []string{}}, + {"*", []string{"a", "b", "dir"}}, + {"d*", []string{"dir"}}, + {"*i*", []string{"dir"}}, + {"*r", []string{"dir"}}, + {"?ir", []string{"dir"}}, + {"?r", []string{}}, + {"d*/*/bin/git.exe", []string{"dir/d/bin/git.exe"}}, + } + + // test absolute paths + for _, test := range tests { + var p string + err = test.globAbs(tmpDir, tmpDir) + if err != nil { + t.Error(err) + } + // test C:\*Documents and Settings\... + p = tmpDir + p = strings.Replace(p, `:\`, `:\*`, 1) + err = test.globAbs(tmpDir, p) + if err != nil { + t.Error(err) + } + // test C:\Documents and Settings*\... + p = tmpDir + p = strings.Replace(p, `:\`, `:`, 1) + p = strings.Replace(p, `\`, `*\`, 1) + p = strings.Replace(p, `:`, `:\`, 1) + err = test.globAbs(tmpDir, p) + if err != nil { + t.Error(err) + } + } + + // test relative paths + wd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + err = os.Chdir(tmpDir) + if err != nil { + t.Fatal(err) + } + defer func() { + err := os.Chdir(wd) + if err != nil { + t.Fatal(err) + } + }() + for _, test := range tests { + err := test.globRel("") + if err != nil { + t.Error(err) + } + err = test.globRel(`.\`) + if err != nil { + t.Error(err) + } + err = test.globRel(tmpDir[:2]) // C: + if err != nil { + t.Error(err) + } + } +} diff --git a/libgo/go/path/filepath/path.go b/libgo/go/path/filepath/path.go index dd6f3e7..0dc559c 100644 --- a/libgo/go/path/filepath/path.go +++ b/libgo/go/path/filepath/path.go @@ -4,9 +4,6 @@ // Package filepath implements utility routines for manipulating filename paths // in a way compatible with the target operating system-defined file paths. -// -// Functions in this package replace any occurrences of the slash ('/') character -// with os.PathSeparator when returning paths unless otherwise specified. package filepath import ( @@ -61,7 +58,7 @@ const ( ) // Clean returns the shortest path name equivalent to path -// by purely lexical processing. It applies the following rules +// by purely lexical processing. It applies the following rules // iteratively until no further processing can be done: // // 1. Replace multiple Separator elements with a single one. @@ -75,12 +72,14 @@ const ( // The returned path ends in a slash only if it represents a root directory, // such as "/" on Unix or `C:\` on Windows. // +// Finally, any occurrences of slash are replaced by Separator. +// // If the result of this process is an empty string, Clean // returns the string ".". // // See also Rob Pike, ``Lexical File Names in Plan 9 or // Getting Dot-Dot Right,'' -// http://plan9.bell-labs.com/sys/doc/lexnames.html +// https://9p.io/sys/doc/lexnames.html func Clean(path string) string { originalPath := path volLen := volumeNameLen(path) @@ -198,7 +197,7 @@ func Split(path string) (dir, file string) { } // Join joins any number of path elements into a single path, adding -// a Separator if necessary. The result is Cleaned, in particular +// a Separator if necessary. Join calls Clean on the result; in particular, // all empty strings are ignored. // On Windows, the result is a UNC path if and only if the first path // element is a UNC path. @@ -223,14 +222,16 @@ func Ext(path string) string { // links. // If path is relative the result will be relative to the current directory, // unless one of the components is an absolute symbolic link. +// EvalSymlinks calls Clean on the result. func EvalSymlinks(path string) (string, error) { return evalSymlinks(path) } // Abs returns an absolute representation of path. // If the path is not absolute it will be joined with the current -// working directory to turn it into an absolute path. The absolute +// working directory to turn it into an absolute path. The absolute // path name for a given file is not guaranteed to be unique. +// Abs calls Clean on the result. func Abs(path string) (string, error) { return abs(path) } @@ -253,6 +254,7 @@ func unixAbs(path string) (string, error) { // even if basepath and targpath share no elements. // 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. +// Rel calls Clean on the result. func Rel(basepath, targpath string) (string, error) { baseVol := VolumeName(basepath) targVol := VolumeName(targpath) @@ -442,7 +444,7 @@ func Base(path string) string { } // Dir returns all but the last element of path, typically the path's directory. -// After dropping the final element, the path is Cleaned and trailing +// After dropping the final element, Dir calls Clean on the path and trailing // slashes are removed. // If the path is empty, Dir returns ".". // If the path consists entirely of separators, Dir returns a single separator. diff --git a/libgo/go/path/filepath/path_test.go b/libgo/go/path/filepath/path_test.go index 10ea795..4d5e3bd 100644 --- a/libgo/go/path/filepath/path_test.go +++ b/libgo/go/path/filepath/path_test.go @@ -452,7 +452,7 @@ func TestWalk(t *testing.T) { checkMarks(t, true) errors = errors[0:0] - // Test permission errors. Only possible if we're not root + // Test permission errors. Only possible if we're not root // and only on some file systems (AFS, FAT). To avoid errors during // all.bash on those file systems, skip during go test -short. if os.Getuid() > 0 && !testing.Short() { @@ -1018,7 +1018,7 @@ func TestAbs(t *testing.T) { vol := filepath.VolumeName(root) var extra []string for _, path := range absTests { - if strings.Index(path, "$") != -1 { + if strings.Contains(path, "$") { continue } path = vol + path diff --git a/libgo/go/path/filepath/path_windows.go b/libgo/go/path/filepath/path_windows.go index ef6e7ca..41c57df 100644 --- a/libgo/go/path/filepath/path_windows.go +++ b/libgo/go/path/filepath/path_windows.go @@ -121,7 +121,7 @@ func join(elem []string) string { // joinNonEmpty is like join, but it assumes that the first element is non-empty. func joinNonEmpty(elem []string) string { if len(elem[0]) == 2 && elem[0][1] == ':' { - // First element is drive leter without terminating slash. + // First element is drive letter without terminating slash. // Keep path relative to current directory on that drive. return Clean(elem[0] + strings.Join(elem[1:], string(Separator))) } diff --git a/libgo/go/path/filepath/symlink.go b/libgo/go/path/filepath/symlink.go index bc287c5..f627a94 100644 --- a/libgo/go/path/filepath/symlink.go +++ b/libgo/go/path/filepath/symlink.go @@ -100,7 +100,7 @@ func walkSymlinks(path string) (string, error) { return "", err } if runtime.GOOS == "windows" { - // walkLinks(".", ...) always retuns "." on unix. + // walkLinks(".", ...) always returns "." on unix. // But on windows it returns symlink target, if current // directory is a symlink. Stop the walk, if symlink // target is not absolute path, and return "." diff --git a/libgo/go/path/filepath/symlink_windows.go b/libgo/go/path/filepath/symlink_windows.go index eb48367..2433528 100644 --- a/libgo/go/path/filepath/symlink_windows.go +++ b/libgo/go/path/filepath/symlink_windows.go @@ -5,45 +5,82 @@ package filepath import ( + "strings" "syscall" ) -func toShort(path string) (string, error) { - p, err := syscall.UTF16FromString(path) +// normVolumeName is like VolumeName, but makes drive letter upper case. +// result of EvalSymlinks must be unique, so we have +// EvalSymlinks(`c:\a`) == EvalSymlinks(`C:\a`). +func normVolumeName(path string) string { + volume := VolumeName(path) + + if len(volume) > 2 { // isUNC + return volume + } + + return strings.ToUpper(volume) +} + +// normBase retruns the last element of path. +func normBase(path string) (string, error) { + p, err := syscall.UTF16PtrFromString(path) if err != nil { return "", err } - b := p // GetShortPathName says we can reuse buffer - n := uint32(len(b)) - for { - n, err = syscall.GetShortPathName(&p[0], &b[0], uint32(len(b))) - if err != nil { - return "", err - } - if n <= uint32(len(b)) { - return syscall.UTF16ToString(b[:n]), nil - } - b = make([]uint16, n) - } -} -func toLong(path string) (string, error) { - p, err := syscall.UTF16FromString(path) + var data syscall.Win32finddata + + h, err := syscall.FindFirstFile(p, &data) if err != nil { return "", err } - b := p // GetLongPathName says we can reuse buffer - n := uint32(len(b)) + syscall.FindClose(h) + + return syscall.UTF16ToString(data.FileName[:]), nil +} + +func toNorm(path string, base func(string) (string, error)) (string, error) { + if path == "" { + return path, nil + } + + path = Clean(path) + + volume := normVolumeName(path) + path = path[len(volume):] + + // skip special cases + if path == "." || path == `\` { + return volume + path, nil + } + + var normPath string + for { - n, err = syscall.GetLongPathName(&p[0], &b[0], uint32(len(b))) + name, err := base(volume + path) if err != nil { return "", err } - if n <= uint32(len(b)) { - return syscall.UTF16ToString(b[:n]), nil + + normPath = name + `\` + normPath + + i := strings.LastIndexByte(path, Separator) + if i == -1 { + break + } + if i == 0 { // `\Go` or `C:\Go` + normPath = `\` + normPath + + break } - b = make([]uint16, n) + + path = path[:i] } + + normPath = normPath[:len(normPath)-1] // remove trailing '\' + + return volume + normPath, nil } func evalSymlinks(path string) (string, error) { @@ -51,20 +88,5 @@ func evalSymlinks(path string) (string, error) { if err != nil { return "", err } - p, err := toShort(path) - if err != nil { - return "", err - } - p, err = toLong(p) - if err != nil { - return "", err - } - // syscall.GetLongPathName does not change the case of the drive letter, - // but the result of EvalSymlinks must be unique, so we have - // EvalSymlinks(`c:\a`) == EvalSymlinks(`C:\a`). - // Make drive letter upper case. - if len(p) >= 2 && p[1] == ':' && 'a' <= p[0] && p[0] <= 'z' { - p = string(p[0]+'A'-'a') + p[1:] - } - return Clean(p), nil + return toNorm(path, normBase) } diff --git a/libgo/go/path/match.go b/libgo/go/path/match.go index 75dd3b3..8d9aa51 100644 --- a/libgo/go/path/match.go +++ b/libgo/go/path/match.go @@ -43,7 +43,7 @@ Pattern: star, chunk, pattern = scanChunk(pattern) if star && chunk == "" { // Trailing * matches rest of string unless it has a /. - return strings.Index(name, "/") < 0, nil + return !strings.Contains(name, "/"), nil } // Look for match at current position. t, ok, err := matchChunk(chunk, name) diff --git a/libgo/go/path/path.go b/libgo/go/path/path.go index 01071a9..c1d4d8a 100644 --- a/libgo/go/path/path.go +++ b/libgo/go/path/path.go @@ -48,7 +48,7 @@ func (b *lazybuf) string() string { } // Clean returns the shortest path name equivalent to path -// by purely lexical processing. It applies the following rules +// by purely lexical processing. It applies the following rules // iteratively until no further processing can be done: // // 1. Replace multiple slashes with a single slash. @@ -65,7 +65,7 @@ func (b *lazybuf) string() string { // // See also Rob Pike, ``Lexical File Names in Plan 9 or // Getting Dot-Dot Right,'' -// http://plan9.bell-labs.com/sys/doc/lexnames.html +// https://9p.io/sys/doc/lexnames.html func Clean(path string) string { if path == "" { return "." diff --git a/libgo/go/path/path_test.go b/libgo/go/path/path_test.go index 512d936..600ff08 100644 --- a/libgo/go/path/path_test.go +++ b/libgo/go/path/path_test.go @@ -138,15 +138,9 @@ var jointests = []JoinTest{ {[]string{"", ""}, ""}, } -// join takes a []string and passes it to Join. -func join(elem []string, args ...string) string { - args = elem - return Join(args...) -} - func TestJoin(t *testing.T) { for _, test := range jointests { - if p := join(test.elem); p != test.path { + if p := Join(test.elem...); p != test.path { t.Errorf("join(%q) = %q, want %q", test.elem, p, test.path) } } |