diff options
author | Michael Stapelberg <stapelberg@debian.org> | 2013-03-04 21:27:36 +0100 |
---|---|---|
committer | Michael Stapelberg <michael@stapelberg.de> | 2013-03-04 21:27:36 +0100 |
commit | 04b08da9af0c450d645ab7389d1467308cfc2db8 (patch) | |
tree | db247935fa4f2f94408edc3acd5d0d4f997aa0d8 /src/pkg/path/filepath/path.go | |
parent | 917c5fb8ec48e22459d77e3849e6d388f93d3260 (diff) | |
download | golang-upstream/1.1_hg20130304.tar.gz |
Imported Upstream version 1.1~hg20130304upstream/1.1_hg20130304
Diffstat (limited to 'src/pkg/path/filepath/path.go')
-rw-r--r-- | src/pkg/path/filepath/path.go | 130 |
1 files changed, 88 insertions, 42 deletions
diff --git a/src/pkg/path/filepath/path.go b/src/pkg/path/filepath/path.go index 815021bd0..f8c7e4b2f 100644 --- a/src/pkg/path/filepath/path.go +++ b/src/pkg/path/filepath/path.go @@ -13,6 +13,45 @@ import ( "strings" ) +// A lazybuf is a lazily constructed path buffer. +// It supports append, reading previously appended bytes, +// and retrieving the final string. It does not allocate a buffer +// to hold the output until that output diverges from s. +type lazybuf struct { + path string + buf []byte + w int + volAndPath string + volLen int +} + +func (b *lazybuf) index(i int) byte { + if b.buf != nil { + return b.buf[i] + } + return b.path[i] +} + +func (b *lazybuf) append(c byte) { + if b.buf == nil { + if b.w < len(b.path) && b.path[b.w] == c { + b.w++ + return + } + b.buf = make([]byte, len(b.path)) + copy(b.buf, b.path[:b.w]) + } + b.buf[b.w] = c + b.w++ +} + +func (b *lazybuf) string() string { + if b.buf == nil { + return b.volAndPath[:b.volLen+b.w] + } + return b.volAndPath[:b.volLen] + string(b.buf[:b.w]) +} + const ( Separator = os.PathSeparator ListSeparator = os.PathListSeparator @@ -40,14 +79,15 @@ const ( // Getting Dot-Dot Right,'' // http://plan9.bell-labs.com/sys/doc/lexnames.html func Clean(path string) string { - vol := VolumeName(path) - path = path[len(vol):] + originalPath := path + volLen := volumeNameLen(path) + path = path[volLen:] if path == "" { - if len(vol) > 1 && vol[1] != ':' { + if volLen > 1 && originalPath[1] != ':' { // should be UNC - return FromSlash(vol) + return FromSlash(originalPath) } - return vol + "." + return originalPath + "." } rooted := os.IsPathSeparator(path[0]) @@ -57,11 +97,11 @@ func Clean(path string) string { // dotdot is index in buf where .. must stop, either because // it is the leading slash or it is a leading ../../.. prefix. n := len(path) - buf := []byte(path) - r, w, dotdot := 0, 0, 0 + out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen} + r, dotdot := 0, 0 if rooted { - buf[0] = Separator - r, w, dotdot = 1, 1, 1 + out.append(Separator) + r, dotdot = 1, 1 } for r < n { @@ -76,46 +116,40 @@ func Clean(path string) string { // .. element: remove to last separator r += 2 switch { - case w > dotdot: + case out.w > dotdot: // can backtrack - w-- - for w > dotdot && !os.IsPathSeparator(buf[w]) { - w-- + out.w-- + for out.w > dotdot && !os.IsPathSeparator(out.index(out.w)) { + out.w-- } case !rooted: // cannot backtrack, but not rooted, so append .. element. - if w > 0 { - buf[w] = Separator - w++ + if out.w > 0 { + out.append(Separator) } - buf[w] = '.' - w++ - buf[w] = '.' - w++ - dotdot = w + out.append('.') + out.append('.') + dotdot = out.w } default: // real path element. // add slash if needed - if rooted && w != 1 || !rooted && w != 0 { - buf[w] = Separator - w++ + if rooted && out.w != 1 || !rooted && out.w != 0 { + out.append(Separator) } // copy element for ; r < n && !os.IsPathSeparator(path[r]); r++ { - buf[w] = path[r] - w++ + out.append(path[r]) } } } // Turn empty string into "." - if w == 0 { - buf[w] = '.' - w++ + if out.w == 0 { + out.append('.') } - return FromSlash(vol + string(buf[0:w])) + return FromSlash(out.string()) } // ToSlash returns the result of replacing each separator character @@ -142,10 +176,7 @@ func FromSlash(path string) string { // usually found in PATH or GOPATH environment variables. // Unlike strings.Split, SplitList returns an empty slice when passed an empty string. func SplitList(path string) []string { - if path == "" { - return []string{} - } - return strings.Split(path, string(ListSeparator)) + return splitList(path) } // Split splits path immediately following the final Separator, @@ -291,13 +322,18 @@ func Rel(basepath, targpath string) (string, error) { var SkipDir = errors.New("skip this directory") // WalkFunc is the type of the function called for each file or directory -// visited by Walk. If there was a problem walking to the file or directory -// named by path, the incoming error will describe the problem and the -// function can decide how to handle that error (and Walk will not descend -// into that directory). If an error is returned, processing stops. The -// sole exception is that if path is a directory and the function returns the -// special value SkipDir, the contents of the directory are skipped -// and processing continues as usual on the next file. +// visited by Walk. The path argument contains the argument to Walk as a +// prefix; that is, if Walk is called with "dir", which is a directory +// containing the file "a", the walk function will be called with argument +// "dir/a". The info argument is the os.FileInfo for the named path. +// +// If there was a problem walking to the file or directory named by path, the +// incoming error will describe the problem and the function can decide how +// to handle that error (and Walk will not descend into that directory). If +// an error is returned, processing stops. The sole exception is that if path +// is a directory and the function returns the special value SkipDir, the +// contents of the directory are skipped and processing continues as usual on +// the next file. type WalkFunc func(path string, info os.FileInfo, err error) error // walk recursively descends path, calling w. @@ -335,6 +371,7 @@ func walk(path string, info os.FileInfo, walkFn WalkFunc) error { // and directories are filtered by walkFn. The files are walked in lexical // order, which makes the output deterministic but means that for very // large directories Walk can be inefficient. +// Walk does not follow symbolic links. func Walk(root string, walkFn WalkFunc) error { info, err := os.Lstat(root) if err != nil { @@ -397,7 +434,8 @@ func Base(path string) string { } // Dir returns all but the last element of path, typically the path's directory. -// Trailing path separators are removed before processing. +// After dropping the final element, the path is Cleaned and trailing +// slashes are removed. // If the path is empty, Dir returns ".". // If the path consists entirely of separators, Dir returns a single separator. // The returned path does not end in a separator unless it is the root directory. @@ -417,3 +455,11 @@ func Dir(path string) string { } return vol + dir } + +// VolumeName returns leading volume name. +// Given "C:\foo\bar" it returns "C:" under windows. +// Given "\\host\share\foo" it returns "\\host\share". +// On other platforms it returns "". +func VolumeName(path string) (v string) { + return path[:volumeNameLen(path)] +} |