diff options
author | Carlos Martín Nieto <cmn@dwim.me> | 2016-04-18 13:34:18 +0200 |
---|---|---|
committer | Carlos Martín Nieto <cmn@dwim.me> | 2016-04-19 19:39:05 +0200 |
commit | 1c3018eb12c03010fe0db740bc9e67af4992e594 (patch) | |
tree | fae675e387a265e1a969fbb6aafa6984ad828290 | |
parent | 0f36271646da455150f821582621f7d0d5e04ddf (diff) | |
download | libgit2-1c3018eb12c03010fe0db740bc9e67af4992e594.tar.gz |
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.
-rw-r--r-- | src/fnmatch.c | 30 | ||||
-rw-r--r-- | 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"); |