fnmatch: replace recursive matcher with iterative greedy algorithm
No binary (build failed)
—
(log yok)
| @@ -248,99 +248,132 @@ fail: | |
| return SETCHARSET_FAIL; | |
| } | |
| - | static int loop(const unsigned char *pattern, const unsigned char *string, |
| - | int maxstars) |
| + | /* match one pattern token against one string character, advancing both */ |
| + | static int matchtoken(const unsigned char **pp, const unsigned char **sp) |
| + | { |
| + | const unsigned char *p = *pp; |
| + | const unsigned char *s = *sp; |
| + | |
| + | switch(*p) { |
| + | case '?': |
| + | break; |
| + | case '\\': |
| + | if(p[1]) |
| + | p++; |
| + | if(*s != *p) |
| + | return 0; |
| + | break; |
| + | case '[': { |
| + | unsigned char charset[CURLFNM_CHSET_SIZE]; |
| + | const unsigned char *pp2 = p + 1; /* Copy in case of syntax error. */ |
| + | bool found = FALSE; |
| + | if(!setcharset(&pp2, charset)) |
| + | return -1; /* Syntax error in set; mismatch! */ |
| + | if(charset[(unsigned int)*s]) |
| + | found = TRUE; |
| + | else if(charset[CURLFNM_ALNUM]) |
| + | found = ISALNUM(*s); |
| + | else if(charset[CURLFNM_ALPHA]) |
| + | found = ISALPHA(*s); |
| + | else if(charset[CURLFNM_DIGIT]) |
| + | found = ISDIGIT(*s); |
| + | else if(charset[CURLFNM_XDIGIT]) |
| + | found = ISXDIGIT(*s); |
| + | else if(charset[CURLFNM_PRINT]) |
| + | found = ISPRINT(*s); |
| + | else if(charset[CURLFNM_SPACE]) |
| + | found = ISBLANK(*s); |
| + | else if(charset[CURLFNM_UPPER]) |
| + | found = ISUPPER(*s); |
| + | else if(charset[CURLFNM_LOWER]) |
| + | found = ISLOWER(*s); |
| + | else if(charset[CURLFNM_BLANK]) |
| + | found = ISBLANK(*s); |
| + | else if(charset[CURLFNM_GRAPH]) |
| + | found = ISGRAPH(*s); |
| + | |
| + | if(charset[CURLFNM_NEGATE]) |
| + | found = !found; |
| + | |
| + | if(!found) |
| + | return 0; |
| + | *pp = pp2 + 1; |
| + | *sp = s + 1; |
| + | return 1; |
| + | } |
| + | default: |
| + | if(*p != *s) |
| + | return 0; |
| + | break; |
| + | } |
| + | *pp = p + 1; |
| + | *sp = s + 1; |
| + | return 1; |
| + | } |
| + | |
| + | /* greedy match with backtracking to the most recent '*' */ |
| + | static int loop(const unsigned char *pattern, const unsigned char *string) |
| { | |
| const unsigned char *p = pattern; | |
| const unsigned char *s = string; | |
| - | unsigned char charset[CURLFNM_CHSET_SIZE] = { 0 }; |
| - | |
| - | for(;;) { |
| - | const unsigned char *pp; |
| + | const unsigned char *star_p = NULL; |
| + | const unsigned char *star_s = NULL; |
| + | int maxstars = 2; |
| - | switch(*p) { |
| - | case '*': |
| + | while(*s) { |
| + | if(*p == '*') { |
| if(!maxstars) | |
| return CURL_FNMATCH_NOMATCH; | |
| - | /* Regroup consecutive stars and question marks. This can be done because |
| - | '*?*?*' can be expressed as '??*'. */ |
| + | maxstars--; |
| for(;;) { | |
| - | if(*++p == '\0') |
| + | p++; |
| + | if(!*p) |
| return CURL_FNMATCH_MATCH; | |
| if(*p == '?') { | |
| - | if(!*s++) |
| + | if(!*s) |
| return CURL_FNMATCH_NOMATCH; | |
| + | s++; |
| } | |
| else if(*p != '*') | |
| break; | |
| } | |
| - | /* Skip string characters until we find a match with pattern suffix. */ |
| - | for(maxstars--; *s; s++) { |
| - | if(loop(p, s, maxstars) == CURL_FNMATCH_MATCH) |
| - | return CURL_FNMATCH_MATCH; |
| - | } |
| - | return CURL_FNMATCH_NOMATCH; |
| - | case '?': |
| - | if(!*s) |
| - | return CURL_FNMATCH_NOMATCH; |
| - | s++; |
| - | p++; |
| - | break; |
| - | case '\0': |
| - | return *s ? CURL_FNMATCH_NOMATCH : CURL_FNMATCH_MATCH; |
| - | case '\\': |
| - | if(p[1]) |
| - | p++; |
| - | if(*s++ != *p++) |
| + | star_p = p; |
| + | star_s = s; |
| + | } |
| + | else if(*p) { |
| + | const unsigned char *np = p; |
| + | const unsigned char *ns = s; |
| + | int r = matchtoken(&np, &ns); |
| + | if(r < 0) |
| return CURL_FNMATCH_NOMATCH; | |
| - | break; |
| - | case '[': |
| - | pp = p + 1; /* Copy in case of syntax error in set. */ |
| - | if(setcharset(&pp, charset)) { |
| - | bool found = FALSE; |
| - | if(!*s) |
| - | return CURL_FNMATCH_NOMATCH; |
| - | if(charset[(unsigned int)*s]) |
| - | found = TRUE; |
| - | else if(charset[CURLFNM_ALNUM]) |
| - | found = ISALNUM(*s); |
| - | else if(charset[CURLFNM_ALPHA]) |
| - | found = ISALPHA(*s); |
| - | else if(charset[CURLFNM_DIGIT]) |
| - | found = ISDIGIT(*s); |
| - | else if(charset[CURLFNM_XDIGIT]) |
| - | found = ISXDIGIT(*s); |
| - | else if(charset[CURLFNM_PRINT]) |
| - | found = ISPRINT(*s); |
| - | else if(charset[CURLFNM_SPACE]) |
| - | found = ISBLANK(*s); |
| - | else if(charset[CURLFNM_UPPER]) |
| - | found = ISUPPER(*s); |
| - | else if(charset[CURLFNM_LOWER]) |
| - | found = ISLOWER(*s); |
| - | else if(charset[CURLFNM_BLANK]) |
| - | found = ISBLANK(*s); |
| - | else if(charset[CURLFNM_GRAPH]) |
| - | found = ISGRAPH(*s); |
| - | |
| - | if(charset[CURLFNM_NEGATE]) |
| - | found = !found; |
| - | |
| - | if(!found) |
| - | return CURL_FNMATCH_NOMATCH; |
| - | p = pp + 1; |
| - | s++; |
| - | break; |
| + | if(r) { |
| + | p = np; |
| + | s = ns; |
| + | continue; |
| } | |
| - | /* Syntax error in set; mismatch! */ |
| - | return CURL_FNMATCH_NOMATCH; |
| - | |
| - | default: |
| - | if(*p++ != *s++) |
| + | if(!star_p) |
| return CURL_FNMATCH_NOMATCH; | |
| - | break; |
| + | p = star_p; |
| + | s = ++star_s; |
| } | |
| + | else if(star_p) { |
| + | p = star_p; |
| + | s = ++star_s; |
| + | } |
| + | else |
| + | return CURL_FNMATCH_NOMATCH; |
| + | } |
| + | while(*p == '*') { |
| + | if(!maxstars) |
| + | return CURL_FNMATCH_NOMATCH; |
| + | maxstars--; |
| + | do { |
| + | p++; |
| + | if(*p == '?') |
| + | return CURL_FNMATCH_NOMATCH; |
| + | } while(*p == '*'); |
| } | |
| + | return *p ? CURL_FNMATCH_NOMATCH : CURL_FNMATCH_MATCH; |
| } | |
| /* | |
| @@ -354,7 +387,7 @@ int Curl_fnmatch(void *ptr, const char *pattern, const char *string) | |
| return CURL_FNMATCH_FAIL; | |
| } | |
| return loop((const unsigned char *)pattern, | |
| - | (const unsigned char *)string, 2); |
| + | (const unsigned char *)string); |
| } | |
| #else /* HAVE_FNMATCH */ | |
| @@ -43,8 +43,8 @@ | |
| #define MAC_DIFFER 0x40 | |
| #define MAC_SHIFT 16 | |
| - | #if 0 |
| #define MAC_MATCH ((CURL_FNMATCH_MATCH << MAC_SHIFT) | MAC_DIFFER) | |
| + | #if 0 |
| #define MAC_NOMATCH ((CURL_FNMATCH_NOMATCH << MAC_SHIFT) | MAC_DIFFER) | |
| #endif | |
| #define MAC_FAIL ((CURL_FNMATCH_FAIL << MAC_SHIFT) | MAC_DIFFER) | |
| @@ -219,6 +219,9 @@ static CURLcode test_unit1307(const char *arg) | |
| { "***.txt", "t.txt", MATCH }, | |
| { "****.txt", ".txt", MATCH }, | |
| + | /* custom matcher limits wildcard star groups */ |
| + | { "*a*b*c", "abc", NOMATCH|LINUX_MATCH|MAC_MATCH }, |
| + | |
| /* empty string or pattern */ | |
| { "", "", MATCH }, | |
| { "", "hello", NOMATCH }, | |