summaryrefslogtreecommitdiff
path: root/src/pkg/path/filepath/path.go
diff options
context:
space:
mode:
authorMichael Stapelberg <stapelberg@debian.org>2013-03-04 21:27:36 +0100
committerMichael Stapelberg <michael@stapelberg.de>2013-03-04 21:27:36 +0100
commit04b08da9af0c450d645ab7389d1467308cfc2db8 (patch)
treedb247935fa4f2f94408edc3acd5d0d4f997aa0d8 /src/pkg/path/filepath/path.go
parent917c5fb8ec48e22459d77e3849e6d388f93d3260 (diff)
downloadgolang-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.go130
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)]
+}