From 0f36271646da455150f821582621f7d0d5e04ddf Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 31 Mar 2016 17:38:40 +0200 Subject: [PATCH 1/3] Add more tests for path matching with globs and path delimiters --- tests/attr/ignore.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/attr/ignore.c b/tests/attr/ignore.c index 91bf984a1..f11dad570 100644 --- a/tests/attr/ignore.c +++ b/tests/attr/ignore.c @@ -132,6 +132,20 @@ void test_attr_ignore__leading_stars(void) assert_is_ignored(false, "dir1/kid2/file"); } +void test_attr_ignore__globs_and_path_delimiters(void) +{ + cl_git_rewritefile("attr/.gitignore", "**/_*/"); + assert_is_ignored(false, "test_folder/file"); + assert_is_ignored(true, "_test/file"); + assert_is_ignored(true, "_test/a/file"); + + cl_git_rewritefile("attr/.gitignore", "**/_*/foo/bar/*ux"); + + assert_is_ignored(true, "_test/foo/bar/qux/file"); + assert_is_ignored(true, "_test/foo/bar/crux/file"); + assert_is_ignored(false, "_test/foo/bar/code/file"); +} + void test_attr_ignore__skip_gitignore_directory(void) { cl_git_rewritefile("attr/.git/info/exclude", "/NewFolder\n/NewFolder/NewFolder"); From 1c3018eb12c03010fe0db740bc9e67af4992e594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 18 Apr 2016 13:34:18 +0200 Subject: [PATCH 2/3] ignore: fix directory limits when searching for star-star In order to match the star-star, we disable the flag that's looking for a single path element, but that leads to searching for the pattern in the middle of elements in the input string. Mark when we're handing a star-star so we jump over the elements in our attempt to match the part of the pattern that comes after the star-star. While here, tighten up the check so we don't allow invalid rules through. --- src/fnmatch.c | 30 ++++++++++++++++++++++++------ tests/attr/ignore.c | 12 ++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/fnmatch.c b/src/fnmatch.c index a2945b8db..ba1964bf8 100644 --- a/src/fnmatch.c +++ b/src/fnmatch.c @@ -69,7 +69,8 @@ p_fnmatchx(const char *pattern, const char *string, int flags, size_t recurs) if (recurs-- == 0) return FNM_NORES; - for (stringstart = string;;) + for (stringstart = string;;) { + bool match_slash = false; switch (c = *pattern++) { case EOS: if ((flags & FNM_LEADING_DIR) && *string == '/') @@ -93,11 +94,17 @@ p_fnmatchx(const char *pattern, const char *string, int flags, size_t recurs) * It will be restored if/when we recurse below. */ if (c == '*') { + c = *++pattern; + /* star-star-slash is at the end, match by default */ + if (c == EOS) + return 0; + /* Double-star must be at end or between slashes */ + if (c != '/') + return (FNM_NOMATCH); + + c = *++pattern; flags &= ~FNM_PATHNAME; - while (c == '*') - c = *++pattern; - if (c == '/') - c = *++pattern; + match_slash = true; } if (*string == '.' && (flags & FNM_PERIOD) && @@ -128,7 +135,17 @@ p_fnmatchx(const char *pattern, const char *string, int flags, size_t recurs) return e; if (test == '/' && (flags & FNM_PATHNAME)) break; - ++string; + + /* searching for star-star, so we jump over entire dirs */ + if (match_slash) { + const char *slash; + if (!(slash = strchr(string, '/'))) + break; + + string = slash + 1; + } else { + ++string; + } } return (FNM_NOMATCH); case '[': @@ -170,6 +187,7 @@ p_fnmatchx(const char *pattern, const char *string, int flags, size_t recurs) ++string; break; } + } /* NOTREACHED */ } diff --git a/tests/attr/ignore.c b/tests/attr/ignore.c index f11dad570..f1fe1c71f 100644 --- a/tests/attr/ignore.c +++ b/tests/attr/ignore.c @@ -134,13 +134,25 @@ void test_attr_ignore__leading_stars(void) void test_attr_ignore__globs_and_path_delimiters(void) { + cl_git_rewritefile("attr/.gitignore", "foo/bar/**"); + assert_is_ignored(true, "foo/bar/baz"); + assert_is_ignored(true, "foo/bar/baz/quux"); + + cl_git_rewritefile("attr/.gitignore", "_*/"); + assert_is_ignored(true, "sub/_test/a/file"); + assert_is_ignored(false, "test_folder/file"); + assert_is_ignored(true, "_test/file"); + assert_is_ignored(true, "_test/a/file"); + cl_git_rewritefile("attr/.gitignore", "**/_*/"); + assert_is_ignored(true, "sub/_test/a/file"); assert_is_ignored(false, "test_folder/file"); assert_is_ignored(true, "_test/file"); assert_is_ignored(true, "_test/a/file"); cl_git_rewritefile("attr/.gitignore", "**/_*/foo/bar/*ux"); + assert_is_ignored(true, "sub/_test/foo/bar/qux/file"); assert_is_ignored(true, "_test/foo/bar/qux/file"); assert_is_ignored(true, "_test/foo/bar/crux/file"); assert_is_ignored(false, "_test/foo/bar/code/file"); From d45928cc0d969f255337e11edd59b4da6dc4926d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 18 Apr 2016 16:05:12 +0200 Subject: [PATCH 3/3] ignore: move star-star matching closer to it use Instead of threading the state down to the larger loop, let's have the loop where we detect the double star so each of them are easier to read. --- src/fnmatch.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/fnmatch.c b/src/fnmatch.c index ba1964bf8..33c8a2512 100644 --- a/src/fnmatch.c +++ b/src/fnmatch.c @@ -69,8 +69,7 @@ p_fnmatchx(const char *pattern, const char *string, int flags, size_t recurs) if (recurs-- == 0) return FNM_NORES; - for (stringstart = string;;) { - bool match_slash = false; + for (stringstart = string;;) switch (c = *pattern++) { case EOS: if ((flags & FNM_LEADING_DIR) && *string == '/') @@ -103,8 +102,15 @@ p_fnmatchx(const char *pattern, const char *string, int flags, size_t recurs) return (FNM_NOMATCH); c = *++pattern; - flags &= ~FNM_PATHNAME; - match_slash = true; + do { + int e = p_fnmatchx(pattern, string, recurs_flags, recurs); + if (e != FNM_NOMATCH) + return e; + string = strchr(string, '/'); + } while (string++); + + /* If we get here, we didn't find a match */ + return FNM_NOMATCH; } if (*string == '.' && (flags & FNM_PERIOD) && @@ -135,17 +141,7 @@ p_fnmatchx(const char *pattern, const char *string, int flags, size_t recurs) return e; if (test == '/' && (flags & FNM_PATHNAME)) break; - - /* searching for star-star, so we jump over entire dirs */ - if (match_slash) { - const char *slash; - if (!(slash = strchr(string, '/'))) - break; - - string = slash + 1; - } else { - ++string; - } + ++string; } return (FNM_NOMATCH); case '[': @@ -187,7 +183,6 @@ p_fnmatchx(const char *pattern, const char *string, int flags, size_t recurs) ++string; break; } - } /* NOTREACHED */ }