From e3e1036dda48ad21d543dc53811db4b5d9f0be6f Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Wed, 2 Sep 2015 20:54:18 +0200 Subject: [PATCH] Correctly handle (?i) in ignores (fixes #1953) --- lib/fnmatch/fnmatch.go | 8 -------- lib/fnmatch/fnmatch_test.go | 4 ---- lib/ignore/ignore.go | 16 +++++++++++----- test/ignore_test.go | 20 ++++++++++---------- 4 files changed, 21 insertions(+), 27 deletions(-) diff --git a/lib/fnmatch/fnmatch.go b/lib/fnmatch/fnmatch.go index 398930455..cc5fa1e91 100644 --- a/lib/fnmatch/fnmatch.go +++ b/lib/fnmatch/fnmatch.go @@ -38,14 +38,6 @@ func Convert(pattern string, flags int) (*regexp.Regexp, error) { } } - // Support case insensitive ignores. We do the loop because we may in some - // circumstances end up with multiple insensitivity prefixes - // ("(?i)(?i)foo"), which should be accepted. - for ignore := strings.TrimPrefix(pattern, "(?i)"); ignore != pattern; ignore = strings.TrimPrefix(pattern, "(?i)") { - flags |= CaseFold - pattern = ignore - } - if flags&NoEscape != 0 { pattern = strings.Replace(pattern, "\\", "\\\\", -1) } else { diff --git a/lib/fnmatch/fnmatch_test.go b/lib/fnmatch/fnmatch_test.go index 10f989090..17679e538 100644 --- a/lib/fnmatch/fnmatch_test.go +++ b/lib/fnmatch/fnmatch_test.go @@ -53,10 +53,6 @@ var testcases = []testcase{ {"**/foo.txt", "bar/baz/foo.txt", PathName, true}, {"foo.txt", "foo.TXT", CaseFold, true}, - {"(?i)foo.txt", "foo.TXT", 0, true}, - {"(?i)(?i)foo.txt", "foo.TXT", 0, true}, // repeated prefix should be fine - {"(?i)**foo.txt", "/dev/tmp/foo.TXT", 0, true}, - {"(?i)!**foo.txt", "/dev/tmp/foo.TXT", 0, false}, // These characters are literals in glob, but not in regexp. {"hey$hello", "hey$hello", 0, true}, diff --git a/lib/ignore/ignore.go b/lib/ignore/ignore.go index fc50f9a83..8c8cabf2c 100644 --- a/lib/ignore/ignore.go +++ b/lib/ignore/ignore.go @@ -206,22 +206,28 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([] include = false } + flags := fnmatch.PathName + if strings.HasPrefix(line, "(?i)") { + line = line[4:] + flags |= fnmatch.CaseFold + } + if strings.HasPrefix(line, "/") { // Pattern is rooted in the current dir only - exp, err := fnmatch.Convert(line[1:], fnmatch.PathName) + exp, err := fnmatch.Convert(line[1:], flags) if err != nil { return fmt.Errorf("Invalid pattern %q in ignore file", line) } patterns = append(patterns, Pattern{exp, include}) } else if strings.HasPrefix(line, "**/") { // Add the pattern as is, and without **/ so it matches in current dir - exp, err := fnmatch.Convert(line, fnmatch.PathName) + exp, err := fnmatch.Convert(line, flags) if err != nil { return fmt.Errorf("Invalid pattern %q in ignore file", line) } patterns = append(patterns, Pattern{exp, include}) - exp, err = fnmatch.Convert(line[3:], fnmatch.PathName) + exp, err = fnmatch.Convert(line[3:], flags) if err != nil { return fmt.Errorf("Invalid pattern %q in ignore file", line) } @@ -236,13 +242,13 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([] } else { // Path name or pattern, add it so it matches files both in // current directory and subdirs. - exp, err := fnmatch.Convert(line, fnmatch.PathName) + exp, err := fnmatch.Convert(line, flags) if err != nil { return fmt.Errorf("Invalid pattern %q in ignore file", line) } patterns = append(patterns, Pattern{exp, include}) - exp, err = fnmatch.Convert("**/"+line, fnmatch.PathName) + exp, err = fnmatch.Convert("**/"+line, flags) if err != nil { return fmt.Errorf("Invalid pattern %q in ignore file", line) } diff --git a/test/ignore_test.go b/test/ignore_test.go index 32e9be804..8fc8edfdb 100644 --- a/test/ignore_test.go +++ b/test/ignore_test.go @@ -31,10 +31,17 @@ func TestIgnores(t *testing.T) { // Create eight empty files and directories - files := []string{"f1", "f2", "f3", "f4", "f11", "f12", "f13", "f14"} dirs := []string{"d1", "d2", "d3", "d4", "d11", "d12", "d13", "d14"} + files := []string{"f1", "f2", "f3", "f4", "f11", "f12", "f13", "f14", "d1/f1.TXT"} all := append(files, dirs...) + for _, dir := range dirs { + err := os.Mkdir(filepath.Join("s1", dir), 0755) + if err != nil { + t.Fatal(err) + } + } + for _, file := range files { fd, err := os.Create(filepath.Join("s1", file)) if err != nil { @@ -43,13 +50,6 @@ func TestIgnores(t *testing.T) { fd.Close() } - for _, dir := range dirs { - err := os.Mkdir(filepath.Join("s1", dir), 0755) - if err != nil { - t.Fatal(err) - } - } - var syms []string if symlinksSupported() { syms = []string{"s1", "s2", "s3", "s4", "s11", "s12", "s13", "s14"} @@ -81,7 +81,7 @@ func TestIgnores(t *testing.T) { if err != nil { t.Fatal(err) } - _, err = fd.WriteString("f1*\nf2\nd1*\nd2\ns1*\ns2") // [fds][34] only non-ignored items + _, err = fd.WriteString("f1*\nf2\nd1*\nd2\ns1*\ns2\n(?i)*.txt") // [fds][34] only non-ignored items if err != nil { t.Fatal(err) } @@ -130,7 +130,7 @@ func TestIgnores(t *testing.T) { if err != nil { t.Fatal(err) } - expected = len(all) * 7 / 8 // seven out of eight items of each type should remain + expected = len(all)*7/8 + 1 // seven out of eight items of each type should remain, plus the foo.TXT if m.LocalFiles != expected { t.Fatalf("Incorrect number of files after second ignore, %d != %d", m.LocalFiles, expected) }