By default wildmatch(,, 0) is equivalent with fnmatch(,, FNM_PATHNAME).

This patch makes wildmatch behave more like fnmatch: FNM_PATHNAME
behavior is always applied when FNM_PATHNAME is passed to
wildmatch. Without FNM_PATHNAME, wildmatch accepts '/' in '?' and '[]'
and treats '*' like '**' in the original version.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclo...@gmail.com>
---
 The choice of name "pathspec" is not good. I couldn't think of
 anything appropriate and just did not care enough at this point.

 dir.c                |  2 +-
 t/t3070-wildmatch.sh | 27 +++++++++++++++++++++++++++
 test-wildmatch.c     |  4 +++-
 wildmatch.c          | 12 ++++++++----
 4 files changed, 39 insertions(+), 6 deletions(-)

diff --git a/dir.c b/dir.c
index cb7328b..7bbd6f8 100644
--- a/dir.c
+++ b/dir.c
@@ -595,7 +595,7 @@ int match_pathname(const char *pathname, int pathlen,
        }
 
        return wildmatch(pattern, name,
-                        ignore_case ? FNM_CASEFOLD : 0) == 0;
+                        FNM_PATHNAME | (ignore_case ? FNM_CASEFOLD : 0)) == 0;
 }
 
 /* Scan the list and let the last match determine the fate.
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index 3155eab..ca4ac46 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -29,6 +29,18 @@ match() {
     fi
 }
 
+pathspec() {
+    if [ $1 = 1 ]; then
+       test_expect_success "pathspec:    match '$2' '$3'" "
+           test-wildmatch pathspec '$2' '$3'
+       "
+    else
+       test_expect_success "pathspec: no match '$2' '$3'" "
+           ! test-wildmatch pathspec '$2' '$3'
+       "
+    fi
+}
+
 # Basic wildmat features
 match 1 1 foo foo
 match 0 0 foo bar
@@ -192,4 +204,19 @@ match 0 0 
'XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' 'XXX/*/
 match 1 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt' '**/*a*b*g*n*t'
 match 0 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz' '**/*a*b*g*n*t'
 
+pathspec 1 foo foo
+pathspec 0 foo fo
+pathspec 1 foo/bar foo/bar
+pathspec 1 foo/bar 'foo/*'
+pathspec 1 foo/bba/arr 'foo/*'
+pathspec 1 foo/bba/arr 'foo/**'
+pathspec 1 foo/bba/arr 'foo*'
+pathspec 1 foo/bba/arr 'foo**'
+pathspec 1 foo/bba/arr 'foo/*arr'
+pathspec 1 foo/bba/arr 'foo/**arr'
+pathspec 0 foo/bba/arr 'foo/*z'
+pathspec 0 foo/bba/arr 'foo/**z'
+pathspec 1 foo/bar 'foo?bar'
+pathspec 1 foo/bar 'foo[/]bar'
+
 test_done
diff --git a/test-wildmatch.c b/test-wildmatch.c
index e384c8e..7fefa4f 100644
--- a/test-wildmatch.c
+++ b/test-wildmatch.c
@@ -12,9 +12,11 @@ int main(int argc, char **argv)
                        argv[i] += 3;
        }
        if (!strcmp(argv[1], "wildmatch"))
+               return !!wildmatch(argv[3], argv[2], FNM_PATHNAME);
+       else if (!strcmp(argv[1], "pathspec"))
                return !!wildmatch(argv[3], argv[2], 0);
        else if (!strcmp(argv[1], "iwildmatch"))
-               return !!wildmatch(argv[3], argv[2], FNM_CASEFOLD);
+               return !!wildmatch(argv[3], argv[2], FNM_PATHNAME | 
FNM_CASEFOLD);
        else if (!strcmp(argv[1], "fnmatch"))
                return !!fnmatch(argv[3], argv[2], FNM_PATHNAME);
        else
diff --git a/wildmatch.c b/wildmatch.c
index 9586ed9..6aa034f 100644
--- a/wildmatch.c
+++ b/wildmatch.c
@@ -80,14 +80,17 @@ static int dowild(const uchar *p, const uchar *text, int 
flags)
                        continue;
                case '?':
                        /* Match anything but '/'. */
-                       if (t_ch == '/')
+                       if (flags & FNM_PATHNAME && t_ch == '/')
                                return NOMATCH;
                        continue;
                case '*':
                        if (*++p == '*') {
                                const uchar *prev_p = p - 2;
                                while (*++p == '*') {}
-                               if ((prev_p == text || *prev_p == '/') ||
+                               if (!(flags & FNM_PATHNAME))
+                                       /* without FNM_PATHNAME, '*' == '**' */
+                                       special = TRUE;
+                               else if ((prev_p == text || *prev_p == '/') ||
                                    (*p == '\0' || *p == '/' ||
                                     (p[0] == '\\' && p[1] == '/'))) {
                                        /*
@@ -106,7 +109,7 @@ static int dowild(const uchar *p, const uchar *text, int 
flags)
                                } else
                                        return ABORT_MALFORMED;
                        } else
-                               special = FALSE;
+                               special = flags & FNM_PATHNAME ? FALSE: TRUE;
                        if (*p == '\0') {
                                /* Trailing "**" matches everything.  Trailing 
"*" matches
                                 * only if there are no more slash characters. 
*/
@@ -217,7 +220,8 @@ static int dowild(const uchar *p, const uchar *text, int 
flags)
                                } else if (t_ch == p_ch)
                                        matched = TRUE;
                        } while (prev_ch = p_ch, (p_ch = *++p) != ']');
-                       if (matched == special || t_ch == '/')
+                       if (matched == special ||
+                           (flags & FNM_PATHNAME && t_ch == '/'))
                                return NOMATCH;
                        continue;
                }
-- 
1.8.0.rc2.23.g1fb49df

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to