lib/ignore: Handle bare commas in ignore patterns (fixes #3042)

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3048
This commit is contained in:
Jakob Borg 2016-05-06 15:45:11 +00:00 committed by Audrius Butkevicius
parent dd5909568f
commit 5d337bb24f
2 changed files with 82 additions and 6 deletions

View File

@ -280,14 +280,14 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([]
// Pattern is rooted in the current dir only
pattern.match, err = glob.Compile(line[1:])
if err != nil {
return fmt.Errorf("invalid pattern %q in ignore file", line)
return fmt.Errorf("invalid pattern %q in ignore file (%v)", line, err)
}
patterns = append(patterns, pattern)
} else if strings.HasPrefix(line, "**/") {
// Add the pattern as is, and without **/ so it matches in current dir
pattern.match, err = glob.Compile(line)
if err != nil {
return fmt.Errorf("invalid pattern %q in ignore file", line)
return fmt.Errorf("invalid pattern %q in ignore file (%v)", line, err)
}
patterns = append(patterns, pattern)
@ -295,7 +295,7 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([]
pattern.pattern = line
pattern.match, err = glob.Compile(line)
if err != nil {
return fmt.Errorf("invalid pattern %q in ignore file", line)
return fmt.Errorf("invalid pattern %q in ignore file (%v)", line, err)
}
patterns = append(patterns, pattern)
} else if strings.HasPrefix(line, "#include ") {
@ -311,7 +311,7 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([]
// current directory and subdirs.
pattern.match, err = glob.Compile(line)
if err != nil {
return fmt.Errorf("invalid pattern %q in ignore file", line)
return fmt.Errorf("invalid pattern %q in ignore file (%v)", line, err)
}
patterns = append(patterns, pattern)
@ -319,7 +319,7 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([]
pattern.pattern = line
pattern.match, err = glob.Compile(line)
if err != nil {
return fmt.Errorf("invalid pattern %q in ignore file", line)
return fmt.Errorf("invalid pattern %q in ignore file (%v)", line, err)
}
patterns = append(patterns, pattern)
}
@ -337,7 +337,7 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([]
continue
}
line = filepath.ToSlash(line)
line = escapeCommas(filepath.ToSlash(line))
switch {
case strings.HasPrefix(line, "#"):
err = addPattern(line)
@ -358,3 +358,49 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([]
return patterns, nil
}
// escapes unescaped commas encountered outside of brackets
func escapeCommas(s string) string {
buf := make([]rune, 0, len(s))
inEscape := false
inBrackets := 0
inSquareBrackets := 0
for _, r := range s {
// Escaped characters are passed on verbatim no matter what, and we
// clear the escape flag for the next character.
if inEscape {
buf = append(buf, r)
inEscape = false
continue
}
// Check for escapes and commas to escape. Also keep track of the
// brackets level by counting start and end brackets of the two
// types.
switch r {
case '\\':
inEscape = true
case '{':
inBrackets++
case '}':
inBrackets--
case '[':
inSquareBrackets++
case ']':
inSquareBrackets--
case ',':
// Commas should be escaped if we're not inside a brackets
// construction, and if they weren't already escaped (in which
// case we'll have taken the first branch way up top).
if inBrackets == 0 && inSquareBrackets == 0 {
buf = append(buf, '\\')
}
}
buf = append(buf, r)
}
return string(buf)
}

View File

@ -635,3 +635,33 @@ func TestAutomaticCaseInsensitivity(t *testing.T) {
}
}
}
func TestCommas(t *testing.T) {
stignore := `
foo,bar.txt
{baz,quux}.txt
`
pats := New(true)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
}
tests := []struct {
name string
match bool
}{
{"foo.txt", false},
{"bar.txt", false},
{"foo,bar.txt", true},
{"baz.txt", true},
{"quux.txt", true},
{"baz,quux.txt", false},
}
for _, tc := range tests {
if pats.Match(tc.name).IsIgnored() != tc.match {
t.Errorf("Match of %s was %v, should be %v", tc.name, !tc.match, tc.match)
}
}
}