summaryrefslogtreecommitdiff
path: root/src/pkg/path
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-01-17 12:40:45 +0100
committerOndřej Surý <ondrej@sury.org>2011-01-17 12:40:45 +0100
commit3e45412327a2654a77944249962b3652e6142299 (patch)
treebc3bf69452afa055423cbe0c5cfa8ca357df6ccf /src/pkg/path
parentc533680039762cacbc37db8dc7eed074c3e497be (diff)
downloadgolang-upstream/2011.01.12.tar.gz
Imported Upstream version 2011.01.12upstream/2011.01.12
Diffstat (limited to 'src/pkg/path')
-rw-r--r--src/pkg/path/Makefile16
-rw-r--r--src/pkg/path/match.go74
-rw-r--r--src/pkg/path/match_test.go128
-rw-r--r--src/pkg/path/path.go20
-rw-r--r--src/pkg/path/path_test.go176
-rw-r--r--src/pkg/path/path_unix.go11
-rw-r--r--src/pkg/path/path_windows.go11
7 files changed, 306 insertions, 130 deletions
diff --git a/src/pkg/path/Makefile b/src/pkg/path/Makefile
index 9372cdf37..4371913e8 100644
--- a/src/pkg/path/Makefile
+++ b/src/pkg/path/Makefile
@@ -2,11 +2,25 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-include ../../Make.$(GOARCH)
+include ../../Make.inc
TARG=path
GOFILES=\
match.go\
path.go\
+GOFILES_freebsd=\
+ path_unix.go
+
+GOFILES_darwin=\
+ path_unix.go
+
+GOFILES_linux=\
+ path_unix.go
+
+GOFILES_windows=\
+ path_windows.go
+
+GOFILES+=$(GOFILES_$(GOOS))
+
include ../../Make.pkg
diff --git a/src/pkg/path/match.go b/src/pkg/path/match.go
index e3cf08cae..dd3422c42 100644
--- a/src/pkg/path/match.go
+++ b/src/pkg/path/match.go
@@ -2,6 +2,7 @@ package path
import (
"os"
+ "sort"
"strings"
"utf8"
)
@@ -202,3 +203,76 @@ func getEsc(chunk string) (r int, nchunk string, err os.Error) {
}
return
}
+
+// Glob returns the names of all files matching pattern or nil
+// if there is no matching file. The syntax of patterns is the same
+// as in Match. The pattern may describe hierarchical names such as
+// /usr/*/bin/ed.
+//
+func Glob(pattern string) (matches []string) {
+ if !hasMeta(pattern) {
+ if _, err := os.Stat(pattern); err == nil {
+ return []string{pattern}
+ }
+ return nil
+ }
+
+ dir, file := Split(pattern)
+ switch dir {
+ case "":
+ dir = "."
+ case "/":
+ // nothing
+ default:
+ dir = dir[0 : len(dir)-1] // chop off trailing '/'
+ }
+
+ if hasMeta(dir) {
+ for _, d := range Glob(dir) {
+ matches = glob(d, file, matches)
+ }
+ } else {
+ return glob(dir, file, nil)
+ }
+ return matches
+}
+
+// glob searches for files matching pattern in the directory dir
+// and appends them to matches.
+func glob(dir, pattern string, matches []string) []string {
+ fi, err := os.Stat(dir)
+ if err != nil {
+ return nil
+ }
+ if !fi.IsDirectory() {
+ return matches
+ }
+ d, err := os.Open(dir, os.O_RDONLY, 0666)
+ if err != nil {
+ return nil
+ }
+ defer d.Close()
+
+ names, err := d.Readdirnames(-1)
+ if err != nil {
+ return nil
+ }
+ sort.SortStrings(names)
+
+ for _, n := range names {
+ matched, err := Match(pattern, n)
+ if err != nil {
+ return matches
+ }
+ if matched {
+ matches = append(matches, Join(dir, n))
+ }
+ }
+ return matches
+}
+
+// hasMeta returns true if path contains any of the magic characters
+// recognized by Match.
+func hasMeta(path string) bool {
+ return strings.IndexAny(path, "*?[") != -1
+}
diff --git a/src/pkg/path/match_test.go b/src/pkg/path/match_test.go
index c02384f92..a1bf508e3 100644
--- a/src/pkg/path/match_test.go
+++ b/src/pkg/path/match_test.go
@@ -16,62 +16,90 @@ type MatchTest struct {
}
var matchTests = []MatchTest{
- MatchTest{"abc", "abc", true, nil},
- MatchTest{"*", "abc", true, nil},
- MatchTest{"*c", "abc", true, nil},
- MatchTest{"a*", "a", true, nil},
- MatchTest{"a*", "abc", true, nil},
- MatchTest{"a*", "ab/c", false, nil},
- MatchTest{"a*/b", "abc/b", true, nil},
- MatchTest{"a*/b", "a/c/b", false, nil},
- MatchTest{"a*b*c*d*e*/f", "axbxcxdxe/f", true, nil},
- MatchTest{"a*b*c*d*e*/f", "axbxcxdxexxx/f", true, nil},
- MatchTest{"a*b*c*d*e*/f", "axbxcxdxe/xxx/f", false, nil},
- MatchTest{"a*b*c*d*e*/f", "axbxcxdxexxx/fff", false, nil},
- MatchTest{"a*b?c*x", "abxbbxdbxebxczzx", true, nil},
- MatchTest{"a*b?c*x", "abxbbxdbxebxczzy", false, nil},
- MatchTest{"ab[c]", "abc", true, nil},
- MatchTest{"ab[b-d]", "abc", true, nil},
- MatchTest{"ab[e-g]", "abc", false, nil},
- MatchTest{"ab[^c]", "abc", false, nil},
- MatchTest{"ab[^b-d]", "abc", false, nil},
- MatchTest{"ab[^e-g]", "abc", true, nil},
- MatchTest{"a\\*b", "a*b", true, nil},
- MatchTest{"a\\*b", "ab", false, nil},
- MatchTest{"a?b", "a☺b", true, nil},
- MatchTest{"a[^a]b", "a☺b", true, nil},
- MatchTest{"a???b", "a☺b", false, nil},
- MatchTest{"a[^a][^a][^a]b", "a☺b", false, nil},
- MatchTest{"[a-ζ]*", "α", true, nil},
- MatchTest{"*[a-ζ]", "A", false, nil},
- MatchTest{"a?b", "a/b", false, nil},
- MatchTest{"a*b", "a/b", false, nil},
- MatchTest{"[\\]a]", "]", true, nil},
- MatchTest{"[\\-]", "-", true, nil},
- MatchTest{"[x\\-]", "x", true, nil},
- MatchTest{"[x\\-]", "-", true, nil},
- MatchTest{"[x\\-]", "z", false, nil},
- MatchTest{"[\\-x]", "x", true, nil},
- MatchTest{"[\\-x]", "-", true, nil},
- MatchTest{"[\\-x]", "a", false, nil},
- MatchTest{"[]a]", "]", false, ErrBadPattern},
- MatchTest{"[-]", "-", false, ErrBadPattern},
- MatchTest{"[x-]", "x", false, ErrBadPattern},
- MatchTest{"[x-]", "-", false, ErrBadPattern},
- MatchTest{"[x-]", "z", false, ErrBadPattern},
- MatchTest{"[-x]", "x", false, ErrBadPattern},
- MatchTest{"[-x]", "-", false, ErrBadPattern},
- MatchTest{"[-x]", "a", false, ErrBadPattern},
- MatchTest{"\\", "a", false, ErrBadPattern},
- MatchTest{"[a-b-c]", "a", false, ErrBadPattern},
- MatchTest{"*x", "xxx", true, nil},
+ {"abc", "abc", true, nil},
+ {"*", "abc", true, nil},
+ {"*c", "abc", true, nil},
+ {"a*", "a", true, nil},
+ {"a*", "abc", true, nil},
+ {"a*", "ab/c", false, nil},
+ {"a*/b", "abc/b", true, nil},
+ {"a*/b", "a/c/b", false, nil},
+ {"a*b*c*d*e*/f", "axbxcxdxe/f", true, nil},
+ {"a*b*c*d*e*/f", "axbxcxdxexxx/f", true, nil},
+ {"a*b*c*d*e*/f", "axbxcxdxe/xxx/f", false, nil},
+ {"a*b*c*d*e*/f", "axbxcxdxexxx/fff", false, nil},
+ {"a*b?c*x", "abxbbxdbxebxczzx", true, nil},
+ {"a*b?c*x", "abxbbxdbxebxczzy", false, nil},
+ {"ab[c]", "abc", true, nil},
+ {"ab[b-d]", "abc", true, nil},
+ {"ab[e-g]", "abc", false, nil},
+ {"ab[^c]", "abc", false, nil},
+ {"ab[^b-d]", "abc", false, nil},
+ {"ab[^e-g]", "abc", true, nil},
+ {"a\\*b", "a*b", true, nil},
+ {"a\\*b", "ab", false, nil},
+ {"a?b", "a☺b", true, nil},
+ {"a[^a]b", "a☺b", true, nil},
+ {"a???b", "a☺b", false, nil},
+ {"a[^a][^a][^a]b", "a☺b", false, nil},
+ {"[a-ζ]*", "α", true, nil},
+ {"*[a-ζ]", "A", false, nil},
+ {"a?b", "a/b", false, nil},
+ {"a*b", "a/b", false, nil},
+ {"[\\]a]", "]", true, nil},
+ {"[\\-]", "-", true, nil},
+ {"[x\\-]", "x", true, nil},
+ {"[x\\-]", "-", true, nil},
+ {"[x\\-]", "z", false, nil},
+ {"[\\-x]", "x", true, nil},
+ {"[\\-x]", "-", true, nil},
+ {"[\\-x]", "a", false, nil},
+ {"[]a]", "]", false, ErrBadPattern},
+ {"[-]", "-", false, ErrBadPattern},
+ {"[x-]", "x", false, ErrBadPattern},
+ {"[x-]", "-", false, ErrBadPattern},
+ {"[x-]", "z", false, ErrBadPattern},
+ {"[-x]", "x", false, ErrBadPattern},
+ {"[-x]", "-", false, ErrBadPattern},
+ {"[-x]", "a", false, ErrBadPattern},
+ {"\\", "a", false, ErrBadPattern},
+ {"[a-b-c]", "a", false, ErrBadPattern},
+ {"*x", "xxx", true, nil},
}
func TestMatch(t *testing.T) {
for _, tt := range matchTests {
ok, err := Match(tt.pattern, tt.s)
if ok != tt.match || err != tt.err {
- t.Errorf("Match(%#q, %#q) = %v, %v want %v, nil\n", tt.pattern, tt.s, ok, err, tt.match)
+ t.Errorf("Match(%#q, %#q) = %v, %v want %v, nil", tt.pattern, tt.s, ok, err, tt.match)
+ }
+ }
+}
+
+// contains returns true if vector contains the string s.
+func contains(vector []string, s string) bool {
+ for _, elem := range vector {
+ if elem == s {
+ return true
+ }
+ }
+ return false
+}
+
+var globTests = []struct {
+ pattern, result string
+}{
+ {"match.go", "match.go"},
+ {"mat?h.go", "match.go"},
+ {"*", "match.go"},
+ {"../*/match.go", "../path/match.go"},
+}
+
+func TestGlob(t *testing.T) {
+ for _, tt := range globTests {
+ matches := Glob(tt.pattern)
+ if !contains(matches, tt.result) {
+ t.Errorf("Glob(%#q) = %#v want %v", tt.pattern, matches, tt.result)
}
}
}
diff --git a/src/pkg/path/path.go b/src/pkg/path/path.go
index 9c1d09374..61eea8858 100644
--- a/src/pkg/path/path.go
+++ b/src/pkg/path/path.go
@@ -102,17 +102,13 @@ func Clean(path string) string {
return string(buf[0:w])
}
-// Split splits path immediately following the final slash,
+// Split splits path immediately following the final path separator,
// separating it into a directory and file name component.
-// If there is no slash in path, DirFile returns an empty dir and
+// If there is no separator in path, Split returns an empty dir and
// file set to path.
func Split(path string) (dir, file string) {
- for i := len(path) - 1; i >= 0; i-- {
- if path[i] == '/' {
- return path[0 : i+1], path[i+1:]
- }
- }
- return "", path
+ i := strings.LastIndexAny(path, PathSeps)
+ return path[:i+1], path[i+1:]
}
// Join joins any number of path elements into a single path, adding a
@@ -140,7 +136,7 @@ func Ext(path string) string {
}
// Visitor methods are invoked for corresponding file tree entries
-// visited by Walk. The parameter path is the full path of d relative
+// 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
@@ -208,3 +204,9 @@ func Base(name string) string {
}
return name
}
+
+// IsAbs returns true if the path is absolute.
+func IsAbs(path string) bool {
+ // TODO: Add Windows support
+ return strings.HasPrefix(path, "/")
+}
diff --git a/src/pkg/path/path_test.go b/src/pkg/path/path_test.go
index 6915b48bb..6b4be07a9 100644
--- a/src/pkg/path/path_test.go
+++ b/src/pkg/path/path_test.go
@@ -6,6 +6,7 @@ package path
import (
"os"
+ "runtime"
"testing"
)
@@ -15,52 +16,52 @@ type CleanTest struct {
var cleantests = []CleanTest{
// Already clean
- CleanTest{"", "."},
- CleanTest{"abc", "abc"},
- CleanTest{"abc/def", "abc/def"},
- CleanTest{"a/b/c", "a/b/c"},
- CleanTest{".", "."},
- CleanTest{"..", ".."},
- CleanTest{"../..", "../.."},
- CleanTest{"../../abc", "../../abc"},
- CleanTest{"/abc", "/abc"},
- CleanTest{"/", "/"},
+ {"", "."},
+ {"abc", "abc"},
+ {"abc/def", "abc/def"},
+ {"a/b/c", "a/b/c"},
+ {".", "."},
+ {"..", ".."},
+ {"../..", "../.."},
+ {"../../abc", "../../abc"},
+ {"/abc", "/abc"},
+ {"/", "/"},
// Remove trailing slash
- CleanTest{"abc/", "abc"},
- CleanTest{"abc/def/", "abc/def"},
- CleanTest{"a/b/c/", "a/b/c"},
- CleanTest{"./", "."},
- CleanTest{"../", ".."},
- CleanTest{"../../", "../.."},
- CleanTest{"/abc/", "/abc"},
+ {"abc/", "abc"},
+ {"abc/def/", "abc/def"},
+ {"a/b/c/", "a/b/c"},
+ {"./", "."},
+ {"../", ".."},
+ {"../../", "../.."},
+ {"/abc/", "/abc"},
// Remove doubled slash
- CleanTest{"abc//def//ghi", "abc/def/ghi"},
- CleanTest{"//abc", "/abc"},
- CleanTest{"///abc", "/abc"},
- CleanTest{"//abc//", "/abc"},
- CleanTest{"abc//", "abc"},
+ {"abc//def//ghi", "abc/def/ghi"},
+ {"//abc", "/abc"},
+ {"///abc", "/abc"},
+ {"//abc//", "/abc"},
+ {"abc//", "abc"},
// Remove . elements
- CleanTest{"abc/./def", "abc/def"},
- CleanTest{"/./abc/def", "/abc/def"},
- CleanTest{"abc/.", "abc"},
+ {"abc/./def", "abc/def"},
+ {"/./abc/def", "/abc/def"},
+ {"abc/.", "abc"},
// Remove .. elements
- CleanTest{"abc/def/ghi/../jkl", "abc/def/jkl"},
- CleanTest{"abc/def/../ghi/../jkl", "abc/jkl"},
- CleanTest{"abc/def/..", "abc"},
- CleanTest{"abc/def/../..", "."},
- CleanTest{"/abc/def/../..", "/"},
- CleanTest{"abc/def/../../..", ".."},
- CleanTest{"/abc/def/../../..", "/"},
- CleanTest{"abc/def/../../../ghi/jkl/../../../mno", "../../mno"},
+ {"abc/def/ghi/../jkl", "abc/def/jkl"},
+ {"abc/def/../ghi/../jkl", "abc/jkl"},
+ {"abc/def/..", "abc"},
+ {"abc/def/../..", "."},
+ {"/abc/def/../..", "/"},
+ {"abc/def/../../..", ".."},
+ {"/abc/def/../../..", "/"},
+ {"abc/def/../../../ghi/jkl/../../../mno", "../../mno"},
// Combinations
- CleanTest{"abc/./../def", "def"},
- CleanTest{"abc//./../def", "def"},
- CleanTest{"abc/../../././../def", "../../def"},
+ {"abc/./../def", "def"},
+ {"abc//./../def", "def"},
+ {"abc/../../././../def", "../../def"},
}
func TestClean(t *testing.T) {
@@ -76,14 +77,25 @@ type SplitTest struct {
}
var splittests = []SplitTest{
- SplitTest{"a/b", "a/", "b"},
- SplitTest{"a/b/", "a/b/", ""},
- SplitTest{"a/", "a/", ""},
- SplitTest{"a", "", "a"},
- SplitTest{"/", "/", ""},
+ {"a/b", "a/", "b"},
+ {"a/b/", "a/b/", ""},
+ {"a/", "a/", ""},
+ {"a", "", "a"},
+ {"/", "/", ""},
+}
+
+var winsplittests = []SplitTest{
+ {`C:\Windows\System32`, `C:\Windows\`, `System32`},
+ {`C:\Windows\`, `C:\Windows\`, ``},
+ {`C:\Windows`, `C:\`, `Windows`},
+ {`C:Windows`, `C:`, `Windows`},
+ {`\\?\c:\`, `\\?\c:\`, ``},
}
func TestSplit(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ splittests = append(splittests, winsplittests...)
+ }
for _, test := range splittests {
if d, f := 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)
@@ -98,27 +110,27 @@ type JoinTest struct {
var jointests = []JoinTest{
// zero parameters
- JoinTest{[]string{}, ""},
+ {[]string{}, ""},
// one parameter
- JoinTest{[]string{""}, ""},
- JoinTest{[]string{"a"}, "a"},
+ {[]string{""}, ""},
+ {[]string{"a"}, "a"},
// two parameters
- JoinTest{[]string{"a", "b"}, "a/b"},
- JoinTest{[]string{"a", ""}, "a"},
- JoinTest{[]string{"", "b"}, "b"},
- JoinTest{[]string{"/", "a"}, "/a"},
- JoinTest{[]string{"/", ""}, "/"},
- JoinTest{[]string{"a/", "b"}, "a/b"},
- JoinTest{[]string{"a/", ""}, "a"},
- JoinTest{[]string{"", ""}, ""},
+ {[]string{"a", "b"}, "a/b"},
+ {[]string{"a", ""}, "a"},
+ {[]string{"", "b"}, "b"},
+ {[]string{"/", "a"}, "/a"},
+ {[]string{"/", ""}, "/"},
+ {[]string{"a/", "b"}, "a/b"},
+ {[]string{"a/", ""}, "a"},
+ {[]string{"", ""}, ""},
}
// join takes a []string and passes it to Join.
func join(elem []string, args ...string) string {
args = elem
- return Join(args)
+ return Join(args...)
}
func TestJoin(t *testing.T) {
@@ -134,11 +146,11 @@ type ExtTest struct {
}
var exttests = []ExtTest{
- ExtTest{"path.go", ".go"},
- ExtTest{"path.pb.go", ".go"},
- ExtTest{"a.dir/b", ""},
- ExtTest{"a.dir/b.go", ".go"},
- ExtTest{"a.dir/", ""},
+ {"path.go", ".go"},
+ {"path.pb.go", ".go"},
+ {"a.dir/b", ""},
+ {"a.dir/b.go", ".go"},
+ {"a.dir/", ""},
}
func TestExt(t *testing.T) {
@@ -245,7 +257,7 @@ func TestWalk(t *testing.T) {
errors := make(chan os.Error, 64)
Walk(tree.name, v, errors)
if err, ok := <-errors; ok {
- t.Errorf("no error expected, found: s", err)
+ t.Errorf("no error expected, found: %s", err)
}
checkMarks(t)
@@ -287,17 +299,17 @@ func TestWalk(t *testing.T) {
var basetests = []CleanTest{
// Already clean
- CleanTest{"", "."},
- CleanTest{".", "."},
- CleanTest{"/.", "."},
- CleanTest{"/", "/"},
- CleanTest{"////", "/"},
- CleanTest{"x/", "x"},
- CleanTest{"abc", "abc"},
- CleanTest{"abc/def", "def"},
- CleanTest{"a/b/.x", ".x"},
- CleanTest{"a/b/c.", "c."},
- CleanTest{"a/b/c.x", "c.x"},
+ {"", "."},
+ {".", "."},
+ {"/.", "."},
+ {"/", "/"},
+ {"////", "/"},
+ {"x/", "x"},
+ {"abc", "abc"},
+ {"abc/def", "def"},
+ {"a/b/.x", ".x"},
+ {"a/b/c.", "c."},
+ {"a/b/c.x", "c.x"},
}
func TestBase(t *testing.T) {
@@ -307,3 +319,27 @@ func TestBase(t *testing.T) {
}
}
}
+
+type IsAbsTest struct {
+ path string
+ isAbs bool
+}
+
+var isAbsTests = []IsAbsTest{
+ {"", false},
+ {"/", true},
+ {"/usr/bin/gcc", true},
+ {"..", false},
+ {"/a/../bb", true},
+ {".", false},
+ {"./", false},
+ {"lala", false},
+}
+
+func TestIsAbs(t *testing.T) {
+ for _, test := range isAbsTests {
+ if r := IsAbs(test.path); r != test.isAbs {
+ t.Errorf("IsAbs(%q) = %v, want %v", test.path, r, test.isAbs)
+ }
+ }
+}
diff --git a/src/pkg/path/path_unix.go b/src/pkg/path/path_unix.go
new file mode 100644
index 000000000..7e8c5eb8b
--- /dev/null
+++ b/src/pkg/path/path_unix.go
@@ -0,0 +1,11 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package path
+
+const (
+ DirSeps = `/` // directory separators
+ VolumeSeps = `` // volume separators
+ PathSeps = DirSeps + VolumeSeps // all path separators
+)
diff --git a/src/pkg/path/path_windows.go b/src/pkg/path/path_windows.go
new file mode 100644
index 000000000..966eb49fb
--- /dev/null
+++ b/src/pkg/path/path_windows.go
@@ -0,0 +1,11 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package path
+
+const (
+ DirSeps = `\/` // directory separators
+ VolumeSeps = `:` // volume separators
+ PathSeps = DirSeps + VolumeSeps // all path separators
+)