Skip to content

Commit 5415d4e

Browse files
committed
util: Glob function
Signed-off-by: Máximo Cuadros <[email protected]>
1 parent e940f8b commit 5415d4e

File tree

2 files changed

+145
-0
lines changed

2 files changed

+145
-0
lines changed

util/glob.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package util
2+
3+
import (
4+
"path/filepath"
5+
"sort"
6+
"strings"
7+
8+
"gopkg.in/src-d/go-billy.v4"
9+
)
10+
11+
// Glob returns the names of all files matching pattern or nil
12+
// if there is no matching file. The syntax of patterns is the same
13+
// as in Match. The pattern may describe hierarchical names such as
14+
// /usr/*/bin/ed (assuming the Separator is '/').
15+
//
16+
// Glob ignores file system errors such as I/O errors reading directories.
17+
// The only possible returned error is ErrBadPattern, when pattern
18+
// is malformed.
19+
//
20+
// Function originally from https://golang.org/src/path/filepath/match_test.go
21+
func Glob(fs billy.Filesystem, pattern string) (matches []string, err error) {
22+
if !hasMeta(pattern) {
23+
if _, err = fs.Lstat(pattern); err != nil {
24+
return nil, nil
25+
}
26+
return []string{pattern}, nil
27+
}
28+
29+
dir, file := filepath.Split(pattern)
30+
// Prevent infinite recursion. See issue 15879.
31+
if dir == pattern {
32+
return nil, filepath.ErrBadPattern
33+
}
34+
35+
var m []string
36+
m, err = Glob(fs, cleanGlobPath(dir))
37+
if err != nil {
38+
return
39+
}
40+
for _, d := range m {
41+
matches, err = glob(fs, d, file, matches)
42+
if err != nil {
43+
return
44+
}
45+
}
46+
return
47+
}
48+
49+
// cleanGlobPath prepares path for glob matching.
50+
func cleanGlobPath(path string) string {
51+
switch path {
52+
case "":
53+
return "."
54+
case string(filepath.Separator):
55+
// do nothing to the path
56+
return path
57+
default:
58+
return path[0 : len(path)-1] // chop off trailing separator
59+
}
60+
}
61+
62+
// glob searches for files matching pattern in the directory dir
63+
// and appends them to matches. If the directory cannot be
64+
// opened, it returns the existing matches. New matches are
65+
// added in lexicographical order.
66+
func glob(fs billy.Filesystem, dir, pattern string, matches []string) (m []string, e error) {
67+
m = matches
68+
fi, err := fs.Stat(dir)
69+
if err != nil {
70+
return
71+
}
72+
73+
if !fi.IsDir() {
74+
return
75+
}
76+
77+
names, _ := readdirnames(fs, dir)
78+
sort.Strings(names)
79+
80+
for _, n := range names {
81+
matched, err := filepath.Match(pattern, n)
82+
if err != nil {
83+
return m, err
84+
}
85+
if matched {
86+
m = append(m, filepath.Join(dir, n))
87+
}
88+
}
89+
return
90+
}
91+
92+
// hasMeta reports whether path contains any of the magic characters
93+
// recognized by Match.
94+
func hasMeta(path string) bool {
95+
// TODO(niemeyer): Should other magic characters be added here?
96+
return strings.ContainsAny(path, "*?[")
97+
}
98+
99+
func readdirnames(fs billy.Filesystem, dir string) ([]string, error) {
100+
files, err := fs.ReadDir(dir)
101+
if err != nil {
102+
return nil, err
103+
}
104+
105+
var names []string
106+
for _, file := range files {
107+
names = append(names, file.Name())
108+
}
109+
110+
return names, nil
111+
}

util/glob_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package util_test
2+
3+
import (
4+
"path/filepath"
5+
"sort"
6+
"testing"
7+
8+
. "gopkg.in/check.v1"
9+
"gopkg.in/src-d/go-billy.v4/memfs"
10+
"gopkg.in/src-d/go-billy.v4/util"
11+
)
12+
13+
func Test(t *testing.T) { TestingT(t) }
14+
15+
var _ = Suite(&UtilSuite{})
16+
17+
type UtilSuite struct{}
18+
19+
func (s *UtilSuite) TestCreate(c *C) {
20+
fs := memfs.New()
21+
util.WriteFile(fs, "foo/qux", nil, 0644)
22+
util.WriteFile(fs, "foo/bar", nil, 0644)
23+
util.WriteFile(fs, "foo/baz/foo", nil, 0644)
24+
25+
names, err := util.Glob(fs, "*/b*")
26+
c.Assert(err, IsNil)
27+
c.Assert(names, HasLen, 2)
28+
sort.Strings(names)
29+
c.Assert(names, DeepEquals, []string{
30+
filepath.Join("foo", "bar"),
31+
filepath.Join("foo", "baz"),
32+
})
33+
34+
}

0 commit comments

Comments
 (0)