diff --git a/storage/pkg/fileutils/fileutils.go b/storage/pkg/fileutils/fileutils.go index 10aa0b82ac..f472c27b90 100644 --- a/storage/pkg/fileutils/fileutils.go +++ b/storage/pkg/fileutils/fileutils.go @@ -164,9 +164,18 @@ func (pm *PatternMatcher) Patterns() []*Pattern { return pm.patterns } -// Pattern defines a single regexp used to filter file paths. +type matchType int + +const ( + unknownMatch matchType = iota + regexpMatch + suffixMatch +) + +// Pattern defines a single pattern used to filter file paths. type Pattern struct { cleanedPattern string + matchType matchType regexp *regexp.Regexp exclusion bool } @@ -181,18 +190,26 @@ func (p *Pattern) Exclusion() bool { } func (p *Pattern) match(path string) (bool, error) { - if p.regexp == nil { + if p.matchType == unknownMatch { if err := p.compile(); err != nil { return false, filepath.ErrBadPattern } } - b := p.regexp.MatchString(path) - - return b, nil + switch p.matchType { + case regexpMatch: + return p.regexp.MatchString(path), nil + case suffixMatch: + suffix := p.cleanedPattern[2:] + return strings.HasSuffix(path, suffix), nil + default: + return false, fmt.Errorf("unknown match type: %d", p.matchType) + } } func (p *Pattern) compile() error { + p.matchType = regexpMatch + var regStrBuilder strings.Builder regStrBuilder.WriteString("^") @@ -209,17 +226,21 @@ func (p *Pattern) compile() error { escSL += bs } - for scan.Peek() != scanner.EOF { + for i := 0; scan.Peek() != scanner.EOF; i++ { ch := scan.Next() if ch == '*' { + p.matchType = regexpMatch + if scan.Peek() == '*' { // is some flavor of "**" scan.Next() + slashEaten := false // Treat **/ as ** so eat the "/" if string(scan.Peek()) == sl { scan.Next() + slashEaten = true } if scan.Peek() == scanner.EOF { @@ -233,6 +254,10 @@ func (p *Pattern) compile() error { regStrBuilder.WriteString(escSL) regStrBuilder.WriteString(")?") } + + if i == 0 && !slashEaten { + p.matchType = suffixMatch + } } else { // is "*" so map it to anything but "/" regStrBuilder.WriteString("[^") @@ -240,6 +265,8 @@ func (p *Pattern) compile() error { regStrBuilder.WriteString("]*") } } else if ch == '?' { + p.matchType = regexpMatch + // "?" is any char except "/" regStrBuilder.WriteString("[^") regStrBuilder.WriteString(escSL) @@ -259,12 +286,18 @@ func (p *Pattern) compile() error { continue } if scan.Peek() != scanner.EOF { + p.matchType = regexpMatch + regStrBuilder.WriteString(bs) regStrBuilder.WriteRune(scan.Next()) } else { return filepath.ErrBadPattern } } else { + if ch == '[' || ch == ']' { + p.matchType = regexpMatch + } + regStrBuilder.WriteRune(ch) } } diff --git a/storage/pkg/fileutils/fileutils_test.go b/storage/pkg/fileutils/fileutils_test.go index 7e9f6afa65..43051b124c 100644 --- a/storage/pkg/fileutils/fileutils_test.go +++ b/storage/pkg/fileutils/fileutils_test.go @@ -356,6 +356,8 @@ func TestMatches(t *testing.T) { {"abc/**", "abc/def/ghi", false, true}, {"**/.foo", ".foo", false, true}, {"**/.foo", "bar.foo", false, false}, + {"**.md", "README.md", false, true}, + {"**.md", "foo/README.md", false, true}, } if runtime.GOOS != windows {