forked from cloudfoundry/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
glob.go
108 lines (93 loc) · 2.38 KB
/
glob.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package glob
import (
"regexp"
"strings"
)
// Glob holds a Unix-style glob pattern in a compiled form for efficient
// matching against paths.
//
// Glob notation:
// - `?` matches a single char in a single path component
// - `*` matches zero or more chars in a single path component
// - `**` matches zero or more chars in zero or more components
// - any other sequence matches itself
type Glob struct {
pattern string // original glob pattern
regexp *regexp.Regexp // compiled regexp
}
const charPat = `[^/]`
func mustBuildRe(p string) *regexp.Regexp {
return regexp.MustCompile(`^/$|^(` + p + `+)?(/` + p + `+)*$`)
}
var globRe = mustBuildRe(`(` + charPat + `|[\*\?])`)
// Supports unix/ruby-style glob patterns:
// - `?` matches a single char in a single path component
// - `*` matches zero or more chars in a single path component
// - `**` matches zero or more chars in zero or more components
func translateGlob(pat string) (string, error) {
if !globRe.MatchString(pat) {
return "", Error(pat)
}
outs := make([]string, len(pat))
i, double := 0, false
for _, c := range pat {
switch c {
default:
outs[i] = string(c)
double = false
case '.', '+', '-', '^', '$', '[', ']', '(', ')':
outs[i] = `\` + string(c)
double = false
case '?':
outs[i] = `[^/]`
double = false
case '*':
if double {
outs[i-1] = `.*`
} else {
outs[i] = `[^/]*`
}
double = !double
}
i++
}
outs = outs[0:i]
return "^" + strings.Join(outs, "") + "$", nil
}
// CompileGlob translates pat into a form more convenient for
// matching against paths in the store.
func CompileGlob(pat string) (glob Glob, err error) {
pat = toSlash(pat)
s, err := translateGlob(pat)
if err != nil {
return
}
r, err := regexp.Compile(s)
if err != nil {
return
}
glob = Glob{pat, r}
return
}
// MustCompileGlob is like CompileGlob, but it panics if an error occurs,
// simplifying safe initialization of global variables holding glob patterns.
func MustCompileGlob(pat string) Glob {
g, err := CompileGlob(pat)
if err != nil {
panic(err)
}
return g
}
func (g Glob) String() string {
return g.pattern
}
func (g Glob) Match(path string) bool {
return g.regexp.MatchString(toSlash(path))
}
type Error string
func (e Error) Error() string {
return "invalid glob pattern: " + string(e)
}
func toSlash(path string) string {
return strings.Replace(path, "\\", "/", -1)
}