mirror of
https://github.com/golang/go
synced 2024-11-18 01:14:48 -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:
parent
929449ddaf
commit
75d4cb6a02
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user