#define FALSE 0
#define TRUE 1
-#define NOMATCH 1
-#define MATCH 0
-#define ABORT_ALL -1
-#define ABORT_TO_STARSTAR -2
-
#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \
&& *(class) == *(litmatch) \
&& strncmp((char*)class, litmatch, len) == 0)
#define ISUPPER(c) (ISASCII(c) && isupper(c))
#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
-static int force_lower_case = 0;
-
/* Match pattern "p" against "text" */
-static int dowild(const uchar *p, const uchar *text)
+static int dowild(const uchar *p, const uchar *text, int force_lower_case)
{
uchar p_ch;
+ const uchar *pattern = p;
for ( ; (p_ch = *p) != '\0'; text++, p++) {
- int matched, special;
+ int matched, match_slash, negated;
uchar t_ch, prev_ch;
if ((t_ch = *text) == '\0' && p_ch != '*')
return ABORT_ALL;
if (force_lower_case && ISUPPER(t_ch))
t_ch = tolower(t_ch);
+ if (force_lower_case && ISUPPER(p_ch))
+ p_ch = tolower(p_ch);
switch (p_ch) {
case '\\':
/* Literal match with following character. Note that the test
continue;
case '*':
if (*++p == '*') {
+ const uchar *prev_p = p - 2;
while (*++p == '*') {}
- special = TRUE;
+ if ((prev_p < pattern || *prev_p == '/') &&
+ (*p == '\0' || *p == '/' ||
+ (p[0] == '\\' && p[1] == '/'))) {
+ /*
+ * Assuming we already match 'foo/' and are at
+ * <star star slash>, just assume it matches
+ * nothing and go ahead match the rest of the
+ * pattern with the remaining string. This
+ * helps make foo/<*><*>/bar (<> because
+ * otherwise it breaks C comment syntax) match
+ * both foo/bar and foo/a/bar.
+ */
+ if (p[0] == '/' &&
+ dowild(p + 1, text, force_lower_case) == MATCH)
+ return MATCH;
+ match_slash = TRUE;
+ } else
+ return ABORT_MALFORMED;
} else
- special = FALSE;
+ match_slash = FALSE;
if (*p == '\0') {
/* Trailing "**" matches everything. Trailing "*" matches
* only if there are no more slash characters. */
- if (!special) {
+ if (!match_slash) {
if (strchr((char*)text, '/') != NULL)
return NOMATCH;
}
while (1) {
if (t_ch == '\0')
break;
- if ((matched = dowild(p, text)) != NOMATCH) {
- if (!special || matched != ABORT_TO_STARSTAR)
+ if ((matched = dowild(p, text, force_lower_case)) != NOMATCH) {
+ if (!match_slash || matched != ABORT_TO_STARSTAR)
return matched;
- } else if (!special && t_ch == '/')
+ } else if (!match_slash && t_ch == '/')
return ABORT_TO_STARSTAR;
t_ch = *++text;
}
p_ch = NEGATE_CLASS;
#endif
/* Assign literal TRUE/FALSE because of "matched" comparison. */
- special = p_ch == NEGATE_CLASS? TRUE : FALSE;
- if (special) {
+ negated = p_ch == NEGATE_CLASS? TRUE : FALSE;
+ if (negated) {
/* Inverted character class. */
p_ch = *++p;
}
} else if (t_ch == p_ch)
matched = TRUE;
} while (prev_ch = p_ch, (p_ch = *++p) != ']');
- if (matched == special || t_ch == '/')
+ if (matched == negated || t_ch == '/')
return NOMATCH;
continue;
}
}
/* Match the "pattern" against the "text" string. */
-int wildmatch(const char *pattern, const char *text)
-{
- return dowild((const uchar*)pattern, (const uchar*)text);
-}
-
-/* Match the "pattern" against the forced-to-lower-case "text" string. */
-int iwildmatch(const char *pattern, const char *text)
+int wildmatch(const char *pattern, const char *text, int flags)
{
- int ret;
- force_lower_case = 1;
- ret = dowild((const uchar*)pattern, (const uchar*)text);
- force_lower_case = 0;
- return ret;
+ return dowild((const uchar*)pattern, (const uchar*)text,
+ flags & FNM_CASEFOLD ? 1 :0);
}