Skip to content

Commit

Permalink
Merge pull request #51 from mcuadros/glob
Browse files Browse the repository at this point in the history
util: Glob function
  • Loading branch information
mcuadros authored Feb 2, 2018
2 parents e940f8b + 5415d4e commit 027dcea
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 0 deletions.
111 changes: 111 additions & 0 deletions util/glob.go
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
}
34 changes: 34 additions & 0 deletions util/glob_test.go
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"),
})

}

0 comments on commit 027dcea

Please sign in to comment.