1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel (at) haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.haxx.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ***************************************************************************/ 22 23 #include "curl_setup.h" 24 25 #include <curl/curl.h> 26 27 #include "curl_fnmatch.h" 28 #include "curl_memory.h" 29 30 /* The last #include file should be: */ 31 #include "memdebug.h" 32 33 #define CURLFNM_CHARSET_LEN (sizeof(char) * 256) 34 #define CURLFNM_CHSET_SIZE (CURLFNM_CHARSET_LEN + 15) 35 36 #define CURLFNM_NEGATE CURLFNM_CHARSET_LEN 37 38 #define CURLFNM_ALNUM (CURLFNM_CHARSET_LEN + 1) 39 #define CURLFNM_DIGIT (CURLFNM_CHARSET_LEN + 2) 40 #define CURLFNM_XDIGIT (CURLFNM_CHARSET_LEN + 3) 41 #define CURLFNM_ALPHA (CURLFNM_CHARSET_LEN + 4) 42 #define CURLFNM_PRINT (CURLFNM_CHARSET_LEN + 5) 43 #define CURLFNM_BLANK (CURLFNM_CHARSET_LEN + 6) 44 #define CURLFNM_LOWER (CURLFNM_CHARSET_LEN + 7) 45 #define CURLFNM_GRAPH (CURLFNM_CHARSET_LEN + 8) 46 #define CURLFNM_SPACE (CURLFNM_CHARSET_LEN + 9) 47 #define CURLFNM_UPPER (CURLFNM_CHARSET_LEN + 10) 48 49 typedef enum { 50 CURLFNM_LOOP_DEFAULT = 0, 51 CURLFNM_LOOP_BACKSLASH 52 } loop_state; 53 54 typedef enum { 55 CURLFNM_SCHS_DEFAULT = 0, 56 CURLFNM_SCHS_MAYRANGE, 57 CURLFNM_SCHS_MAYRANGE2, 58 CURLFNM_SCHS_RIGHTBR, 59 CURLFNM_SCHS_RIGHTBRLEFTBR 60 } setcharset_state; 61 62 typedef enum { 63 CURLFNM_PKW_INIT = 0, 64 CURLFNM_PKW_DDOT 65 } parsekey_state; 66 67 #define SETCHARSET_OK 1 68 #define SETCHARSET_FAIL 0 69 70 static int parsekeyword(unsigned char **pattern, unsigned char *charset) 71 { 72 parsekey_state state = CURLFNM_PKW_INIT; 73 #define KEYLEN 10 74 char keyword[KEYLEN] = { 0 }; 75 int found = FALSE; 76 int i; 77 unsigned char *p = *pattern; 78 for(i = 0; !found; i++) { 79 char c = *p++; 80 if(i >= KEYLEN) 81 return SETCHARSET_FAIL; 82 switch(state) { 83 case CURLFNM_PKW_INIT: 84 if(ISALPHA(c) && ISLOWER(c)) 85 keyword[i] = c; 86 else if(c == ':') 87 state = CURLFNM_PKW_DDOT; 88 else 89 return 0; 90 break; 91 case CURLFNM_PKW_DDOT: 92 if(c == ']') 93 found = TRUE; 94 else 95 return SETCHARSET_FAIL; 96 } 97 } 98 #undef KEYLEN 99 100 *pattern = p; /* move caller's pattern pointer */ 101 if(strcmp(keyword, "digit") == 0) 102 charset[CURLFNM_DIGIT] = 1; 103 else if(strcmp(keyword, "alnum") == 0) 104 charset[CURLFNM_ALNUM] = 1; 105 else if(strcmp(keyword, "alpha") == 0) 106 charset[CURLFNM_ALPHA] = 1; 107 else if(strcmp(keyword, "xdigit") == 0) 108 charset[CURLFNM_XDIGIT] = 1; 109 else if(strcmp(keyword, "print") == 0) 110 charset[CURLFNM_PRINT] = 1; 111 else if(strcmp(keyword, "graph") == 0) 112 charset[CURLFNM_GRAPH] = 1; 113 else if(strcmp(keyword, "space") == 0) 114 charset[CURLFNM_SPACE] = 1; 115 else if(strcmp(keyword, "blank") == 0) 116 charset[CURLFNM_BLANK] = 1; 117 else if(strcmp(keyword, "upper") == 0) 118 charset[CURLFNM_UPPER] = 1; 119 else if(strcmp(keyword, "lower") == 0) 120 charset[CURLFNM_LOWER] = 1; 121 else 122 return SETCHARSET_FAIL; 123 return SETCHARSET_OK; 124 } 125 126 /* returns 1 (true) if pattern is OK, 0 if is bad ("p" is pattern pointer) */ 127 static int setcharset(unsigned char **p, unsigned char *charset) 128 { 129 setcharset_state state = CURLFNM_SCHS_DEFAULT; 130 unsigned char rangestart = 0; 131 unsigned char lastchar = 0; 132 bool something_found = FALSE; 133 unsigned char c; 134 for(;;) { 135 c = **p; 136 switch(state) { 137 case CURLFNM_SCHS_DEFAULT: 138 if(ISALNUM(c)) { /* ASCII value */ 139 rangestart = c; 140 charset[c] = 1; 141 (*p)++; 142 state = CURLFNM_SCHS_MAYRANGE; 143 something_found = TRUE; 144 } 145 else if(c == ']') { 146 if(something_found) 147 return SETCHARSET_OK; 148 else 149 something_found = TRUE; 150 state = CURLFNM_SCHS_RIGHTBR; 151 charset[c] = 1; 152 (*p)++; 153 } 154 else if(c == '[') { 155 char c2 = *((*p)+1); 156 if(c2 == ':') { /* there has to be a keyword */ 157 (*p) += 2; 158 if(parsekeyword(p, charset)) { 159 state = CURLFNM_SCHS_DEFAULT; 160 } 161 else 162 return SETCHARSET_FAIL; 163 } 164 else { 165 charset[c] = 1; 166 (*p)++; 167 } 168 something_found = TRUE; 169 } 170 else if(c == '?' || c == '*') { 171 something_found = TRUE; 172 charset[c] = 1; 173 (*p)++; 174 } 175 else if(c == '^' || c == '!') { 176 if(!something_found) { 177 if(charset[CURLFNM_NEGATE]) { 178 charset[c] = 1; 179 something_found = TRUE; 180 } 181 else 182 charset[CURLFNM_NEGATE] = 1; /* negate charset */ 183 } 184 else 185 charset[c] = 1; 186 (*p)++; 187 } 188 else if(c == '\\') { 189 c = *(++(*p)); 190 if(ISPRINT((c))) { 191 something_found = TRUE; 192 state = CURLFNM_SCHS_MAYRANGE; 193 charset[c] = 1; 194 rangestart = c; 195 (*p)++; 196 } 197 else 198 return SETCHARSET_FAIL; 199 } 200 else if(c == '\0') { 201 return SETCHARSET_FAIL; 202 } 203 else { 204 charset[c] = 1; 205 (*p)++; 206 something_found = TRUE; 207 } 208 break; 209 case CURLFNM_SCHS_MAYRANGE: 210 if(c == '-') { 211 charset[c] = 1; 212 (*p)++; 213 lastchar = '-'; 214 state = CURLFNM_SCHS_MAYRANGE2; 215 } 216 else if(c == '[') { 217 state = CURLFNM_SCHS_DEFAULT; 218 } 219 else if(ISALNUM(c)) { 220 charset[c] = 1; 221 (*p)++; 222 } 223 else if(c == '\\') { 224 c = *(++(*p)); 225 if(ISPRINT(c)) { 226 charset[c] = 1; 227 (*p)++; 228 } 229 else 230 return SETCHARSET_FAIL; 231 } 232 else if(c == ']') { 233 return SETCHARSET_OK; 234 } 235 else 236 return SETCHARSET_FAIL; 237 break; 238 case CURLFNM_SCHS_MAYRANGE2: 239 if(c == '\\') { 240 c = *(++(*p)); 241 if(!ISPRINT(c)) 242 return SETCHARSET_FAIL; 243 } 244 if(c == ']') { 245 return SETCHARSET_OK; 246 } 247 else if(c == '\\') { 248 c = *(++(*p)); 249 if(ISPRINT(c)) { 250 charset[c] = 1; 251 state = CURLFNM_SCHS_DEFAULT; 252 (*p)++; 253 } 254 else 255 return SETCHARSET_FAIL; 256 } 257 if(c >= rangestart) { 258 if((ISLOWER(c) && ISLOWER(rangestart)) || 259 (ISDIGIT(c) && ISDIGIT(rangestart)) || 260 (ISUPPER(c) && ISUPPER(rangestart))) { 261 charset[lastchar] = 0; 262 rangestart++; 263 while(rangestart++ <= c) 264 charset[rangestart-1] = 1; 265 (*p)++; 266 state = CURLFNM_SCHS_DEFAULT; 267 } 268 else 269 return SETCHARSET_FAIL; 270 } 271 break; 272 case CURLFNM_SCHS_RIGHTBR: 273 if(c == '[') { 274 state = CURLFNM_SCHS_RIGHTBRLEFTBR; 275 charset[c] = 1; 276 (*p)++; 277 } 278 else if(c == ']') { 279 return SETCHARSET_OK; 280 } 281 else if(c == '\0') { 282 return SETCHARSET_FAIL; 283 } 284 else if(ISPRINT(c)) { 285 charset[c] = 1; 286 (*p)++; 287 state = CURLFNM_SCHS_DEFAULT; 288 } 289 else 290 /* used 'goto fail' instead of 'return SETCHARSET_FAIL' to avoid a 291 * nonsense warning 'statement not reached' at end of the fnc when 292 * compiling on Solaris */ 293 goto fail; 294 break; 295 case CURLFNM_SCHS_RIGHTBRLEFTBR: 296 if(c == ']') { 297 return SETCHARSET_OK; 298 } 299 else { 300 state = CURLFNM_SCHS_DEFAULT; 301 charset[c] = 1; 302 (*p)++; 303 } 304 break; 305 } 306 } 307 fail: 308 return SETCHARSET_FAIL; 309 } 310 311 static int loop(const unsigned char *pattern, const unsigned char *string) 312 { 313 loop_state state = CURLFNM_LOOP_DEFAULT; 314 unsigned char *p = (unsigned char *)pattern; 315 unsigned char *s = (unsigned char *)string; 316 unsigned char charset[CURLFNM_CHSET_SIZE] = { 0 }; 317 int rc = 0; 318 319 for(;;) { 320 switch(state) { 321 case CURLFNM_LOOP_DEFAULT: 322 if(*p == '*') { 323 while(*(p+1) == '*') /* eliminate multiple stars */ 324 p++; 325 if(*s == '\0' && *(p+1) == '\0') 326 return CURL_FNMATCH_MATCH; 327 rc = loop(p + 1, s); /* *.txt matches .txt <=> .txt matches .txt */ 328 if(rc == CURL_FNMATCH_MATCH) 329 return CURL_FNMATCH_MATCH; 330 if(*s) /* let the star eat up one character */ 331 s++; 332 else 333 return CURL_FNMATCH_NOMATCH; 334 } 335 else if(*p == '?') { 336 if(ISPRINT(*s)) { 337 s++; 338 p++; 339 } 340 else if(*s == '\0') 341 return CURL_FNMATCH_NOMATCH; 342 else 343 return CURL_FNMATCH_FAIL; /* cannot deal with other character */ 344 } 345 else if(*p == '\0') { 346 if(*s == '\0') 347 return CURL_FNMATCH_MATCH; 348 else 349 return CURL_FNMATCH_NOMATCH; 350 } 351 else if(*p == '\\') { 352 state = CURLFNM_LOOP_BACKSLASH; 353 p++; 354 } 355 else if(*p == '[') { 356 unsigned char *pp = p+1; /* cannot handle with pointer to register */ 357 if(setcharset(&pp, charset)) { 358 int found = FALSE; 359 if(charset[(unsigned int)*s]) 360 found = TRUE; 361 else if(charset[CURLFNM_ALNUM]) 362 found = ISALNUM(*s); 363 else if(charset[CURLFNM_ALPHA]) 364 found = ISALPHA(*s); 365 else if(charset[CURLFNM_DIGIT]) 366 found = ISDIGIT(*s); 367 else if(charset[CURLFNM_XDIGIT]) 368 found = ISXDIGIT(*s); 369 else if(charset[CURLFNM_PRINT]) 370 found = ISPRINT(*s); 371 else if(charset[CURLFNM_SPACE]) 372 found = ISSPACE(*s); 373 else if(charset[CURLFNM_UPPER]) 374 found = ISUPPER(*s); 375 else if(charset[CURLFNM_LOWER]) 376 found = ISLOWER(*s); 377 else if(charset[CURLFNM_BLANK]) 378 found = ISBLANK(*s); 379 else if(charset[CURLFNM_GRAPH]) 380 found = ISGRAPH(*s); 381 382 if(charset[CURLFNM_NEGATE]) 383 found = !found; 384 385 if(found) { 386 p = pp+1; 387 s++; 388 memset(charset, 0, CURLFNM_CHSET_SIZE); 389 } 390 else 391 return CURL_FNMATCH_NOMATCH; 392 } 393 else 394 return CURL_FNMATCH_FAIL; 395 } 396 else { 397 if(*p++ != *s++) 398 return CURL_FNMATCH_NOMATCH; 399 } 400 break; 401 case CURLFNM_LOOP_BACKSLASH: 402 if(ISPRINT(*p)) { 403 if(*p++ == *s++) 404 state = CURLFNM_LOOP_DEFAULT; 405 else 406 return CURL_FNMATCH_NOMATCH; 407 } 408 else 409 return CURL_FNMATCH_FAIL; 410 break; 411 } 412 } 413 } 414 415 /* 416 * @unittest: 1307 417 */ 418 int Curl_fnmatch(void *ptr, const char *pattern, const char *string) 419 { 420 (void)ptr; /* the argument is specified by the curl_fnmatch_callback 421 prototype, but not used by Curl_fnmatch() */ 422 if(!pattern || !string) { 423 return CURL_FNMATCH_FAIL; 424 } 425 return loop((unsigned char *)pattern, (unsigned char *)string); 426 } 427