summaryrefslogtreecommitdiff
path: root/libgo/go/path
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/path')
-rw-r--r--libgo/go/path/filepath/example_unix_test.go2
-rw-r--r--libgo/go/path/filepath/match_test.go2
-rw-r--r--libgo/go/path/filepath/path.go10
-rw-r--r--libgo/go/path/filepath/path_plan9.go4
-rw-r--r--libgo/go/path/filepath/path_test.go193
-rw-r--r--libgo/go/path/filepath/path_unix.go4
-rw-r--r--libgo/go/path/filepath/path_windows.go9
-rw-r--r--libgo/go/path/filepath/symlink.go135
-rw-r--r--libgo/go/path/filepath/symlink_windows.go1
-rw-r--r--libgo/go/path/path.go2
10 files changed, 280 insertions, 82 deletions
diff --git a/libgo/go/path/filepath/example_unix_test.go b/libgo/go/path/filepath/example_unix_test.go
index 27d85d15c6c..893be1b1988 100644
--- a/libgo/go/path/filepath/example_unix_test.go
+++ b/libgo/go/path/filepath/example_unix_test.go
@@ -35,7 +35,7 @@ func ExampleRel() {
// On Unix:
// "/a/b/c": "b/c" <nil>
// "/b/c": "../b/c" <nil>
- // "./b/c": "" Rel: can't make b/c relative to /a
+ // "./b/c": "" Rel: can't make ./b/c relative to /a
}
func ExampleSplit() {
diff --git a/libgo/go/path/filepath/match_test.go b/libgo/go/path/filepath/match_test.go
index c29f93fb7bb..209c67f30fd 100644
--- a/libgo/go/path/filepath/match_test.go
+++ b/libgo/go/path/filepath/match_test.go
@@ -168,7 +168,7 @@ var globSymlinkTests = []struct {
func TestGlobSymlink(t *testing.T) {
switch runtime.GOOS {
- case "nacl", "plan9":
+ case "android", "nacl", "plan9":
t.Skipf("skipping on %s", runtime.GOOS)
case "windows":
if !supportsSymlinks {
diff --git a/libgo/go/path/filepath/path.go b/libgo/go/path/filepath/path.go
index 5dc5cfd49e7..dd6f3e7a994 100644
--- a/libgo/go/path/filepath/path.go
+++ b/libgo/go/path/filepath/path.go
@@ -258,7 +258,7 @@ func Rel(basepath, targpath string) (string, error) {
targVol := VolumeName(targpath)
base := Clean(basepath)
targ := Clean(targpath)
- if targ == base {
+ if sameWord(targ, base) {
return ".", nil
}
base = base[len(baseVol):]
@@ -269,8 +269,8 @@ func Rel(basepath, targpath string) (string, error) {
// 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 "", errors.New("Rel: can't make " + targ + " relative to " + base)
+ if baseSlashed != targSlashed || !sameWord(baseVol, targVol) {
+ return "", errors.New("Rel: can't make " + targpath + " relative to " + basepath)
}
// Position base[b0:bi] and targ[t0:ti] at the first differing elements.
bl := len(base)
@@ -283,7 +283,7 @@ func Rel(basepath, targpath string) (string, error) {
for ti < tl && targ[ti] != Separator {
ti++
}
- if targ[t0:ti] != base[b0:bi] {
+ if !sameWord(targ[t0:ti], base[b0:bi]) {
break
}
if bi < bl {
@@ -296,7 +296,7 @@ func Rel(basepath, targpath string) (string, error) {
t0 = ti
}
if base[b0:bi] == ".." {
- return "", errors.New("Rel: can't make " + targ + " relative to " + base)
+ return "", errors.New("Rel: can't make " + targpath + " relative to " + basepath)
}
if b0 != bl {
// Base elements left. Must go up before going down.
diff --git a/libgo/go/path/filepath/path_plan9.go b/libgo/go/path/filepath/path_plan9.go
index 962774efd5d..60d46d9d421 100644
--- a/libgo/go/path/filepath/path_plan9.go
+++ b/libgo/go/path/filepath/path_plan9.go
@@ -42,3 +42,7 @@ func join(elem []string) string {
}
return ""
}
+
+func sameWord(a, b string) bool {
+ return a == b
+}
diff --git a/libgo/go/path/filepath/path_test.go b/libgo/go/path/filepath/path_test.go
index b2536cb77a4..10ea795e90e 100644
--- a/libgo/go/path/filepath/path_test.go
+++ b/libgo/go/path/filepath/path_test.go
@@ -270,7 +270,12 @@ var winjointests = []JoinTest{
{[]string{`C:\Windows\`, `System32`}, `C:\Windows\System32`},
{[]string{`C:\Windows\`, ``}, `C:\Windows`},
{[]string{`C:\`, `Windows`}, `C:\Windows`},
- {[]string{`C:`, `Windows`}, `C:\Windows`},
+ {[]string{`C:`, `a`}, `C:a`},
+ {[]string{`C:`, `a\b`}, `C:a\b`},
+ {[]string{`C:`, `a`, `b`}, `C:a\b`},
+ {[]string{`C:.`, `a`}, `C:a`},
+ {[]string{`C:a`, `b`}, `C:a\b`},
+ {[]string{`C:a`, `b`, `d`}, `C:a\b\d`},
{[]string{`\\host\share`, `foo`}, `\\host\share\foo`},
{[]string{`\\host\share\foo`}, `\\host\share\foo`},
{[]string{`//host/share`, `foo/bar`}, `\\host\share\foo\bar`},
@@ -405,18 +410,18 @@ func mark(path string, info os.FileInfo, err error, errors *[]error, clear bool)
func chtmpdir(t *testing.T) (restore func()) {
oldwd, err := os.Getwd()
if err != nil {
- t.Fatal("chtmpdir: %v", err)
+ t.Fatalf("chtmpdir: %v", err)
}
d, err := ioutil.TempDir("", "test")
if err != nil {
- t.Fatal("chtmpdir: %v", err)
+ t.Fatalf("chtmpdir: %v", err)
}
if err := os.Chdir(d); err != nil {
- t.Fatal("chtmpdir: %v", err)
+ t.Fatalf("chtmpdir: %v", err)
}
return func() {
if err := os.Chdir(oldwd); err != nil {
- t.Fatal("chtmpdir: %v", err)
+ t.Fatalf("chtmpdir: %v", err)
}
os.RemoveAll(d)
}
@@ -755,8 +760,16 @@ var EvalSymlinksTests = []EvalSymlinksTest{
{"test/linkabs", "/"},
}
-var EvalSymlinksAbsWindowsTests = []EvalSymlinksTest{
- {`c:\`, `c:\`},
+// findEvalSymlinksTestDirsDest searches testDirs
+// for matching path and returns correspondent dest.
+func findEvalSymlinksTestDirsDest(t *testing.T, testDirs []EvalSymlinksTest, path string) string {
+ for _, d := range testDirs {
+ if d.path == path {
+ return d.dest
+ }
+ }
+ t.Fatalf("did not find %q in testDirs slice", path)
+ return ""
}
// simpleJoin builds a file name from the directory and path.
@@ -767,9 +780,12 @@ func simpleJoin(dir, path string) string {
func TestEvalSymlinks(t *testing.T) {
switch runtime.GOOS {
- case "nacl", "plan9":
+ case "android", "nacl", "plan9":
t.Skipf("skipping on %s", runtime.GOOS)
}
+ if !supportsSymlinks {
+ t.Skip("skipping because symlinks are not supported")
+ }
tmpDir, err := ioutil.TempDir("", "evalsymlink")
if err != nil {
@@ -784,38 +800,37 @@ func TestEvalSymlinks(t *testing.T) {
t.Fatal("eval symlink for tmp dir:", err)
}
+ tests := EvalSymlinksTests
+ testdirs := EvalSymlinksTestDirs
+ if runtime.GOOS == "windows" {
+ 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)
+ }
+ newtest := EvalSymlinksTest{"test/linkabswin", tmpDir[:3]}
+ tests = append(tests, newtest)
+ testdirs = append(testdirs, newtest)
+ }
+
// Create the symlink farm using relative paths.
- for _, d := range EvalSymlinksTestDirs {
+ for _, d := range testdirs {
var err error
path := simpleJoin(tmpDir, d.path)
if d.dest == "" {
err = os.Mkdir(path, 0755)
} else {
- if supportsSymlinks {
- err = os.Symlink(d.dest, path)
- }
+ err = os.Symlink(d.dest, path)
}
if err != nil {
t.Fatal(err)
}
}
- var tests []EvalSymlinksTest
- if supportsSymlinks {
- tests = EvalSymlinksTests
- } else {
- for _, d := range EvalSymlinksTests {
- if d.path == d.dest {
- // will test only real files and directories
- tests = append(tests, d)
- // test "canonical" names
- d2 := EvalSymlinksTest{
- path: strings.ToUpper(d.path),
- dest: d.dest,
- }
- tests = append(tests, d2)
- }
- }
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
}
// Evaluate the symlink farm.
@@ -830,6 +845,125 @@ func TestEvalSymlinks(t *testing.T) {
} else if filepath.Clean(p) != filepath.Clean(dest) {
t.Errorf("Clean(%q)=%q, want %q", path, p, dest)
}
+
+ // test EvalSymlinks(".")
+ func() {
+ defer func() {
+ err := os.Chdir(wd)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }()
+
+ err := os.Chdir(path)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ p, err := filepath.EvalSymlinks(".")
+ if err != nil {
+ t.Errorf(`EvalSymlinks(".") in %q directory error: %v`, d.path, err)
+ return
+ }
+ if p == "." {
+ return
+ }
+ want := filepath.Clean(findEvalSymlinksTestDirsDest(t, testdirs, d.path))
+ if p == want {
+ return
+ }
+ t.Errorf(`EvalSymlinks(".") in %q directory returns %q, want "." or %q`, d.path, p, want)
+ }()
+
+ // test EvalSymlinks where parameter is relative path
+ func() {
+ defer func() {
+ err := os.Chdir(wd)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }()
+
+ err := os.Chdir(tmpDir)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if p, err := filepath.EvalSymlinks(d.path); err != nil {
+ t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
+ } else if filepath.Clean(p) != filepath.Clean(d.dest) {
+ t.Errorf("Clean(%q)=%q, want %q", d.path, p, d.dest)
+ }
+ }()
+ }
+}
+
+func TestIssue13582(t *testing.T) {
+ switch runtime.GOOS {
+ case "android", "nacl", "plan9":
+ t.Skipf("skipping on %s", runtime.GOOS)
+ }
+ if !supportsSymlinks {
+ t.Skip("skipping because symlinks are not supported")
+ }
+
+ tmpDir, err := ioutil.TempDir("", "issue13582")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpDir)
+
+ dir := filepath.Join(tmpDir, "dir")
+ err = os.Mkdir(dir, 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+ linkToDir := filepath.Join(tmpDir, "link_to_dir")
+ err = os.Symlink(dir, linkToDir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ file := filepath.Join(linkToDir, "file")
+ err = ioutil.WriteFile(file, nil, 0644)
+ if err != nil {
+ t.Fatal(err)
+ }
+ link1 := filepath.Join(linkToDir, "link1")
+ err = os.Symlink(file, link1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ link2 := filepath.Join(linkToDir, "link2")
+ err = os.Symlink(link1, link2)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // /tmp may itself be a symlink!
+ realTmpDir, err := filepath.EvalSymlinks(tmpDir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ realDir := filepath.Join(realTmpDir, "dir")
+ realFile := filepath.Join(realDir, "file")
+
+ tests := []struct {
+ path, want string
+ }{
+ {dir, realDir},
+ {linkToDir, realDir},
+ {file, realFile},
+ {link1, realFile},
+ {link2, realFile},
+ }
+ for i, test := range tests {
+ have, err := filepath.EvalSymlinks(test.path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if have != test.want {
+ t.Errorf("test#%d: EvalSymlinks(%q) returns %q, want %q", i, test.path, have, test.want)
+ }
}
}
@@ -976,6 +1110,9 @@ var winreltests = []RelTests{
{`C:a\b\c`, `C:a/b/d`, `..\d`},
{`C:\`, `D:\`, `err`},
{`C:`, `D:`, `err`},
+ {`C:\Projects`, `c:\projects\src`, `src`},
+ {`C:\Projects`, `c:\projects`, `.`},
+ {`C:\Projects\a\..`, `c:\projects`, `.`},
}
func TestRel(t *testing.T) {
diff --git a/libgo/go/path/filepath/path_unix.go b/libgo/go/path/filepath/path_unix.go
index d241d78fa78..2d242cc0b5d 100644
--- a/libgo/go/path/filepath/path_unix.go
+++ b/libgo/go/path/filepath/path_unix.go
@@ -44,3 +44,7 @@ func join(elem []string) string {
}
return ""
}
+
+func sameWord(a, b string) bool {
+ return a == b
+}
diff --git a/libgo/go/path/filepath/path_windows.go b/libgo/go/path/filepath/path_windows.go
index bcfe0a34b0a..ef6e7ca93f4 100644
--- a/libgo/go/path/filepath/path_windows.go
+++ b/libgo/go/path/filepath/path_windows.go
@@ -120,6 +120,11 @@ 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.
+ // Keep path relative to current directory on that drive.
+ return Clean(elem[0] + strings.Join(elem[1:], string(Separator)))
+ }
// The following logic prevents Join from inadvertently creating a
// UNC path on Windows. Unless the first element is a UNC path, Join
// shouldn't create a UNC path. See golang.org/issue/9167.
@@ -145,3 +150,7 @@ func joinNonEmpty(elem []string) string {
func isUNC(path string) bool {
return volumeNameLen(path) > 2
}
+
+func sameWord(a, b string) bool {
+ return strings.EqualFold(a, b)
+}
diff --git a/libgo/go/path/filepath/symlink.go b/libgo/go/path/filepath/symlink.go
index df0a9e0c2ba..bc287c5ecb3 100644
--- a/libgo/go/path/filepath/symlink.go
+++ b/libgo/go/path/filepath/symlink.go
@@ -5,68 +5,113 @@
package filepath
import (
- "bytes"
"errors"
"os"
+ "runtime"
)
-const utf8RuneSelf = 0x80
+// isRoot returns true if path is root of file system
+// (`/` on unix and `/`, `\`, `c:\` or `c:/` on windows).
+func isRoot(path string) bool {
+ if runtime.GOOS != "windows" {
+ return path == "/"
+ }
+ switch len(path) {
+ case 1:
+ return os.IsPathSeparator(path[0])
+ case 3:
+ return path[1] == ':' && os.IsPathSeparator(path[2])
+ }
+ return false
+}
-func walkSymlinks(path string) (string, error) {
- const maxIter = 255
- originalPath := path
- // consume path by taking each frontmost path element,
- // expanding it if it's a symlink, and appending it to b
- var b bytes.Buffer
- for n := 0; path != ""; n++ {
- if n > maxIter {
- return "", errors.New("EvalSymlinks: too many links in " + originalPath)
- }
+// isDriveLetter returns true if path is Windows drive letter (like "c:").
+func isDriveLetter(path string) bool {
+ if runtime.GOOS != "windows" {
+ return false
+ }
+ return len(path) == 2 && path[1] == ':'
+}
- // find next path component, p
- var i = -1
- for j, c := range path {
- if c < utf8RuneSelf && os.IsPathSeparator(uint8(c)) {
- i = j
- break
- }
- }
- var p string
- if i == -1 {
- p, path = path, ""
- } else {
- p, path = path[:i], path[i+1:]
- }
+func walkLink(path string, linksWalked *int) (newpath string, islink bool, err error) {
+ if *linksWalked > 255 {
+ return "", false, errors.New("EvalSymlinks: too many links")
+ }
+ fi, err := os.Lstat(path)
+ if err != nil {
+ return "", false, err
+ }
+ if fi.Mode()&os.ModeSymlink == 0 {
+ return path, false, nil
+ }
+ newpath, err = os.Readlink(path)
+ if err != nil {
+ return "", false, err
+ }
+ *linksWalked++
+ return newpath, true, nil
+}
- if p == "" {
- if b.Len() == 0 {
- // must be absolute path
- b.WriteRune(Separator)
+func walkLinks(path string, linksWalked *int) (string, error) {
+ switch dir, file := Split(path); {
+ case dir == "":
+ newpath, _, err := walkLink(file, linksWalked)
+ return newpath, err
+ case file == "":
+ if isDriveLetter(dir) {
+ return dir, nil
+ }
+ if os.IsPathSeparator(dir[len(dir)-1]) {
+ if isRoot(dir) {
+ return dir, nil
}
- continue
+ return walkLinks(dir[:len(dir)-1], linksWalked)
}
-
- fi, err := os.Lstat(b.String() + p)
+ newpath, _, err := walkLink(dir, linksWalked)
+ return newpath, err
+ default:
+ newdir, err := walkLinks(dir, linksWalked)
if err != nil {
return "", err
}
- if fi.Mode()&os.ModeSymlink == 0 {
- b.WriteString(p)
- if path != "" || (b.Len() == 2 && len(p) == 2 && p[1] == ':') {
- b.WriteRune(Separator)
- }
- continue
+ newpath, islink, err := walkLink(Join(newdir, file), linksWalked)
+ if err != nil {
+ return "", err
+ }
+ if !islink {
+ return newpath, nil
}
+ if IsAbs(newpath) || os.IsPathSeparator(newpath[0]) {
+ return newpath, nil
+ }
+ return Join(newdir, newpath), nil
+ }
+}
- // it's a symlink, put it at the front of path
- dest, err := os.Readlink(b.String() + p)
+func walkSymlinks(path string) (string, error) {
+ if path == "" {
+ return path, nil
+ }
+ var linksWalked int // to protect against cycles
+ for {
+ i := linksWalked
+ newpath, err := walkLinks(path, &linksWalked)
if err != nil {
return "", err
}
- if IsAbs(dest) || os.IsPathSeparator(dest[0]) {
- b.Reset()
+ if runtime.GOOS == "windows" {
+ // walkLinks(".", ...) always retuns "." 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 "."
+ // to the caller (just like unix does).
+ if path == "." && !IsAbs(newpath) {
+ return ".", nil
+ }
+ }
+ if i == linksWalked {
+ return Clean(newpath), nil
}
- path = dest + string(Separator) + path
+ path = newpath
}
- return Clean(b.String()), nil
}
diff --git a/libgo/go/path/filepath/symlink_windows.go b/libgo/go/path/filepath/symlink_windows.go
index 4b38f6fac3f..eb48367ec2b 100644
--- a/libgo/go/path/filepath/symlink_windows.go
+++ b/libgo/go/path/filepath/symlink_windows.go
@@ -51,7 +51,6 @@ func evalSymlinks(path string) (string, error) {
if err != nil {
return "", err
}
-
p, err := toShort(path)
if err != nil {
return "", err
diff --git a/libgo/go/path/path.go b/libgo/go/path/path.go
index 77f2185eaec..01071a9a826 100644
--- a/libgo/go/path/path.go
+++ b/libgo/go/path/path.go
@@ -136,7 +136,7 @@ func Clean(path string) string {
// Split splits path immediately following the final slash,
// separating it into a directory and file name component.
-// If there is no slash path, Split returns an empty dir and
+// If there is no slash in path, Split returns an empty dir and
// file set to path.
// The returned values have the property that path = dir+file.
func Split(path string) (dir, file string) {