filter: make [^...] in globs exclude / and document it
A glob like file[^a].txt was translated to (^|/)file[^a]\.txt$ in path mode, so it matched file/.txt across a directory boundary, unlike ? and the [!...] form. Treat a leading ^ in a character class the same as a leading !: in path mode it now also excludes /, and [^] is rejected as an empty character class. Document both negation forms in the filtering grammar.
This commit is contained in:
1 parent
c434df94d1
commit
0de32e2269
4 files changed
+33
-21
No files matched your search
@@ -43,7 +43,7 @@ Rclone matching rules follow a glob style:
|
||||
* matches any sequence of non-separator (/) characters
|
||||
** matches any sequence of characters including / separators
|
||||
? matches any single non-separator (/) character
|
||||
[ [ ! ] { character-range } ]
|
||||
[ [ ! | ^ ] { character-range } ]
|
||||
character class (must be non-empty)
|
||||
{ pattern-list }
|
||||
pattern alternatives
|
||||
|
||||
+19
-15
@@ -627,22 +627,26 @@ func TestNewFilterMatchesRegexp(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewFilterMatchesNegatedCharacterClass(t *testing.T) {
|
||||
f, err := NewFilter(nil)
|
||||
require.NoError(t, err)
|
||||
add := func(s string) {
|
||||
err := f.AddRule(s)
|
||||
require.NoError(t, err)
|
||||
for _, negate := range []string{"!", "^"} {
|
||||
t.Run(negate, func(t *testing.T) {
|
||||
f, err := NewFilter(nil)
|
||||
require.NoError(t, err)
|
||||
add := func(s string) {
|
||||
err := f.AddRule(s)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
add("+ file[" + negate + "a].txt")
|
||||
add("- *")
|
||||
testInclude(t, f, []includeTest{
|
||||
{"fileb.txt", 100, 0, true},
|
||||
{"sub/fileb.txt", 100, 0, true},
|
||||
{"filea.txt", 100, 0, false},
|
||||
{"file" + negate + ".txt", 100, 0, true},
|
||||
{"file/.txt", 100, 0, false},
|
||||
})
|
||||
assert.False(t, f.InActive())
|
||||
})
|
||||
}
|
||||
add("+ file[!a].txt")
|
||||
add("- *")
|
||||
testInclude(t, f, []includeTest{
|
||||
{"fileb.txt", 100, 0, true},
|
||||
{"sub/fileb.txt", 100, 0, true},
|
||||
{"filea.txt", 100, 0, false},
|
||||
{"file!.txt", 100, 0, true},
|
||||
{"file/.txt", 100, 0, false},
|
||||
})
|
||||
assert.False(t, f.InActive())
|
||||
}
|
||||
|
||||
func TestNewFilterHashFilter(t *testing.T) {
|
||||
|
||||
+5
-5
@@ -86,7 +86,7 @@ func globToRegexp(glob string, pathMode bool, addAnchors bool, ignoreCase bool)
|
||||
braceDepth := 0
|
||||
inBrackets := 0
|
||||
bracketStart := false // just opened a character class
|
||||
bracketNegated := false // just read the ! of a [! character class
|
||||
bracketNegated := false // just read the ! or ^ of a [! or [^ character class
|
||||
slashed := false
|
||||
inRegexp := false // inside {{ ... }}
|
||||
inRegexpEnd := false // have received }} waiting for more
|
||||
@@ -127,10 +127,10 @@ func globToRegexp(glob string, pathMode bool, addAnchors bool, ignoreCase bool)
|
||||
}
|
||||
}
|
||||
if inBrackets > 0 {
|
||||
if bracketStart && c == '!' {
|
||||
// [!...] is a negated character class in glob
|
||||
// syntax, which is [^...] in a regexp. Like ?, it
|
||||
// does not match the / separator in path mode.
|
||||
if bracketStart && (c == '!' || c == '^') {
|
||||
// [!...] and [^...] are negated character classes
|
||||
// in glob syntax, which is [^...] in a regexp. Like
|
||||
// ?, they do not match the / separator in path mode.
|
||||
bracketStart = false
|
||||
bracketNegated = true
|
||||
_ = re.WriteByte('^')
|
||||
|
||||
@@ -25,6 +25,10 @@ func TestGlobStringToRegexp(t *testing.T) {
|
||||
{`potat[![:alpha:]]or`, `potat[^[:alpha:]]or`, ``},
|
||||
{`potat[a!]`, `potat[a!]`, ``},
|
||||
{`potat[!]`, ``, `empty character class`},
|
||||
{`potat[^oa]`, `potat[^oa]`, ``},
|
||||
{`potat[^[:alpha:]]or`, `potat[^[:alpha:]]or`, ``},
|
||||
{`potat[a^]`, `potat[a^]`, ``},
|
||||
{`potat[^]`, ``, `empty character class`},
|
||||
{`'.' '+' '(' ')' '|' '^' '$'`, `'\.' '\+' '\(' '\)' '\|' '\^' '\$'`, ``},
|
||||
{`*.jpg`, `.*\.jpg`, ``},
|
||||
{`a{b,c,d}e`, `a(b|c|d)e`, ``},
|
||||
@@ -99,6 +103,10 @@ func TestGlobPathToRegexp(t *testing.T) {
|
||||
{`potat[![:alpha:]]or`, `(^|/)potat[^/[:alpha:]]or$`, ``},
|
||||
{`potat[a!]`, `(^|/)potat[a!]$`, ``},
|
||||
{`potat[!]`, ``, `empty character class`},
|
||||
{`potat[^oa]`, `(^|/)potat[^/oa]$`, ``},
|
||||
{`potat[^[:alpha:]]or`, `(^|/)potat[^/[:alpha:]]or$`, ``},
|
||||
{`potat[a^]`, `(^|/)potat[a^]$`, ``},
|
||||
{`potat[^]`, ``, `empty character class`},
|
||||
{`'.' '+' '(' ')' '|' '^' '$'`, `(^|/)'\.' '\+' '\(' '\)' '\|' '\^' '\$'$`, ``},
|
||||
{`*.jpg`, `(^|/)[^/]*\.jpg$`, ``},
|
||||
{`a{b,c,d}e`, `(^|/)a(b|c|d)e$`, ``},
|
||||
|
||||
Reference in new issue
Block a user