80 lines
1.9 KiB
Go
80 lines
1.9 KiB
Go
package policy
|
|
|
|
import "fmt"
|
|
|
|
type glob string
|
|
|
|
// parseGlob ensures that the pattern conforms to the spec: only '*' and escaped '\*' are allowed.
|
|
func parseGlob(pattern string) (glob, error) {
|
|
for i := 0; i < len(pattern); i++ {
|
|
if pattern[i] == '*' {
|
|
continue
|
|
}
|
|
if pattern[i] == '\\' && i+1 < len(pattern) && pattern[i+1] == '*' {
|
|
i++ // skip the escaped '*'
|
|
continue
|
|
}
|
|
if pattern[i] == '\\' && i+1 < len(pattern) {
|
|
i++ // skip the escaped character
|
|
continue
|
|
}
|
|
if pattern[i] == '\\' {
|
|
return "", fmt.Errorf("invalid escape sequence")
|
|
}
|
|
}
|
|
|
|
return glob(pattern), nil
|
|
}
|
|
|
|
func mustParseGlob(pattern string) glob {
|
|
g, err := parseGlob(pattern)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return g
|
|
}
|
|
|
|
// Match matches a string against the glob pattern with * wildcards, handling escaped '\*' literals.
|
|
func (pattern glob) Match(str string) bool {
|
|
// i is the index for the pattern
|
|
// j is the index for the string
|
|
var i, j int
|
|
|
|
// starIdx keeps track of the position of the last * in the pattern.
|
|
// matchIdx keeps track of the position in the string where the last * matched.
|
|
var starIdx, matchIdx int = -1, -1
|
|
|
|
for j < len(str) {
|
|
if i < len(pattern) && (pattern[i] == str[j] || pattern[i] == '\\' && i+1 < len(pattern) && pattern[i+1] == str[j]) {
|
|
// characters match or if there's an escaped character that matches
|
|
if pattern[i] == '\\' {
|
|
// skip the escape character
|
|
i++
|
|
}
|
|
i++
|
|
j++
|
|
} else if i < len(pattern) && pattern[i] == '*' {
|
|
// there's a * wildcard in the pattern
|
|
starIdx = i
|
|
matchIdx = j
|
|
i++
|
|
} else if starIdx != -1 {
|
|
// there's a previous * wildcard, backtrack
|
|
i = starIdx + 1
|
|
matchIdx++
|
|
j = matchIdx
|
|
} else {
|
|
// no match found
|
|
return false
|
|
}
|
|
}
|
|
|
|
// check for remaining characters in the pattern
|
|
for i < len(pattern) && pattern[i] == '*' {
|
|
i++
|
|
}
|
|
|
|
// the entire pattern is processed, it's a match
|
|
return i == len(pattern)
|
|
}
|