aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/path
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2016-07-22 18:15:38 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2016-07-22 18:15:38 +0000
commit22b955cca564a9a3a5b8c9d9dd1e295b7943c128 (patch)
treeabdbd898676e1f853fca2d7e031d105d7ebcf676 /libgo/go/path
parent9d04a3af4c6491536badf6bde9707c907e4d196b (diff)
downloadgcc-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.go9
-rw-r--r--libgo/go/path/filepath/example_unix_test.go14
-rw-r--r--libgo/go/path/filepath/export_windows_test.go7
-rw-r--r--libgo/go/path/filepath/match.go49
-rw-r--r--libgo/go/path/filepath/match_test.go171
-rw-r--r--libgo/go/path/filepath/path.go18
-rw-r--r--libgo/go/path/filepath/path_test.go4
-rw-r--r--libgo/go/path/filepath/path_windows.go2
-rw-r--r--libgo/go/path/filepath/symlink.go2
-rw-r--r--libgo/go/path/filepath/symlink_windows.go100
-rw-r--r--libgo/go/path/match.go2
-rw-r--r--libgo/go/path/path.go4
-rw-r--r--libgo/go/path/path_test.go8
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)
}
}