1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
10 * nsWildCard.cpp: shell-like wildcard match routines
12 * See nsIZipReader.findEntries documentation in nsIZipReader.idl for
13 * a description of the syntax supported by the routines in this file.
19 #include "nsWildCard.h"
21 #include "nsCRTGlue.h"
22 #include "nsCharTraits.h"
24 /* -------------------- ASCII-specific character methods ------------------- */
26 typedef int static_assert_character_code_arrangement
['a' > 'A' ? 1 : -1];
32 return ('a' <= aChar
&& aChar
<= 'z') ||
33 ('A' <= aChar
&& aChar
<= 'Z');
40 return ('0' <= aChar
&& aChar
<= '9') || ::alpha(aChar
);
47 return ('A' <= aChar
&& aChar
<= 'Z') ? aChar
+ ('a' - 'A') : aChar
;
54 return ('a' <= aChar
&& aChar
<= 'z') ? aChar
- ('a' - 'A') : aChar
;
57 /* ----------------------------- _valid_subexp ---------------------------- */
61 _valid_subexp(const T
* aExpr
, T aStop1
, T aStop2
)
64 int nsc
= 0; /* Number of special characters */
65 int np
; /* Number of pipe characters in union */
66 int tld
= 0; /* Number of tilde characters */
68 for (x
= 0; aExpr
[x
] && (aExpr
[x
] != aStop1
) && (aExpr
[x
] != aStop2
); ++x
) {
71 if (tld
) { /* at most one exclusion */
74 if (aStop1
) { /* no exclusions within unions */
77 if (!aExpr
[x
+ 1]) { /* exclusion cannot be last character */
80 if (!x
) { /* exclusion cannot be first character */
92 if ((!aExpr
[++x
]) || (aExpr
[x
] == ']')) {
95 for (; aExpr
[x
] && (aExpr
[x
] != ']'); ++x
) {
96 if (aExpr
[x
] == '\\' && !aExpr
[++x
]) {
106 if (aStop1
) { /* no nested unions */
111 int t
= ::_valid_subexp(&aExpr
[++x
], T(')'), T('|'));
112 if (t
== 0 || t
== INVALID_SXP
) {
120 } while (aExpr
[x
] == '|');
121 if (np
< 1) { /* must be at least one pipe */
139 if (!aStop1
&& !nsc
) { /* must be at least one special character */
142 return ((aExpr
[x
] == aStop1
|| aExpr
[x
] == aStop2
) ? x
: INVALID_SXP
);
148 NS_WildCardValid_(const T
* aExpr
)
150 int x
= ::_valid_subexp(aExpr
, T('\0'), T('\0'));
151 return (x
< 0 ? x
: VALID_SXP
);
155 NS_WildCardValid(const char* aExpr
)
157 return NS_WildCardValid_(aExpr
);
161 NS_WildCardValid(const char16_t
* aExpr
)
163 return NS_WildCardValid_(aExpr
);
166 /* ----------------------------- _shexp_match ----------------------------- */
175 _shexp_match(const T
* aStr
, const T
* aExpr
, bool aCaseInsensitive
,
176 unsigned int aLevel
);
179 * Count characters until we reach a NUL character or either of the
180 * two delimiter characters, stop1 or stop2. If we encounter a bracketed
181 * expression, look only for NUL or ']' inside it. Do not look for stop1
182 * or stop2 inside it. Return ABORTED if bracketed expression is unterminated.
183 * Handle all escaping.
184 * Return index in input string of first stop found, or ABORTED if not found.
185 * If "dest" is non-nullptr, copy counted characters to it and null terminate.
189 _scan_and_copy(const T
* aExpr
, T aStop1
, T aStop2
, T
* aDest
)
191 int sx
; /* source index */
194 for (sx
= 0; (cc
= aExpr
[sx
]) && cc
!= aStop1
&& cc
!= aStop2
; ++sx
) {
197 return ABORTED
; /* should be impossible */
199 } else if (cc
== '[') {
200 while ((cc
= aExpr
[++sx
]) && cc
!= ']') {
201 if (cc
== '\\' && !aExpr
[++sx
]) {
206 return ABORTED
; /* should be impossible */
211 /* Copy all but the closing delimiter. */
212 memcpy(aDest
, aExpr
, sx
* sizeof(T
));
215 return cc
? sx
: ABORTED
; /* index of closing delimiter */
218 /* On input, expr[0] is the opening parenthesis of a union.
219 * See if any of the alternatives in the union matches as a pattern.
220 * The strategy is to take each of the alternatives, in turn, and append
221 * the rest of the expression (after the closing ')' that marks the end of
222 * this union) to that alternative, and then see if the resultant expression
223 * matches the input string. Repeat this until some alternative matches,
224 * or we have an abort.
228 _handle_union(const T
* aStr
, const T
* aExpr
, bool aCaseInsensitive
,
231 int sx
; /* source index */
232 int cp
; /* source index of closing parenthesis */
237 /* Find the closing parenthesis that ends this union in the expression */
238 cp
= ::_scan_and_copy(aExpr
, T(')'), T('\0'), static_cast<T
*>(nullptr));
239 if (cp
== ABORTED
|| cp
< 4) { /* must be at least "(a|b" before ')' */
242 ++cp
; /* now index of char after closing parenthesis */
243 e2
= (T
*)moz_xmalloc((1 + nsCharTraits
<T
>::length(aExpr
)) * sizeof(T
));
244 for (sx
= 1; ; ++sx
) {
245 /* Here, aExpr[sx] is one character past the preceding '(' or '|'. */
246 /* Copy everything up to the next delimiter to e2 */
247 count
= ::_scan_and_copy(aExpr
+ sx
, T(')'), T('|'), e2
);
248 if (count
== ABORTED
|| !count
) {
253 /* Append everything after closing parenthesis to e2. This is safe. */
254 nsCharTraits
<T
>::copy(e2
+ count
, aExpr
+ cp
,
255 nsCharTraits
<T
>::length(aExpr
+ cp
) + 1);
256 ret
= ::_shexp_match(aStr
, e2
, aCaseInsensitive
, aLevel
+ 1);
257 if (ret
!= NOMATCH
|| !aExpr
[sx
] || aExpr
[sx
] == ')') {
268 /* returns 1 if val is in range from start..end, case insensitive. */
270 _is_char_in_range(unsigned char aStart
, unsigned char aEnd
, unsigned char aVal
)
273 memset(map
, 0, sizeof(map
));
274 while (aStart
<= aEnd
) {
275 map
[lower(aStart
++)] = 1;
277 return map
[lower(aVal
)];
282 _shexp_match(const T
* aStr
, const T
* aExpr
, bool aCaseInsensitive
,
285 int x
; /* input string index */
286 int y
; /* expression index */
289 if (aLevel
> 20) { /* Don't let the stack get too deep. */
292 for (x
= 0, y
= 0; aExpr
[y
]; ++y
, ++x
) {
293 if (!aStr
[x
] && aExpr
[y
] != '$' && aExpr
[y
] != '*') {
301 --x
; /* we don't want loop to increment x */
304 while (aExpr
[++y
] == '*') {
310 ret
= ::_shexp_match(&aStr
[x
++], &aExpr
[y
], aCaseInsensitive
,
321 if (aExpr
[y
] == '$' && aExpr
[y
+ 1] == '\0' && !aStr
[x
]) {
330 neg
= (aExpr
[y
] == '^' && aExpr
[y
+ 1] != ']');
339 if (::alphanumeric(start
) && aExpr
[i
++] == '-') {
345 if (::alphanumeric(end
) && aExpr
[i
] == ']') {
346 /* This is a range form: a-b */
348 if (end
< start
) { /* swap them */
353 if (aCaseInsensitive
&& ::alpha(val
)) {
354 val
= ::_is_char_in_range((unsigned char)start
,
360 } else if (neg
!= (val
< start
|| val
> end
)) {
367 for (; aExpr
[y
] != ']'; ++y
) {
368 if (aExpr
[y
] == '\\') {
371 if (aCaseInsensitive
) {
372 matched
|= (::upper(aStr
[x
]) == ::upper(aExpr
[y
]));
374 matched
|= (aStr
[x
] == aExpr
[y
]);
377 if (neg
== matched
) {
387 return ::_handle_union(&aStr
[x
], &aExpr
[y
], aCaseInsensitive
,
399 if (aCaseInsensitive
) {
400 if (::upper(aStr
[x
]) != ::upper(aExpr
[y
])) {
404 if (aStr
[x
] != aExpr
[y
]) {
411 return (aStr
[x
] ? NOMATCH
: MATCH
);
416 ns_WildCardMatch(const T
* aStr
, const T
* aXp
, bool aCaseInsensitive
)
421 if (!nsCharTraits
<T
>::find(aXp
, nsCharTraits
<T
>::length(aXp
), T('~'))) {
422 return ::_shexp_match(aStr
, aXp
, aCaseInsensitive
, 0);
425 expr
= (T
*)moz_xmalloc((nsCharTraits
<T
>::length(aXp
) + 1) * sizeof(T
));
426 memcpy(expr
, aXp
, (nsCharTraits
<T
>::length(aXp
) + 1) * sizeof(T
));
428 int x
= ::_scan_and_copy(expr
, T('~'), T('\0'), static_cast<T
*>(nullptr));
429 if (x
!= ABORTED
&& expr
[x
] == '~') {
431 ret
= ::_shexp_match(aStr
, &expr
[x
], aCaseInsensitive
, 0);
444 ret
= ::_shexp_match(aStr
, expr
, aCaseInsensitive
, 0);
453 NS_WildCardMatch_(const T
* aStr
, const T
* aExpr
, bool aCaseInsensitive
)
455 int is_valid
= NS_WildCardValid(aExpr
);
460 return ::ns_WildCardMatch(aStr
, aExpr
, aCaseInsensitive
);
465 NS_WildCardMatch(const char* aStr
, const char* aXp
, bool aCaseInsensitive
)
467 return NS_WildCardMatch_(aStr
, aXp
, aCaseInsensitive
);
471 NS_WildCardMatch(const char16_t
* aStr
, const char16_t
* aXp
,
472 bool aCaseInsensitive
)
474 return NS_WildCardMatch_(aStr
, aXp
, aCaseInsensitive
);