-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #51 from mcuadros/glob
util: Glob function
- Loading branch information
Showing
2 changed files
with
145 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
package util | ||
|
||
import ( | ||
"path/filepath" | ||
"sort" | ||
"strings" | ||
|
||
"gopkg.in/src-d/go-billy.v4" | ||
) | ||
|
||
// 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 (assuming the Separator is '/'). | ||
// | ||
// Glob ignores file system errors such as I/O errors reading directories. | ||
// The only possible returned error is ErrBadPattern, when pattern | ||
// is malformed. | ||
// | ||
// Function originally from https://golang.org/src/path/filepath/match_test.go | ||
func Glob(fs billy.Filesystem, pattern string) (matches []string, err error) { | ||
if !hasMeta(pattern) { | ||
if _, err = fs.Lstat(pattern); err != nil { | ||
return nil, nil | ||
} | ||
return []string{pattern}, nil | ||
} | ||
|
||
dir, file := filepath.Split(pattern) | ||
// Prevent infinite recursion. See issue 15879. | ||
if dir == pattern { | ||
return nil, filepath.ErrBadPattern | ||
} | ||
|
||
var m []string | ||
m, err = Glob(fs, cleanGlobPath(dir)) | ||
if err != nil { | ||
return | ||
} | ||
for _, d := range m { | ||
matches, err = glob(fs, d, file, matches) | ||
if err != nil { | ||
return | ||
} | ||
} | ||
return | ||
} | ||
|
||
// cleanGlobPath prepares path for glob matching. | ||
func cleanGlobPath(path string) string { | ||
switch path { | ||
case "": | ||
return "." | ||
case string(filepath.Separator): | ||
// do nothing to the path | ||
return path | ||
default: | ||
return path[0 : len(path)-1] // chop off trailing separator | ||
} | ||
} | ||
|
||
// glob searches for files matching pattern in the directory dir | ||
// and appends them to matches. If the directory cannot be | ||
// opened, it returns the existing matches. New matches are | ||
// added in lexicographical order. | ||
func glob(fs billy.Filesystem, dir, pattern string, matches []string) (m []string, e error) { | ||
m = matches | ||
fi, err := fs.Stat(dir) | ||
if err != nil { | ||
return | ||
} | ||
|
||
if !fi.IsDir() { | ||
return | ||
} | ||
|
||
names, _ := readdirnames(fs, dir) | ||
sort.Strings(names) | ||
|
||
for _, n := range names { | ||
matched, err := filepath.Match(pattern, n) | ||
if err != nil { | ||
return m, err | ||
} | ||
if matched { | ||
m = append(m, filepath.Join(dir, n)) | ||
} | ||
} | ||
return | ||
} | ||
|
||
// hasMeta reports whether path contains any of the magic characters | ||
// recognized by Match. | ||
func hasMeta(path string) bool { | ||
// TODO(niemeyer): Should other magic characters be added here? | ||
return strings.ContainsAny(path, "*?[") | ||
} | ||
|
||
func readdirnames(fs billy.Filesystem, dir string) ([]string, error) { | ||
files, err := fs.ReadDir(dir) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var names []string | ||
for _, file := range files { | ||
names = append(names, file.Name()) | ||
} | ||
|
||
return names, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package util_test | ||
|
||
import ( | ||
"path/filepath" | ||
"sort" | ||
"testing" | ||
|
||
. "gopkg.in/check.v1" | ||
"gopkg.in/src-d/go-billy.v4/memfs" | ||
"gopkg.in/src-d/go-billy.v4/util" | ||
) | ||
|
||
func Test(t *testing.T) { TestingT(t) } | ||
|
||
var _ = Suite(&UtilSuite{}) | ||
|
||
type UtilSuite struct{} | ||
|
||
func (s *UtilSuite) TestCreate(c *C) { | ||
fs := memfs.New() | ||
util.WriteFile(fs, "foo/qux", nil, 0644) | ||
util.WriteFile(fs, "foo/bar", nil, 0644) | ||
util.WriteFile(fs, "foo/baz/foo", nil, 0644) | ||
|
||
names, err := util.Glob(fs, "*/b*") | ||
c.Assert(err, IsNil) | ||
c.Assert(names, HasLen, 2) | ||
sort.Strings(names) | ||
c.Assert(names, DeepEquals, []string{ | ||
filepath.Join("foo", "bar"), | ||
filepath.Join("foo", "baz"), | ||
}) | ||
|
||
} |