this also avoids calling fnmatch() if the non-wildcard prefix is longer than basename Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> --- dir.c | 41 +++++++++++++++++++++++++++-------------- dir.h | 2 +- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/dir.c b/dir.c index 8535cf2..50d744f 100644 --- a/dir.c +++ b/dir.c @@ -295,9 +295,11 @@ int match_pathspec_depth(const struct pathspec *ps, return retval; } +const char *wildcards = "*?[{\\"; + static int no_wildcard(const char *string) { - return string[strcspn(string, "*?[{\\")] == '\0'; + return string[strcspn(string, wildcards)] == '\0'; } void add_exclude(const char *string, const char *base, @@ -336,8 +338,7 @@ void add_exclude(const char *string, const char *base, x->flags = flags; if (!strchr(string, '/')) x->flags |= EXC_FLAG_NODIR; - if (no_wildcard(string)) - x->flags |= EXC_FLAG_NOWILDCARD; + x->nowildcardlen = strcspn(string, wildcards); if (*string == '*' && no_wildcard(string+1)) x->flags |= EXC_FLAG_ENDSWITH; ALLOC_GROW(which->excludes, which->nr + 1, which->alloc); @@ -513,8 +514,9 @@ int excluded_from_list(const char *pathname, for (i = el->nr - 1; 0 <= i; i--) { struct exclude *x = el->excludes[i]; - const char *exclude = x->pattern; + const char *name, *exclude = x->pattern; int to_exclude = x->to_exclude; + int namelen, prefix = x->nowildcardlen; if (x->flags & EXC_FLAG_MUSTBEDIR) { if (*dtype == DT_UNKNOWN) @@ -525,7 +527,7 @@ int excluded_from_list(const char *pathname, if (x->flags & EXC_FLAG_NODIR) { /* match basename */ - if (x->flags & EXC_FLAG_NOWILDCARD) { + if (prefix == x->patternlen) { if (!strcmp_icase(exclude, basename)) return to_exclude; } else if (x->flags & EXC_FLAG_ENDSWITH) { @@ -539,26 +541,37 @@ int excluded_from_list(const char *pathname, continue; } - /* match with FNM_PATHNAME: * exclude has base (baselen long) implicitly in front of it. */ - if (*exclude == '/') + if (*exclude == '/') { exclude++; + prefix--; + } if (pathlen < x->baselen || (x->baselen && pathname[x->baselen-1] != '/') || strncmp_icase(pathname, x->base, x->baselen)) continue; - if (x->flags & EXC_FLAG_NOWILDCARD) { - if (!strcmp_icase(exclude, pathname + x->baselen)) - return to_exclude; - } else { - if (fnmatch_icase(exclude, pathname+x->baselen, - FNM_PATHNAME) == 0) - return to_exclude; + namelen = x->baselen ? pathlen - x->baselen : pathlen; + name = pathname + pathlen - namelen; + + /* if the non-wildcard part is longer than the + remaining pathname, surely it cannot match */ + if (prefix > namelen) + continue; + + if (prefix) { + if (strncmp_icase(exclude, name, prefix)) + continue; + exclude += prefix; + name += prefix; + namelen -= prefix; } + + if (!namelen || !fnmatch_icase(exclude, name, FNM_PATHNAME)) + return to_exclude; } return -1; /* undecided */ } diff --git a/dir.h b/dir.h index 58b6fc7..39fc145 100644 --- a/dir.h +++ b/dir.h @@ -7,7 +7,6 @@ struct dir_entry { }; #define EXC_FLAG_NODIR 1 -#define EXC_FLAG_NOWILDCARD 2 #define EXC_FLAG_ENDSWITH 4 #define EXC_FLAG_MUSTBEDIR 8 @@ -17,6 +16,7 @@ struct exclude_list { struct exclude { const char *pattern; int patternlen; + int nowildcardlen; const char *base; int baselen; int to_exclude; -- 1.7.10.2.549.g9354186 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html