static List *extractsinglematch(const char *subject, const char *pattern, const char *quoting, List *result) { int i; const char *s; if (!haswild(pattern, quoting) /* no wildcards, so no matches */ || !match(subject, pattern, quoting)) return NULL; for (s = subject, i = 0; pattern[i] != '\0'; s++) { if (ISQUOTED(quoting, i)) i++; else { int c = pattern[i++]; switch (c) { case '*': { const char *begin; if (pattern[i] == '\0') return mklist(mkstr(gcdup(s)), result); for (begin = s;; s++) { const char *q = TAILQUOTE(quoting, i); assert(*s != '\0'); if (match(s, pattern + i, q)) { result = mklist(mkstr(gcndup(begin, s - begin)), result); return haswild(pattern + i, q) ? extractsinglematch(s, pattern + i, q, result) : result; } } } case '[': { int j = rangematch(pattern + i, TAILQUOTE(quoting, i), *s); assert(j != RANGE_FAIL); if (j == RANGE_ERROR) { assert(*s == '['); break; } i += j; } /* FALLTHROUGH */ case '?': result = mklist(mkstr(str("%c", *s)), result); break; default: break; } } } return result; }
/* match -- match a single pattern against a single string. */ extern Boolean match(const char *s, const char *p, const char *q) { int i; if (q == QUOTED) return streq(s, p); for (i = 0;;) { int c = p[i++]; if (c == '\0') return *s == '\0'; else if (q == UNQUOTED || q[i - 1] == 'r') { switch (c) { case '?': if (*s++ == '\0') return FALSE; break; case '*': while (p[i] == '*' && (q == UNQUOTED || q[i] == 'r')) /* collapse multiple stars */ i++; if (p[i] == '\0') /* star at end of pattern? */ return TRUE; while (*s != '\0') if (match(s++, p + i, TAILQUOTE(q, i))) return TRUE; return FALSE; case '[': { int j; if (*s == '\0') return FALSE; switch (j = rangematch(p + i, TAILQUOTE(q, i), *s)) { default: i += j; break; case RANGE_FAIL: return FALSE; case RANGE_ERROR: if (*s != '[') return FALSE; } s++; break; } default: if (c != *s++) return FALSE; } } else if (c != *s++) return FALSE; } }
extern bool match(char *p, char *m, char *s) { int i, j; if (m == NULL) return streq(p, s); i = 0; while (1) { if (p[i] == '\0') return *s == '\0'; else if (m[i]) { switch (p[i++]) { case '?': if (*s++ == '\0') return FALSE; break; case '*': while (p[i] == '*' && m[i] == 1) /* collapse multiple stars */ i++; if (p[i] == '\0') /* star at end of pattern? */ return TRUE; while (*s != '\0') if (match(p + i, m + i, s++)) return TRUE; return FALSE; case '[': if (*s == '\0') return FALSE; switch (j = rangematch(p + i, *s)) { default: i += j; break; case RANGE_FAIL: return FALSE; case RANGE_ERROR: if (*s != '[') return FALSE; } s++; break; default: panic("bad metacharacter in match"); /* NOTREACHED */ return FALSE; /* hush up gcc -Wall */ } } else if (p[i++] != *s++) return FALSE; } }
int fnmatch(const char *pattern, const char *string, int flags) { const char *stringstart; char *newp; char c, test; for (stringstart = string;;) switch (c = *pattern++) { case EOS: if ((flags & FNM_LEADING_DIR) && *string == '/') return (0); return (*string == EOS ? 0 : FNM_NOMATCH); case '?': if (*string == EOS) return (FNM_NOMATCH); if (*string == '/' && (flags & FNM_PATHNAME)) return (FNM_NOMATCH); if (*string == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) return (FNM_NOMATCH); ++string; break; case '*': c = *pattern; /* Collapse multiple stars. */ while (c == '*') c = *++pattern; if (*string == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) return (FNM_NOMATCH); /* Optimize for pattern with * at end or before /. */ if (c == EOS) if (flags & FNM_PATHNAME) return ((flags & FNM_LEADING_DIR) || strchr(string, '/') == NULL ? 0 : FNM_NOMATCH); else return (0); else if (c == '/' && flags & FNM_PATHNAME) { if ((string = strchr(string, '/')) == NULL) return (FNM_NOMATCH); break; } /* General case, use recursion. */ while ((test = *string) != EOS) { if (!fnmatch(pattern, string, flags & ~FNM_PERIOD)) return (0); if (test == '/' && flags & FNM_PATHNAME) break; ++string; } return (FNM_NOMATCH); case '[': if (*string == EOS) return (FNM_NOMATCH); if (*string == '/' && (flags & FNM_PATHNAME)) return (FNM_NOMATCH); if (*string == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) return (FNM_NOMATCH); switch (rangematch(pattern, *string, flags, &newp)) { case RANGE_ERROR: goto norm; case RANGE_MATCH: pattern = newp; break; case RANGE_NOMATCH: return (FNM_NOMATCH); } ++string; break; case '\\': if (!(flags & FNM_NOESCAPE)) { if ((c = *pattern++) == EOS) { c = '\\'; --pattern; } } /* FALLTHROUGH */ default: norm: if (c == *string) ; else if ((flags & FNM_CASEFOLD) && (tolower((unsigned char)c) == tolower((unsigned char)*string))) ; else return (FNM_NOMATCH); string++; break; } /* NOTREACHED */ }
static int fnmatch1(const char *pattern, const char *string, const char *stringstart, int flags, mbstate_t patmbs, mbstate_t strmbs) { const char *bt_pattern, *bt_string; mbstate_t bt_patmbs, bt_strmbs; char *newp; char c; wchar_t pc, sc; size_t pclen, sclen; bt_pattern = bt_string = NULL; for (;;) { pclen = mbrtowc(&pc, pattern, MB_LEN_MAX, &patmbs); if (pclen == (size_t)-1 || pclen == (size_t)-2) return (FNM_NOMATCH); pattern += pclen; sclen = mbrtowc(&sc, string, MB_LEN_MAX, &strmbs); if (sclen == (size_t)-1 || sclen == (size_t)-2) { sc = (unsigned char)*string; sclen = 1; memset(&strmbs, 0, sizeof(strmbs)); } switch (pc) { case EOS: if ((flags & FNM_LEADING_DIR) && sc == '/') return (0); if (sc == EOS) return (0); goto backtrack; case '?': if (sc == EOS) return (FNM_NOMATCH); if (sc == '/' && (flags & FNM_PATHNAME)) goto backtrack; if (sc == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) goto backtrack; string += sclen; break; case '*': c = *pattern; /* Collapse multiple stars. */ while (c == '*') c = *++pattern; if (sc == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) goto backtrack; /* Optimize for pattern with * at end or before /. */ if (c == EOS) if (flags & FNM_PATHNAME) return ((flags & FNM_LEADING_DIR) || strchr(string, '/') == NULL ? 0 : FNM_NOMATCH); else return (0); else if (c == '/' && flags & FNM_PATHNAME) { if ((string = strchr(string, '/')) == NULL) return (FNM_NOMATCH); break; } /* * First try the shortest match for the '*' that * could work. We can forget any earlier '*' since * there is no way having it match more characters * can help us, given that we are already here. */ bt_pattern = pattern, bt_patmbs = patmbs; bt_string = string, bt_strmbs = strmbs; break; case '[': if (sc == EOS) return (FNM_NOMATCH); if (sc == '/' && (flags & FNM_PATHNAME)) goto backtrack; if (sc == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) goto backtrack; switch (rangematch(pattern, sc, flags, &newp, &patmbs)) { case RANGE_ERROR: goto norm; case RANGE_MATCH: pattern = newp; break; case RANGE_NOMATCH: goto backtrack; } string += sclen; break; case '\\': if (!(flags & FNM_NOESCAPE)) { pclen = mbrtowc(&pc, pattern, MB_LEN_MAX, &patmbs); if (pclen == (size_t)-1 || pclen == (size_t)-2) return (FNM_NOMATCH); pattern += pclen; } /* FALLTHROUGH */ default: norm: string += sclen; if (pc == sc) ; else if ((flags & FNM_CASEFOLD) && (towlower(pc) == towlower(sc))) ; else { backtrack: /* * If we have a mismatch (other than hitting * the end of the string), go back to the last * '*' seen and have it match one additional * character. */ if (bt_pattern == NULL) return (FNM_NOMATCH); sclen = mbrtowc(&sc, bt_string, MB_LEN_MAX, &bt_strmbs); if (sclen == (size_t)-1 || sclen == (size_t)-2) { sc = (unsigned char)*bt_string; sclen = 1; memset(&bt_strmbs, 0, sizeof(bt_strmbs)); } if (sc == EOS) return (FNM_NOMATCH); if (sc == '/' && flags & FNM_PATHNAME) return (FNM_NOMATCH); bt_string += sclen; pattern = bt_pattern, patmbs = bt_patmbs; string = bt_string, strmbs = bt_strmbs; } break; } } /* NOTREACHED */ }
API_EXPORT(int) ap_fnmatch(const char *pattern, const char *string, int flags) { const char *stringstart; char c, test; for (stringstart = string;;) { switch (c = *pattern++) { case EOS: return (*string == EOS ? 0 : FNM_NOMATCH); case '?': if (*string == EOS) { return (FNM_NOMATCH); } if (*string == '/' && (flags & FNM_PATHNAME)) { return (FNM_NOMATCH); } if (*string == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) { return (FNM_NOMATCH); } ++string; break; case '*': c = *pattern; /* Collapse multiple stars. */ while (c == '*') { c = *++pattern; } if (*string == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) { return (FNM_NOMATCH); } /* Optimize for pattern with * at end or before /. */ if (c == EOS) { if (flags & FNM_PATHNAME) { return (strchr(string, '/') == NULL ? 0 : FNM_NOMATCH); } else { return (0); } } else if (c == '/' && flags & FNM_PATHNAME) { if ((string = strchr(string, '/')) == NULL) { return (FNM_NOMATCH); } break; } /* General case, use recursion. */ while ((test = *string) != EOS) { if (!ap_fnmatch(pattern, string, flags & ~FNM_PERIOD)) { return (0); } if (test == '/' && flags & FNM_PATHNAME) { break; } ++string; } return (FNM_NOMATCH); case '[': if (*string == EOS) { return (FNM_NOMATCH); } if (*string == '/' && flags & FNM_PATHNAME) { return (FNM_NOMATCH); } if (*string == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) { return (FNM_NOMATCH); } if ((pattern = rangematch(pattern, *string, flags)) == NULL) { return (FNM_NOMATCH); } ++string; break; case '\\': if (!(flags & FNM_NOESCAPE)) { if ((c = *pattern++) == EOS) { c = '\\'; --pattern; } } /* FALLTHROUGH */ default: if (flags & FNM_CASE_BLIND) { if (ap_tolower(c) != ap_tolower(*string)) { return (FNM_NOMATCH); } } else if (c != *string) { return (FNM_NOMATCH); } string++; break; } /* NOTREACHED */ } }
EDITORCONFIG_LOCAL int ec_fnmatch(const char *pattern, const char *string, int flags) { const char *stringstart; char c, test; /* When multi stars presents, should_fnm_pathname will be set to 0 * regardless of whether EC_FNM_PATHNAME is set into flags. Otherwise, the * value equals to flags & EC_FNM_PATHNAME */ _Bool should_fnm_pathname = 0; for (stringstart = string;;) switch (c = *pattern++) { case EOS: if ((flags & EC_FNM_LEADING_DIR) && *string == '/') return (0); return (*string == EOS ? 0 : EC_FNM_NOMATCH); case '?': if (*string == EOS) return (EC_FNM_NOMATCH); if (*string == '/' && (flags & EC_FNM_PATHNAME)) return (EC_FNM_NOMATCH); if (*string == '.' && (flags & EC_FNM_PERIOD) && (string == stringstart || ((flags & EC_FNM_PATHNAME) && *(string - 1) == '/'))) return (EC_FNM_NOMATCH); ++string; break; case '*': c = *pattern; /* see the comments above the declaration of should_fnm_pathname */ should_fnm_pathname = (_Bool)((c != '*') && (flags & EC_FNM_PATHNAME)); /* Collapse multiple stars. */ while (c == '*') c = *++pattern; if (*string == '.' && (flags & EC_FNM_PERIOD) && (string == stringstart || (should_fnm_pathname && *(string - 1) == '/'))) return (EC_FNM_NOMATCH); /* Optimize for pattern with * at end or before /. */ if (c == EOS) if (should_fnm_pathname) return ((flags & EC_FNM_LEADING_DIR) || strchr(string, '/') == NULL ? 0 : EC_FNM_NOMATCH); else return (0); else if (c == '/' && should_fnm_pathname) { if ((string = strchr(string, '/')) == NULL) return (EC_FNM_NOMATCH); break; } /* General case, use recursion. */ while ((test = *string) != EOS) { if (!ec_fnmatch(pattern, string, flags & ~EC_FNM_PERIOD)) return (0); if (test == '/' && should_fnm_pathname) break; ++string; } return (EC_FNM_NOMATCH); case '[': if (*string == EOS) return (EC_FNM_NOMATCH); if (*string == '/' && flags & EC_FNM_PATHNAME) return (EC_FNM_NOMATCH); if ((pattern = rangematch(pattern, *string, flags)) == NULL) return (EC_FNM_NOMATCH); ++string; break; case '\\': if (!(flags & EC_FNM_NOESCAPE)) { if ((c = *pattern++) == EOS) { c = '\\'; --pattern; } } /* FALLTHROUGH */ default: if (c == *string) ; else if ((flags & EC_FNM_CASEFOLD) && (tolower((unsigned char)c) == tolower((unsigned char)*string))) ; else if ((flags & EC_FNM_PREFIX_DIRS) && *string == EOS && ((c == '/' && string != stringstart) || (string == stringstart+1 && *stringstart == '/'))) return (0); else return (EC_FNM_NOMATCH); string++; break; } /* NOTREACHED */ }
int fnmatch(const char *pattern,const char *string,int flags) { const char *stringstart; char c, test; //_DIAGASSERT(pattern != NULL); //_DIAGASSERT(string != NULL); for (stringstart = string;;) switch (c = FOLDCASE(*pattern++, flags)) { case EOS: if ((flags & FNM_LEADING_DIR) && *string == '/') return (0); return (*string == EOS ? 0 : FNM_NOMATCH); case '?': if (*string == EOS) return FNM_NOMATCH; if (*string == '/' && (flags & FNM_PATHNAME)) return FNM_NOMATCH; if (*string == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) return FNM_NOMATCH; ++string; break; case '*': c = FOLDCASE(*pattern, flags); /* Collapse multiple stars. */ while (c == '*') c = FOLDCASE(*++pattern, flags); if (*string == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) return FNM_NOMATCH; /* Optimize for pattern with * at end or before /. */ if (c == EOS) { if (flags & FNM_PATHNAME) return ((flags & FNM_LEADING_DIR) || strchr(string, '/') == NULL ? 0 : FNM_NOMATCH); else return (0); } else if (c == '/' && flags & FNM_PATHNAME) { if ((string = strchr(string, '/')) == NULL) return FNM_NOMATCH; break; } /* General case, use recursion. */ while ((test = FOLDCASE(*string, flags)) != EOS) { if (!fnmatch(pattern, string, flags & ~FNM_PERIOD)) return (0); if (test == '/' && flags & FNM_PATHNAME) break; ++string; } return FNM_NOMATCH; case '[': if (*string == EOS) return FNM_NOMATCH; if (*string == '/' && flags & FNM_PATHNAME) return FNM_NOMATCH; if ((pattern = rangematch(pattern, FOLDCASE(*string, flags), flags)) == NULL) return FNM_NOMATCH; ++string; break; case '\\': if (!(flags & FNM_NOESCAPE)) { if ((c = FOLDCASE(*pattern++, flags)) == EOS) { c = '\\'; --pattern; } } /* FALLTHROUGH */ default: if (c != FOLDCASE(*string++, flags)) return FNM_NOMATCH; break; } /* NOTREACHED */ }
int wildmatch(const char *pattern, const char *string, int flags) { const char *stringstart; const char *newp; const char *slash; char c, test; char prev; int wild = 0; /* WM_WILDSTAR implies WM_PATHNAME. */ if (check_flag(flags, WM_WILDSTAR)) { flags |= WM_PATHNAME; } for (stringstart = string;;) { switch (c = *pattern++) { case EOS: if (check_flag(flags, WM_LEADING_DIR) && *string == '/') return WM_MATCH; return (*string == EOS) ? WM_MATCH : WM_NOMATCH; case '?': if (*string == EOS) return WM_NOMATCH; if (*string == '/' && check_flag(flags, WM_PATHNAME)) return WM_NOMATCH; if (*string == '.' && check_flag(flags, WM_PERIOD) && (string == stringstart || (check_flag(flags, WM_PATHNAME) && *(string - 1) == '/'))) return WM_NOMATCH; ++string; break; case '*': c = *pattern; wild = check_flag(flags, WM_WILDSTAR) && c == '*'; if (wild) { prev = pattern[-2]; /* Collapse multiple stars and slash-** patterns, * e.g. "** / *** / **** / **" (without spaces) * is treated as a single ** wildstar. */ while (c == '*') { c = *++pattern; } while (c == '/' && pattern[1] == '*' && pattern[2] == '*') { prev = c; c = *++pattern; while (c == '*') { c = *++pattern; } } if (c == '/' && wildmatch(pattern+1, string, flags) == WM_MATCH) { return WM_MATCH; } } else { /* Collapse multiple stars. */ while (c == '*') { c = *++pattern; } } if (!wild && *string == '.' && check_flag(flags, WM_PERIOD) && (string == stringstart || (check_flag(flags, WM_PATHNAME) && *(string - 1) == '/'))) { return WM_NOMATCH; } /* Optimize for pattern with * or ** at end or before /. */ if (c == EOS) { if (wild && prev == '/') { return WM_MATCH; } if (check_flag(flags, WM_PATHNAME)) { return (check_flag(flags, WM_LEADING_DIR) || strchr(string, '/') == NULL ? WM_MATCH : WM_NOMATCH); } else { return WM_MATCH; } } else if (c == '/') { if (wild) { slash = strchr(stringstart, '/'); if (!slash) { return WM_NOMATCH; } while (slash) { if (wildmatch(pattern+1, slash+1, flags) == 0) { return WM_MATCH; } slash = strchr(slash+1, '/'); } } else { if (check_flag(flags, WM_PATHNAME)) { if ((string = strchr(string, '/')) == NULL) { return WM_NOMATCH; } } } } else if (wild) { return WM_NOMATCH; } /* General case, use recursion. */ while ((test = *string) != EOS) { if (!wildmatch(pattern, string, flags & ~WM_PERIOD)) return WM_MATCH; if (test == '/' && check_flag(flags, WM_PATHNAME)) break; ++string; } return WM_NOMATCH; case '[': if (*string == EOS) return WM_NOMATCH; if (*string == '/' && check_flag(flags, WM_PATHNAME)) return WM_NOMATCH; if (*string == '.' && check_flag(flags, WM_PERIOD) && (string == stringstart || (check_flag(flags, WM_PATHNAME) && *(string - 1) == '/'))) return WM_NOMATCH; switch (rangematch(pattern, *string, flags, &newp)) { case RANGE_ERROR: /* not a good range, treat as normal text */ ++string; goto normal; case RANGE_MATCH: pattern = newp; break; case RANGE_NOMATCH: return (WM_NOMATCH); } ++string; break; case '\\': if (!check_flag(flags, WM_NOESCAPE)) { if ((c = *pattern++) == EOS) { c = '\\'; --pattern; if (*(string+1) == EOS) { return WM_NOMATCH; } } } /* FALLTHROUGH */ default: normal: if (c != *string && !(check_flag(flags, WM_CASEFOLD) && (tolower((unsigned char)c) == tolower((unsigned char)*string)))) return WM_NOMATCH; ++string; break; } /* NOTREACHED */ } }
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_fnmatch(const char *pattern, const char *string, int flags) { const char *stringstart; char c, test; for (stringstart = string;;) switch (c = *pattern++) { case EOS: return (*string == EOS ? 0 : FNM_NOMATCH); case '?': if (*string == EOS) return (FNM_NOMATCH); if (*string == '/' && (flags & FNM_PATHNAME)) return (FNM_NOMATCH); if (*string == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) return (FNM_NOMATCH); ++string; break; case '*': c = *pattern; /* Collapse multiple stars. */ while (c == '*') c = *++pattern; if (*string == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) return (FNM_NOMATCH); /* Optimize for pattern with * at end or before /. */ if (c == EOS) if (flags & FNM_PATHNAME) return (strchr(string, '/') == NULL ? 0 : FNM_NOMATCH); else return (0); else if (c == '/' && flags & FNM_PATHNAME) { if ((string = strchr(string, '/')) == NULL) return (FNM_NOMATCH); break; } /* General case, use recursion. */ while ((test = *string) != EOS) { if (!rk_fnmatch(pattern, string, flags & ~FNM_PERIOD)) return (0); if (test == '/' && flags & FNM_PATHNAME) break; ++string; } return (FNM_NOMATCH); case '[': if (*string == EOS) return (FNM_NOMATCH); if (*string == '/' && flags & FNM_PATHNAME) return (FNM_NOMATCH); if ((pattern = rangematch(pattern, *string, flags)) == NULL) return (FNM_NOMATCH); ++string; break; case '\\': if (!(flags & FNM_NOESCAPE)) { if ((c = *pattern++) == EOS) { c = '\\'; --pattern; } } /* FALLTHROUGH */ default: if (c != *string++) return (FNM_NOMATCH); break; } /* NOTREACHED */ }
static int fnmatch1(const char *pattern, const char *string, const char *stringstart, int flags, mbstate_t patmbs, mbstate_t strmbs) { char *newp; char c; wchar_t pc, sc; size_t pclen, sclen; for (;;) { pclen = mbrtowc(&pc, pattern, MB_LEN_MAX, &patmbs); if (pclen == (size_t)-1 || pclen == (size_t)-2) return (FNM_NOMATCH); pattern += pclen; sclen = mbrtowc(&sc, string, MB_LEN_MAX, &strmbs); if (sclen == (size_t)-1 || sclen == (size_t)-2) { sc = (unsigned char)*string; sclen = 1; memset(&strmbs, 0, sizeof(strmbs)); } switch (pc) { case EOS: if ((flags & FNM_LEADING_DIR) && sc == '/') return (0); return (sc == EOS ? 0 : FNM_NOMATCH); case '?': if (sc == EOS) return (FNM_NOMATCH); if (sc == '/' && (flags & FNM_PATHNAME)) return (FNM_NOMATCH); if (sc == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) return (FNM_NOMATCH); string += sclen; break; case '*': c = *pattern; /* Collapse multiple stars. */ while (c == '*') c = *++pattern; if (sc == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) return (FNM_NOMATCH); /* Optimize for pattern with * at end or before /. */ if (c == EOS) if (flags & FNM_PATHNAME) return ((flags & FNM_LEADING_DIR) || strchr(string, '/') == NULL ? 0 : FNM_NOMATCH); else return (0); else if (c == '/' && flags & FNM_PATHNAME) { if ((string = strchr(string, '/')) == NULL) return (FNM_NOMATCH); break; } /* General case, use recursion. */ while (sc != EOS) { if (!fnmatch1(pattern, string, stringstart, flags, patmbs, strmbs)) return (0); sclen = mbrtowc(&sc, string, MB_LEN_MAX, &strmbs); if (sclen == (size_t)-1 || sclen == (size_t)-2) { sc = (unsigned char)*string; sclen = 1; memset(&strmbs, 0, sizeof(strmbs)); } if (sc == '/' && flags & FNM_PATHNAME) break; string += sclen; } return (FNM_NOMATCH); case '[': if (sc == EOS) return (FNM_NOMATCH); if (sc == '/' && (flags & FNM_PATHNAME)) return (FNM_NOMATCH); if (sc == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) return (FNM_NOMATCH); switch (rangematch(pattern, sc, flags, &newp, &patmbs)) { case RANGE_ERROR: goto norm; case RANGE_MATCH: pattern = newp; break; case RANGE_NOMATCH: return (FNM_NOMATCH); } string += sclen; break; case '\\': if (!(flags & FNM_NOESCAPE)) { pclen = mbrtowc(&pc, pattern, MB_LEN_MAX, &patmbs); if (pclen == (size_t)-1 || pclen == (size_t)-2) return (FNM_NOMATCH); pattern += pclen; } /* FALLTHROUGH */ default: norm: if (pc == sc) ; else if ((flags & FNM_CASEFOLD) && (towlower(pc) == towlower(sc))) ; else return (FNM_NOMATCH); string += sclen; break; } } /* NOTREACHED */ }
int fnmatch(const char *pattern, const char *string, int flags) { char c; char test; for (;;) switch ((c = *pattern++)) { case 0: return *string == 0 ? 0 : FNM_NOMATCH; case '?': if ((test = *string++) == 0 || (isslash(test) && (flags & FNM_PATHNAME))) return(FNM_NOMATCH); break; case '*': c = *pattern; /* collapse multiple stars */ while (c == '*') c = *++pattern; /* optimize for pattern with * at end or before / */ if (c == 0) { if (flags & FNM_PATHNAME) return find_slash(string) ? FNM_NOMATCH : 0; else return 0; } else if (isslash(c) && flags & FNM_PATHNAME) { if ((string = find_slash(string)) == NULL) return FNM_NOMATCH; break; } /* general case, use recursion */ while ((test = *string) != 0) { if (fnmatch(pattern, string, flags) == 0) return(0); if (isslash(test) && flags & FNM_PATHNAME) break; ++string; } return FNM_NOMATCH; case '[': if ((test = *string++) == 0 || (isslash(test) && flags & FNM_PATHNAME)) return FNM_NOMATCH; if ((pattern = rangematch(pattern, test, flags & FNM_NOCASE)) == NULL) return FNM_NOMATCH; break; case '\\': if (!(flags & FNM_NOESCAPE) && pattern[1] && strchr("*?[\\", pattern[1])) { if ((c = *pattern++) == 0) { c = '\\'; --pattern; } if (c != *string++) return FNM_NOMATCH; break; } /* FALLTHROUGH */ default: if (isslash(c) && isslash(*string)) { string++; break; } if (flags & FNM_NOCASE) { if (toupper(c) != toupper(*string++)) return FNM_NOMATCH; } else { if (c != *string++) return FNM_NOMATCH; } break; } }