dir.c::match_basename(): pay attention to the length of string parameters
authorJunio C Hamano <gitster@pobox.com>
Thu, 28 Mar 2013 21:47:28 +0000 (17:47 -0400)
committerJunio C Hamano <gitster@pobox.com>
Fri, 29 Mar 2013 04:48:12 +0000 (21:48 -0700)
The function takes two counted strings (<basename, basenamelen> and
<pattern, patternlen>) 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 <gitster@pobox.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
dir.c
diff --git a/dir.c b/dir.c
index 5a83aa7897f270279c403778f43aea6db1efc5af..0db79f9cf6a6b3c1f21b4e0b286bcd9f0ca7df0d 100644 (file)
--- 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;