inline int git_fnmatch(const struct pathspec_item *item, const char *pattern, const char *string, int prefix) { if (prefix > 0) { if (ps_strncmp(item, pattern, string, prefix)) return WM_NOMATCH; pattern += prefix; string += prefix; } if (item->flags & PATHSPEC_ONESTAR) { int pattern_len = strlen(++pattern); int string_len = strlen(string); return string_len < pattern_len || ps_strcmp(item, pattern, string + string_len - pattern_len); } if (item->magic & PATHSPEC_GLOB) return wildmatch(pattern, string, WM_PATHNAME | (item->magic & PATHSPEC_ICASE ? WM_CASEFOLD : 0), NULL); else /* wildmatch has not learned no FNM_PATHNAME mode yet */ return wildmatch(pattern, string, item->magic & PATHSPEC_ICASE ? WM_CASEFOLD : 0, NULL); }
/* * 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_pathspec_item(const struct pathspec_item *item, int prefix, const char *name, int namelen) { /* name/namelen has prefix cut off by caller */ const char *match = item->match + prefix; int matchlen = item->len - prefix; /* * The normal call pattern is: * 1. prefix = common_prefix_len(ps); * 2. prune something, or fill_directory * 3. match_pathspec_depth() * * 'prefix' at #1 may be shorter than the command's prefix and * it's ok for #2 to match extra files. Those extras will be * trimmed at #3. * * Suppose the pathspec is 'foo' and '../bar' running from * subdir 'xyz'. The common prefix at #1 will be empty, thanks * to "../". We may have xyz/foo _and_ XYZ/foo after #2. The * user does not want XYZ/foo, only the "foo" part should be * case-insensitive. We need to filter out XYZ/foo here. In * other words, we do not trust the caller on comparing the * prefix part when :(icase) is involved. We do exact * comparison ourselves. * * Normally the caller (common_prefix_len() in fact) does * _exact_ matching on name[-prefix+1..-1] and we do not need * to check that part. Be defensive and check it anyway, in * case common_prefix_len is changed, or a new caller is * introduced that does not use common_prefix_len. * * If the penalty turns out too high when prefix is really * long, maybe change it to * strncmp(match, name, item->prefix - prefix) */ if (item->prefix && (item->magic & PATHSPEC_ICASE) && strncmp(item->match, name - prefix, item->prefix)) return 0; /* If the match was just the prefix, we matched */ if (!*match) return MATCHED_RECURSIVELY; if (matchlen <= namelen && !ps_strncmp(item, match, name, matchlen)) { if (matchlen == namelen) return MATCHED_EXACTLY; if (match[matchlen-1] == '/' || name[matchlen] == '/') return MATCHED_RECURSIVELY; } if (item->nowildcard_len < item->len && !git_fnmatch(item, match, name, item->nowildcard_len - prefix)) return MATCHED_FNMATCH; return 0; }
/* :(icase)-aware string compare */ static int basecmp(const struct pathspec_item *item, const char *base, const char *match, int len) { if (item->magic & PATHSPEC_ICASE) { int ret, n = len > item->prefix ? item->prefix : len; ret = strncmp(base, match, n); if (ret) return ret; base += n; match += n; len -= n; } return ps_strncmp(item, base, match, len); }
static int match_entry(const struct pathspec_item *item, const struct name_entry *entry, int pathlen, const char *match, int matchlen, enum interesting *never_interesting) { int m = -1; /* signals that we haven't called strncmp() */ if (item->magic & PATHSPEC_ICASE) /* * "Never interesting" trick requires exact * matching. We could do something clever with inexact * matching, but it's trickier (and not to forget that * strcasecmp is locale-dependent, at least in * glibc). Just disable it for now. It can't be worse * than the wildcard's codepath of '[Tt][Hi][Is][Ss]' * pattern. */ *never_interesting = entry_not_interesting; else if (*never_interesting != entry_not_interesting) { /* * We have not seen any match that sorts later * than the current path. */ /* * Does match sort strictly earlier than path * with their common parts? */ m = strncmp(match, entry->path, (matchlen < pathlen) ? matchlen : pathlen); if (m < 0) return 0; /* * If we come here even once, that means there is at * least one pathspec that would sort equal to or * later than the path we are currently looking at. * In other words, if we have never reached this point * after iterating all pathspecs, it means all * pathspecs are either outside of base, or inside the * base but sorts strictly earlier than the current * one. In either case, they will never match the * subsequent entries. In such a case, we initialized * the variable to -1 and that is what will be * returned, allowing the caller to terminate early. */ *never_interesting = entry_not_interesting; } if (pathlen > matchlen) return 0; if (matchlen > pathlen) { if (match[pathlen] != '/') return 0; if (!S_ISDIR(entry->mode) && !S_ISGITLINK(entry->mode)) return 0; } if (m == -1) /* * we cheated and did not do strncmp(), so we do * that here. */ m = ps_strncmp(item, match, entry->path, pathlen); /* * If common part matched earlier then it is a hit, * because we rejected the case where path is not a * leading directory and is shorter than match. */ if (!m) /* * match_entry does not check if the prefix part is * matched case-sensitively. If the entry is a * directory and part of prefix, it'll be rematched * eventually by basecmp with special treatment for * the prefix. */ return 1; return 0; }