From 0b6e56dfe6c7f75c2a02cc0cf8731c9e62b6d3d1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 28 Mar 2013 17:47:28 -0400 Subject: dir.c::match_basename(): pay attention to the length of string parameters The function takes two counted strings ( and ) as parameters, together with prefix (the length of the prefix in pattern that is to be matched literally without globbing against the basename) and EXC_* flags that tells it how to match the pattern against the basename. However, it did not pay attention to the length of these counted strings. Update them to do the following: * When the entire pattern is to be matched literally, the pattern matches the basename only when the lengths of them are the same, and they match up to that length. * When the pattern is "*" followed by a string to be matched literally, make sure that the basenamelen is equal or longer than the "literal" part of the pattern, and the tail of the basename string matches that literal part. * Otherwise, use the new fnmatch_icase_mem helper to make sure we only lookmake sure we use only look at the counted part of the strings. Because these counted strings are full strings most of the time, we check for termination to avoid unnecessary allocation. Signed-off-by: Junio C Hamano Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- dir.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) (limited to 'dir.c') diff --git a/dir.c b/dir.c index 5a83aa7897..0db79f9cf6 100644 --- a/dir.c +++ b/dir.c @@ -34,6 +34,33 @@ int fnmatch_icase(const char *pattern, const char *string, int flags) return fnmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0)); } +static int fnmatch_icase_mem(const char *pattern, int patternlen, + const char *string, int stringlen, + int flags) +{ + int match_status; + struct strbuf pat_buf = STRBUF_INIT; + struct strbuf str_buf = STRBUF_INIT; + const char *use_pat = pattern; + const char *use_str = string; + + if (pattern[patternlen]) { + strbuf_add(&pat_buf, pattern, patternlen); + use_pat = pat_buf.buf; + } + if (string[stringlen]) { + strbuf_add(&str_buf, string, stringlen); + use_str = str_buf.buf; + } + + match_status = fnmatch_icase(use_pat, use_str, flags); + + strbuf_release(&pat_buf); + strbuf_release(&str_buf); + + return match_status; +} + static size_t common_prefix_len(const char **pathspec) { const char *n, *first; @@ -537,15 +564,20 @@ int match_basename(const char *basename, int basenamelen, int flags) { if (prefix == patternlen) { - if (!strcmp_icase(pattern, basename)) + if (patternlen == basenamelen && + !strncmp_icase(pattern, basename, basenamelen)) return 1; } else if (flags & EXC_FLAG_ENDSWITH) { + /* "*literal" matching against "fooliteral" */ if (patternlen - 1 <= basenamelen && - !strcmp_icase(pattern + 1, - basename + basenamelen - patternlen + 1)) + !strncmp_icase(pattern + 1, + basename + basenamelen - (patternlen - 1), + patternlen - 1)) return 1; } else { - if (fnmatch_icase(pattern, basename, 0) == 0) + if (fnmatch_icase_mem(pattern, patternlen, + basename, basenamelen, + 0) == 0) return 1; } return 0; -- cgit v1.2.3 From 982ac87316a1cf5126888157bdcbfa32268ebe47 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 28 Mar 2013 17:47:47 -0400 Subject: dir.c::match_pathname(): adjust patternlen when shifting pattern If we receive a pattern that starts with "/", we shift it forward to avoid looking at the "/" part. Since the prefix and patternlen parameters are counts of what is in the pattern, we must decrement them as we increment the pointer. We remembered to handle prefix, but not patternlen. This didn't cause any bugs, though, because the patternlen parameter is not actually used. Since it will be used in future patches, let's correct this oversight. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- dir.c | 1 + 1 file changed, 1 insertion(+) (limited to 'dir.c') diff --git a/dir.c b/dir.c index 0db79f9cf6..1757a3f4e0 100644 --- a/dir.c +++ b/dir.c @@ -597,6 +597,7 @@ int match_pathname(const char *pathname, int pathlen, */ if (*pattern == '/') { pattern++; + patternlen--; prefix--; } -- cgit v1.2.3 From ab3aebc15c13d083013591777688cecbcb703fb2 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 28 Mar 2013 17:48:21 -0400 Subject: dir.c::match_pathname(): pay attention to the length of string parameters This function takes two counted strings: a pair and a pair. But we end up feeding the result to fnmatch, which expects NUL-terminated strings. We can fix this by calling the fnmatch_icase_mem function, which handles re-allocating into a NUL-terminated string if necessary. While we're at it, we can avoid even calling fnmatch in some cases. In addition to patternlen, we get "prefix", the size of the pattern that contains no wildcard characters. We do a straight match of the prefix part first, and then use fnmatch to cover the rest. But if there are no wildcards in the pattern at all, we do not even need to call fnmatch; we would simply be comparing two empty strings. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- dir.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'dir.c') diff --git a/dir.c b/dir.c index 1757a3f4e0..8fea94e185 100644 --- a/dir.c +++ b/dir.c @@ -624,11 +624,22 @@ int match_pathname(const char *pathname, int pathlen, if (strncmp_icase(pattern, name, prefix)) return 0; pattern += prefix; + patternlen -= prefix; name += prefix; namelen -= prefix; + + /* + * If the whole pattern did not have a wildcard, + * then our prefix match is all we need; we + * do not need to call fnmatch at all. + */ + if (!patternlen && !namelen) + return 1; } - return fnmatch_icase(pattern, name, FNM_PATHNAME) == 0; + return fnmatch_icase_mem(pattern, patternlen, + name, namelen, + FNM_PATHNAME) == 0; } /* Scan the list and let the last match determine the fate. -- cgit v1.2.3