diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-01-17 12:40:45 +0100 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-01-17 12:40:45 +0100 |
commit | 3e45412327a2654a77944249962b3652e6142299 (patch) | |
tree | bc3bf69452afa055423cbe0c5cfa8ca357df6ccf /src/pkg/path | |
parent | c533680039762cacbc37db8dc7eed074c3e497be (diff) | |
download | golang-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/Makefile | 16 | ||||
-rw-r--r-- | src/pkg/path/match.go | 74 | ||||
-rw-r--r-- | src/pkg/path/match_test.go | 128 | ||||
-rw-r--r-- | src/pkg/path/path.go | 20 | ||||
-rw-r--r-- | src/pkg/path/path_test.go | 176 | ||||
-rw-r--r-- | src/pkg/path/path_unix.go | 11 | ||||
-rw-r--r-- | src/pkg/path/path_windows.go | 11 |
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 +) |