By default wildmatch(,, 0) is equivalent with fnmatch(,, FNM_PATHNAME). This patch makes wildmatch behave more like fnmatch: FNM_PATHNAME behavior is always applied when FNM_PATHNAME is passed to wildmatch. Without FNM_PATHNAME, wildmatch accepts '/' in '?' and '[]' and treats '*' like '**' in the original version. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> --- The choice of name "pathspec" is not good. I couldn't think of anything appropriate and just did not care enough at this point. dir.c | 2 +- t/t3070-wildmatch.sh | 27 +++++++++++++++++++++++++++ test-wildmatch.c | 4 +++- wildmatch.c | 12 ++++++++---- 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/dir.c b/dir.c index cb7328b..7bbd6f8 100644 --- a/dir.c +++ b/dir.c @@ -595,7 +595,7 @@ int match_pathname(const char *pathname, int pathlen, } return wildmatch(pattern, name, - ignore_case ? FNM_CASEFOLD : 0) == 0; + FNM_PATHNAME | (ignore_case ? FNM_CASEFOLD : 0)) == 0; } /* Scan the list and let the last match determine the fate. diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh index 3155eab..ca4ac46 100755 --- a/t/t3070-wildmatch.sh +++ b/t/t3070-wildmatch.sh @@ -29,6 +29,18 @@ match() { fi } +pathspec() { + if [ $1 = 1 ]; then + test_expect_success "pathspec: match '$2' '$3'" " + test-wildmatch pathspec '$2' '$3' + " + else + test_expect_success "pathspec: no match '$2' '$3'" " + ! test-wildmatch pathspec '$2' '$3' + " + fi +} + # Basic wildmat features match 1 1 foo foo match 0 0 foo bar @@ -192,4 +204,19 @@ match 0 0 'XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' 'XXX/*/ match 1 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt' '**/*a*b*g*n*t' match 0 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz' '**/*a*b*g*n*t' +pathspec 1 foo foo +pathspec 0 foo fo +pathspec 1 foo/bar foo/bar +pathspec 1 foo/bar 'foo/*' +pathspec 1 foo/bba/arr 'foo/*' +pathspec 1 foo/bba/arr 'foo/**' +pathspec 1 foo/bba/arr 'foo*' +pathspec 1 foo/bba/arr 'foo**' +pathspec 1 foo/bba/arr 'foo/*arr' +pathspec 1 foo/bba/arr 'foo/**arr' +pathspec 0 foo/bba/arr 'foo/*z' +pathspec 0 foo/bba/arr 'foo/**z' +pathspec 1 foo/bar 'foo?bar' +pathspec 1 foo/bar 'foo[/]bar' + test_done diff --git a/test-wildmatch.c b/test-wildmatch.c index e384c8e..7fefa4f 100644 --- a/test-wildmatch.c +++ b/test-wildmatch.c @@ -12,9 +12,11 @@ int main(int argc, char **argv) argv[i] += 3; } if (!strcmp(argv[1], "wildmatch")) + return !!wildmatch(argv[3], argv[2], FNM_PATHNAME); + else if (!strcmp(argv[1], "pathspec")) return !!wildmatch(argv[3], argv[2], 0); else if (!strcmp(argv[1], "iwildmatch")) - return !!wildmatch(argv[3], argv[2], FNM_CASEFOLD); + return !!wildmatch(argv[3], argv[2], FNM_PATHNAME | FNM_CASEFOLD); else if (!strcmp(argv[1], "fnmatch")) return !!fnmatch(argv[3], argv[2], FNM_PATHNAME); else diff --git a/wildmatch.c b/wildmatch.c index 9586ed9..6aa034f 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -80,14 +80,17 @@ static int dowild(const uchar *p, const uchar *text, int flags) continue; case '?': /* Match anything but '/'. */ - if (t_ch == '/') + if (flags & FNM_PATHNAME && t_ch == '/') return NOMATCH; continue; case '*': if (*++p == '*') { const uchar *prev_p = p - 2; while (*++p == '*') {} - if ((prev_p == text || *prev_p == '/') || + if (!(flags & FNM_PATHNAME)) + /* without FNM_PATHNAME, '*' == '**' */ + special = TRUE; + else if ((prev_p == text || *prev_p == '/') || (*p == '\0' || *p == '/' || (p[0] == '\\' && p[1] == '/'))) { /* @@ -106,7 +109,7 @@ static int dowild(const uchar *p, const uchar *text, int flags) } else return ABORT_MALFORMED; } else - special = FALSE; + special = flags & FNM_PATHNAME ? FALSE: TRUE; if (*p == '\0') { /* Trailing "**" matches everything. Trailing "*" matches * only if there are no more slash characters. */ @@ -217,7 +220,8 @@ static int dowild(const uchar *p, const uchar *text, int flags) } else if (t_ch == p_ch) matched = TRUE; } while (prev_ch = p_ch, (p_ch = *++p) != ']'); - if (matched == special || t_ch == '/') + if (matched == special || + (flags & FNM_PATHNAME && t_ch == '/')) return NOMATCH; continue; } -- 1.8.0.rc2.23.g1fb49df -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html