| OLD | NEW |
| (Empty) |
| 1 /* This Source Code Form is subject to the terms of the Mozilla Public | |
| 2 * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
| 4 | |
| 5 /* | |
| 6 * shexp.c: shell-like wildcard match routines | |
| 7 * | |
| 8 * See shexp.h for public documentation. | |
| 9 */ | |
| 10 | |
| 11 #include "seccomon.h" | |
| 12 #include "portreg.h" | |
| 13 | |
| 14 /* ----------------------------- shexp_valid ------------------------------ */ | |
| 15 | |
| 16 | |
| 17 static int | |
| 18 _valid_subexp(const char *exp, char stop1, char stop2) | |
| 19 { | |
| 20 register int x; | |
| 21 int nsc = 0; /* Number of special characters */ | |
| 22 int np; /* Number of pipe characters in union */ | |
| 23 int tld = 0; /* Number of tilde characters */ | |
| 24 | |
| 25 for (x = 0; exp[x] && (exp[x] != stop1) && (exp[x] != stop2); ++x) { | |
| 26 switch(exp[x]) { | |
| 27 case '~': | |
| 28 if(tld) /* at most one exclusion */ | |
| 29 return INVALID_SXP; | |
| 30 if (stop1) /* no exclusions within unions */ | |
| 31 return INVALID_SXP; | |
| 32 if (!exp[x+1]) /* exclusion cannot be last character */ | |
| 33 return INVALID_SXP; | |
| 34 if (!x) /* exclusion cannot be first character */ | |
| 35 return INVALID_SXP; | |
| 36 ++tld; | |
| 37 /* fall through */ | |
| 38 case '*': | |
| 39 case '?': | |
| 40 case '$': | |
| 41 ++nsc; | |
| 42 break; | |
| 43 case '[': | |
| 44 ++nsc; | |
| 45 if((!exp[++x]) || (exp[x] == ']')) | |
| 46 return INVALID_SXP; | |
| 47 for(; exp[x] && (exp[x] != ']'); ++x) { | |
| 48 if(exp[x] == '\\' && !exp[++x]) | |
| 49 return INVALID_SXP; | |
| 50 } | |
| 51 if(!exp[x]) | |
| 52 return INVALID_SXP; | |
| 53 break; | |
| 54 case '(': | |
| 55 ++nsc; | |
| 56 if (stop1) /* no nested unions */ | |
| 57 return INVALID_SXP; | |
| 58 np = -1; | |
| 59 do { | |
| 60 int t = _valid_subexp(&exp[++x], ')', '|'); | |
| 61 if(t == 0 || t == INVALID_SXP) | |
| 62 return INVALID_SXP; | |
| 63 x+=t; | |
| 64 if(!exp[x]) | |
| 65 return INVALID_SXP; | |
| 66 ++np; | |
| 67 } while (exp[x] == '|' ); | |
| 68 if(np < 1) /* must be at least one pipe */ | |
| 69 return INVALID_SXP; | |
| 70 break; | |
| 71 case ')': | |
| 72 case '|': | |
| 73 case ']': | |
| 74 return INVALID_SXP; | |
| 75 case '\\': | |
| 76 ++nsc; | |
| 77 if(!exp[++x]) | |
| 78 return INVALID_SXP; | |
| 79 break; | |
| 80 default: | |
| 81 break; | |
| 82 } | |
| 83 } | |
| 84 if((!stop1) && (!nsc)) /* must be at least one special character */ | |
| 85 return NON_SXP; | |
| 86 return ((exp[x] == stop1 || exp[x] == stop2) ? x : INVALID_SXP); | |
| 87 } | |
| 88 | |
| 89 int | |
| 90 PORT_RegExpValid(const char *exp) | |
| 91 { | |
| 92 int x; | |
| 93 | |
| 94 x = _valid_subexp(exp, '\0', '\0'); | |
| 95 return (x < 0 ? x : VALID_SXP); | |
| 96 } | |
| 97 | |
| 98 | |
| 99 /* ----------------------------- shexp_match ----------------------------- */ | |
| 100 | |
| 101 | |
| 102 #define MATCH 0 | |
| 103 #define NOMATCH 1 | |
| 104 #define ABORTED -1 | |
| 105 | |
| 106 static int | |
| 107 _shexp_match(const char *str, const char *exp, PRBool case_insensitive, | |
| 108 unsigned int level); | |
| 109 | |
| 110 /* Count characters until we reach a NUL character or either of the | |
| 111 * two delimiter characters, stop1 or stop2. If we encounter a bracketed | |
| 112 * expression, look only for NUL or ']' inside it. Do not look for stop1 | |
| 113 * or stop2 inside it. Return ABORTED if bracketed expression is unterminated. | |
| 114 * Handle all escaping. | |
| 115 * Return index in input string of first stop found, or ABORTED if not found. | |
| 116 * If "dest" is non-NULL, copy counted characters to it and NUL terminate. | |
| 117 */ | |
| 118 static int | |
| 119 _scan_and_copy(const char *exp, char stop1, char stop2, char *dest) | |
| 120 { | |
| 121 register int sx; /* source index */ | |
| 122 register char cc; | |
| 123 | |
| 124 for (sx = 0; (cc = exp[sx]) && cc != stop1 && cc != stop2; sx++) { | |
| 125 if (cc == '\\') { | |
| 126 if (!exp[++sx]) | |
| 127 return ABORTED; /* should be impossible */ | |
| 128 } else if (cc == '[') { | |
| 129 while ((cc = exp[++sx]) && cc != ']') { | |
| 130 if(cc == '\\' && !exp[++sx]) | |
| 131 return ABORTED; | |
| 132 } | |
| 133 if (!cc) | |
| 134 return ABORTED; /* should be impossible */ | |
| 135 } | |
| 136 } | |
| 137 if (dest && sx) { | |
| 138 /* Copy all but the closing delimiter. */ | |
| 139 memcpy(dest, exp, sx); | |
| 140 dest[sx] = 0; | |
| 141 } | |
| 142 return cc ? sx : ABORTED; /* index of closing delimiter */ | |
| 143 } | |
| 144 | |
| 145 /* On input, exp[0] is the opening parenthesis of a union. | |
| 146 * See if any of the alternatives in the union matches as a pattern. | |
| 147 * The strategy is to take each of the alternatives, in turn, and append | |
| 148 * the rest of the expression (after the closing ')' that marks the end of | |
| 149 * this union) to that alternative, and then see if the resultant expression | |
| 150 * matches the input string. Repeat this until some alternative matches, | |
| 151 * or we have an abort. | |
| 152 */ | |
| 153 static int | |
| 154 _handle_union(const char *str, const char *exp, PRBool case_insensitive, | |
| 155 unsigned int level) | |
| 156 { | |
| 157 register int sx; /* source index */ | |
| 158 int cp; /* source index of closing parenthesis */ | |
| 159 int count; | |
| 160 int ret = NOMATCH; | |
| 161 char *e2; | |
| 162 | |
| 163 /* Find the closing parenthesis that ends this union in the expression */ | |
| 164 cp = _scan_and_copy(exp, ')', '\0', NULL); | |
| 165 if (cp == ABORTED || cp < 4) /* must be at least "(a|b" before ')' */ | |
| 166 return ABORTED; | |
| 167 ++cp; /* now index of char after closing parenthesis */ | |
| 168 e2 = (char *) PORT_Alloc(1 + strlen(exp)); | |
| 169 if (!e2) | |
| 170 return ABORTED; | |
| 171 for (sx = 1; ; ++sx) { | |
| 172 /* Here, exp[sx] is one character past the preceding '(' or '|'. */ | |
| 173 /* Copy everything up to the next delimiter to e2 */ | |
| 174 count = _scan_and_copy(exp + sx, ')', '|', e2); | |
| 175 if (count == ABORTED || !count) { | |
| 176 ret = ABORTED; | |
| 177 break; | |
| 178 } | |
| 179 sx += count; | |
| 180 /* Append everything after closing parenthesis to e2. This is safe. */ | |
| 181 strcpy(e2+count, exp+cp); | |
| 182 ret = _shexp_match(str, e2, case_insensitive, level + 1); | |
| 183 if (ret != NOMATCH || !exp[sx] || exp[sx] == ')') | |
| 184 break; | |
| 185 } | |
| 186 PORT_Free(e2); | |
| 187 if (sx < 2) | |
| 188 ret = ABORTED; | |
| 189 return ret; | |
| 190 } | |
| 191 | |
| 192 /* returns 1 if val is in range from start..end, case insensitive. */ | |
| 193 static int | |
| 194 _is_char_in_range(int start, int end, int val) | |
| 195 { | |
| 196 char map[256]; | |
| 197 memset(map, 0, sizeof map); | |
| 198 while (start <= end) | |
| 199 map[tolower(start++)] = 1; | |
| 200 return map[tolower(val)]; | |
| 201 } | |
| 202 | |
| 203 static int | |
| 204 _shexp_match(const char *str, const char *exp, PRBool case_insensitive, | |
| 205 unsigned int level) | |
| 206 { | |
| 207 register int x; /* input string index */ | |
| 208 register int y; /* expression index */ | |
| 209 int ret,neg; | |
| 210 | |
| 211 if (level > 20) /* Don't let the stack get too deep. */ | |
| 212 return ABORTED; | |
| 213 for(x = 0, y = 0; exp[y]; ++y, ++x) { | |
| 214 if((!str[x]) && (exp[y] != '$') && (exp[y] != '*')) { | |
| 215 return NOMATCH; | |
| 216 } | |
| 217 switch(exp[y]) { | |
| 218 case '$': | |
| 219 if(str[x]) | |
| 220 return NOMATCH; | |
| 221 --x; /* we don't want loop to increment x */ | |
| 222 break; | |
| 223 case '*': | |
| 224 while(exp[++y] == '*'){} | |
| 225 if(!exp[y]) | |
| 226 return MATCH; | |
| 227 while(str[x]) { | |
| 228 ret = _shexp_match(&str[x++], &exp[y], case_insensitive, | |
| 229 level + 1); | |
| 230 switch(ret) { | |
| 231 case NOMATCH: | |
| 232 continue; | |
| 233 case ABORTED: | |
| 234 return ABORTED; | |
| 235 default: | |
| 236 return MATCH; | |
| 237 } | |
| 238 } | |
| 239 if((exp[y] == '$') && (exp[y+1] == '\0') && (!str[x])) | |
| 240 return MATCH; | |
| 241 else | |
| 242 return NOMATCH; | |
| 243 case '[': { | |
| 244 int start, end = 0, i; | |
| 245 neg = ((exp[++y] == '^') && (exp[y+1] != ']')); | |
| 246 if (neg) | |
| 247 ++y; | |
| 248 i = y; | |
| 249 start = (unsigned char)(exp[i++]); | |
| 250 if (start == '\\') | |
| 251 start = (unsigned char)(exp[i++]); | |
| 252 if (isalnum(start) && exp[i++] == '-') { | |
| 253 end = (unsigned char)(exp[i++]); | |
| 254 if (end == '\\') | |
| 255 end = (unsigned char)(exp[i++]); | |
| 256 } | |
| 257 if (isalnum(end) && exp[i] == ']') { | |
| 258 /* This is a range form: a-b */ | |
| 259 int val = (unsigned char)(str[x]); | |
| 260 if (end < start) { /* swap them */ | |
| 261 start ^= end; | |
| 262 end ^= start; | |
| 263 start ^= end; | |
| 264 } | |
| 265 if (case_insensitive && isalpha(val)) { | |
| 266 val = _is_char_in_range(start, end, val); | |
| 267 if (neg == val) | |
| 268 return NOMATCH; | |
| 269 } else if (neg != ((val < start) || (val > end))) { | |
| 270 return NOMATCH; | |
| 271 } | |
| 272 y = i; | |
| 273 } else { | |
| 274 /* Not range form */ | |
| 275 int matched = 0; | |
| 276 for (; exp[y] != ']'; y++) { | |
| 277 if (exp[y] == '\\') | |
| 278 ++y; | |
| 279 if(case_insensitive) { | |
| 280 matched |= (toupper(str[x]) == toupper(exp[y])); | |
| 281 } else { | |
| 282 matched |= (str[x] == exp[y]); | |
| 283 } | |
| 284 } | |
| 285 if (neg == matched) | |
| 286 return NOMATCH; | |
| 287 } | |
| 288 } | |
| 289 break; | |
| 290 case '(': | |
| 291 if (!exp[y+1]) | |
| 292 return ABORTED; | |
| 293 return _handle_union(&str[x], &exp[y], case_insensitive, level); | |
| 294 case '?': | |
| 295 break; | |
| 296 case '|': | |
| 297 case ']': | |
| 298 case ')': | |
| 299 return ABORTED; | |
| 300 case '\\': | |
| 301 ++y; | |
| 302 /* fall through */ | |
| 303 default: | |
| 304 if(case_insensitive) { | |
| 305 if(toupper(str[x]) != toupper(exp[y])) | |
| 306 return NOMATCH; | |
| 307 } else { | |
| 308 if(str[x] != exp[y]) | |
| 309 return NOMATCH; | |
| 310 } | |
| 311 break; | |
| 312 } | |
| 313 } | |
| 314 return (str[x] ? NOMATCH : MATCH); | |
| 315 } | |
| 316 | |
| 317 static int | |
| 318 port_RegExpMatch(const char *str, const char *xp, PRBool case_insensitive) | |
| 319 { | |
| 320 char *exp = 0; | |
| 321 int x, ret = MATCH; | |
| 322 | |
| 323 if (!strchr(xp, '~')) | |
| 324 return _shexp_match(str, xp, case_insensitive, 0); | |
| 325 | |
| 326 exp = PORT_Strdup(xp); | |
| 327 if(!exp) | |
| 328 return NOMATCH; | |
| 329 | |
| 330 x = _scan_and_copy(exp, '~', '\0', NULL); | |
| 331 if (x != ABORTED && exp[x] == '~') { | |
| 332 exp[x++] = '\0'; | |
| 333 ret = _shexp_match(str, &exp[x], case_insensitive, 0); | |
| 334 switch (ret) { | |
| 335 case NOMATCH: ret = MATCH; break; | |
| 336 case MATCH: ret = NOMATCH; break; | |
| 337 default: break; | |
| 338 } | |
| 339 } | |
| 340 if (ret == MATCH) | |
| 341 ret = _shexp_match(str, exp, case_insensitive, 0); | |
| 342 | |
| 343 PORT_Free(exp); | |
| 344 return ret; | |
| 345 } | |
| 346 | |
| 347 | |
| 348 /* ------------------------------ shexp_cmp ------------------------------- */ | |
| 349 | |
| 350 int | |
| 351 PORT_RegExpSearch(const char *str, const char *exp) | |
| 352 { | |
| 353 switch(PORT_RegExpValid(exp)) | |
| 354 { | |
| 355 case INVALID_SXP: | |
| 356 return -1; | |
| 357 case NON_SXP: | |
| 358 return (strcmp(exp,str) ? 1 : 0); | |
| 359 default: | |
| 360 return port_RegExpMatch(str, exp, PR_FALSE); | |
| 361 } | |
| 362 } | |
| 363 | |
| 364 int | |
| 365 PORT_RegExpCaseSearch(const char *str, const char *exp) | |
| 366 { | |
| 367 switch(PORT_RegExpValid(exp)) | |
| 368 { | |
| 369 case INVALID_SXP: | |
| 370 return -1; | |
| 371 case NON_SXP: | |
| 372 return (PORT_Strcasecmp(exp,str) ? 1 : 0); | |
| 373 default: | |
| 374 return port_RegExpMatch(str, exp, PR_TRUE); | |
| 375 } | |
| 376 } | |
| 377 | |
| OLD | NEW |