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