diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh index 5d871fde96..4dd42df38c 100755 --- a/t/t3070-wildmatch.sh +++ b/t/t3070-wildmatch.sh @@ -431,4 +431,15 @@ match 1 1 1 1 'a' '[B-a]' match 0 1 0 1 'z' '[Z-y]' match 1 1 1 1 'Z' '[Z-y]' +test_expect_success 'matching does not exhibit exponential behavior' ' + { + test-tool wildmatch wildmatch \ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab \ + "*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a" & + pid=$! + } && + sleep 2 && + ! kill $! +' + test_done diff --git a/wildmatch.c b/wildmatch.c index 42e38e3459..8ea29141bd 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -14,6 +14,10 @@ typedef unsigned char uchar; +/* Internal return values */ +#define WM_ABORT_ALL -1 +#define WM_ABORT_TO_STARSTAR -2 + /* What character marks an inverted character class? */ #define NEGATE_CLASS '!' #define NEGATE_CLASS2 '^' @@ -83,12 +87,12 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags) continue; case '*': if (*++p == '*') { - const uchar *prev_p = p - 2; + const uchar *prev_p = p; while (*++p == '*') {} if (!(flags & WM_PATHNAME)) /* without WM_PATHNAME, '*' == '**' */ match_slash = 1; - else if ((prev_p < pattern || *prev_p == '/') && + else if ((prev_p - pattern < 2 || *(prev_p - 2) == '/') && (*p == '\0' || *p == '/' || (p[0] == '\\' && p[1] == '/'))) { /* @@ -114,7 +118,7 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags) * only if there are no more slash characters. */ if (!match_slash) { if (strchr((char *)text, '/')) - return WM_NOMATCH; + return WM_ABORT_TO_STARSTAR; } return WM_MATCH; } else if (!match_slash && *p == '/') { @@ -125,7 +129,7 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags) */ const char *slash = strchr((char*)text, '/'); if (!slash) - return WM_NOMATCH; + return WM_ABORT_ALL; text = (const uchar*)slash; /* the slash is consumed by the top-level for loop */ break; @@ -153,8 +157,12 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags) break; text++; } - if (t_ch != p_ch) - return WM_NOMATCH; + if (t_ch != p_ch) { + if (match_slash) + return WM_ABORT_ALL; + else + return WM_ABORT_TO_STARSTAR; + } } if ((matched = dowild(p, text, flags)) != WM_NOMATCH) { if (!match_slash || matched != WM_ABORT_TO_STARSTAR) @@ -274,5 +282,6 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags) /* Match the "pattern" against the "text" string. */ int wildmatch(const char *pattern, const char *text, unsigned int flags) { - return dowild((const uchar*)pattern, (const uchar*)text, flags); + int res = dowild((const uchar*)pattern, (const uchar*)text, flags); + return res == WM_MATCH ? WM_MATCH : WM_NOMATCH; } diff --git a/wildmatch.h b/wildmatch.h index 5993696298..0c890cb56b 100644 --- a/wildmatch.h +++ b/wildmatch.h @@ -6,8 +6,6 @@ #define WM_NOMATCH 1 #define WM_MATCH 0 -#define WM_ABORT_ALL -1 -#define WM_ABORT_TO_STARSTAR -2 int wildmatch(const char *pattern, const char *text, unsigned int flags); #endif