aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/path
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2011-10-26 23:57:58 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2011-10-26 23:57:58 +0000
commitd8f412571f8768df2d3239e72392dfeabbad1559 (patch)
tree19d182df05ead7ff8ba7ee00a7d57555e1383fdf /libgo/go/path
parente0c39d66d4f0607177b1cf8995dda56a667e07b3 (diff)
downloadgcc-d8f412571f8768df2d3239e72392dfeabbad1559.zip
gcc-d8f412571f8768df2d3239e72392dfeabbad1559.tar.gz
gcc-d8f412571f8768df2d3239e72392dfeabbad1559.tar.bz2
Update Go library to last weekly.
From-SVN: r180552
Diffstat (limited to 'libgo/go/path')
-rw-r--r--libgo/go/path/filepath/match.go2
-rw-r--r--libgo/go/path/filepath/match_test.go10
-rw-r--r--libgo/go/path/filepath/path.go154
-rw-r--r--libgo/go/path/filepath/path_test.go264
-rw-r--r--libgo/go/path/filepath/path_unix.go2
-rw-r--r--libgo/go/path/filepath/path_windows.go35
6 files changed, 369 insertions, 98 deletions
diff --git a/libgo/go/path/filepath/match.go b/libgo/go/path/filepath/match.go
index 7fcc214..0ccc87e 100644
--- a/libgo/go/path/filepath/match.go
+++ b/libgo/go/path/filepath/match.go
@@ -215,7 +215,7 @@ func getEsc(chunk string) (r int, nchunk string, err os.Error) {
func Glob(pattern string) (matches []string, err os.Error) {
if !hasMeta(pattern) {
if _, err = os.Stat(pattern); err != nil {
- return
+ return nil, nil
}
return []string{pattern}, nil
}
diff --git a/libgo/go/path/filepath/match_test.go b/libgo/go/path/filepath/match_test.go
index 5fc7b9c..fa7aad9 100644
--- a/libgo/go/path/filepath/match_test.go
+++ b/libgo/go/path/filepath/match_test.go
@@ -125,6 +125,16 @@ func TestGlob(t *testing.T) {
t.Errorf("Glob(%#q) = %#v want %v", tt.pattern, matches, tt.result)
}
}
+ for _, pattern := range []string{"no_match", "../*/no_match"} {
+ matches, err := Glob(pattern)
+ if err != nil {
+ t.Errorf("Glob error for %q: %s", pattern, err)
+ continue
+ }
+ if len(matches) != 0 {
+ t.Errorf("Glob(%#q) = %#v want []", pattern, matches)
+ }
+ }
}
func TestGlobError(t *testing.T) {
diff --git a/libgo/go/path/filepath/path.go b/libgo/go/path/filepath/path.go
index 3d5b915..afb8f10 100644
--- a/libgo/go/path/filepath/path.go
+++ b/libgo/go/path/filepath/path.go
@@ -41,9 +41,12 @@ func Clean(path string) string {
vol := VolumeName(path)
path = path[len(vol):]
if path == "" {
+ if len(vol) > 1 && vol[1] != ':' {
+ // should be UNC
+ return FromSlash(vol)
+ }
return vol + "."
}
-
rooted := os.IsPathSeparator(path[0])
// Invariants:
@@ -144,8 +147,9 @@ func SplitList(path string) []string {
// If there is no Separator in path, Split returns an empty dir
// and file set to path.
func Split(path string) (dir, file string) {
+ vol := VolumeName(path)
i := len(path) - 1
- for i >= 0 && !os.IsPathSeparator(path[i]) {
+ for i >= len(vol) && !os.IsPathSeparator(path[i]) {
i--
}
return path[:i+1], path[i+1:]
@@ -254,38 +258,132 @@ func Abs(path string) (string, os.Error) {
return Join(wd, path), 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.
-type Visitor interface {
- VisitDir(path string, f *os.FileInfo) bool
- VisitFile(path string, f *os.FileInfo)
+// Rel returns a relative path that is lexically equivalent to targpath when
+// joined to basepath with an intervening separator. That is,
+// Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself.
+// An error is returned if targpath can't be made relative to basepath or if
+// knowing the current working directory would be necessary to compute it.
+func Rel(basepath, targpath string) (string, os.Error) {
+ baseVol := VolumeName(basepath)
+ targVol := VolumeName(targpath)
+ base := Clean(basepath)
+ targ := Clean(targpath)
+ if targ == base {
+ return ".", nil
+ }
+ base = base[len(baseVol):]
+ targ = targ[len(targVol):]
+ if base == "." {
+ base = ""
+ }
+ // Can't use IsAbs - `\a` and `a` are both relative in Windows.
+ baseSlashed := len(base) > 0 && base[0] == Separator
+ targSlashed := len(targ) > 0 && targ[0] == Separator
+ if baseSlashed != targSlashed || baseVol != targVol {
+ return "", os.NewError("Rel: can't make " + targ + " relative to " + base)
+ }
+ // Position base[b0:bi] and targ[t0:ti] at the first differing elements.
+ bl := len(base)
+ tl := len(targ)
+ var b0, bi, t0, ti int
+ for {
+ for bi < bl && base[bi] != Separator {
+ bi++
+ }
+ for ti < tl && targ[ti] != Separator {
+ ti++
+ }
+ if targ[t0:ti] != base[b0:bi] {
+ break
+ }
+ if bi < bl {
+ bi++
+ }
+ if ti < tl {
+ ti++
+ }
+ b0 = bi
+ t0 = ti
+ }
+ if base[b0:bi] == ".." {
+ return "", os.NewError("Rel: can't make " + targ + " relative to " + base)
+ }
+ if b0 != bl {
+ // Base elements left. Must go up before going down.
+ seps := strings.Count(base[b0:bl], string(Separator))
+ buf := make([]byte, 3+seps*3+tl-t0)
+ n := copy(buf, "..")
+ for i := 0; i < seps; i++ {
+ buf[n] = Separator
+ copy(buf[n+1:], "..")
+ n += 3
+ }
+ if t0 != tl {
+ buf[n] = Separator
+ copy(buf[n+1:], targ[t0:])
+ }
+ return string(buf), nil
+ }
+ return targ[t0:], nil
}
-func walk(path string, f *os.FileInfo, v Visitor, errors chan<- os.Error) {
- if !f.IsDirectory() {
- v.VisitFile(path, f)
- return
+// SkipDir is used as a return value from WalkFuncs to indicate that
+// the directory named in the call is to be skipped. It is not returned
+// as an error by any function.
+var SkipDir = os.NewError("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.
+type WalkFunc func(path string, info *os.FileInfo, err os.Error) os.Error
+
+// walk recursively descends path, calling w.
+func walk(path string, info *os.FileInfo, walkFn WalkFunc) os.Error {
+ err := walkFn(path, info, nil)
+ if err != nil {
+ if info.IsDirectory() && err == SkipDir {
+ return nil
+ }
+ return err
}
- if !v.VisitDir(path, f) {
- return // skip directory entries
+ if !info.IsDirectory() {
+ return nil
}
list, err := readDir(path)
if err != nil {
- if errors != nil {
- errors <- err
+ return walkFn(path, info, err)
+ }
+
+ for _, fileInfo := range list {
+ if err = walk(Join(path, fileInfo.Name), fileInfo, walkFn); err != nil {
+ return err
}
}
+ return nil
+}
- for _, e := range list {
- walk(Join(path, e.Name), e, v, errors)
+// Walk walks the file tree rooted at root, calling walkFn for each file or
+// directory in the tree, including root. All errors that arise visiting files
+// 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.
+func Walk(root string, walkFn WalkFunc) os.Error {
+ info, err := os.Lstat(root)
+ if err != nil {
+ return walkFn(root, nil, err)
}
+ return walk(root, info, walkFn)
}
// readDir reads the directory named by dirname and returns
-// a list of sorted directory entries.
+// a sorted list of directory entries.
// Copied from io/ioutil to avoid the circular import.
func readDir(dirname string) ([]*os.FileInfo, os.Error) {
f, err := os.Open(dirname)
@@ -312,24 +410,6 @@ func (f fileInfoList) Len() int { return len(f) }
func (f fileInfoList) Less(i, j int) bool { return f[i].Name < f[j].Name }
func (f fileInfoList) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
-// Walk walks the file tree rooted at root, calling v.VisitDir or
-// v.VisitFile for each directory or file in the tree, including root.
-// If v.VisitDir returns false, Walk skips the directory's entries;
-// otherwise it invokes itself for each directory entry in sorted order.
-// An error reading a directory does not abort the Walk.
-// If errors != nil, Walk sends each directory read error
-// to the channel. Otherwise Walk discards the error.
-func Walk(root string, v Visitor, errors chan<- os.Error) {
- f, err := os.Lstat(root)
- if err != nil {
- if errors != nil {
- errors <- err
- }
- return // can't progress
- }
- walk(root, f, v, errors)
-}
-
// Base returns the last element of path.
// Trailing path separators are removed before extracting the last element.
// If the path is empty, Base returns ".".
diff --git a/libgo/go/path/filepath/path_test.go b/libgo/go/path/filepath/path_test.go
index e944bf4..f8e055b 100644
--- a/libgo/go/path/filepath/path_test.go
+++ b/libgo/go/path/filepath/path_test.go
@@ -72,15 +72,23 @@ var wincleantests = []PathTest{
{`c:\abc`, `c:\abc`},
{`c:abc\..\..\.\.\..\def`, `c:..\..\def`},
{`c:\abc\def\..\..`, `c:\`},
+ {`c:\..\abc`, `c:\abc`},
{`c:..\abc`, `c:..\abc`},
{`\`, `\`},
{`/`, `\`},
+ {`\\i\..\c$`, `\c$`},
+ {`\\i\..\i\c$`, `\i\c$`},
+ {`\\i\..\I\c$`, `\I\c$`},
+ {`\\host\share\foo\..\bar`, `\\host\share\bar`},
+ {`//host/share/foo/../baz`, `\\host\share\baz`},
+ {`\\a\b\..\c`, `\\a\b\c`},
+ {`\\a\b`, `\\a\b`},
}
func TestClean(t *testing.T) {
tests := cleantests
if runtime.GOOS == "windows" {
- for i, _ := range tests {
+ for i := range tests {
tests[i].result = filepath.FromSlash(tests[i].result)
}
tests = append(tests, wincleantests...)
@@ -145,9 +153,25 @@ var unixsplittests = []SplitTest{
{"/", "/", ""},
}
+var winsplittests = []SplitTest{
+ {`c:`, `c:`, ``},
+ {`c:/`, `c:/`, ``},
+ {`c:/foo`, `c:/`, `foo`},
+ {`c:/foo/bar`, `c:/foo/`, `bar`},
+ {`//host/share`, `//host/share`, ``},
+ {`//host/share/`, `//host/share/`, ``},
+ {`//host/share/foo`, `//host/share/`, `foo`},
+ {`\\host\share`, `\\host\share`, ``},
+ {`\\host\share\`, `\\host\share\`, ``},
+ {`\\host\share\foo`, `\\host\share\`, `foo`},
+}
+
func TestSplit(t *testing.T) {
var splittests []SplitTest
splittests = unixsplittests
+ if runtime.GOOS == "windows" {
+ splittests = append(splittests, winsplittests...)
+ }
for _, test := range splittests {
if d, f := filepath.Split(test.path); d != test.dir || f != test.file {
t.Errorf("Split(%q) = %q, %q, want %q, %q", test.path, d, f, test.dir, test.file)
@@ -185,6 +209,8 @@ var winjointests = []JoinTest{
{[]string{`C:\Windows\`, ``}, `C:\Windows`},
{[]string{`C:\`, `Windows`}, `C:\Windows`},
{[]string{`C:`, `Windows`}, `C:\Windows`},
+ {[]string{`\\host\share`, `foo`}, `\\host\share\foo`},
+ {[]string{`//host/share`, `foo/bar`}, `\\host\share\foo\bar`},
}
// join takes a []string and passes it to Join.
@@ -279,9 +305,9 @@ func makeTree(t *testing.T) {
func markTree(n *Node) { walkTree(n, "", func(path string, n *Node) { n.mark++ }) }
-func checkMarks(t *testing.T) {
+func checkMarks(t *testing.T, report bool) {
walkTree(tree, tree.name, func(path string, n *Node) {
- if n.mark != 1 {
+ if n.mark != 1 && report {
t.Errorf("node %s mark = %d; expected 1", path, n.mark)
}
n.mark = 0
@@ -289,44 +315,41 @@ func checkMarks(t *testing.T) {
}
// Assumes that each node name is unique. Good enough for a test.
-func mark(name string) {
- name = filepath.ToSlash(name)
+// If clear is true, any incoming error is cleared before return. The errors
+// are always accumulated, though.
+func mark(path string, info *os.FileInfo, err os.Error, errors *[]os.Error, clear bool) os.Error {
+ if err != nil {
+ *errors = append(*errors, err)
+ if clear {
+ return nil
+ }
+ return err
+ }
walkTree(tree, tree.name, func(path string, n *Node) {
- if n.name == name {
+ if n.name == info.Name {
n.mark++
}
})
-}
-
-type TestVisitor struct{}
-
-func (v *TestVisitor) VisitDir(path string, f *os.FileInfo) bool {
- mark(f.Name)
- return true
-}
-
-func (v *TestVisitor) VisitFile(path string, f *os.FileInfo) {
- mark(f.Name)
+ return nil
}
func TestWalk(t *testing.T) {
makeTree(t)
-
- // 1) ignore error handling, expect none
- v := &TestVisitor{}
- filepath.Walk(tree.name, v, nil)
- checkMarks(t)
-
- // 2) handle errors, expect none
- errors := make(chan os.Error, 64)
- filepath.Walk(tree.name, v, errors)
- select {
- case err := <-errors:
+ errors := make([]os.Error, 0, 10)
+ clear := true
+ markFn := func(path string, info *os.FileInfo, err os.Error) os.Error {
+ return mark(path, info, err, &errors, clear)
+ }
+ // Expect no errors.
+ err := filepath.Walk(tree.name, markFn)
+ if err != nil {
t.Errorf("no error expected, found: %s", err)
- default:
- // ok
}
- checkMarks(t)
+ if len(errors) != 0 {
+ t.Errorf("unexpected errors: %s", errors)
+ }
+ checkMarks(t, true)
+ errors = errors[0:0]
// Test permission errors. Only possible if we're not root
// and only on some file systems (AFS, FAT). To avoid errors during
@@ -335,40 +358,50 @@ func TestWalk(t *testing.T) {
// introduce 2 errors: chmod top-level directories to 0
os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0)
os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0)
+
+ // 3) capture errors, expect two.
// mark respective subtrees manually
markTree(tree.entries[1])
markTree(tree.entries[3])
// correct double-marking of directory itself
tree.entries[1].mark--
tree.entries[3].mark--
+ err := filepath.Walk(tree.name, markFn)
+ if err != nil {
+ t.Errorf("expected no error return from Walk, %s", err)
+ }
+ if len(errors) != 2 {
+ t.Errorf("expected 2 errors, got %d: %s", len(errors), errors)
+ }
+ // the inaccessible subtrees were marked manually
+ checkMarks(t, true)
+ errors = errors[0:0]
- // 3) handle errors, expect two
- errors = make(chan os.Error, 64)
- os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0)
- filepath.Walk(tree.name, v, errors)
- Loop:
- for i := 1; i <= 2; i++ {
- select {
- case <-errors:
- // ok
- default:
- t.Errorf("%d. error expected, none found", i)
- break Loop
- }
+ // 4) capture errors, stop after first error.
+ // mark respective subtrees manually
+ markTree(tree.entries[1])
+ markTree(tree.entries[3])
+ // correct double-marking of directory itself
+ tree.entries[1].mark--
+ tree.entries[3].mark--
+ clear = false // error will stop processing
+ err = filepath.Walk(tree.name, markFn)
+ if err == nil {
+ t.Errorf("expected error return from Walk")
}
- select {
- case err := <-errors:
- t.Errorf("only two errors expected, found 3rd: %v", err)
- default:
- // ok
+ if len(errors) != 1 {
+ t.Errorf("expected 1 error, got %d: %s", len(errors), errors)
}
// the inaccessible subtrees were marked manually
- checkMarks(t)
+ checkMarks(t, false)
+ errors = errors[0:0]
+
+ // restore permissions
+ os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770)
+ os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770)
}
// cleanup
- os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770)
- os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770)
if err := os.RemoveAll(tree.name); err != nil {
t.Errorf("removeTree: %v", err)
}
@@ -421,6 +454,8 @@ var winisabstests = []IsAbsTest{
{`\`, false},
{`\Windows`, false},
{`c:a\b`, false},
+ {`\\host\share\foo`, true},
+ {`//host/share/foo/bar`, true},
}
func TestIsAbs(t *testing.T) {
@@ -540,10 +575,11 @@ var abstests = []string{
"pkg/../../AUTHORS",
"Make.pkg",
"pkg/Makefile",
-
- // Already absolute
+ ".",
"$GOROOT/src/Make.pkg",
"$GOROOT/src/../src/Make.pkg",
+ "$GOROOT/misc/cgo",
+ "$GOROOT",
}
func TestAbs(t *testing.T) {
@@ -557,14 +593,15 @@ func TestAbs(t *testing.T) {
os.Chdir(cwd)
for _, path := range abstests {
path = strings.Replace(path, "$GOROOT", goroot, -1)
- abspath, err := filepath.Abs(path)
- if err != nil {
- t.Errorf("Abs(%q) error: %v", path, err)
- }
info, err := os.Stat(path)
if err != nil {
t.Errorf("%s: %s", path, err)
}
+
+ abspath, err := filepath.Abs(path)
+ if err != nil {
+ t.Errorf("Abs(%q) error: %v", path, err)
+ }
absinfo, err := os.Stat(abspath)
if err != nil || absinfo.Ino != info.Ino {
t.Errorf("Abs(%q)=%q, not the same file", path, abspath)
@@ -579,3 +616,114 @@ func TestAbs(t *testing.T) {
}
*/
+
+type RelTests struct {
+ root, path, want string
+}
+
+var reltests = []RelTests{
+ {"a/b", "a/b", "."},
+ {"a/b/.", "a/b", "."},
+ {"a/b", "a/b/.", "."},
+ {"./a/b", "a/b", "."},
+ {"a/b", "./a/b", "."},
+ {"ab/cd", "ab/cde", "../cde"},
+ {"ab/cd", "ab/c", "../c"},
+ {"a/b", "a/b/c/d", "c/d"},
+ {"a/b", "a/b/../c", "../c"},
+ {"a/b/../c", "a/b", "../b"},
+ {"a/b/c", "a/c/d", "../../c/d"},
+ {"a/b", "c/d", "../../c/d"},
+ {"../../a/b", "../../a/b/c/d", "c/d"},
+ {"/a/b", "/a/b", "."},
+ {"/a/b/.", "/a/b", "."},
+ {"/a/b", "/a/b/.", "."},
+ {"/ab/cd", "/ab/cde", "../cde"},
+ {"/ab/cd", "/ab/c", "../c"},
+ {"/a/b", "/a/b/c/d", "c/d"},
+ {"/a/b", "/a/b/../c", "../c"},
+ {"/a/b/../c", "/a/b", "../b"},
+ {"/a/b/c", "/a/c/d", "../../c/d"},
+ {"/a/b", "/c/d", "../../c/d"},
+ {"/../../a/b", "/../../a/b/c/d", "c/d"},
+ {".", "a/b", "a/b"},
+ {".", "..", ".."},
+
+ // can't do purely lexically
+ {"..", ".", "err"},
+ {"..", "a", "err"},
+ {"../..", "..", "err"},
+ {"a", "/a", "err"},
+ {"/a", "a", "err"},
+}
+
+var winreltests = []RelTests{
+ {`C:a\b\c`, `C:a/b/d`, `..\d`},
+ {`C:\`, `D:\`, `err`},
+ {`C:`, `D:`, `err`},
+}
+
+func TestRel(t *testing.T) {
+ tests := append([]RelTests{}, reltests...)
+ if runtime.GOOS == "windows" {
+ for i := range tests {
+ tests[i].want = filepath.FromSlash(tests[i].want)
+ }
+ tests = append(tests, winreltests...)
+ }
+ for _, test := range tests {
+ got, err := filepath.Rel(test.root, test.path)
+ if test.want == "err" {
+ if err == nil {
+ t.Errorf("Rel(%q, %q)=%q, want error", test.root, test.path, got)
+ }
+ continue
+ }
+ if err != nil {
+ t.Errorf("Rel(%q, %q): want %q, got error: %s", test.root, test.path, test.want, err)
+ }
+ if got != test.want {
+ t.Errorf("Rel(%q, %q)=%q, want %q", test.root, test.path, got, test.want)
+ }
+ }
+}
+
+type VolumeNameTest struct {
+ path string
+ vol string
+}
+
+var volumenametests = []VolumeNameTest{
+ {`c:/foo/bar`, `c:`},
+ {`c:`, `c:`},
+ {``, ``},
+ {`\\\host`, ``},
+ {`\\\host\`, ``},
+ {`\\\host\share`, ``},
+ {`\\\host\\share`, ``},
+ {`\\host`, ``},
+ {`//host`, ``},
+ {`\\host\`, ``},
+ {`//host/`, ``},
+ {`\\host\share`, `\\host\share`},
+ {`//host/share`, `//host/share`},
+ {`\\host\share\`, `\\host\share`},
+ {`//host/share/`, `//host/share`},
+ {`\\host\share\foo`, `\\host\share`},
+ {`//host/share/foo`, `//host/share`},
+ {`\\host\share\\foo\\\bar\\\\baz`, `\\host\share`},
+ {`//host/share//foo///bar////baz`, `//host/share`},
+ {`\\host\share\foo\..\bar`, `\\host\share`},
+ {`//host/share/foo/../bar`, `//host/share`},
+}
+
+func TestVolumeName(t *testing.T) {
+ if runtime.GOOS != "windows" {
+ return
+ }
+ for _, v := range volumenametests {
+ if vol := filepath.VolumeName(v.path); vol != v.vol {
+ t.Errorf("VolumeName(%q)=%q, want %q", v.path, vol, v.vol)
+ }
+ }
+}
diff --git a/libgo/go/path/filepath/path_unix.go b/libgo/go/path/filepath/path_unix.go
index b2a4151..daf0eb2 100644
--- a/libgo/go/path/filepath/path_unix.go
+++ b/libgo/go/path/filepath/path_unix.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build darwin freebsd linux openbsd
+
package filepath
import "strings"
diff --git a/libgo/go/path/filepath/path_windows.go b/libgo/go/path/filepath/path_windows.go
index 2535697..9692fd9 100644
--- a/libgo/go/path/filepath/path_windows.go
+++ b/libgo/go/path/filepath/path_windows.go
@@ -4,7 +4,13 @@
package filepath
-import "strings"
+import (
+ "strings"
+)
+
+func isSlash(c uint8) bool {
+ return c == '\\' || c == '/'
+}
// IsAbs returns true if the path is absolute.
func IsAbs(path string) (b bool) {
@@ -16,11 +22,12 @@ func IsAbs(path string) (b bool) {
if path == "" {
return false
}
- return path[0] == '/' || path[0] == '\\'
+ return isSlash(path[0])
}
// 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) {
if len(path) < 2 {
@@ -33,6 +40,30 @@ func VolumeName(path string) (v string) {
'A' <= c && c <= 'Z') {
return path[:2]
}
+ // is it UNC
+ if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
+ !isSlash(path[2]) && path[2] != '.' {
+ // first, leading `\\` and next shouldn't be `\`. its server name.
+ for n := 3; n < l-1; n++ {
+ // second, next '\' shouldn't be repeated.
+ if isSlash(path[n]) {
+ n++
+ // third, following something characters. its share name.
+ if !isSlash(path[n]) {
+ if path[n] == '.' {
+ break
+ }
+ for ; n < l; n++ {
+ if isSlash(path[n]) {
+ break
+ }
+ }
+ return path[:n]
+ }
+ break
+ }
+ }
+ }
return ""
}