/* * Does 'match' match the given name? * A match is found if * * (1) the 'match' string is leading directory of 'name', or * (2) the 'match' string is a wildcard and matches 'name', or * (3) the 'match' string is exactly the same as 'name'. * * and the return value tells which case it was. * * It returns 0 when there is no match. */ static int match_one(const char *match, const char *name, int namelen) { int matchlen; int literal = limit_pathspec_to_literal(); /* If the match was just the prefix, we matched */ if (!*match) return MATCHED_RECURSIVELY; if (ignore_case) { for (;;) { unsigned char c1 = tolower(*match); unsigned char c2 = tolower(*name); if (c1 == '\0' || (!literal && is_glob_special(c1))) break; if (c1 != c2) return 0; match++; name++; namelen--; } } else { for (;;) { unsigned char c1 = *match; unsigned char c2 = *name; if (c1 == '\0' || (!literal && is_glob_special(c1))) break; if (c1 != c2) return 0; match++; name++; namelen--; } } /* * If we don't match the matchstring exactly, * we need to match by fnmatch */ matchlen = strlen(match); if (strncmp_icase(match, name, matchlen)) { if (literal) return 0; return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0; } if (namelen == matchlen) return MATCHED_EXACTLY; if (match[matchlen-1] == '/' || name[matchlen] == '/') return MATCHED_RECURSIVELY; return 0; }
static size_t common_prefix_len(const char **pathspec) { const char *n, *first; size_t max = 0; if (!pathspec) return max; first = *pathspec; while ((n = *pathspec++)) { size_t i, len = 0; for (i = 0; first == n || i < max; i++) { char c = n[i]; if (!c || c != first[i] || is_glob_special(c)) break; if (c == '/') len = i + 1; } if (first == n || len < max) { max = len; if (!max) break; } } return max; }
/* * Does 'match' matches the given name? * A match is found if * * (1) the 'match' string is leading directory of 'name', or * (2) the 'match' string is a wildcard and matches 'name', or * (3) the 'match' string is exactly the same as 'name'. * * and the return value tells which case it was. * * It returns 0 when there is no match. */ static int match_one(const char *match, const char *name, int namelen) { int matchlen; /* If the match was just the prefix, we matched */ if (!*match) return MATCHED_RECURSIVELY; for (;;) { unsigned char c1 = *match; unsigned char c2 = *name; if (c1 == '\0' || is_glob_special(c1)) break; if (c1 != c2) return 0; match++; name++; namelen--; } /* * If we don't match the matchstring exactly, * we need to match by fnmatch */ matchlen = strlen(match); if (strncmp(match, name, matchlen)) return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0; if (namelen == matchlen) return MATCHED_EXACTLY; if (match[matchlen-1] == '/' || name[matchlen] == '/') return MATCHED_RECURSIVELY; return 0; }
/* * Return the length of the "simple" part of a path match limiter. */ static int simple_length(const char *match) { int len = -1; for (;;) { unsigned char c = *match++; len++; if (c == '\0' || is_glob_special(c)) return len; } }
/* Match pattern "p" against "text" */ static int dowild(const uchar *p, const uchar *text, unsigned int flags) { uchar p_ch; const uchar *pattern = p; int pattern_needs_leading_period = (flags & WM_PERIOD) && (text[0] == '.'); for ( ; (p_ch = *p) != '\0'; text++, p++) { int matched, match_slash, negated; uchar t_ch, prev_ch; if ((t_ch = *text) == '\0' && p_ch != '*') return WM_ABORT_ALL; if ((flags & WM_CASEFOLD) && ISUPPER(t_ch)) t_ch = tolower(t_ch); if ((flags & WM_CASEFOLD) && ISUPPER(p_ch)) p_ch = tolower(p_ch); switch (p_ch) { case '\\': if (!(flags & WM_NOESCAPE)) /* Literal match with following character. Note that the test * in "default" handles the p[1] == '\0' failure case. */ p_ch = *++p; /* FALLTHROUGH */ default: if (t_ch != p_ch) return WM_NOMATCH; if ((flags & WM_PERIOD) && (flags & WM_PATHNAME) && t_ch == '/' && text[1] == '.' && p[1] != '.') return WM_NOMATCH; /* If we needed a leading period, we've matched it. */ pattern_needs_leading_period = 0; continue; case '?': /* Match anything but '/'. */ if ((flags & WM_PATHNAME) && t_ch == '/') return WM_NOMATCH; continue; case '*': if (*++p == '*') { const uchar *prev_p = p - 2; while (*++p == '*') {} if (!(flags & WM_PATHNAME)) /* without WM_PATHNAME, '*' == '**' */ match_slash = 1; else 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, flags) == WM_MATCH) return WM_MATCH; match_slash = 1; } else return WM_ABORT_MALFORMED; } else /* without WM_PATHNAME, '*' == '**' */ match_slash = flags & WM_PATHNAME ? 0 : 1; if (*p == '\0') { /* If we needed a leading period in the pattern but only * found stars, we didn't match. */ if (pattern_needs_leading_period) return WM_NOMATCH; /* Trailing "**" matches everything. Trailing "*" matches * only if there are no more slash characters. */ if (!match_slash) { if (strchr((char*)text, '/') != NULL) return WM_NOMATCH; if ((flags & WM_PERIOD) && strstr((const char*)text, "/.") != NULL) return WM_NOMATCH; } return WM_MATCH; } else if (!match_slash && *p == '/') { /* * _one_ asterisk followed by a slash * with WM_PATHNAME matches the next * directory */ const char *slash = strchr((char*)text, '/'); if (!slash) return WM_NOMATCH; text = (const uchar*)slash; /* the slash is consumed by the top-level for loop */ break; } while (1) { if (t_ch == '\0') break; /* * Try to advance faster when an asterisk is * followed by a literal. We know in this case * that the the string before the literal * must belong to "*". * If match_slash is false, do not look past * the first slash as it cannot belong to '*'. */ if (!is_glob_special(*p)) { p_ch = *p; if ((flags & WM_CASEFOLD) && ISUPPER(p_ch)) p_ch = tolower(p_ch); while ((t_ch = *text) != '\0' && (match_slash || t_ch != '/')) { if ((flags & WM_CASEFOLD) && ISUPPER(t_ch)) t_ch = tolower(t_ch); if (t_ch == p_ch) break; text++; } if (t_ch != p_ch) return WM_NOMATCH; } if (pattern_needs_leading_period) return WM_NOMATCH; if ((matched = dowild(p, text, flags)) != WM_NOMATCH) { if (!match_slash || matched != WM_ABORT_TO_STARSTAR) return matched; } else if (!match_slash && t_ch == '/') return WM_ABORT_TO_STARSTAR; else if ((flags & WM_PERIOD) && (flags & WM_PATHNAME) && t_ch == '/' && text[1] == '.' && p[1] != '.') return WM_NOMATCH; t_ch = *++text; } return WM_ABORT_ALL; case '[': p_ch = *++p; #ifdef NEGATE_CLASS2 if (p_ch == NEGATE_CLASS2) p_ch = NEGATE_CLASS; #endif /* Assign literal 1/0 because of "matched" comparison. */ negated = p_ch == NEGATE_CLASS ? 1 : 0; if (negated) { /* Inverted character class. */ p_ch = *++p; } prev_ch = 0; matched = 0; do { if (!p_ch) return WM_ABORT_ALL; if (p_ch == '\\') { p_ch = *++p; if (!p_ch) return WM_ABORT_ALL; if (t_ch == p_ch) matched = 1; } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') { p_ch = *++p; if (p_ch == '\\') { p_ch = *++p; if (!p_ch) return WM_ABORT_ALL; } if (t_ch <= p_ch && t_ch >= prev_ch) matched = 1; else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch)) { uchar t_ch_upper = toupper(t_ch); if (t_ch_upper <= p_ch && t_ch_upper >= prev_ch) matched = 1; } p_ch = 0; /* This makes "prev_ch" get set to 0. */ } else if (p_ch == '[' && p[1] == ':') { const uchar *s; int i; for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/ if (!p_ch) return WM_ABORT_ALL; i = p - s - 1; if (i < 0 || p[-1] != ':') { /* Didn't find ":]", so treat like a normal set. */ p = s - 2; p_ch = '['; if (t_ch == p_ch) matched = 1; continue; } if (CC_EQ(s,i, "alnum")) { if (ISALNUM(t_ch)) matched = 1; } else if (CC_EQ(s,i, "alpha")) { if (ISALPHA(t_ch)) matched = 1; } else if (CC_EQ(s,i, "blank")) { if (ISBLANK(t_ch)) matched = 1; } else if (CC_EQ(s,i, "cntrl")) { if (ISCNTRL(t_ch)) matched = 1; } else if (CC_EQ(s,i, "digit")) { if (ISDIGIT(t_ch)) matched = 1; } else if (CC_EQ(s,i, "graph")) { if (ISGRAPH(t_ch)) matched = 1; } else if (CC_EQ(s,i, "lower")) { if (ISLOWER(t_ch)) matched = 1; } else if (CC_EQ(s,i, "print")) { if (ISPRINT(t_ch)) matched = 1; } else if (CC_EQ(s,i, "punct")) { if (ISPUNCT(t_ch)) matched = 1; } else if (CC_EQ(s,i, "space")) { if (ISSPACE(t_ch)) matched = 1; } else if (CC_EQ(s,i, "upper")) { if (ISUPPER(t_ch)) matched = 1; else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch)) matched = 1; } else if (CC_EQ(s,i, "xdigit")) { if (ISXDIGIT(t_ch)) matched = 1; } else /* malformed [:class:] string */ return WM_ABORT_ALL; p_ch = 0; /* This makes "prev_ch" get set to 0. */ } else if (t_ch == p_ch) matched = 1; } while (prev_ch = p_ch, (p_ch = *++p) != ']'); if (matched == negated || ((flags & WM_PATHNAME) && t_ch == '/')) return WM_NOMATCH; if ((flags & WM_PATHNAME) && t_ch == '/' && (flags & WM_PERIOD) && (text[1] == '.') && (p[1] != '.')) return WM_NOMATCH; continue; } } return *text ? WM_NOMATCH : WM_MATCH; }