main
Raw Download raw file
  1package util
  2
  3import (
  4	"path/filepath"
  5	"sort"
  6	"strings"
  7
  8	"github.com/go-git/go-billy/v5"
  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
 21func 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.
 50func 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.
 66func 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.
 94func hasMeta(path string) bool {
 95	// TODO(niemeyer): Should other magic characters be added here?
 96	return strings.ContainsAny(path, "*?[")
 97}
 98
 99func 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}