1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
23 #include "curl_setup.h"
25 #include "curl_fnmatch.h"
26 #include "curl_memory.h"
28 /* The last #include file should be: */
31 #define CURLFNM_CHARSET_LEN (sizeof(char) * 256)
32 #define CURLFNM_CHSET_SIZE (CURLFNM_CHARSET_LEN + 15)
34 #define CURLFNM_NEGATE CURLFNM_CHARSET_LEN
36 #define CURLFNM_ALNUM (CURLFNM_CHARSET_LEN + 1)
37 #define CURLFNM_DIGIT (CURLFNM_CHARSET_LEN + 2)
38 #define CURLFNM_XDIGIT (CURLFNM_CHARSET_LEN + 3)
39 #define CURLFNM_ALPHA (CURLFNM_CHARSET_LEN + 4)
40 #define CURLFNM_PRINT (CURLFNM_CHARSET_LEN + 5)
41 #define CURLFNM_BLANK (CURLFNM_CHARSET_LEN + 6)
42 #define CURLFNM_LOWER (CURLFNM_CHARSET_LEN + 7)
43 #define CURLFNM_GRAPH (CURLFNM_CHARSET_LEN + 8)
44 #define CURLFNM_SPACE (CURLFNM_CHARSET_LEN + 9)
45 #define CURLFNM_UPPER (CURLFNM_CHARSET_LEN + 10)
48 CURLFNM_LOOP_DEFAULT = 0,
49 CURLFNM_LOOP_BACKSLASH
53 CURLFNM_SCHS_DEFAULT = 0,
54 CURLFNM_SCHS_MAYRANGE,
55 CURLFNM_SCHS_MAYRANGE2,
57 CURLFNM_SCHS_RIGHTBRLEFTBR
65 #define SETCHARSET_OK 1
66 #define SETCHARSET_FAIL 0
68 static int parsekeyword(unsigned char **pattern, unsigned char *charset)
70 parsekey_state state = CURLFNM_PKW_INIT;
72 char keyword[KEYLEN] = { 0 };
75 unsigned char *p = *pattern;
76 for(i = 0; !found; i++) {
79 return SETCHARSET_FAIL;
81 case CURLFNM_PKW_INIT:
82 if(ISALPHA(c) && ISLOWER(c))
85 state = CURLFNM_PKW_DDOT;
89 case CURLFNM_PKW_DDOT:
93 return SETCHARSET_FAIL;
98 *pattern = p; /* move caller's pattern pointer */
99 if(strcmp(keyword, "digit") == 0)
100 charset[CURLFNM_DIGIT] = 1;
101 else if(strcmp(keyword, "alnum") == 0)
102 charset[CURLFNM_ALNUM] = 1;
103 else if(strcmp(keyword, "alpha") == 0)
104 charset[CURLFNM_ALPHA] = 1;
105 else if(strcmp(keyword, "xdigit") == 0)
106 charset[CURLFNM_XDIGIT] = 1;
107 else if(strcmp(keyword, "print") == 0)
108 charset[CURLFNM_PRINT] = 1;
109 else if(strcmp(keyword, "graph") == 0)
110 charset[CURLFNM_GRAPH] = 1;
111 else if(strcmp(keyword, "space") == 0)
112 charset[CURLFNM_SPACE] = 1;
113 else if(strcmp(keyword, "blank") == 0)
114 charset[CURLFNM_BLANK] = 1;
115 else if(strcmp(keyword, "upper") == 0)
116 charset[CURLFNM_UPPER] = 1;
117 else if(strcmp(keyword, "lower") == 0)
118 charset[CURLFNM_LOWER] = 1;
120 return SETCHARSET_FAIL;
121 return SETCHARSET_OK;
124 /* returns 1 (true) if pattern is OK, 0 if is bad ("p" is pattern pointer) */
125 static int setcharset(unsigned char **p, unsigned char *charset)
127 setcharset_state state = CURLFNM_SCHS_DEFAULT;
128 unsigned char rangestart = 0;
129 unsigned char lastchar = 0;
130 bool something_found = FALSE;
135 case CURLFNM_SCHS_DEFAULT:
136 if(ISALNUM(c)) { /* ASCII value */
140 state = CURLFNM_SCHS_MAYRANGE;
141 something_found = TRUE;
145 return SETCHARSET_OK;
147 something_found = TRUE;
148 state = CURLFNM_SCHS_RIGHTBR;
154 if(c2 == ':') { /* there has to be a keyword */
156 if(parsekeyword(p, charset)) {
157 state = CURLFNM_SCHS_DEFAULT;
160 return SETCHARSET_FAIL;
166 something_found = TRUE;
168 else if(c == '?' || c == '*') {
169 something_found = TRUE;
173 else if(c == '^' || c == '!') {
174 if(!something_found) {
175 if(charset[CURLFNM_NEGATE]) {
177 something_found = TRUE;
180 charset[CURLFNM_NEGATE] = 1; /* negate charset */
189 something_found = TRUE;
190 state = CURLFNM_SCHS_MAYRANGE;
196 return SETCHARSET_FAIL;
199 return SETCHARSET_FAIL;
204 something_found = TRUE;
207 case CURLFNM_SCHS_MAYRANGE:
212 state = CURLFNM_SCHS_MAYRANGE2;
215 state = CURLFNM_SCHS_DEFAULT;
217 else if(ISALNUM(c)) {
228 return SETCHARSET_FAIL;
231 return SETCHARSET_OK;
234 return SETCHARSET_FAIL;
236 case CURLFNM_SCHS_MAYRANGE2:
240 return SETCHARSET_FAIL;
243 return SETCHARSET_OK;
249 state = CURLFNM_SCHS_DEFAULT;
253 return SETCHARSET_FAIL;
255 if(c >= rangestart) {
256 if((ISLOWER(c) && ISLOWER(rangestart)) ||
257 (ISDIGIT(c) && ISDIGIT(rangestart)) ||
258 (ISUPPER(c) && ISUPPER(rangestart))) {
259 charset[lastchar] = 0;
261 while(rangestart++ <= c)
262 charset[rangestart-1] = 1;
264 state = CURLFNM_SCHS_DEFAULT;
267 return SETCHARSET_FAIL;
270 case CURLFNM_SCHS_RIGHTBR:
272 state = CURLFNM_SCHS_RIGHTBRLEFTBR;
277 return SETCHARSET_OK;
280 return SETCHARSET_FAIL;
282 else if(ISPRINT(c)) {
285 state = CURLFNM_SCHS_DEFAULT;
288 /* used 'goto fail' instead of 'return SETCHARSET_FAIL' to avoid a
289 * nonsense warning 'statement not reached' at end of the fnc when
290 * compiling on Solaris */
293 case CURLFNM_SCHS_RIGHTBRLEFTBR:
295 return SETCHARSET_OK;
298 state = CURLFNM_SCHS_DEFAULT;
306 return SETCHARSET_FAIL;
309 static int loop(const unsigned char *pattern, const unsigned char *string)
311 loop_state state = CURLFNM_LOOP_DEFAULT;
312 unsigned char *p = (unsigned char *)pattern;
313 unsigned char *s = (unsigned char *)string;
314 unsigned char charset[CURLFNM_CHSET_SIZE] = { 0 };
319 case CURLFNM_LOOP_DEFAULT:
321 while(*(p+1) == '*') /* eliminate multiple stars */
323 if(*s == '\0' && *(p+1) == '\0')
324 return CURL_FNMATCH_MATCH;
325 rc = loop(p + 1, s); /* *.txt matches .txt <=> .txt matches .txt */
326 if(rc == CURL_FNMATCH_MATCH)
327 return CURL_FNMATCH_MATCH;
328 if(*s) /* let the star eat up one character */
331 return CURL_FNMATCH_NOMATCH;
339 return CURL_FNMATCH_NOMATCH;
341 return CURL_FNMATCH_FAIL; /* cannot deal with other character */
343 else if(*p == '\0') {
345 return CURL_FNMATCH_MATCH;
347 return CURL_FNMATCH_NOMATCH;
349 else if(*p == '\\') {
350 state = CURLFNM_LOOP_BACKSLASH;
354 unsigned char *pp = p+1; /* cannot handle with pointer to register */
355 if(setcharset(&pp, charset)) {
357 if(charset[(unsigned int)*s])
359 else if(charset[CURLFNM_ALNUM])
361 else if(charset[CURLFNM_ALPHA])
363 else if(charset[CURLFNM_DIGIT])
365 else if(charset[CURLFNM_XDIGIT])
366 found = ISXDIGIT(*s);
367 else if(charset[CURLFNM_PRINT])
369 else if(charset[CURLFNM_SPACE])
371 else if(charset[CURLFNM_UPPER])
373 else if(charset[CURLFNM_LOWER])
375 else if(charset[CURLFNM_BLANK])
377 else if(charset[CURLFNM_GRAPH])
380 if(charset[CURLFNM_NEGATE])
386 memset(charset, 0, CURLFNM_CHSET_SIZE);
389 return CURL_FNMATCH_NOMATCH;
392 return CURL_FNMATCH_FAIL;
396 return CURL_FNMATCH_NOMATCH;
399 case CURLFNM_LOOP_BACKSLASH:
402 state = CURLFNM_LOOP_DEFAULT;
404 return CURL_FNMATCH_NOMATCH;
407 return CURL_FNMATCH_FAIL;
416 int Curl_fnmatch(void *ptr, const char *pattern, const char *string)
418 (void)ptr; /* the argument is specified by the curl_fnmatch_callback
419 prototype, but not used by Curl_fnmatch() */
420 if(!pattern || !string) {
421 return CURL_FNMATCH_FAIL;
423 return loop((unsigned char *)pattern, (unsigned char *)string);