From ca5ab7d1e813435bbf4ba0615a2adf0cdc35960e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 15 Oct 2012 13:25:50 +0700 Subject: [PATCH 01/18] ctype: make sane_ctype[] const array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- ctype.c | 2 +- git-compat-util.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ctype.c b/ctype.c index 935327164b..faeaf34e67 100644 --- a/ctype.c +++ b/ctype.c @@ -14,7 +14,7 @@ enum { P = GIT_PATHSPEC_MAGIC /* other non-alnum, except for ] and } */ }; -unsigned char sane_ctype[256] = { +const unsigned char sane_ctype[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */ S, P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */ diff --git a/git-compat-util.h b/git-compat-util.h index 5bd9ad7d2a..80767ff3a1 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -470,7 +470,7 @@ extern const char tolower_trans_tbl[256]; #undef isupper #undef tolower #undef toupper -extern unsigned char sane_ctype[256]; +extern const unsigned char sane_ctype[256]; #define GIT_SPACE 0x01 #define GIT_DIGIT 0x02 #define GIT_ALPHA 0x04 From 1c149ab2ddc4a6d253e76d90067ea0fff2880ded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 15 Oct 2012 13:25:51 +0700 Subject: [PATCH 02/18] ctype: support iscntrl, ispunct, isxdigit and isprint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- ctype.c | 13 ++++++++----- git-compat-util.h | 13 +++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/ctype.c b/ctype.c index faeaf34e67..0bfebb4e75 100644 --- a/ctype.c +++ b/ctype.c @@ -11,18 +11,21 @@ enum { D = GIT_DIGIT, G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */ R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | */ - P = GIT_PATHSPEC_MAGIC /* other non-alnum, except for ] and } */ + P = GIT_PATHSPEC_MAGIC, /* other non-alnum, except for ] and } */ + X = GIT_CNTRL, + U = GIT_PUNCT, + Z = GIT_CNTRL | GIT_SPACE }; const unsigned char sane_ctype[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */ + X, X, X, X, X, X, X, X, X, Z, Z, X, X, Z, X, X, /* 0.. 15 */ + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 16.. 31 */ S, P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */ D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G, /* 48.. 63 */ P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */ - A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, P, /* 80.. 95 */ + A, A, A, A, A, A, A, A, A, A, A, G, G, U, R, P, /* 80.. 95 */ P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */ - A, A, A, A, A, A, A, A, A, A, A, R, R, 0, P, 0, /* 112..127 */ + A, A, A, A, A, A, A, A, A, A, A, R, R, U, P, X, /* 112..127 */ /* Nothing in the 128.. range */ }; diff --git a/git-compat-util.h b/git-compat-util.h index 80767ff3a1..02f48f653b 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -470,6 +470,10 @@ extern const char tolower_trans_tbl[256]; #undef isupper #undef tolower #undef toupper +#undef iscntrl +#undef ispunct +#undef isxdigit +#undef isprint extern const unsigned char sane_ctype[256]; #define GIT_SPACE 0x01 #define GIT_DIGIT 0x02 @@ -477,6 +481,8 @@ extern const unsigned char sane_ctype[256]; #define GIT_GLOB_SPECIAL 0x08 #define GIT_REGEX_SPECIAL 0x10 #define GIT_PATHSPEC_MAGIC 0x20 +#define GIT_CNTRL 0x40 +#define GIT_PUNCT 0x80 #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0) #define isascii(x) (((x) & ~0x7f) == 0) #define isspace(x) sane_istest(x,GIT_SPACE) @@ -487,6 +493,13 @@ extern const unsigned char sane_ctype[256]; #define isupper(x) sane_iscase(x, 0) #define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL) #define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL) +#define iscntrl(x) (sane_istest(x,GIT_CNTRL)) +#define ispunct(x) sane_istest(x, GIT_PUNCT | GIT_REGEX_SPECIAL | \ + GIT_GLOB_SPECIAL | GIT_PATHSPEC_MAGIC) +#define isxdigit(x) (hexval_table[x] != -1) +#define isprint(x) (sane_istest(x, GIT_ALPHA | GIT_DIGIT | GIT_SPACE | \ + GIT_PUNCT | GIT_REGEX_SPECIAL | GIT_GLOB_SPECIAL | \ + GIT_PATHSPEC_MAGIC)) #define tolower(x) sane_case((unsigned char)(x), 0x20) #define toupper(x) sane_case((unsigned char)(x), 0) #define is_pathspec_magic(x) sane_istest(x,GIT_PATHSPEC_MAGIC) From 5230f605e1afd4e3824aae069a5f1bead6f1647f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 15 Oct 2012 13:25:52 +0700 Subject: [PATCH 03/18] Import wildmatch from rsync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These files are from rsync.git commit f92f5b166e3019db42bc7fe1aa2f1a9178cd215d, which was the last commit before rsync turned GPL-3. All files are imported as-is and no-op. Adaptation is done in a separate patch. rsync.git -> git.git lib/wildmatch.[ch] wildmatch.[ch] wildtest.txt t/t3070/wildtest.txt Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- t/t3070/wildtest.txt | 165 +++++++++++++++++++ wildmatch.c | 368 +++++++++++++++++++++++++++++++++++++++++++ wildmatch.h | 6 + 3 files changed, 539 insertions(+) create mode 100644 t/t3070/wildtest.txt create mode 100644 wildmatch.c create mode 100644 wildmatch.h diff --git a/t/t3070/wildtest.txt b/t/t3070/wildtest.txt new file mode 100644 index 0000000000..42c1678996 --- /dev/null +++ b/t/t3070/wildtest.txt @@ -0,0 +1,165 @@ +# Input is in the following format (all items white-space separated): +# +# The first two items are 1 or 0 indicating if the wildmat call is expected to +# succeed and if fnmatch works the same way as wildmat, respectively. After +# that is a text string for the match, and a pattern string. Strings can be +# quoted (if desired) in either double or single quotes, as well as backticks. +# +# MATCH FNMATCH_SAME "text to match" 'pattern to use' + +# Basic wildmat features +1 1 foo foo +0 1 foo bar +1 1 '' "" +1 1 foo ??? +0 1 foo ?? +1 1 foo * +1 1 foo f* +0 1 foo *f +1 1 foo *foo* +1 1 foobar *ob*a*r* +1 1 aaaaaaabababab *ab +1 1 foo* foo\* +0 1 foobar foo\*bar +1 1 f\oo f\\oo +1 1 ball *[al]? +0 1 ten [ten] +1 1 ten **[!te] +0 1 ten **[!ten] +1 1 ten t[a-g]n +0 1 ten t[!a-g]n +1 1 ton t[!a-g]n +1 1 ton t[^a-g]n +1 1 a]b a[]]b +1 1 a-b a[]-]b +1 1 a]b a[]-]b +0 1 aab a[]-]b +1 1 aab a[]a-]b +1 1 ] ] + +# Extended slash-matching features +0 1 foo/baz/bar foo*bar +1 1 foo/baz/bar foo**bar +0 1 foo/bar foo?bar +0 1 foo/bar foo[/]bar +0 1 foo/bar f[^eiu][^eiu][^eiu][^eiu][^eiu]r +1 1 foo-bar f[^eiu][^eiu][^eiu][^eiu][^eiu]r +0 1 foo **/foo +1 1 /foo **/foo +1 1 bar/baz/foo **/foo +0 1 bar/baz/foo */foo +0 0 foo/bar/baz **/bar* +1 1 deep/foo/bar/baz **/bar/* +0 1 deep/foo/bar/baz/ **/bar/* +1 1 deep/foo/bar/baz/ **/bar/** +0 1 deep/foo/bar **/bar/* +1 1 deep/foo/bar/ **/bar/** +1 1 foo/bar/baz **/bar** +1 1 foo/bar/baz/x */bar/** +0 0 deep/foo/bar/baz/x */bar/** +1 1 deep/foo/bar/baz/x **/bar/*/* + +# Various additional tests +0 1 acrt a[c-c]st +1 1 acrt a[c-c]rt +0 1 ] [!]-] +1 1 a [!]-] +0 1 '' \ +0 1 \ \ +0 1 /\ */\ +1 1 /\ */\\ +1 1 foo foo +1 1 @foo @foo +0 1 foo @foo +1 1 [ab] \[ab] +1 1 [ab] [[]ab] +1 1 [ab] [[:]ab] +0 1 [ab] [[::]ab] +1 1 [ab] [[:digit]ab] +1 1 [ab] [\[:]ab] +1 1 ?a?b \??\?b +1 1 abc \a\b\c +0 1 foo '' +1 1 foo/bar/baz/to **/t[o] + +# Character class tests +1 1 a1B [[:alpha:]][[:digit:]][[:upper:]] +0 1 a [[:digit:][:upper:][:space:]] +1 1 A [[:digit:][:upper:][:space:]] +1 1 1 [[:digit:][:upper:][:space:]] +0 1 1 [[:digit:][:upper:][:spaci:]] +1 1 ' ' [[:digit:][:upper:][:space:]] +0 1 . [[:digit:][:upper:][:space:]] +1 1 . [[:digit:][:punct:][:space:]] +1 1 5 [[:xdigit:]] +1 1 f [[:xdigit:]] +1 1 D [[:xdigit:]] +1 1 _ [[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]] +#1 1 [^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]] +1 1  [^[:alnum:][:alpha:][:blank:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]] +1 1 . [^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]] +1 1 5 [a-c[:digit:]x-z] +1 1 b [a-c[:digit:]x-z] +1 1 y [a-c[:digit:]x-z] +0 1 q [a-c[:digit:]x-z] + +# Additional tests, including some malformed wildmats +1 1 ] [\\-^] +0 1 [ [\\-^] +1 1 - [\-_] +1 1 ] [\]] +0 1 \] [\]] +0 1 \ [\]] +0 1 ab a[]b +0 1 a[]b a[]b +0 1 ab[ ab[ +0 1 ab [! +0 1 ab [- +1 1 - [-] +0 1 - [a- +0 1 - [!a- +1 1 - [--A] +1 1 5 [--A] +1 1 ' ' '[ --]' +1 1 $ '[ --]' +1 1 - '[ --]' +0 1 0 '[ --]' +1 1 - [---] +1 1 - [------] +0 1 j [a-e-n] +1 1 - [a-e-n] +1 1 a [!------] +0 1 [ []-a] +1 1 ^ []-a] +0 1 ^ [!]-a] +1 1 [ [!]-a] +1 1 ^ [a^bc] +1 1 -b] [a-]b] +0 1 \ [\] +1 1 \ [\\] +0 1 \ [!\\] +1 1 G [A-\\] +0 1 aaabbb b*a +0 1 aabcaa *ba* +1 1 , [,] +1 1 , [\\,] +1 1 \ [\\,] +1 1 - [,-.] +0 1 + [,-.] +0 1 -.] [,-.] +1 1 2 [\1-\3] +1 1 3 [\1-\3] +0 1 4 [\1-\3] +1 1 \ [[-\]] +1 1 [ [[-\]] +1 1 ] [[-\]] +0 1 - [[-\]] + +# Test recursion and the abort code (use "wildtest -i" to see iteration counts) +1 1 -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1 -*-*-*-*-*-*-12-*-*-*-m-*-*-* +0 1 -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1 -*-*-*-*-*-*-12-*-*-*-m-*-*-* +0 1 -adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1 -*-*-*-*-*-*-12-*-*-*-m-*-*-* +1 1 /adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1 /*/*/*/*/*/*/12/*/*/*/m/*/*/* +0 1 /adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1 /*/*/*/*/*/*/12/*/*/*/m/*/*/* +1 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt **/*a*b*g*n*t +0 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz **/*a*b*g*n*t diff --git a/wildmatch.c b/wildmatch.c new file mode 100644 index 0000000000..f3a1731eb0 --- /dev/null +++ b/wildmatch.c @@ -0,0 +1,368 @@ +/* +** Do shell-style pattern matching for ?, \, [], and * characters. +** It is 8bit clean. +** +** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. +** Rich $alz is now . +** +** Modified by Wayne Davison to special-case '/' matching, to make '**' +** work differently than '*', and to fix the character-class code. +*/ + +#include "rsync.h" + +/* What character marks an inverted character class? */ +#define NEGATE_CLASS '!' +#define NEGATE_CLASS2 '^' + +#define FALSE 0 +#define TRUE 1 +#define ABORT_ALL -1 +#define ABORT_TO_STARSTAR -2 + +#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \ + && *(class) == *(litmatch) \ + && strncmp((char*)class, litmatch, len) == 0) + +#if defined STDC_HEADERS || !defined isascii +# define ISASCII(c) 1 +#else +# define ISASCII(c) isascii(c) +#endif + +#ifdef isblank +# define ISBLANK(c) (ISASCII(c) && isblank(c)) +#else +# define ISBLANK(c) ((c) == ' ' || (c) == '\t') +#endif + +#ifdef isgraph +# define ISGRAPH(c) (ISASCII(c) && isgraph(c)) +#else +# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c)) +#endif + +#define ISPRINT(c) (ISASCII(c) && isprint(c)) +#define ISDIGIT(c) (ISASCII(c) && isdigit(c)) +#define ISALNUM(c) (ISASCII(c) && isalnum(c)) +#define ISALPHA(c) (ISASCII(c) && isalpha(c)) +#define ISCNTRL(c) (ISASCII(c) && iscntrl(c)) +#define ISLOWER(c) (ISASCII(c) && islower(c)) +#define ISPUNCT(c) (ISASCII(c) && ispunct(c)) +#define ISSPACE(c) (ISASCII(c) && isspace(c)) +#define ISUPPER(c) (ISASCII(c) && isupper(c)) +#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c)) + +#ifdef WILD_TEST_ITERATIONS +int wildmatch_iteration_count; +#endif + +static int force_lower_case = 0; + +/* Match pattern "p" against the a virtually-joined string consisting + * of "text" and any strings in array "a". */ +static int dowild(const uchar *p, const uchar *text, const uchar*const *a) +{ + uchar p_ch; + +#ifdef WILD_TEST_ITERATIONS + wildmatch_iteration_count++; +#endif + + for ( ; (p_ch = *p) != '\0'; text++, p++) { + int matched, special; + uchar t_ch, prev_ch; + while ((t_ch = *text) == '\0') { + if (*a == NULL) { + if (p_ch != '*') + return ABORT_ALL; + break; + } + text = *a++; + } + if (force_lower_case && ISUPPER(t_ch)) + t_ch = tolower(t_ch); + switch (p_ch) { + case '\\': + /* Literal match with following character. Note that the test + * in "default" handles the p[1] == '\0' failure case. */ + p_ch = *++p; + /* FALLTHROUGH */ + default: + if (t_ch != p_ch) + return FALSE; + continue; + case '?': + /* Match anything but '/'. */ + if (t_ch == '/') + return FALSE; + continue; + case '*': + if (*++p == '*') { + while (*++p == '*') {} + special = TRUE; + } else + special = FALSE; + if (*p == '\0') { + /* Trailing "**" matches everything. Trailing "*" matches + * only if there are no more slash characters. */ + if (!special) { + do { + if (strchr((char*)text, '/') != NULL) + return FALSE; + } while ((text = *a++) != NULL); + } + return TRUE; + } + while (1) { + if (t_ch == '\0') { + if ((text = *a++) == NULL) + break; + t_ch = *text; + continue; + } + if ((matched = dowild(p, text, a)) != FALSE) { + if (!special || matched != ABORT_TO_STARSTAR) + return matched; + } else if (!special && t_ch == '/') + return ABORT_TO_STARSTAR; + t_ch = *++text; + } + return ABORT_ALL; + case '[': + p_ch = *++p; +#ifdef NEGATE_CLASS2 + if (p_ch == NEGATE_CLASS2) + p_ch = NEGATE_CLASS; +#endif + /* Assign literal TRUE/FALSE because of "matched" comparison. */ + special = p_ch == NEGATE_CLASS? TRUE : FALSE; + if (special) { + /* Inverted character class. */ + p_ch = *++p; + } + prev_ch = 0; + matched = FALSE; + do { + if (!p_ch) + return ABORT_ALL; + if (p_ch == '\\') { + p_ch = *++p; + if (!p_ch) + return ABORT_ALL; + if (t_ch == p_ch) + matched = TRUE; + } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') { + p_ch = *++p; + if (p_ch == '\\') { + p_ch = *++p; + if (!p_ch) + return ABORT_ALL; + } + if (t_ch <= p_ch && t_ch >= prev_ch) + matched = TRUE; + p_ch = 0; /* This makes "prev_ch" get set to 0. */ + } else if (p_ch == '[' && p[1] == ':') { + const uchar *s; + int i; + for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/ + if (!p_ch) + return ABORT_ALL; + i = p - s - 1; + if (i < 0 || p[-1] != ':') { + /* Didn't find ":]", so treat like a normal set. */ + p = s - 2; + p_ch = '['; + if (t_ch == p_ch) + matched = TRUE; + continue; + } + if (CC_EQ(s,i, "alnum")) { + if (ISALNUM(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "alpha")) { + if (ISALPHA(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "blank")) { + if (ISBLANK(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "cntrl")) { + if (ISCNTRL(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "digit")) { + if (ISDIGIT(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "graph")) { + if (ISGRAPH(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "lower")) { + if (ISLOWER(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "print")) { + if (ISPRINT(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "punct")) { + if (ISPUNCT(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "space")) { + if (ISSPACE(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "upper")) { + if (ISUPPER(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "xdigit")) { + if (ISXDIGIT(t_ch)) + matched = TRUE; + } else /* malformed [:class:] string */ + return ABORT_ALL; + p_ch = 0; /* This makes "prev_ch" get set to 0. */ + } else if (t_ch == p_ch) + matched = TRUE; + } while (prev_ch = p_ch, (p_ch = *++p) != ']'); + if (matched == special || t_ch == '/') + return FALSE; + continue; + } + } + + do { + if (*text) + return FALSE; + } while ((text = *a++) != NULL); + + return TRUE; +} + +/* Match literal string "s" against the a virtually-joined string consisting + * of "text" and any strings in array "a". */ +static int doliteral(const uchar *s, const uchar *text, const uchar*const *a) +{ + for ( ; *s != '\0'; text++, s++) { + while (*text == '\0') { + if ((text = *a++) == NULL) + return FALSE; + } + if (*text != *s) + return FALSE; + } + + do { + if (*text) + return FALSE; + } while ((text = *a++) != NULL); + + return TRUE; +} + +/* Return the last "count" path elements from the concatenated string. + * We return a string pointer to the start of the string, and update the + * array pointer-pointer to point to any remaining string elements. */ +static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count) +{ + const uchar*const *a = *a_ptr; + const uchar*const *first_a = a; + + while (*a) + a++; + + while (a != first_a) { + const uchar *s = *--a; + s += strlen((char*)s); + while (--s >= *a) { + if (*s == '/' && !--count) { + *a_ptr = a+1; + return s+1; + } + } + } + + if (count == 1) { + *a_ptr = a+1; + return *a; + } + + return NULL; +} + +/* Match the "pattern" against the "text" string. */ +int wildmatch(const char *pattern, const char *text) +{ + static const uchar *nomore[1]; /* A NULL pointer. */ +#ifdef WILD_TEST_ITERATIONS + wildmatch_iteration_count = 0; +#endif + return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE; +} + +/* Match the "pattern" against the forced-to-lower-case "text" string. */ +int iwildmatch(const char *pattern, const char *text) +{ + static const uchar *nomore[1]; /* A NULL pointer. */ + int ret; +#ifdef WILD_TEST_ITERATIONS + wildmatch_iteration_count = 0; +#endif + force_lower_case = 1; + ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE; + force_lower_case = 0; + return ret; +} + +/* Match pattern "p" against the a virtually-joined string consisting + * of all the pointers in array "texts" (which has a NULL pointer at the + * end). The int "where" can be 0 (normal matching), > 0 (match only + * the trailing N slash-separated filename components of "texts"), or < 0 + * (match the "pattern" at the start or after any slash in "texts"). */ +int wildmatch_array(const char *pattern, const char*const *texts, int where) +{ + const uchar *p = (const uchar*)pattern; + const uchar*const *a = (const uchar*const*)texts; + const uchar *text; + int matched; + +#ifdef WILD_TEST_ITERATIONS + wildmatch_iteration_count = 0; +#endif + + if (where > 0) + text = trailing_N_elements(&a, where); + else + text = *a++; + if (!text) + return FALSE; + + if ((matched = dowild(p, text, a)) != TRUE && where < 0 + && matched != ABORT_ALL) { + while (1) { + if (*text == '\0') { + if ((text = (uchar*)*a++) == NULL) + return FALSE; + continue; + } + if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE + && matched != ABORT_TO_STARSTAR) + break; + } + } + return matched == TRUE; +} + +/* Match literal string "s" against the a virtually-joined string consisting + * of all the pointers in array "texts" (which has a NULL pointer at the + * end). The int "where" can be 0 (normal matching), or > 0 (match + * only the trailing N slash-separated filename components of "texts"). */ +int litmatch_array(const char *string, const char*const *texts, int where) +{ + const uchar *s = (const uchar*)string; + const uchar*const *a = (const uchar* const*)texts; + const uchar *text; + + if (where > 0) + text = trailing_N_elements(&a, where); + else + text = *a++; + if (!text) + return FALSE; + + return doliteral(s, text, a) == TRUE; +} diff --git a/wildmatch.h b/wildmatch.h new file mode 100644 index 0000000000..e7f1a35f2a --- /dev/null +++ b/wildmatch.h @@ -0,0 +1,6 @@ +/* wildmatch.h */ + +int wildmatch(const char *pattern, const char *text); +int iwildmatch(const char *pattern, const char *text); +int wildmatch_array(const char *pattern, const char*const *texts, int where); +int litmatch_array(const char *string, const char*const *texts, int where); From b0e0287626d6257dd45df5502a07efd15a7255bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 15 Oct 2012 13:25:53 +0700 Subject: [PATCH 04/18] wildmatch: remove unnecessary functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- wildmatch.c | 164 ++++------------------------------------------------ wildmatch.h | 2 - 2 files changed, 10 insertions(+), 156 deletions(-) diff --git a/wildmatch.c b/wildmatch.c index f3a1731eb0..fae73976eb 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -53,33 +53,18 @@ #define ISUPPER(c) (ISASCII(c) && isupper(c)) #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c)) -#ifdef WILD_TEST_ITERATIONS -int wildmatch_iteration_count; -#endif - static int force_lower_case = 0; -/* Match pattern "p" against the a virtually-joined string consisting - * of "text" and any strings in array "a". */ -static int dowild(const uchar *p, const uchar *text, const uchar*const *a) +/* Match pattern "p" against "text" */ +static int dowild(const uchar *p, const uchar *text) { uchar p_ch; -#ifdef WILD_TEST_ITERATIONS - wildmatch_iteration_count++; -#endif - for ( ; (p_ch = *p) != '\0'; text++, p++) { int matched, special; uchar t_ch, prev_ch; - while ((t_ch = *text) == '\0') { - if (*a == NULL) { - if (p_ch != '*') - return ABORT_ALL; - break; - } - text = *a++; - } + if ((t_ch = *text) == '\0' && p_ch != '*') + return ABORT_ALL; if (force_lower_case && ISUPPER(t_ch)) t_ch = tolower(t_ch); switch (p_ch) { @@ -107,21 +92,15 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a) /* Trailing "**" matches everything. Trailing "*" matches * only if there are no more slash characters. */ if (!special) { - do { if (strchr((char*)text, '/') != NULL) return FALSE; - } while ((text = *a++) != NULL); } return TRUE; } while (1) { - if (t_ch == '\0') { - if ((text = *a++) == NULL) - break; - t_ch = *text; - continue; - } - if ((matched = dowild(p, text, a)) != FALSE) { + if (t_ch == '\0') + break; + if ((matched = dowild(p, text)) != FALSE) { if (!special || matched != ABORT_TO_STARSTAR) return matched; } else if (!special && t_ch == '/') @@ -225,144 +204,21 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a) } } - do { - if (*text) - return FALSE; - } while ((text = *a++) != NULL); - - return TRUE; -} - -/* Match literal string "s" against the a virtually-joined string consisting - * of "text" and any strings in array "a". */ -static int doliteral(const uchar *s, const uchar *text, const uchar*const *a) -{ - for ( ; *s != '\0'; text++, s++) { - while (*text == '\0') { - if ((text = *a++) == NULL) - return FALSE; - } - if (*text != *s) - return FALSE; - } - - do { - if (*text) - return FALSE; - } while ((text = *a++) != NULL); - - return TRUE; -} - -/* Return the last "count" path elements from the concatenated string. - * We return a string pointer to the start of the string, and update the - * array pointer-pointer to point to any remaining string elements. */ -static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count) -{ - const uchar*const *a = *a_ptr; - const uchar*const *first_a = a; - - while (*a) - a++; - - while (a != first_a) { - const uchar *s = *--a; - s += strlen((char*)s); - while (--s >= *a) { - if (*s == '/' && !--count) { - *a_ptr = a+1; - return s+1; - } - } - } - - if (count == 1) { - *a_ptr = a+1; - return *a; - } - - return NULL; + return *text ? FALSE : TRUE; } /* Match the "pattern" against the "text" string. */ int wildmatch(const char *pattern, const char *text) { - static const uchar *nomore[1]; /* A NULL pointer. */ -#ifdef WILD_TEST_ITERATIONS - wildmatch_iteration_count = 0; -#endif - return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE; + return dowild((const uchar*)pattern, (const uchar*)text) == TRUE; } /* Match the "pattern" against the forced-to-lower-case "text" string. */ int iwildmatch(const char *pattern, const char *text) { - static const uchar *nomore[1]; /* A NULL pointer. */ int ret; -#ifdef WILD_TEST_ITERATIONS - wildmatch_iteration_count = 0; -#endif force_lower_case = 1; - ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE; + ret = dowild((const uchar*)pattern, (const uchar*)text) == TRUE; force_lower_case = 0; return ret; } - -/* Match pattern "p" against the a virtually-joined string consisting - * of all the pointers in array "texts" (which has a NULL pointer at the - * end). The int "where" can be 0 (normal matching), > 0 (match only - * the trailing N slash-separated filename components of "texts"), or < 0 - * (match the "pattern" at the start or after any slash in "texts"). */ -int wildmatch_array(const char *pattern, const char*const *texts, int where) -{ - const uchar *p = (const uchar*)pattern; - const uchar*const *a = (const uchar*const*)texts; - const uchar *text; - int matched; - -#ifdef WILD_TEST_ITERATIONS - wildmatch_iteration_count = 0; -#endif - - if (where > 0) - text = trailing_N_elements(&a, where); - else - text = *a++; - if (!text) - return FALSE; - - if ((matched = dowild(p, text, a)) != TRUE && where < 0 - && matched != ABORT_ALL) { - while (1) { - if (*text == '\0') { - if ((text = (uchar*)*a++) == NULL) - return FALSE; - continue; - } - if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE - && matched != ABORT_TO_STARSTAR) - break; - } - } - return matched == TRUE; -} - -/* Match literal string "s" against the a virtually-joined string consisting - * of all the pointers in array "texts" (which has a NULL pointer at the - * end). The int "where" can be 0 (normal matching), or > 0 (match - * only the trailing N slash-separated filename components of "texts"). */ -int litmatch_array(const char *string, const char*const *texts, int where) -{ - const uchar *s = (const uchar*)string; - const uchar*const *a = (const uchar* const*)texts; - const uchar *text; - - if (where > 0) - text = trailing_N_elements(&a, where); - else - text = *a++; - if (!text) - return FALSE; - - return doliteral(s, text, a) == TRUE; -} diff --git a/wildmatch.h b/wildmatch.h index e7f1a35f2a..562faa35a5 100644 --- a/wildmatch.h +++ b/wildmatch.h @@ -2,5 +2,3 @@ int wildmatch(const char *pattern, const char *text); int iwildmatch(const char *pattern, const char *text); -int wildmatch_array(const char *pattern, const char*const *texts, int where); -int litmatch_array(const char *string, const char*const *texts, int where); From 327f2f3ebb2310653e78244293f6f34b28571142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 15 Oct 2012 13:25:54 +0700 Subject: [PATCH 05/18] wildmatch: follow Git's coding convention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wildmatch's coding style is pretty close to Git's except the use of 4 space indentation instead of 8. This patch should produce empty diff with "git diff -b" Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- wildmatch.c | 292 ++++++++++++++++++++++++++-------------------------- 1 file changed, 146 insertions(+), 146 deletions(-) diff --git a/wildmatch.c b/wildmatch.c index fae73976eb..4653dd6eb8 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -58,167 +58,167 @@ static int force_lower_case = 0; /* Match pattern "p" against "text" */ static int dowild(const uchar *p, const uchar *text) { - uchar p_ch; + uchar p_ch; - for ( ; (p_ch = *p) != '\0'; text++, p++) { - int matched, special; - uchar t_ch, prev_ch; - if ((t_ch = *text) == '\0' && p_ch != '*') - return ABORT_ALL; - if (force_lower_case && ISUPPER(t_ch)) - t_ch = tolower(t_ch); - switch (p_ch) { - case '\\': - /* Literal match with following character. Note that the test - * in "default" handles the p[1] == '\0' failure case. */ - p_ch = *++p; - /* FALLTHROUGH */ - default: - if (t_ch != p_ch) - return FALSE; - continue; - case '?': - /* Match anything but '/'. */ - if (t_ch == '/') - return FALSE; - continue; - case '*': - if (*++p == '*') { - while (*++p == '*') {} - special = TRUE; - } else - special = FALSE; - if (*p == '\0') { - /* Trailing "**" matches everything. Trailing "*" matches - * only if there are no more slash characters. */ - if (!special) { - if (strchr((char*)text, '/') != NULL) - return FALSE; - } - return TRUE; - } - while (1) { - if (t_ch == '\0') - break; - if ((matched = dowild(p, text)) != FALSE) { - if (!special || matched != ABORT_TO_STARSTAR) - return matched; - } else if (!special && t_ch == '/') - return ABORT_TO_STARSTAR; - t_ch = *++text; - } - return ABORT_ALL; - case '[': - p_ch = *++p; -#ifdef NEGATE_CLASS2 - if (p_ch == NEGATE_CLASS2) - p_ch = NEGATE_CLASS; -#endif - /* Assign literal TRUE/FALSE because of "matched" comparison. */ - special = p_ch == NEGATE_CLASS? TRUE : FALSE; - if (special) { - /* Inverted character class. */ - p_ch = *++p; - } - prev_ch = 0; - matched = FALSE; - do { - if (!p_ch) - return ABORT_ALL; - if (p_ch == '\\') { - p_ch = *++p; - if (!p_ch) + for ( ; (p_ch = *p) != '\0'; text++, p++) { + int matched, special; + uchar t_ch, prev_ch; + if ((t_ch = *text) == '\0' && p_ch != '*') return ABORT_ALL; - if (t_ch == p_ch) - matched = TRUE; - } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') { - p_ch = *++p; - if (p_ch == '\\') { + if (force_lower_case && ISUPPER(t_ch)) + t_ch = tolower(t_ch); + switch (p_ch) { + case '\\': + /* Literal match with following character. Note that the test + * in "default" handles the p[1] == '\0' failure case. */ p_ch = *++p; - if (!p_ch) - return ABORT_ALL; - } - if (t_ch <= p_ch && t_ch >= prev_ch) - matched = TRUE; - p_ch = 0; /* This makes "prev_ch" get set to 0. */ - } else if (p_ch == '[' && p[1] == ':') { - const uchar *s; - int i; - for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/ - if (!p_ch) - return ABORT_ALL; - i = p - s - 1; - if (i < 0 || p[-1] != ':') { - /* Didn't find ":]", so treat like a normal set. */ - p = s - 2; - p_ch = '['; - if (t_ch == p_ch) - matched = TRUE; + /* FALLTHROUGH */ + default: + if (t_ch != p_ch) + return FALSE; continue; - } - if (CC_EQ(s,i, "alnum")) { - if (ISALNUM(t_ch)) - matched = TRUE; - } else if (CC_EQ(s,i, "alpha")) { - if (ISALPHA(t_ch)) - matched = TRUE; - } else if (CC_EQ(s,i, "blank")) { - if (ISBLANK(t_ch)) - matched = TRUE; - } else if (CC_EQ(s,i, "cntrl")) { - if (ISCNTRL(t_ch)) - matched = TRUE; - } else if (CC_EQ(s,i, "digit")) { - if (ISDIGIT(t_ch)) - matched = TRUE; - } else if (CC_EQ(s,i, "graph")) { - if (ISGRAPH(t_ch)) - matched = TRUE; - } else if (CC_EQ(s,i, "lower")) { - if (ISLOWER(t_ch)) - matched = TRUE; - } else if (CC_EQ(s,i, "print")) { - if (ISPRINT(t_ch)) - matched = TRUE; - } else if (CC_EQ(s,i, "punct")) { - if (ISPUNCT(t_ch)) - matched = TRUE; - } else if (CC_EQ(s,i, "space")) { - if (ISSPACE(t_ch)) - matched = TRUE; - } else if (CC_EQ(s,i, "upper")) { - if (ISUPPER(t_ch)) - matched = TRUE; - } else if (CC_EQ(s,i, "xdigit")) { - if (ISXDIGIT(t_ch)) - matched = TRUE; - } else /* malformed [:class:] string */ + case '?': + /* Match anything but '/'. */ + if (t_ch == '/') + return FALSE; + continue; + case '*': + if (*++p == '*') { + while (*++p == '*') {} + special = TRUE; + } else + special = FALSE; + if (*p == '\0') { + /* Trailing "**" matches everything. Trailing "*" matches + * only if there are no more slash characters. */ + if (!special) { + if (strchr((char*)text, '/') != NULL) + return FALSE; + } + return TRUE; + } + while (1) { + if (t_ch == '\0') + break; + if ((matched = dowild(p, text)) != FALSE) { + if (!special || matched != ABORT_TO_STARSTAR) + return matched; + } else if (!special && t_ch == '/') + return ABORT_TO_STARSTAR; + t_ch = *++text; + } return ABORT_ALL; - p_ch = 0; /* This makes "prev_ch" get set to 0. */ - } else if (t_ch == p_ch) - matched = TRUE; - } while (prev_ch = p_ch, (p_ch = *++p) != ']'); - if (matched == special || t_ch == '/') - return FALSE; - continue; + case '[': + p_ch = *++p; +#ifdef NEGATE_CLASS2 + if (p_ch == NEGATE_CLASS2) + p_ch = NEGATE_CLASS; +#endif + /* Assign literal TRUE/FALSE because of "matched" comparison. */ + special = p_ch == NEGATE_CLASS? TRUE : FALSE; + if (special) { + /* Inverted character class. */ + p_ch = *++p; + } + prev_ch = 0; + matched = FALSE; + do { + if (!p_ch) + return ABORT_ALL; + if (p_ch == '\\') { + p_ch = *++p; + if (!p_ch) + return ABORT_ALL; + if (t_ch == p_ch) + matched = TRUE; + } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') { + p_ch = *++p; + if (p_ch == '\\') { + p_ch = *++p; + if (!p_ch) + return ABORT_ALL; + } + if (t_ch <= p_ch && t_ch >= prev_ch) + matched = TRUE; + p_ch = 0; /* This makes "prev_ch" get set to 0. */ + } else if (p_ch == '[' && p[1] == ':') { + const uchar *s; + int i; + for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/ + if (!p_ch) + return ABORT_ALL; + i = p - s - 1; + if (i < 0 || p[-1] != ':') { + /* Didn't find ":]", so treat like a normal set. */ + p = s - 2; + p_ch = '['; + if (t_ch == p_ch) + matched = TRUE; + continue; + } + if (CC_EQ(s,i, "alnum")) { + if (ISALNUM(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "alpha")) { + if (ISALPHA(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "blank")) { + if (ISBLANK(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "cntrl")) { + if (ISCNTRL(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "digit")) { + if (ISDIGIT(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "graph")) { + if (ISGRAPH(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "lower")) { + if (ISLOWER(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "print")) { + if (ISPRINT(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "punct")) { + if (ISPUNCT(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "space")) { + if (ISSPACE(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "upper")) { + if (ISUPPER(t_ch)) + matched = TRUE; + } else if (CC_EQ(s,i, "xdigit")) { + if (ISXDIGIT(t_ch)) + matched = TRUE; + } else /* malformed [:class:] string */ + return ABORT_ALL; + p_ch = 0; /* This makes "prev_ch" get set to 0. */ + } else if (t_ch == p_ch) + matched = TRUE; + } while (prev_ch = p_ch, (p_ch = *++p) != ']'); + if (matched == special || t_ch == '/') + return FALSE; + continue; + } } - } - return *text ? FALSE : TRUE; + return *text ? FALSE : TRUE; } /* Match the "pattern" against the "text" string. */ int wildmatch(const char *pattern, const char *text) { - return dowild((const uchar*)pattern, (const uchar*)text) == TRUE; + return dowild((const uchar*)pattern, (const uchar*)text) == TRUE; } /* Match the "pattern" against the forced-to-lower-case "text" string. */ int iwildmatch(const char *pattern, const char *text) { - int ret; - force_lower_case = 1; - ret = dowild((const uchar*)pattern, (const uchar*)text) == TRUE; - force_lower_case = 0; - return ret; + int ret; + force_lower_case = 1; + ret = dowild((const uchar*)pattern, (const uchar*)text) == TRUE; + force_lower_case = 0; + return ret; } From feabcc173b7a5f55c2b1ec78f230276c63ae4d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 15 Oct 2012 13:25:55 +0700 Subject: [PATCH 06/18] Integrate wildmatch to git MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- .gitignore | 1 + Makefile | 3 + t/t3070-wildmatch.sh | 188 +++++++++++++++++++++++++++++++++++++++++++ t/t3070/wildtest.txt | 165 ------------------------------------- test-wildmatch.c | 14 ++++ wildmatch.c | 5 +- 6 files changed, 210 insertions(+), 166 deletions(-) create mode 100755 t/t3070-wildmatch.sh delete mode 100644 t/t3070/wildtest.txt create mode 100644 test-wildmatch.c diff --git a/.gitignore b/.gitignore index f1acd3e777..1153a4bb3a 100644 --- a/.gitignore +++ b/.gitignore @@ -193,6 +193,7 @@ /test-sigchain /test-subprocess /test-svn-fe +/test-wildmatch /common-cmds.h *.tar.gz *.dsc diff --git a/Makefile b/Makefile index 13293d33fd..bc868d1548 100644 --- a/Makefile +++ b/Makefile @@ -503,6 +503,7 @@ TEST_PROGRAMS_NEED_X += test-sha1 TEST_PROGRAMS_NEED_X += test-sigchain TEST_PROGRAMS_NEED_X += test-subprocess TEST_PROGRAMS_NEED_X += test-svn-fe +TEST_PROGRAMS_NEED_X += test-wildmatch TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X)) @@ -674,6 +675,7 @@ LIB_H += unpack-trees.h LIB_H += userdiff.h LIB_H += utf8.h LIB_H += varint.h +LIB_H += wildmatch.h LIB_H += xdiff-interface.h LIB_H += xdiff/xdiff.h @@ -803,6 +805,7 @@ LIB_OBJS += userdiff.o LIB_OBJS += utf8.o LIB_OBJS += varint.o LIB_OBJS += walker.o +LIB_OBJS += wildmatch.o LIB_OBJS += wrapper.o LIB_OBJS += write_or_die.o LIB_OBJS += ws.o diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh new file mode 100755 index 0000000000..dbd3c8bc0c --- /dev/null +++ b/t/t3070-wildmatch.sh @@ -0,0 +1,188 @@ +#!/bin/sh + +test_description='wildmatch tests' + +. ./test-lib.sh + +match() { + if [ $1 = 1 ]; then + test_expect_success "wildmatch: match '$3' '$4'" " + test-wildmatch wildmatch '$3' '$4' + " + else + test_expect_success "wildmatch: no match '$3' '$4'" " + ! test-wildmatch wildmatch '$3' '$4' + " + fi + if [ $2 = 1 ]; then + test_expect_success "fnmatch: match '$3' '$4'" " + test-wildmatch fnmatch '$3' '$4' + " + elif [ $2 = 0 ]; then + test_expect_success "fnmatch: no match '$3' '$4'" " + ! test-wildmatch fnmatch '$3' '$4' + " +# else +# test_expect_success BROKEN_FNMATCH "fnmatch: '$3' '$4'" " +# ! test-wildmatch fnmatch '$3' '$4' +# " + fi +} + +# Basic wildmat features +match 1 1 foo foo +match 0 0 foo bar +match 1 1 '' "" +match 1 1 foo '???' +match 0 0 foo '??' +match 1 1 foo '*' +match 1 1 foo 'f*' +match 0 0 foo '*f' +match 1 1 foo '*foo*' +match 1 1 foobar '*ob*a*r*' +match 1 1 aaaaaaabababab '*ab' +match 1 1 'foo*' 'foo\*' +match 0 0 foobar 'foo\*bar' +match 1 1 'f\oo' 'f\\oo' +match 1 1 ball '*[al]?' +match 0 0 ten '[ten]' +match 1 1 ten '**[!te]' +match 0 0 ten '**[!ten]' +match 1 1 ten 't[a-g]n' +match 0 0 ten 't[!a-g]n' +match 1 1 ton 't[!a-g]n' +match 1 1 ton 't[^a-g]n' +match 1 1 'a]b' 'a[]]b' +match 1 1 a-b 'a[]-]b' +match 1 1 'a]b' 'a[]-]b' +match 0 0 aab 'a[]-]b' +match 1 1 aab 'a[]a-]b' +match 1 1 ']' ']' + +# Extended slash-matching features +match 0 0 'foo/baz/bar' 'foo*bar' +match 1 0 'foo/baz/bar' 'foo**bar' +match 0 0 'foo/bar' 'foo?bar' +match 0 0 'foo/bar' 'foo[/]bar' +match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' +match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' +match 0 0 'foo' '**/foo' +match 1 1 '/foo' '**/foo' +match 1 0 'bar/baz/foo' '**/foo' +match 0 0 'bar/baz/foo' '*/foo' +match 0 0 'foo/bar/baz' '**/bar*' +match 1 0 'deep/foo/bar/baz' '**/bar/*' +match 0 0 'deep/foo/bar/baz/' '**/bar/*' +match 1 0 'deep/foo/bar/baz/' '**/bar/**' +match 0 0 'deep/foo/bar' '**/bar/*' +match 1 0 'deep/foo/bar/' '**/bar/**' +match 1 0 'foo/bar/baz' '**/bar**' +match 1 0 'foo/bar/baz/x' '*/bar/**' +match 0 0 'deep/foo/bar/baz/x' '*/bar/**' +match 1 0 'deep/foo/bar/baz/x' '**/bar/*/*' + +# Various additional tests +match 0 0 'acrt' 'a[c-c]st' +match 1 1 'acrt' 'a[c-c]rt' +match 0 0 ']' '[!]-]' +match 1 1 'a' '[!]-]' +match 0 0 '' '\' +match 0 0 '\' '\' +match 0 0 '/\' '*/\' +match 1 1 '/\' '*/\\' +match 1 1 'foo' 'foo' +match 1 1 '@foo' '@foo' +match 0 0 'foo' '@foo' +match 1 1 '[ab]' '\[ab]' +match 1 1 '[ab]' '[[]ab]' +match 1 1 '[ab]' '[[:]ab]' +match 0 0 '[ab]' '[[::]ab]' +match 1 1 '[ab]' '[[:digit]ab]' +match 1 1 '[ab]' '[\[:]ab]' +match 1 1 '?a?b' '\??\?b' +match 1 1 'abc' '\a\b\c' +match 0 0 'foo' '' +match 1 0 'foo/bar/baz/to' '**/t[o]' + +# Character class tests +match 1 1 'a1B' '[[:alpha:]][[:digit:]][[:upper:]]' +match 0 0 'a' '[[:digit:][:upper:][:space:]]' +match 1 1 'A' '[[:digit:][:upper:][:space:]]' +match 1 0 '1' '[[:digit:][:upper:][:space:]]' +match 0 0 '1' '[[:digit:][:upper:][:spaci:]]' +match 1 1 ' ' '[[:digit:][:upper:][:space:]]' +match 0 0 '.' '[[:digit:][:upper:][:space:]]' +match 1 1 '.' '[[:digit:][:punct:][:space:]]' +match 1 1 '5' '[[:xdigit:]]' +match 1 1 'f' '[[:xdigit:]]' +match 1 1 'D' '[[:xdigit:]]' +match 1 0 '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]' +match 1 0 '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]' +match 1 1 '.' '[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]' +match 1 1 '5' '[a-c[:digit:]x-z]' +match 1 1 'b' '[a-c[:digit:]x-z]' +match 1 1 'y' '[a-c[:digit:]x-z]' +match 0 0 'q' '[a-c[:digit:]x-z]' + +# Additional tests, including some malformed wildmats +match 1 1 ']' '[\\-^]' +match 0 0 '[' '[\\-^]' +match 1 1 '-' '[\-_]' +match 1 1 ']' '[\]]' +match 0 0 '\]' '[\]]' +match 0 0 '\' '[\]]' +match 0 0 'ab' 'a[]b' +match 0 1 'a[]b' 'a[]b' +match 0 1 'ab[' 'ab[' +match 0 0 'ab' '[!' +match 0 0 'ab' '[-' +match 1 1 '-' '[-]' +match 0 0 '-' '[a-' +match 0 0 '-' '[!a-' +match 1 1 '-' '[--A]' +match 1 1 '5' '[--A]' +match 1 1 ' ' '[ --]' +match 1 1 '$' '[ --]' +match 1 1 '-' '[ --]' +match 0 0 '0' '[ --]' +match 1 1 '-' '[---]' +match 1 1 '-' '[------]' +match 0 0 'j' '[a-e-n]' +match 1 1 '-' '[a-e-n]' +match 1 1 'a' '[!------]' +match 0 0 '[' '[]-a]' +match 1 1 '^' '[]-a]' +match 0 0 '^' '[!]-a]' +match 1 1 '[' '[!]-a]' +match 1 1 '^' '[a^bc]' +match 1 1 '-b]' '[a-]b]' +match 0 0 '\' '[\]' +match 1 1 '\' '[\\]' +match 0 0 '\' '[!\\]' +match 1 1 'G' '[A-\\]' +match 0 0 'aaabbb' 'b*a' +match 0 0 'aabcaa' '*ba*' +match 1 1 ',' '[,]' +match 1 1 ',' '[\\,]' +match 1 1 '\' '[\\,]' +match 1 1 '-' '[,-.]' +match 0 0 '+' '[,-.]' +match 0 0 '-.]' '[,-.]' +match 1 1 '2' '[\1-\3]' +match 1 1 '3' '[\1-\3]' +match 0 0 '4' '[\1-\3]' +match 1 1 '\' '[[-\]]' +match 1 1 '[' '[[-\]]' +match 1 1 ']' '[[-\]]' +match 0 0 '-' '[[-\]]' + +# Test recursion and the abort code (use "wildtest -i" to see iteration counts) +match 1 1 '-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*' +match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*' +match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*' +match 1 1 '/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*' +match 0 0 '/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*' +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' + +test_done diff --git a/t/t3070/wildtest.txt b/t/t3070/wildtest.txt deleted file mode 100644 index 42c1678996..0000000000 --- a/t/t3070/wildtest.txt +++ /dev/null @@ -1,165 +0,0 @@ -# Input is in the following format (all items white-space separated): -# -# The first two items are 1 or 0 indicating if the wildmat call is expected to -# succeed and if fnmatch works the same way as wildmat, respectively. After -# that is a text string for the match, and a pattern string. Strings can be -# quoted (if desired) in either double or single quotes, as well as backticks. -# -# MATCH FNMATCH_SAME "text to match" 'pattern to use' - -# Basic wildmat features -1 1 foo foo -0 1 foo bar -1 1 '' "" -1 1 foo ??? -0 1 foo ?? -1 1 foo * -1 1 foo f* -0 1 foo *f -1 1 foo *foo* -1 1 foobar *ob*a*r* -1 1 aaaaaaabababab *ab -1 1 foo* foo\* -0 1 foobar foo\*bar -1 1 f\oo f\\oo -1 1 ball *[al]? -0 1 ten [ten] -1 1 ten **[!te] -0 1 ten **[!ten] -1 1 ten t[a-g]n -0 1 ten t[!a-g]n -1 1 ton t[!a-g]n -1 1 ton t[^a-g]n -1 1 a]b a[]]b -1 1 a-b a[]-]b -1 1 a]b a[]-]b -0 1 aab a[]-]b -1 1 aab a[]a-]b -1 1 ] ] - -# Extended slash-matching features -0 1 foo/baz/bar foo*bar -1 1 foo/baz/bar foo**bar -0 1 foo/bar foo?bar -0 1 foo/bar foo[/]bar -0 1 foo/bar f[^eiu][^eiu][^eiu][^eiu][^eiu]r -1 1 foo-bar f[^eiu][^eiu][^eiu][^eiu][^eiu]r -0 1 foo **/foo -1 1 /foo **/foo -1 1 bar/baz/foo **/foo -0 1 bar/baz/foo */foo -0 0 foo/bar/baz **/bar* -1 1 deep/foo/bar/baz **/bar/* -0 1 deep/foo/bar/baz/ **/bar/* -1 1 deep/foo/bar/baz/ **/bar/** -0 1 deep/foo/bar **/bar/* -1 1 deep/foo/bar/ **/bar/** -1 1 foo/bar/baz **/bar** -1 1 foo/bar/baz/x */bar/** -0 0 deep/foo/bar/baz/x */bar/** -1 1 deep/foo/bar/baz/x **/bar/*/* - -# Various additional tests -0 1 acrt a[c-c]st -1 1 acrt a[c-c]rt -0 1 ] [!]-] -1 1 a [!]-] -0 1 '' \ -0 1 \ \ -0 1 /\ */\ -1 1 /\ */\\ -1 1 foo foo -1 1 @foo @foo -0 1 foo @foo -1 1 [ab] \[ab] -1 1 [ab] [[]ab] -1 1 [ab] [[:]ab] -0 1 [ab] [[::]ab] -1 1 [ab] [[:digit]ab] -1 1 [ab] [\[:]ab] -1 1 ?a?b \??\?b -1 1 abc \a\b\c -0 1 foo '' -1 1 foo/bar/baz/to **/t[o] - -# Character class tests -1 1 a1B [[:alpha:]][[:digit:]][[:upper:]] -0 1 a [[:digit:][:upper:][:space:]] -1 1 A [[:digit:][:upper:][:space:]] -1 1 1 [[:digit:][:upper:][:space:]] -0 1 1 [[:digit:][:upper:][:spaci:]] -1 1 ' ' [[:digit:][:upper:][:space:]] -0 1 . [[:digit:][:upper:][:space:]] -1 1 . [[:digit:][:punct:][:space:]] -1 1 5 [[:xdigit:]] -1 1 f [[:xdigit:]] -1 1 D [[:xdigit:]] -1 1 _ [[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]] -#1 1 [^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]] -1 1  [^[:alnum:][:alpha:][:blank:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]] -1 1 . [^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]] -1 1 5 [a-c[:digit:]x-z] -1 1 b [a-c[:digit:]x-z] -1 1 y [a-c[:digit:]x-z] -0 1 q [a-c[:digit:]x-z] - -# Additional tests, including some malformed wildmats -1 1 ] [\\-^] -0 1 [ [\\-^] -1 1 - [\-_] -1 1 ] [\]] -0 1 \] [\]] -0 1 \ [\]] -0 1 ab a[]b -0 1 a[]b a[]b -0 1 ab[ ab[ -0 1 ab [! -0 1 ab [- -1 1 - [-] -0 1 - [a- -0 1 - [!a- -1 1 - [--A] -1 1 5 [--A] -1 1 ' ' '[ --]' -1 1 $ '[ --]' -1 1 - '[ --]' -0 1 0 '[ --]' -1 1 - [---] -1 1 - [------] -0 1 j [a-e-n] -1 1 - [a-e-n] -1 1 a [!------] -0 1 [ []-a] -1 1 ^ []-a] -0 1 ^ [!]-a] -1 1 [ [!]-a] -1 1 ^ [a^bc] -1 1 -b] [a-]b] -0 1 \ [\] -1 1 \ [\\] -0 1 \ [!\\] -1 1 G [A-\\] -0 1 aaabbb b*a -0 1 aabcaa *ba* -1 1 , [,] -1 1 , [\\,] -1 1 \ [\\,] -1 1 - [,-.] -0 1 + [,-.] -0 1 -.] [,-.] -1 1 2 [\1-\3] -1 1 3 [\1-\3] -0 1 4 [\1-\3] -1 1 \ [[-\]] -1 1 [ [[-\]] -1 1 ] [[-\]] -0 1 - [[-\]] - -# Test recursion and the abort code (use "wildtest -i" to see iteration counts) -1 1 -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1 -*-*-*-*-*-*-12-*-*-*-m-*-*-* -0 1 -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1 -*-*-*-*-*-*-12-*-*-*-m-*-*-* -0 1 -adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1 -*-*-*-*-*-*-12-*-*-*-m-*-*-* -1 1 /adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1 /*/*/*/*/*/*/12/*/*/*/m/*/*/* -0 1 /adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1 /*/*/*/*/*/*/12/*/*/*/m/*/*/* -1 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt **/*a*b*g*n*t -0 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz **/*a*b*g*n*t diff --git a/test-wildmatch.c b/test-wildmatch.c new file mode 100644 index 0000000000..ac5642037d --- /dev/null +++ b/test-wildmatch.c @@ -0,0 +1,14 @@ +#include "cache.h" +#include "wildmatch.h" + +int main(int argc, char **argv) +{ + if (!strcmp(argv[1], "wildmatch")) + return wildmatch(argv[3], argv[2]) ? 0 : 1; + else if (!strcmp(argv[1], "iwildmatch")) + return iwildmatch(argv[3], argv[2]) ? 0 : 1; + else if (!strcmp(argv[1], "fnmatch")) + return !!fnmatch(argv[3], argv[2], FNM_PATHNAME); + else + return 1; +} diff --git a/wildmatch.c b/wildmatch.c index 4653dd6eb8..ac29471660 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -9,7 +9,10 @@ ** work differently than '*', and to fix the character-class code. */ -#include "rsync.h" +#include "cache.h" +#include "wildmatch.h" + +typedef unsigned char uchar; /* What character marks an inverted character class? */ #define NEGATE_CLASS '!' From f1cf7b798305787d5d1272c13ead207e3e04a183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 15 Oct 2012 13:25:56 +0700 Subject: [PATCH 07/18] t3070: disable unreliable fnmatch tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These tests show different results on different fnmatch() versions. We don't want to test fnmatch here. We want to make sure wildmatch behavior matches fnmatch and that only makes sense in cases when fnmatch() behaves consistently. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- t/t3070-wildmatch.sh | 86 ++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh index dbd3c8bc0c..dd95b00627 100755 --- a/t/t3070-wildmatch.sh +++ b/t/t3070-wildmatch.sh @@ -52,11 +52,11 @@ match 1 1 ten 't[a-g]n' match 0 0 ten 't[!a-g]n' match 1 1 ton 't[!a-g]n' match 1 1 ton 't[^a-g]n' -match 1 1 'a]b' 'a[]]b' -match 1 1 a-b 'a[]-]b' -match 1 1 'a]b' 'a[]-]b' -match 0 0 aab 'a[]-]b' -match 1 1 aab 'a[]a-]b' +match 1 x 'a]b' 'a[]]b' +match 1 x a-b 'a[]-]b' +match 1 x 'a]b' 'a[]-]b' +match 0 x aab 'a[]-]b' +match 1 x aab 'a[]a-]b' match 1 1 ']' ']' # Extended slash-matching features @@ -67,7 +67,7 @@ match 0 0 'foo/bar' 'foo[/]bar' match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' match 0 0 'foo' '**/foo' -match 1 1 '/foo' '**/foo' +match 1 x '/foo' '**/foo' match 1 0 'bar/baz/foo' '**/foo' match 0 0 'bar/baz/foo' '*/foo' match 0 0 'foo/bar/baz' '**/bar*' @@ -85,77 +85,77 @@ match 1 0 'deep/foo/bar/baz/x' '**/bar/*/*' match 0 0 'acrt' 'a[c-c]st' match 1 1 'acrt' 'a[c-c]rt' match 0 0 ']' '[!]-]' -match 1 1 'a' '[!]-]' +match 1 x 'a' '[!]-]' match 0 0 '' '\' -match 0 0 '\' '\' -match 0 0 '/\' '*/\' -match 1 1 '/\' '*/\\' +match 0 x '\' '\' +match 0 x '/\' '*/\' +match 1 x '/\' '*/\\' match 1 1 'foo' 'foo' match 1 1 '@foo' '@foo' match 0 0 'foo' '@foo' match 1 1 '[ab]' '\[ab]' match 1 1 '[ab]' '[[]ab]' -match 1 1 '[ab]' '[[:]ab]' -match 0 0 '[ab]' '[[::]ab]' -match 1 1 '[ab]' '[[:digit]ab]' -match 1 1 '[ab]' '[\[:]ab]' +match 1 x '[ab]' '[[:]ab]' +match 0 x '[ab]' '[[::]ab]' +match 1 x '[ab]' '[[:digit]ab]' +match 1 x '[ab]' '[\[:]ab]' match 1 1 '?a?b' '\??\?b' match 1 1 'abc' '\a\b\c' match 0 0 'foo' '' match 1 0 'foo/bar/baz/to' '**/t[o]' # Character class tests -match 1 1 'a1B' '[[:alpha:]][[:digit:]][[:upper:]]' -match 0 0 'a' '[[:digit:][:upper:][:space:]]' -match 1 1 'A' '[[:digit:][:upper:][:space:]]' -match 1 0 '1' '[[:digit:][:upper:][:space:]]' -match 0 0 '1' '[[:digit:][:upper:][:spaci:]]' -match 1 1 ' ' '[[:digit:][:upper:][:space:]]' -match 0 0 '.' '[[:digit:][:upper:][:space:]]' -match 1 1 '.' '[[:digit:][:punct:][:space:]]' +match 1 x 'a1B' '[[:alpha:]][[:digit:]][[:upper:]]' +match 0 x 'a' '[[:digit:][:upper:][:space:]]' +match 1 x 'A' '[[:digit:][:upper:][:space:]]' +match 1 x '1' '[[:digit:][:upper:][:space:]]' +match 0 x '1' '[[:digit:][:upper:][:spaci:]]' +match 1 x ' ' '[[:digit:][:upper:][:space:]]' +match 0 x '.' '[[:digit:][:upper:][:space:]]' +match 1 x '.' '[[:digit:][:punct:][:space:]]' match 1 1 '5' '[[:xdigit:]]' match 1 1 'f' '[[:xdigit:]]' match 1 1 'D' '[[:xdigit:]]' -match 1 0 '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]' -match 1 0 '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]' -match 1 1 '.' '[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]' -match 1 1 '5' '[a-c[:digit:]x-z]' -match 1 1 'b' '[a-c[:digit:]x-z]' -match 1 1 'y' '[a-c[:digit:]x-z]' -match 0 0 'q' '[a-c[:digit:]x-z]' +match 1 x '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]' +match 1 x '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]' +match 1 x '.' '[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]' +match 1 x '5' '[a-c[:digit:]x-z]' +match 1 x 'b' '[a-c[:digit:]x-z]' +match 1 x 'y' '[a-c[:digit:]x-z]' +match 0 x 'q' '[a-c[:digit:]x-z]' # Additional tests, including some malformed wildmats -match 1 1 ']' '[\\-^]' +match 1 x ']' '[\\-^]' match 0 0 '[' '[\\-^]' -match 1 1 '-' '[\-_]' -match 1 1 ']' '[\]]' +match 1 x '-' '[\-_]' +match 1 x ']' '[\]]' match 0 0 '\]' '[\]]' match 0 0 '\' '[\]]' match 0 0 'ab' 'a[]b' -match 0 1 'a[]b' 'a[]b' -match 0 1 'ab[' 'ab[' +match 0 x 'a[]b' 'a[]b' +match 0 x 'ab[' 'ab[' match 0 0 'ab' '[!' match 0 0 'ab' '[-' match 1 1 '-' '[-]' match 0 0 '-' '[a-' match 0 0 '-' '[!a-' -match 1 1 '-' '[--A]' -match 1 1 '5' '[--A]' +match 1 x '-' '[--A]' +match 1 x '5' '[--A]' match 1 1 ' ' '[ --]' match 1 1 '$' '[ --]' match 1 1 '-' '[ --]' match 0 0 '0' '[ --]' -match 1 1 '-' '[---]' -match 1 1 '-' '[------]' +match 1 x '-' '[---]' +match 1 x '-' '[------]' match 0 0 'j' '[a-e-n]' -match 1 1 '-' '[a-e-n]' -match 1 1 'a' '[!------]' +match 1 x '-' '[a-e-n]' +match 1 x 'a' '[!------]' match 0 0 '[' '[]-a]' -match 1 1 '^' '[]-a]' +match 1 x '^' '[]-a]' match 0 0 '^' '[!]-a]' -match 1 1 '[' '[!]-a]' +match 1 x '[' '[!]-a]' match 1 1 '^' '[a^bc]' -match 1 1 '-b]' '[a-]b]' +match 1 x '-b]' '[a-]b]' match 0 0 '\' '[\]' match 1 1 '\' '[\\]' match 0 0 '\' '[!\\]' From 3ae5396cf719000039c5d0d35f9bf934f647b030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 15 Oct 2012 13:25:57 +0700 Subject: [PATCH 08/18] wildmatch: make wildmatch's return value compatible with fnmatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wildmatch returns non-zero if matched, zero otherwise. This patch makes it return zero if matches, non-zero otherwise, like fnmatch(). Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- test-wildmatch.c | 4 ++-- wildmatch.c | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/test-wildmatch.c b/test-wildmatch.c index ac5642037d..77014e99dc 100644 --- a/test-wildmatch.c +++ b/test-wildmatch.c @@ -4,9 +4,9 @@ int main(int argc, char **argv) { if (!strcmp(argv[1], "wildmatch")) - return wildmatch(argv[3], argv[2]) ? 0 : 1; + return !!wildmatch(argv[3], argv[2]); else if (!strcmp(argv[1], "iwildmatch")) - return iwildmatch(argv[3], argv[2]) ? 0 : 1; + return !!iwildmatch(argv[3], argv[2]); else if (!strcmp(argv[1], "fnmatch")) return !!fnmatch(argv[3], argv[2], FNM_PATHNAME); else diff --git a/wildmatch.c b/wildmatch.c index ac29471660..6d992d314a 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -20,6 +20,9 @@ typedef unsigned char uchar; #define FALSE 0 #define TRUE 1 + +#define NOMATCH 1 +#define MATCH 0 #define ABORT_ALL -1 #define ABORT_TO_STARSTAR -2 @@ -78,12 +81,12 @@ static int dowild(const uchar *p, const uchar *text) /* FALLTHROUGH */ default: if (t_ch != p_ch) - return FALSE; + return NOMATCH; continue; case '?': /* Match anything but '/'. */ if (t_ch == '/') - return FALSE; + return NOMATCH; continue; case '*': if (*++p == '*') { @@ -96,14 +99,14 @@ static int dowild(const uchar *p, const uchar *text) * only if there are no more slash characters. */ if (!special) { if (strchr((char*)text, '/') != NULL) - return FALSE; + return NOMATCH; } - return TRUE; + return MATCH; } while (1) { if (t_ch == '\0') break; - if ((matched = dowild(p, text)) != FALSE) { + if ((matched = dowild(p, text)) != NOMATCH) { if (!special || matched != ABORT_TO_STARSTAR) return matched; } else if (!special && t_ch == '/') @@ -202,18 +205,18 @@ static int dowild(const uchar *p, const uchar *text) matched = TRUE; } while (prev_ch = p_ch, (p_ch = *++p) != ']'); if (matched == special || t_ch == '/') - return FALSE; + return NOMATCH; continue; } } - return *text ? FALSE : TRUE; + return *text ? NOMATCH : MATCH; } /* Match the "pattern" against the "text" string. */ int wildmatch(const char *pattern, const char *text) { - return dowild((const uchar*)pattern, (const uchar*)text) == TRUE; + return dowild((const uchar*)pattern, (const uchar*)text); } /* Match the "pattern" against the forced-to-lower-case "text" string. */ @@ -221,7 +224,7 @@ int iwildmatch(const char *pattern, const char *text) { int ret; force_lower_case = 1; - ret = dowild((const uchar*)pattern, (const uchar*)text) == TRUE; + ret = dowild((const uchar*)pattern, (const uchar*)text); force_lower_case = 0; return ret; } From 9b4edc0a49abd4b5c6505c63c8fa40d527df6ae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 15 Oct 2012 13:25:58 +0700 Subject: [PATCH 09/18] wildmatch: remove static variable force_lower_case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One place less to worry about thread safety. Also combine wildmatch and iwildmatch into one. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- test-wildmatch.c | 4 ++-- wildmatch.c | 21 +++++---------------- wildmatch.h | 3 +-- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/test-wildmatch.c b/test-wildmatch.c index 77014e99dc..74c08644ee 100644 --- a/test-wildmatch.c +++ b/test-wildmatch.c @@ -4,9 +4,9 @@ int main(int argc, char **argv) { if (!strcmp(argv[1], "wildmatch")) - return !!wildmatch(argv[3], argv[2]); + return !!wildmatch(argv[3], argv[2], 0); else if (!strcmp(argv[1], "iwildmatch")) - return !!iwildmatch(argv[3], argv[2]); + return !!wildmatch(argv[3], argv[2], 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 6d992d314a..eef7b13aa4 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -59,10 +59,8 @@ typedef unsigned char uchar; #define ISUPPER(c) (ISASCII(c) && isupper(c)) #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c)) -static int force_lower_case = 0; - /* Match pattern "p" against "text" */ -static int dowild(const uchar *p, const uchar *text) +static int dowild(const uchar *p, const uchar *text, int force_lower_case) { uchar p_ch; @@ -106,7 +104,7 @@ static int dowild(const uchar *p, const uchar *text) while (1) { if (t_ch == '\0') break; - if ((matched = dowild(p, text)) != NOMATCH) { + if ((matched = dowild(p, text, force_lower_case)) != NOMATCH) { if (!special || matched != ABORT_TO_STARSTAR) return matched; } else if (!special && t_ch == '/') @@ -214,17 +212,8 @@ static int dowild(const uchar *p, const uchar *text) } /* Match the "pattern" against the "text" string. */ -int wildmatch(const char *pattern, const char *text) +int wildmatch(const char *pattern, const char *text, int flags) { - return dowild((const uchar*)pattern, (const uchar*)text); -} - -/* Match the "pattern" against the forced-to-lower-case "text" string. */ -int iwildmatch(const char *pattern, const char *text) -{ - int ret; - force_lower_case = 1; - ret = dowild((const uchar*)pattern, (const uchar*)text); - force_lower_case = 0; - return ret; + return dowild((const uchar*)pattern, (const uchar*)text, + flags & FNM_CASEFOLD ? 1 :0); } diff --git a/wildmatch.h b/wildmatch.h index 562faa35a5..e974f9abce 100644 --- a/wildmatch.h +++ b/wildmatch.h @@ -1,4 +1,3 @@ /* wildmatch.h */ -int wildmatch(const char *pattern, const char *text); -int iwildmatch(const char *pattern, const char *text); +int wildmatch(const char *pattern, const char *text, int flags); From 164bf83af6466e03b306b9d63dcf7d36dda2bdae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 15 Oct 2012 13:25:59 +0700 Subject: [PATCH 10/18] wildmatch: fix case-insensitive matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dowild() does case insensitive matching by lower-casing the text. That means lower case letters in patterns imply case-insensitive matching, but upper case means exact matching. We do not want that subtlety. Lower case pattern too so iwildmatch() always does what we expect it to do. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- wildmatch.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wildmatch.c b/wildmatch.c index eef7b13aa4..5469866e8a 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -71,6 +71,8 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case) return ABORT_ALL; if (force_lower_case && ISUPPER(t_ch)) t_ch = tolower(t_ch); + if (force_lower_case && ISUPPER(p_ch)) + p_ch = tolower(p_ch); switch (p_ch) { case '\\': /* Literal match with following character. Note that the test From 40bbee0ab07d0ee4f21b11d597c878245c1b05a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 15 Oct 2012 13:26:00 +0700 Subject: [PATCH 11/18] wildmatch: adjust "**" behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Standard wildmatch() sees consecutive asterisks as "*" that can also match slashes. But that may be hard to explain to users as "abc/**/def" can match "abcdef", "abcxyzdef", "abc/def", "abc/x/def", "abc/x/y/def"... This patch changes wildmatch so that users can do - "**/def" -> all paths ending with file/directory 'def' - "abc/**" - equivalent to "/abc/" - "abc/**/def" -> "abc/x/def", "abc/x/y/def"... - otherwise consider the pattern malformed if "**" is found Basically the magic of "**" only remains if it's wrapped around by slashes. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- t/t3070-wildmatch.sh | 5 +++-- wildmatch.c | 13 +++++++------ wildmatch.h | 6 ++++++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh index dd95b00627..15848d5935 100755 --- a/t/t3070-wildmatch.sh +++ b/t/t3070-wildmatch.sh @@ -46,7 +46,7 @@ match 0 0 foobar 'foo\*bar' match 1 1 'f\oo' 'f\\oo' match 1 1 ball '*[al]?' match 0 0 ten '[ten]' -match 1 1 ten '**[!te]' +match 0 1 ten '**[!te]' match 0 0 ten '**[!ten]' match 1 1 ten 't[a-g]n' match 0 0 ten 't[!a-g]n' @@ -61,7 +61,8 @@ match 1 1 ']' ']' # Extended slash-matching features match 0 0 'foo/baz/bar' 'foo*bar' -match 1 0 'foo/baz/bar' 'foo**bar' +match 0 0 'foo/baz/bar' 'foo**bar' +match 0 1 'foobazbar' 'foo**bar' match 0 0 'foo/bar' 'foo?bar' match 0 0 'foo/bar' 'foo[/]bar' match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' diff --git a/wildmatch.c b/wildmatch.c index 5469866e8a..85bc0df8f8 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -21,11 +21,6 @@ typedef unsigned char uchar; #define FALSE 0 #define TRUE 1 -#define NOMATCH 1 -#define MATCH 0 -#define ABORT_ALL -1 -#define ABORT_TO_STARSTAR -2 - #define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \ && *(class) == *(litmatch) \ && strncmp((char*)class, litmatch, len) == 0) @@ -90,8 +85,14 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case) continue; case '*': if (*++p == '*') { + const uchar *prev_p = p - 2; while (*++p == '*') {} - special = TRUE; + if ((prev_p == text || *prev_p == '/') || + (*p == '\0' || *p == '/' || + (p[0] == '\\' && p[1] == '/'))) { + special = TRUE; + } else + return ABORT_MALFORMED; } else special = FALSE; if (*p == '\0') { diff --git a/wildmatch.h b/wildmatch.h index e974f9abce..984a38cdc2 100644 --- a/wildmatch.h +++ b/wildmatch.h @@ -1,3 +1,9 @@ /* wildmatch.h */ +#define ABORT_MALFORMED 2 +#define NOMATCH 1 +#define MATCH 0 +#define ABORT_ALL -1 +#define ABORT_TO_STARSTAR -2 + int wildmatch(const char *pattern, const char *text, int flags); From 4c251e5cb5c245ee3bb98c7cedbe944df93e45f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 15 Oct 2012 13:26:01 +0700 Subject: [PATCH 12/18] wildmatch: make /**/ match zero or more directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "foo/**/bar" matches "foo/x/bar", "foo/x/y/bar"... but not "foo/bar". We make a special case, when foo/**/ is detected (and "foo/" part is already matched), try matching "bar" with the rest of the string. "Match one or more directories" semantics can be easily achieved using "foo/*/**/bar". This also makes "**/foo" match "foo" in addition to "x/foo", "x/y/foo".. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- t/t3070-wildmatch.sh | 8 +++++++- wildmatch.c | 12 ++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh index 15848d5935..e6ad6f4703 100755 --- a/t/t3070-wildmatch.sh +++ b/t/t3070-wildmatch.sh @@ -63,11 +63,17 @@ match 1 1 ']' ']' match 0 0 'foo/baz/bar' 'foo*bar' match 0 0 'foo/baz/bar' 'foo**bar' match 0 1 'foobazbar' 'foo**bar' +match 1 1 'foo/baz/bar' 'foo/**/bar' +match 1 0 'foo/baz/bar' 'foo/**/**/bar' +match 1 0 'foo/b/a/z/bar' 'foo/**/bar' +match 1 0 'foo/b/a/z/bar' 'foo/**/**/bar' +match 1 0 'foo/bar' 'foo/**/bar' +match 1 0 'foo/bar' 'foo/**/**/bar' match 0 0 'foo/bar' 'foo?bar' match 0 0 'foo/bar' 'foo[/]bar' match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' -match 0 0 'foo' '**/foo' +match 1 0 'foo' '**/foo' match 1 x '/foo' '**/foo' match 1 0 'bar/baz/foo' '**/foo' match 0 0 'bar/baz/foo' '*/foo' diff --git a/wildmatch.c b/wildmatch.c index 85bc0df8f8..3972e26e83 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -90,6 +90,18 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case) if ((prev_p == text || *prev_p == '/') || (*p == '\0' || *p == '/' || (p[0] == '\\' && p[1] == '/'))) { + /* + * Assuming we already match 'foo/' and are at + * , just assume it matches + * nothing and go ahead match the rest of the + * pattern with the remaining string. This + * helps make foo/<*><*>/bar (<> because + * otherwise it breaks C comment syntax) match + * both foo/bar and foo/a/bar. + */ + if (p[0] == '/' && + dowild(p + 1, text, force_lower_case) == MATCH) + return MATCH; special = TRUE; } else return ABORT_MALFORMED; From 237ec6e40d4fd1a0190c4ffde6d18278abc5853a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 15 Oct 2012 13:26:02 +0700 Subject: [PATCH 13/18] Support "**" wildcard in .gitignore and .gitattributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- Documentation/gitignore.txt | 19 +++++++++++++++ dir.c | 4 +++- t/t0003-attributes.sh | 37 ++++++++++++++++++++++++++++++ t/t3001-ls-files-others-exclude.sh | 18 +++++++++++++++ 4 files changed, 77 insertions(+), 1 deletion(-) diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt index 2e7328b830..d4747cee41 100644 --- a/Documentation/gitignore.txt +++ b/Documentation/gitignore.txt @@ -96,6 +96,25 @@ PATTERN FORMAT For example, "/{asterisk}.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c". +Two consecutive asterisks ("`**`") in patterns matched against +full pathname may have special meaning: + + - A leading "`**`" followed by a slash means match in all + directories. For example, "`**/foo`" matches file or directory + "`foo`" anywhere, the same as pattern "`foo`". "**/foo/bar" + matches file or directory "`bar`" anywhere that is directly + under directory "`foo`". + + - A trailing "/**" matches everything inside. For example, + "abc/**" matches all files inside directory "abc", relative + to the location of the `.gitignore` file, with infinite depth. + + - A slash followed by two consecutive asterisks then a slash + matches zero or more directories. For example, "`a/**/b`" + matches "`a/b`", "`a/x/b`", "`a/x/y/b`" and so on. + + - Other consecutive asterisks are considered invalid. + NOTES ----- diff --git a/dir.c b/dir.c index ee8e7115a8..cb7328b548 100644 --- a/dir.c +++ b/dir.c @@ -8,6 +8,7 @@ #include "cache.h" #include "dir.h" #include "refs.h" +#include "wildmatch.h" struct path_simplify { int len; @@ -593,7 +594,8 @@ int match_pathname(const char *pathname, int pathlen, namelen -= prefix; } - return fnmatch_icase(pattern, name, FNM_PATHNAME) == 0; + return wildmatch(pattern, name, + ignore_case ? FNM_CASEFOLD : 0) == 0; } /* Scan the list and let the last match determine the fate. diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh index f6c21ea4ea..c962403844 100755 --- a/t/t0003-attributes.sh +++ b/t/t0003-attributes.sh @@ -216,6 +216,43 @@ test_expect_success 'patterns starting with exclamation' ' attr_check "!f" foo ' +test_expect_success '"**" test' ' + echo "**/f foo=bar" >.gitattributes && + cat <<\EOF >expect && +f: foo: bar +a/f: foo: bar +a/b/f: foo: bar +a/b/c/f: foo: bar +EOF + git check-attr foo -- "f" >actual 2>err && + git check-attr foo -- "a/f" >>actual 2>>err && + git check-attr foo -- "a/b/f" >>actual 2>>err && + git check-attr foo -- "a/b/c/f" >>actual 2>>err && + test_cmp expect actual && + test_line_count = 0 err +' + +test_expect_success '"**" with no slashes test' ' + echo "a**f foo=bar" >.gitattributes && + git check-attr foo -- "f" >actual && + cat <<\EOF >expect && +f: foo: unspecified +af: foo: bar +axf: foo: bar +a/f: foo: unspecified +a/b/f: foo: unspecified +a/b/c/f: foo: unspecified +EOF + git check-attr foo -- "f" >actual 2>err && + git check-attr foo -- "af" >>actual 2>err && + git check-attr foo -- "axf" >>actual 2>err && + git check-attr foo -- "a/f" >>actual 2>>err && + git check-attr foo -- "a/b/f" >>actual 2>>err && + git check-attr foo -- "a/b/c/f" >>actual 2>>err && + test_cmp expect actual && + test_line_count = 0 err +' + test_expect_success 'setup bare' ' git clone --bare . bare.git && cd bare.git diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh index dc2f0458fd..efb7ebc91f 100755 --- a/t/t3001-ls-files-others-exclude.sh +++ b/t/t3001-ls-files-others-exclude.sh @@ -220,4 +220,22 @@ test_expect_success 'pattern matches prefix completely' ' test_cmp expect actual ' +test_expect_success 'ls-files with "**" patterns' ' + cat <<\EOF >expect && +a.1 +one/a.1 +one/two/a.1 +three/a.1 +EOF + git ls-files -o -i --exclude "**/a.1" >actual + test_cmp expect actual +' + + +test_expect_success 'ls-files with "**" patterns and no slashes' ' + : >expect && + git ls-files -o -i --exclude "one**a.1" >actual && + test_cmp expect actual +' + test_done From ef49841ddf98ed1eb40c60153072fa1a91fc2f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Tue, 20 Nov 2012 08:02:39 +0100 Subject: [PATCH 14/18] test-wildmatch: avoid Windows path mangling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MSYS bash mangles arguments that begin with a forward slash when they are passed to test-wildmatch. This causes tests to fail. Avoid mangling by prepending "XXX", which is removed by test-wildmatch before further processing. [J6t: reworded commit message] Reported-by: Johannes Sixt Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Johannes Sixt --- t/t3070-wildmatch.sh | 10 +++++----- test-wildmatch.c | 8 ++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh index e6ad6f4703..3155eabf33 100755 --- a/t/t3070-wildmatch.sh +++ b/t/t3070-wildmatch.sh @@ -74,7 +74,7 @@ match 0 0 'foo/bar' 'foo[/]bar' match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' match 1 0 'foo' '**/foo' -match 1 x '/foo' '**/foo' +match 1 x 'XXX/foo' '**/foo' match 1 0 'bar/baz/foo' '**/foo' match 0 0 'bar/baz/foo' '*/foo' match 0 0 'foo/bar/baz' '**/bar*' @@ -95,8 +95,8 @@ match 0 0 ']' '[!]-]' match 1 x 'a' '[!]-]' match 0 0 '' '\' match 0 x '\' '\' -match 0 x '/\' '*/\' -match 1 x '/\' '*/\\' +match 0 x 'XXX/\' '*/\' +match 1 x 'XXX/\' '*/\\' match 1 1 'foo' 'foo' match 1 1 '@foo' '@foo' match 0 0 'foo' '@foo' @@ -187,8 +187,8 @@ match 0 0 '-' '[[-\]]' match 1 1 '-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*' match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*' match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*' -match 1 1 '/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*' -match 0 0 '/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*' +match 1 1 'XXX/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*' +match 0 0 'XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*' 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' diff --git a/test-wildmatch.c b/test-wildmatch.c index 74c08644ee..e384c8edb1 100644 --- a/test-wildmatch.c +++ b/test-wildmatch.c @@ -3,6 +3,14 @@ int main(int argc, char **argv) { + int i; + for (i = 2; i < argc; i++) { + if (argv[i][0] == '/') + die("Forward slash is not allowed at the beginning of the\n" + "pattern because Windows does not like it. Use `XXX/' instead."); + else if (!strncmp(argv[i], "XXX/", 4)) + argv[i] += 3; + } if (!strcmp(argv[1], "wildmatch")) return !!wildmatch(argv[3], argv[2], 0); else if (!strcmp(argv[1], "iwildmatch")) From d5d80e12bd755197352bdb0e9b2291c896946abd Mon Sep 17 00:00:00 2001 From: Ramsay Jones Date: Sat, 15 Dec 2012 19:19:18 +0000 Subject: [PATCH 15/18] t3070: Disable some failing fnmatch tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The failing tests make use of a POSIX character class, '[:xdigit:]' in this case, which some versions of the fnmatch() library function do not support. In the spirit of commit f1cf7b79 ("t3070: disable unreliable fnmatch tests", 15-10-2012), we disable the fnmatch() half of these tests. Signed-off-by: Ramsay Jones Acked-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- t/t3070-wildmatch.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh index 3155eabf33..d5bafefbe2 100755 --- a/t/t3070-wildmatch.sh +++ b/t/t3070-wildmatch.sh @@ -120,9 +120,9 @@ match 0 x '1' '[[:digit:][:upper:][:spaci:]]' match 1 x ' ' '[[:digit:][:upper:][:space:]]' match 0 x '.' '[[:digit:][:upper:][:space:]]' match 1 x '.' '[[:digit:][:punct:][:space:]]' -match 1 1 '5' '[[:xdigit:]]' -match 1 1 'f' '[[:xdigit:]]' -match 1 1 'D' '[[:xdigit:]]' +match 1 x '5' '[[:xdigit:]]' +match 1 x 'f' '[[:xdigit:]]' +match 1 x 'D' '[[:xdigit:]]' match 1 x '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]' match 1 x '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]' match 1 x '.' '[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]' From 3a078dec3327fb68faa7c800040f89db3e38e6e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Tue, 1 Jan 2013 09:44:02 +0700 Subject: [PATCH 16/18] wildmatch: fix "**" special case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "**" is adjusted to only be effective when surrounded by slashes, in 40bbee0 (wildmatch: adjust "**" behavior - 2012-10-15). Except that the commit did it wrong: 1. when it checks for "the preceding slash unless ** is at the beginning", it compares to wrong pointer. It should have compared to the beginning of the pattern, not the text. 2. prev_p points to the character before "**", not the first "*". The correct comparison must be "prev_p < pattern" or "prev_p + 1 == pattern", not "prev_p == pattern". 3. The pattern must be surrounded by slashes unless it's at the beginning or the end of the pattern. We do two checks: one for the preceding slash and one the trailing slash. Both checks must be met. The use of "||" is wrong. This patch fixes all above. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- t/t3070-wildmatch.sh | 2 +- wildmatch.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh index d5bafefbe2..af54c83111 100755 --- a/t/t3070-wildmatch.sh +++ b/t/t3070-wildmatch.sh @@ -83,7 +83,7 @@ match 0 0 'deep/foo/bar/baz/' '**/bar/*' match 1 0 'deep/foo/bar/baz/' '**/bar/**' match 0 0 'deep/foo/bar' '**/bar/*' match 1 0 'deep/foo/bar/' '**/bar/**' -match 1 0 'foo/bar/baz' '**/bar**' +match 0 0 'foo/bar/baz' '**/bar**' match 1 0 'foo/bar/baz/x' '*/bar/**' match 0 0 'deep/foo/bar/baz/x' '*/bar/**' match 1 0 'deep/foo/bar/baz/x' '**/bar/*/*' diff --git a/wildmatch.c b/wildmatch.c index 3972e26e83..5f976e91d8 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -58,6 +58,7 @@ typedef unsigned char uchar; static int dowild(const uchar *p, const uchar *text, int force_lower_case) { uchar p_ch; + const uchar *pattern = p; for ( ; (p_ch = *p) != '\0'; text++, p++) { int matched, special; @@ -87,7 +88,7 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case) if (*++p == '*') { const uchar *prev_p = p - 2; while (*++p == '*') {} - if ((prev_p == text || *prev_p == '/') || + if ((prev_p < pattern || *prev_p == '/') && (*p == '\0' || *p == '/' || (p[0] == '\\' && p[1] == '/'))) { /* From 889316d2528e93cb79f3e6fdd92cea4ccaab4a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Tue, 1 Jan 2013 09:44:03 +0700 Subject: [PATCH 17/18] compat/fnmatch: respect NO_FNMATCH* even on glibc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- compat/fnmatch/fnmatch.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compat/fnmatch/fnmatch.c b/compat/fnmatch/fnmatch.c index 9473aed2bb..6f7387d036 100644 --- a/compat/fnmatch/fnmatch.c +++ b/compat/fnmatch/fnmatch.c @@ -55,7 +55,8 @@ program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ -#if defined _LIBC || !defined __GNU_LIBRARY__ +#if defined NO_FNMATCH || defined NO_FNMATCH_CASEFOLD || \ + defined _LIBC || !defined __GNU_LIBRARY__ # if defined STDC_HEADERS || !defined isascii From b6a3d3353f799c8c5afedb2da4df6e7cdc5d00c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Tue, 1 Jan 2013 09:44:04 +0700 Subject: [PATCH 18/18] wildmatch: replace variable 'special' with better named ones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'special' is too generic and is used for two different purposes. Replace it with 'match_slash' to indicate "**" pattern and 'negated' for "[!...]" and "[^...]". Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- wildmatch.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/wildmatch.c b/wildmatch.c index 5f976e91d8..2d3ed84364 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -61,7 +61,7 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case) const uchar *pattern = p; for ( ; (p_ch = *p) != '\0'; text++, p++) { - int matched, special; + int matched, match_slash, negated; uchar t_ch, prev_ch; if ((t_ch = *text) == '\0' && p_ch != '*') return ABORT_ALL; @@ -103,15 +103,15 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case) if (p[0] == '/' && dowild(p + 1, text, force_lower_case) == MATCH) return MATCH; - special = TRUE; + match_slash = TRUE; } else return ABORT_MALFORMED; } else - special = FALSE; + match_slash = FALSE; if (*p == '\0') { /* Trailing "**" matches everything. Trailing "*" matches * only if there are no more slash characters. */ - if (!special) { + if (!match_slash) { if (strchr((char*)text, '/') != NULL) return NOMATCH; } @@ -121,9 +121,9 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case) if (t_ch == '\0') break; if ((matched = dowild(p, text, force_lower_case)) != NOMATCH) { - if (!special || matched != ABORT_TO_STARSTAR) + if (!match_slash || matched != ABORT_TO_STARSTAR) return matched; - } else if (!special && t_ch == '/') + } else if (!match_slash && t_ch == '/') return ABORT_TO_STARSTAR; t_ch = *++text; } @@ -135,8 +135,8 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case) p_ch = NEGATE_CLASS; #endif /* Assign literal TRUE/FALSE because of "matched" comparison. */ - special = p_ch == NEGATE_CLASS? TRUE : FALSE; - if (special) { + negated = p_ch == NEGATE_CLASS? TRUE : FALSE; + if (negated) { /* Inverted character class. */ p_ch = *++p; } @@ -218,7 +218,7 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case) } else if (t_ch == p_ch) matched = TRUE; } while (prev_ch = p_ch, (p_ch = *++p) != ']'); - if (matched == special || t_ch == '/') + if (matched == negated || t_ch == '/') return NOMATCH; continue; }