118 lines
2.3 KiB
Go
118 lines
2.3 KiB
Go
|
// rsync style glob parser
|
||
|
|
||
|
package fs
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"regexp"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// globToRegexp converts an rsync style glob to a regexp
|
||
|
//
|
||
|
// documented in filtering.md
|
||
|
func globToRegexp(glob string) (*regexp.Regexp, error) {
|
||
|
var re bytes.Buffer
|
||
|
if strings.HasPrefix(glob, "/") {
|
||
|
glob = glob[1:]
|
||
|
_, _ = re.WriteRune('^')
|
||
|
} else {
|
||
|
_, _ = re.WriteString("(^|/)")
|
||
|
}
|
||
|
consecutiveStars := 0
|
||
|
insertStars := func() error {
|
||
|
if consecutiveStars > 0 {
|
||
|
switch consecutiveStars {
|
||
|
case 1:
|
||
|
_, _ = re.WriteString(`[^/]*`)
|
||
|
case 2:
|
||
|
_, _ = re.WriteString(`.*`)
|
||
|
default:
|
||
|
return fmt.Errorf("too many stars in %q", glob)
|
||
|
}
|
||
|
}
|
||
|
consecutiveStars = 0
|
||
|
return nil
|
||
|
}
|
||
|
inBraces := false
|
||
|
inBrackets := 0
|
||
|
slashed := false
|
||
|
for _, c := range glob {
|
||
|
if slashed {
|
||
|
_, _ = re.WriteRune(c)
|
||
|
slashed = false
|
||
|
continue
|
||
|
}
|
||
|
if c != '*' {
|
||
|
err := insertStars()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
}
|
||
|
if inBrackets > 0 {
|
||
|
_, _ = re.WriteRune(c)
|
||
|
if c == '[' {
|
||
|
inBrackets++
|
||
|
}
|
||
|
if c == ']' {
|
||
|
inBrackets--
|
||
|
}
|
||
|
continue
|
||
|
}
|
||
|
switch c {
|
||
|
case '\\':
|
||
|
_, _ = re.WriteRune(c)
|
||
|
slashed = true
|
||
|
case '*':
|
||
|
consecutiveStars++
|
||
|
case '?':
|
||
|
_, _ = re.WriteString(`[^/]`)
|
||
|
case '[':
|
||
|
_, _ = re.WriteRune(c)
|
||
|
inBrackets++
|
||
|
case ']':
|
||
|
return nil, fmt.Errorf("mismatched ']' in glob %q", glob)
|
||
|
case '{':
|
||
|
if inBraces {
|
||
|
return nil, fmt.Errorf("can't nest '{' '}' in glob %q", glob)
|
||
|
}
|
||
|
inBraces = true
|
||
|
_, _ = re.WriteRune('(')
|
||
|
case '}':
|
||
|
if !inBraces {
|
||
|
return nil, fmt.Errorf("mismatched '{' and '}' in glob %q", glob)
|
||
|
}
|
||
|
_, _ = re.WriteRune(')')
|
||
|
inBraces = false
|
||
|
case ',':
|
||
|
if inBraces {
|
||
|
_, _ = re.WriteRune('|')
|
||
|
} else {
|
||
|
_, _ = re.WriteRune(c)
|
||
|
}
|
||
|
case '.', '+', '(', ')', '|', '^', '$': // regexp meta characters not dealt with above
|
||
|
_, _ = re.WriteRune('\\')
|
||
|
_, _ = re.WriteRune(c)
|
||
|
default:
|
||
|
_, _ = re.WriteRune(c)
|
||
|
}
|
||
|
}
|
||
|
err := insertStars()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if inBrackets > 0 {
|
||
|
return nil, fmt.Errorf("mismatched '[' and ']' in glob %q", glob)
|
||
|
}
|
||
|
if inBraces {
|
||
|
return nil, fmt.Errorf("mismatched '{' and '}' in glob %q", glob)
|
||
|
}
|
||
|
_, _ = re.WriteRune('$')
|
||
|
result, err := regexp.Compile(re.String())
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("Bad glob pattern %q: %v (%q)", glob, err, re.String())
|
||
|
}
|
||
|
return result, nil
|
||
|
}
|