]> Cypherpunks repositories - gostls13.git/commitdiff
path/filepath: add EvalSymlinks function
authorAndrew Gerrand <adg@golang.org>
Thu, 17 Mar 2011 05:36:37 +0000 (16:36 +1100)
committerAndrew Gerrand <adg@golang.org>
Thu, 17 Mar 2011 05:36:37 +0000 (16:36 +1100)
R=rsc, niemeyer, r2, rog, iant2, r
CC=golang-dev
https://golang.org/cl/4235060

src/pkg/path/filepath/path.go
src/pkg/path/filepath/path_test.go

index 64cef291e65883cce18d6ba84ca75485b7444540..6cd6cf2ab094034a4ac55ad8befbbae83c6a185a 100644 (file)
@@ -8,11 +8,17 @@
 package filepath
 
 import (
+       "bytes"
        "os"
        "sort"
        "strings"
 )
 
+const (
+       SeparatorString     = string(Separator)
+       ListSeparatorString = string(ListSeparator)
+)
+
 // Clean returns the shortest path name equivalent to path
 // by purely lexical processing.  It applies the following rules
 // iteratively until no further processing can be done:
@@ -113,7 +119,7 @@ func ToSlash(path string) string {
        if Separator == '/' {
                return path
        }
-       return strings.Replace(path, string(Separator), "/", -1)
+       return strings.Replace(path, SeparatorString, "/", -1)
 }
 
 // FromSlash returns the result of replacing each slash ('/') character
@@ -122,7 +128,7 @@ func FromSlash(path string) string {
        if Separator == '/' {
                return path
        }
-       return strings.Replace(path, "/", string(Separator), -1)
+       return strings.Replace(path, "/", SeparatorString, -1)
 }
 
 // SplitList splits a list of paths joined by the OS-specific ListSeparator.
@@ -130,7 +136,7 @@ func SplitList(path string) []string {
        if path == "" {
                return []string{}
        }
-       return strings.Split(path, string(ListSeparator), -1)
+       return strings.Split(path, ListSeparatorString, -1)
 }
 
 // Split splits path immediately following the final Separator,
@@ -150,7 +156,7 @@ func Split(path string) (dir, file string) {
 func Join(elem ...string) string {
        for i, e := range elem {
                if e != "" {
-                       return Clean(strings.Join(elem[i:], string(Separator)))
+                       return Clean(strings.Join(elem[i:], SeparatorString))
                }
        }
        return ""
@@ -169,6 +175,62 @@ func Ext(path string) string {
        return ""
 }
 
+// EvalSymlinks returns the path name after the evaluation of any symbolic
+// links.
+// If path is relative it will be evaluated relative to the current directory.
+func EvalSymlinks(path string) (string, os.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 "", os.NewError("EvalSymlinks: too many links in " + originalPath)
+               }
+
+               // find next path component, p
+               i := strings.IndexRune(path, Separator)
+               var p string
+               if i == -1 {
+                       p, path = path, ""
+               } else {
+                       p, path = path[:i], path[i+1:]
+               }
+
+               if p == "" {
+                       if b.Len() == 0 {
+                               // must be absolute path
+                               b.WriteRune(Separator)
+                       }
+                       continue
+               }
+
+               fi, err := os.Lstat(b.String() + p)
+               if err != nil {
+                       return "", err
+               }
+               if !fi.IsSymlink() {
+                       b.WriteString(p)
+                       if path != "" {
+                               b.WriteRune(Separator)
+                       }
+                       continue
+               }
+
+               // it's a symlink, put it at the front of path
+               dest, err := os.Readlink(b.String() + p)
+               if err != nil {
+                       return "", err
+               }
+               if IsAbs(dest) {
+                       b.Reset()
+               }
+               path = dest + SeparatorString + path
+       }
+       return Clean(b.String()), 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.
@@ -267,7 +329,7 @@ func Base(path string) string {
        }
        // If empty now, it had only slashes.
        if path == "" {
-               return string(Separator)
+               return SeparatorString
        }
        return path
 }
index c23cb6c0ec6b75ef1639efd2a4799ae8b1a0eb51..e07b6b0c6fb51b6b1c0e0de1edfe765fe318b8eb 100644 (file)
@@ -414,3 +414,63 @@ func TestIsAbs(t *testing.T) {
                }
        }
 }
+
+type EvalSymlinksTest struct {
+       path, dest string
+}
+
+var EvalSymlinksTestDirs = []EvalSymlinksTest{
+       {"test", ""},
+       {"test/dir", ""},
+       {"test/dir/link3", "../../"},
+       {"test/link1", "../test"},
+       {"test/link2", "dir"},
+}
+
+var EvalSymlinksTests = []EvalSymlinksTest{
+       {"test", "test"},
+       {"test/dir", "test/dir"},
+       {"test/dir/../..", "."},
+       {"test/link1", "test"},
+       {"test/link2", "test/dir"},
+       {"test/link1/dir", "test/dir"},
+       {"test/link2/..", "test"},
+       {"test/dir/link3", "."},
+       {"test/link2/link3/test", "test"},
+}
+
+func TestEvalSymlinks(t *testing.T) {
+       defer os.RemoveAll("test")
+       for _, d := range EvalSymlinksTestDirs {
+               var err os.Error
+               if d.dest == "" {
+                       err = os.Mkdir(d.path, 0755)
+               } else {
+                       err = os.Symlink(d.dest, d.path)
+               }
+               if err != nil {
+                       t.Fatal(err)
+               }
+       }
+       // relative
+       for _, d := range EvalSymlinksTests {
+               if p, err := filepath.EvalSymlinks(d.path); err != nil {
+                       t.Errorf("EvalSymlinks(%v) error: %v", d.path, err)
+               } else if p != d.dest {
+                       t.Errorf("EvalSymlinks(%v)=%v, want %v", d.path, p, d.dest)
+               }
+       }
+       // absolute
+       testroot := filepath.Join(os.Getenv("GOROOT"), "src", "pkg", "path", "filepath")
+       for _, d := range EvalSymlinksTests {
+               a := EvalSymlinksTest{
+                       filepath.Join(testroot, d.path),
+                       filepath.Join(testroot, d.dest),
+               }
+               if p, err := filepath.EvalSymlinks(a.path); err != nil {
+                       t.Errorf("EvalSymlinks(%v) error: %v", a.path, err)
+               } else if p != a.dest {
+                       t.Errorf("EvalSymlinks(%v)=%v, want %v", a.path, p, a.dest)
+               }
+       }
+}