1
0
mirror of https://github.com/golang/go synced 2024-11-17 23:04:56 -07:00

path/filepath: add EvalSymlinks function

R=rsc, niemeyer, r2, rog, iant2, r
CC=golang-dev
https://golang.org/cl/4235060
This commit is contained in:
Andrew Gerrand 2011-03-17 16:36:37 +11:00
parent 929449ddaf
commit 75d4cb6a02
2 changed files with 127 additions and 5 deletions

View 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
}

View 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)
}
}
}