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:
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
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.
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,
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 ""
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.
}
// If empty now, it had only slashes.
if path == "" {
- return string(Separator)
+ return SeparatorString
}
return path
}
}
}
}
+
+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)
+ }
+ }
+}