threads / patch / 31780

v4, 12 partsWildmatch v4

Subject: [PATCH v4 00/12] Wildmatch v4

## tl;dr

28 messages between Oct 10, 2012 and Oct 13, 2012. Diffs are folded; open one to read it.

replies: 27people: 5as markdown or json

Nguyễn Thái Ngọc Duy· Oct 10, 2012, 10:40 UTC · lore

Really small updates. I did not want to resend it this soon but this may fix the compile errors for Junio. Changes are

 - cleanup in wildmatch.c so #include "cache.h" is at the top of the
   file.
 - wildmatch() returns no match if it encounters "**" without
   surrounding slashes. It returns a special code so we can actually
   show a warning at higher level. I don't want to do that now
   because I want to mark the pattern "broken" in attr.c/dir.c so
   the pattern will never be used again, and the message shown only
   once. That needs nd/attr-match-optim-more, but I don't want to tie
   this series to that just yet.
Nguyễn Thái Ngọc Duy (12):
  ctype: make sane_ctype[] const array
  ctype: support iscntrl, ispunct, isxdigit and isprint
  Import wildmatch from rsync
  wildmatch: remove unnecessary functions
  Integrate wildmatch to git
  wildmatch: make wildmatch's return value compatible with fnmatch
  wildmatch: remove static variable force_lower_case
  wildmatch: fix case-insensitive matching
  wildmatch: adjust "**" behavior
  wildmatch: make /**/ match zero or more directories
  Support "**" wildcard in .gitignore and .gitattributes
  t3070: disable two fnmatch tests that have different results on
    different libc
 .gitignore                         |   1 +
 Documentation/gitattributes.txt    |   6 +
 Documentation/gitignore.txt        |  19 +++
 Makefile                           |   3 +
 attr.c                             |   4 +-
 ctype.c                            |  20 +++-
 dir.c                              |   4 +-
 git-compat-util.h                  |  15 ++-
 t/t0003-attributes.sh              |  38 ++++++
 t/t3001-ls-files-others-exclude.sh |  19 +++
 t/t3070-wildmatch.sh               | 187 +++++++++++++++++++++++++++++
 test-wildmatch.c                   |  14 +++
 wildmatch.c                        | 240 +++++++++++++++++++++++++++++++++++++
 wildmatch.h                        |   9 ++
 14 files changed, 575 insertions(+), 4 deletions(-)
 create mode 100755 t/t3070-wildmatch.sh
 create mode 100644 test-wildmatch.c
 create mode 100644 wildmatch.c
 create mode 100644 wildmatch.h
-- 
1.7.12.1.406.g6ab07c4
Nguyễn Thái Ngọc Duy· Oct 10, 2012, 10:40 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH v4 01/12] ctype: make sane_ctype[] const array

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 ctype.c           | 2 +-
 git-compat-util.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
Show changes to 2 files +2 −2

ctype.c, git-compat-util.h

diff --git a/ctype.c b/ctype.c
index 9353271..faeaf34 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 2fbf1fd..f8b859c 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -510,7 +510,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
-- 
1.7.12.1.406.g6ab07c4
Nguyễn Thái Ngọc Duy· Oct 10, 2012, 10:40 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH v4 02/12] ctype: support iscntrl, ispunct, isxdigit and isprint

A new table sane_ctype2[] is added, otherwise we'll need to OR with current bits in sane_ctype[] and that looks ugly.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 ctype.c           | 18 ++++++++++++++++++
 git-compat-util.h | 13 +++++++++++++
 2 files changed, 31 insertions(+)
Show changes to 2 files +31 −0

ctype.c, git-compat-util.h

diff --git a/ctype.c b/ctype.c
index faeaf34..b4bf48a 100644
--- a/ctype.c
+++ b/ctype.c
@@ -26,6 +26,24 @@ const unsigned char sane_ctype[256] = {
 	/* Nothing in the 128.. range */
 };
 
+enum {
+	CN = GIT_CNTRL,
+	PU = GIT_PUNCT,
+	XD = GIT_XDIGIT,
+};
+
+const unsigned char sane_ctype2[256] = {
+	CN, CN, CN, CN, CN, CN, CN, CN, CN, CN, CN, CN, CN, CN, CN, CN, /*    0..15 */
+	CN, CN, CN, CN, CN, CN, CN, CN, CN, CN, CN, CN, CN, CN, CN, CN, /*   16..31 */
+	0,  PU, PU, PU, PU, PU, PU, PU, PU, PU, PU, PU, PU, PU, PU, PU, /*   32..47 */
+	XD, XD, XD, XD, XD, XD, XD, XD, XD, XD, PU, PU, PU, PU, PU, PU, /*   48..63 */
+	PU, 0,	XD, 0,	XD, 0,	XD, 0,	0,  0,	0,  0,	0,  0,	0,  0,	/*   64..79 */
+	0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  PU, PU, PU, PU, PU, /*   80..95 */
+	PU, 0,	XD, 0,	XD, 0,	XD, 0,	0,  0,	0,  0,	0,  0,	0,  0,	/*  96..111 */
+	0,  0,	0,  0,	0,  0,	0,  0,	0,  0,	0,  PU, PU, PU, PU, CN, /* 112..127 */
+	/* Nothing in the 128.. range */
+};
+
 /* For case-insensitive kwset */
 const char tolower_trans_tbl[256] = {
 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
diff --git a/git-compat-util.h b/git-compat-util.h
index f8b859c..ea11694 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -510,14 +510,23 @@ 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];
+extern const unsigned char sane_ctype2[256];
 #define GIT_SPACE 0x01
 #define GIT_DIGIT 0x02
 #define GIT_ALPHA 0x04
 #define GIT_GLOB_SPECIAL 0x08
 #define GIT_REGEX_SPECIAL 0x10
 #define GIT_PATHSPEC_MAGIC 0x20
+#define GIT_CNTRL 0x01
+#define GIT_PUNCT 0x02
+#define GIT_XDIGIT 0x04
 #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
+#define sane_istest2(x,mask) ((sane_ctype2[(unsigned char)(x)] & (mask)) != 0)
 #define isascii(x) (((x) & ~0x7f) == 0)
 #define isspace(x) sane_istest(x,GIT_SPACE)
 #define isdigit(x) sane_istest(x,GIT_DIGIT)
@@ -527,6 +536,10 @@ 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_istest2(x, GIT_CNTRL)
+#define ispunct(x) sane_istest2(x, GIT_PUNCT)
+#define isxdigit(x) sane_istest2(x, GIT_XDIGIT)
+#define isprint(x) (isalnum(x) || isspace(x) || ispunct(x))
 #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)
-- 
1.7.12.1.406.g6ab07c4
Nguyễn Thái Ngọc Duy· Oct 10, 2012, 10:40 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH v4 03/12] Import wildmatch from rsync

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 <pclouds@gmail.com>
---
 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
Show changes to 3 files +539 −0

t/t3070/wildtest.txt, wildmatch.c, wildmatch.h

diff --git a/t/t3070/wildtest.txt b/t/t3070/wildtest.txt
new file mode 100644
index 0000000..42c1678
--- /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 0000000..f3a1731
--- /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 <rsalz@bbn.com>.
+**
+**  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 0000000..e7f1a35
--- /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);
-- 
1.7.12.1.406.g6ab07c4
Nguyễn Thái Ngọc Duy· Oct 10, 2012, 10:40 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH v4 04/12] wildmatch: remove unnecessary functions

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 wildmatch.c | 161 ++++--------------------------------------------------------
 wildmatch.h |   2 -
 2 files changed, 9 insertions(+), 154 deletions(-)
Show changes to 2 files +9 −154

wildmatch.c, wildmatch.h

diff --git a/wildmatch.c b/wildmatch.c
index f3a1731..71dba76 100644
--- a/wildmatch.c
+++ b/wildmatch.c
@@ -53,33 +53,19 @@
 #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)
+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 +93,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 +205,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 e7f1a35..562faa3 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);
-- 
1.7.12.1.406.g6ab07c4
Michael Haggerty· Oct 10, 2012, 15:38 UTC · re: Nguyễn Thái Ngọc Duy · lore

Re: [PATCH v4 04/12] wildmatch: remove unnecessary functions

On 10/10/2012 12:40 PM, Nguyễn Thái Ngọc Duy wrote:
Show 25 quoted lines
> 
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  wildmatch.c | 161 ++++--------------------------------------------------------
>  wildmatch.h |   2 -
>  2 files changed, 9 insertions(+), 154 deletions(-)
> 
> diff --git a/wildmatch.c b/wildmatch.c
> index f3a1731..71dba76 100644
> --- a/wildmatch.c
> +++ b/wildmatch.c
> @@ -53,33 +53,19 @@
>  #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)
> +static int dowild(const uchar *p, const uchar *text)
The comment still refers to array "a".
Michael
-- 
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/
Nguyễn Thái Ngọc Duy· Oct 10, 2012, 10:40 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH v4 05/12] Integrate wildmatch to git

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 .gitignore           |   1 +
 Makefile             |   3 +
 t/t3070-wildmatch.sh | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++
 t/t3070/wildtest.txt | 165 -----------------------------------------------
 test-wildmatch.c     |  14 ++++
 wildmatch.c          |   5 +-
 6 files changed, 200 insertions(+), 166 deletions(-)
 create mode 100755 t/t3070-wildmatch.sh
 delete mode 100644 t/t3070/wildtest.txt
 create mode 100644 test-wildmatch.c
Show changes to 6 files +200 −166

.gitignore, Makefile, t/t3070-wildmatch.sh, t/t3070/wildtest.txt, test-wildmatch.c, wildmatch.c

diff --git a/.gitignore b/.gitignore
index a188a82..37c3507 100644
--- a/.gitignore
+++ b/.gitignore
@@ -197,6 +197,7 @@
 /test-string-list
 /test-subprocess
 /test-svn-fe
+/test-wildmatch
 /common-cmds.h
 *.tar.gz
 *.dsc
diff --git a/Makefile b/Makefile
index 8413606..9a97379 100644
--- a/Makefile
+++ b/Makefile
@@ -523,6 +523,7 @@ TEST_PROGRAMS_NEED_X += test-sigchain
 TEST_PROGRAMS_NEED_X += test-string-list
 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))
 
@@ -695,6 +696,7 @@ LIB_H += userdiff.h
 LIB_H += utf8.h
 LIB_H += varint.h
 LIB_H += walker.h
+LIB_H += wildmatch.h
 LIB_H += wt-status.h
 LIB_H += xdiff-interface.h
 LIB_H += xdiff/xdiff.h
@@ -826,6 +828,7 @@ LIB_OBJS += utf8.o
 LIB_OBJS += varint.o
 LIB_OBJS += version.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 0000000..bb92f8d
--- /dev/null
+++ b/t/t3070-wildmatch.sh
@@ -0,0 +1,178 @@
+#!/bin/sh
+
+test_description='wildmatch tests'
+
+. ./test-lib.sh
+
+match() {
+    test_expect_success "wildmatch $*" "
+	if [ $1 = 1 ]; then
+	    test-wildmatch wildmatch '$3' '$4'
+	else
+	    ! test-wildmatch wildmatch '$3' '$4'
+	fi &&
+	if [ $2 = 1 ]; then
+	    test-wildmatch fnmatch '$3' '$4'
+	else
+	    ! 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 42c1678..0000000
--- 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 0000000..08962d5
--- /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 71dba76..6092bde 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	'!'
-- 
1.7.12.1.406.g6ab07c4
Johannes Sixt· Oct 12, 2012, 07:27 UTC · re: Nguyễn Thái Ngọc Duy · lore

Re: [PATCH v4 05/12] Integrate wildmatch to git

--- >8 ---
From: Johannes Sixt <j6t@kdbg.org>
Subject: [PATCH] test-wildmatch: avoid exit code -1
Our bash on Windows does not recognize -1 as failure.
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
---
 Please squash this in, in the next round.
 test-wildmatch.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
Show changes to test-wildmatch.c +3 −3
diff --git a/test-wildmatch.c b/test-wildmatch.c
index d716852..74c0864 100644
--- a/test-wildmatch.c
+++ b/test-wildmatch.c
@@ -4,11 +4,11 @@
 int main(int argc, char **argv)
 {
 	if (!strcmp(argv[1], "wildmatch"))
-		return wildmatch(argv[3], argv[2], 0);
+		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_CASEFOLD);
 	else if (!strcmp(argv[1], "fnmatch"))
-		return fnmatch(argv[3], argv[2], FNM_PATHNAME);
+		return !!fnmatch(argv[3], argv[2], FNM_PATHNAME);
 	else
 		return 1;
 }
-- 
1.8.0.rc2.1237.g5522246
Junio C Hamano· Oct 12, 2012, 17:30 UTC · re: Johannes Sixt · lore

Re: [PATCH v4 05/12] Integrate wildmatch to git

Johannes Sixt <j.sixt@viscovery.net> writes:
Show 9 quoted lines
> --- >8 ---
> From: Johannes Sixt <j6t@kdbg.org>
> Subject: [PATCH] test-wildmatch: avoid exit code -1
>
> Our bash on Windows does not recognize -1 as failure.
>
> Signed-off-by: Johannes Sixt <j6t@kdbg.org>
> ---
>  Please squash this in, in the next round.

I do think the patch is good, but I am curious if people who ported bash to windows consider this a bug.

Shouldn't the argument to exit() be truncated to its least significant 8 bits (that is, status & 0377) and be made available to the waiting parent process (which is bash in this case)?

Show 22 quoted lines
>  test-wildmatch.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/test-wildmatch.c b/test-wildmatch.c
> index d716852..74c0864 100644
> --- a/test-wildmatch.c
> +++ b/test-wildmatch.c
> @@ -4,11 +4,11 @@
>  int main(int argc, char **argv)
>  {
>  	if (!strcmp(argv[1], "wildmatch"))
> -		return wildmatch(argv[3], argv[2], 0);
> +		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_CASEFOLD);
>  	else if (!strcmp(argv[1], "fnmatch"))
> -		return fnmatch(argv[3], argv[2], FNM_PATHNAME);
> +		return !!fnmatch(argv[3], argv[2], FNM_PATHNAME);
>  	else
>  		return 1;
>  }
Nguyễn Thái Ngọc Duy· Oct 10, 2012, 10:40 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH v4 06/12] wildmatch: make wildmatch's return value compatible with fnmatch

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 <pclouds@gmail.com>
---
 test-wildmatch.c |  4 ++--
 wildmatch.c      | 21 ++++++++++++---------
 2 files changed, 14 insertions(+), 11 deletions(-)
Show changes to 2 files +14 −11

test-wildmatch.c, wildmatch.c

diff --git a/test-wildmatch.c b/test-wildmatch.c
index 08962d5..596029c 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 6092bde..7c180e3 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
 
@@ -79,12 +82,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 == '*') {
@@ -97,14 +100,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 == '/')
@@ -203,18 +206,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. */
@@ -222,7 +225,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;
 }
-- 
1.7.12.1.406.g6ab07c4
Nguyễn Thái Ngọc Duy· Oct 10, 2012, 10:40 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH v4 07/12] wildmatch: remove static variable force_lower_case

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 <pclouds@gmail.com>
---
 test-wildmatch.c |  4 ++--
 wildmatch.c      | 21 +++++----------------
 wildmatch.h      |  3 +--
 3 files changed, 8 insertions(+), 20 deletions(-)
Show changes to 3 files +8 −20

test-wildmatch.c, wildmatch.c, wildmatch.h

diff --git a/test-wildmatch.c b/test-wildmatch.c
index 596029c..d716852 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 7c180e3..d18d721 100644
--- a/wildmatch.c
+++ b/wildmatch.c
@@ -59,11 +59,9 @@ 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 the a virtually-joined string consisting
  * of "text" and any strings in array "a". */
-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;
 
@@ -107,7 +105,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 == '/')
@@ -215,17 +213,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)
-{
-    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 wildmatch(const char *pattern, const char *text, int flags)
 {
-    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 562faa3..e974f9a 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);
-- 
1.7.12.1.406.g6ab07c4
Nguyễn Thái Ngọc Duy· Oct 10, 2012, 10:40 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH v4 08/12] wildmatch: fix case-insensitive matching

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 <pclouds@gmail.com>
---
 wildmatch.c | 2 ++
 1 file changed, 2 insertions(+)
Show changes to wildmatch.c +2 −0
diff --git a/wildmatch.c b/wildmatch.c
index d18d721..b700631 100644
--- a/wildmatch.c
+++ b/wildmatch.c
@@ -72,6 +72,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
-- 
1.7.12.1.406.g6ab07c4
Nguyễn Thái Ngọc Duy· Oct 10, 2012, 10:40 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH v4 09/12] wildmatch: adjust "**" behavior

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 <pclouds@gmail.com>
---
 t/t3070-wildmatch.sh |  5 +++--
 wildmatch.c          | 13 +++++++------
 wildmatch.h          |  6 ++++++
 3 files changed, 16 insertions(+), 8 deletions(-)
Show changes to 3 files +16 −8

t/t3070-wildmatch.sh, wildmatch.c, wildmatch.h

diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index bb92f8d..f6c64be 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -36,7 +36,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'
@@ -51,7 +51,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 b700631..e09a459 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)
@@ -91,8 +86,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 e974f9a..984a38c 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);
-- 
1.7.12.1.406.g6ab07c4
Nguyễn Thái Ngọc Duy· Oct 10, 2012, 10:40 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH v4 10/12] wildmatch: make /**/ match zero or more directories

"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 <pclouds@gmail.com>
---
 t/t3070-wildmatch.sh |  8 +++++++-
 wildmatch.c          | 17 +++++++++++++++++
 2 files changed, 24 insertions(+), 1 deletion(-)
Show changes to 2 files +24 −1

t/t3070-wildmatch.sh, wildmatch.c

diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index f6c64be..f21da6c 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -53,11 +53,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 1 '/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 e09a459..f060bb0 100644
--- a/wildmatch.c
+++ b/wildmatch.c
@@ -91,6 +91,23 @@ 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
+			 * <star star slash>, 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.
+			 *
+			 * Crazy patterns like /<*><*>/<*><*>/ are
+			 * treated like /<*><*>/. But undefined
+			 * behavior is even appropriate for people
+			 * writing such a pattern.
+			 */
+			if (p[0] == '/' &&
+			    dowild(p + 1, text, force_lower_case) == MATCH)
+				return MATCH;
 		    special = TRUE;
 		} else
 		    return ABORT_MALFORMED;
-- 
1.7.12.1.406.g6ab07c4
Nguyễn Thái Ngọc Duy· Oct 10, 2012, 10:40 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH v4 11/12] Support "**" wildcard in .gitignore and .gitattributes

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/gitattributes.txt    |  6 ++++++
 Documentation/gitignore.txt        | 19 +++++++++++++++++++
 attr.c                             |  4 +++-
 dir.c                              |  4 +++-
 t/t0003-attributes.sh              | 38 ++++++++++++++++++++++++++++++++++++++
 t/t3001-ls-files-others-exclude.sh | 19 +++++++++++++++++++
 6 files changed, 88 insertions(+), 2 deletions(-)
Show changes to 6 files +88 −2

Documentation/gitattributes.txt, Documentation/gitignore.txt, attr.c, dir.c, t/t0003-attributes.sh, t/t3001-ls-files-others-exclude.sh

diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 99ed04d..93c542b 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -57,6 +57,12 @@ overrides an earlier line.  This overriding is done per
 attribute.  The rules how the pattern matches paths are the
 same as in `.gitignore` files; see linkgit:gitignore[5].
 
+Note that if a .gitignore rule matches a directory, the directory
+is ignored, which may be seen as "ignore" attribute propagated to
+to all files and directories inside. If a .gitattributes rule
+matches a directory, it manipulates attributes on that directory
+only, not files and directories inside.
+
 When deciding what attributes are assigned to a path, git
 consults `$GIT_DIR/info/attributes` file (which has the highest
 precedence), `.gitattributes` file in the same directory as the
diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt
index 96639e0..b564b4b 100644
--- a/Documentation/gitignore.txt
+++ b/Documentation/gitignore.txt
@@ -104,6 +104,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/attr.c b/attr.c
index 887a9ae..8010429 100644
--- a/attr.c
+++ b/attr.c
@@ -12,6 +12,7 @@
 #include "exec_cmd.h"
 #include "attr.h"
 #include "dir.h"
+#include "wildmatch.h"
 
 const char git_attr__true[] = "(builtin)true";
 const char git_attr__false[] = "\0(builtin)false";
@@ -666,7 +667,8 @@ static int path_matches(const char *pathname, int pathlen,
 		return 0;
 	if (baselen != 0)
 		baselen++;
-	return fnmatch_icase(pattern, pathname + baselen, FNM_PATHNAME) == 0;
+	return wildmatch(pattern, pathname + baselen,
+			 ignore_case ? FNM_CASEFOLD : 0) == 0;
 }
 
 static int macroexpand_one(int attr_nr, int rem);
diff --git a/dir.c b/dir.c
index 4868339..442db1c 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;
@@ -575,7 +576,8 @@ int excluded_from_list(const char *pathname,
 			namelen -= prefix;
 		}
 
-		if (!namelen || !fnmatch_icase(exclude, name, FNM_PATHNAME))
+		if (!namelen ||
+		    wildmatch(exclude, name, ignore_case ? FNM_CASEFOLD : 0) == 0)
 			return to_exclude;
 	}
 	return -1; /* undecided */
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index febc45c..67a5694 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -232,4 +232,42 @@ test_expect_success 'bare repository: test info/attributes' '
 	attr_check subdir/a/i unspecified
 '
 
+test_expect_success '"**" test' '
+	cd .. &&
+	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_done
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index c8fe978..278315d 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -214,4 +214,23 @@ test_expect_success 'subdirectory ignore (l1)' '
 	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
-- 
1.7.12.1.406.g6ab07c4
Nguyễn Thái Ngọc Duy· Oct 10, 2012, 10:40 UTC · re: Nguyễn Thái Ngọc Duy · lore

[PATCH v4 12/12] t3070: disable two fnmatch tests that have different results on different libc

fnmatch on glibc-2.12.1 returns no match. glibc-2.15 returns ok.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/t3070-wildmatch.sh | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
Show changes to t/t3070-wildmatch.sh +4 −2
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index f21da6c..38042eb 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -13,6 +13,8 @@ match() {
 	fi &&
 	if [ $2 = 1 ]; then
 	    test-wildmatch fnmatch '$3' '$4'
+	elif [ $2 = x ]; then
+	    true
 	else
 	    ! test-wildmatch fnmatch '$3' '$4'
 	fi
@@ -129,8 +131,8 @@ 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 x 'a[]b' 'a[]b'
+match 0 x 'ab[' 'ab['
 match 0 0 'ab' '[!'
 match 0 0 'ab' '[-'
 match 1 1 '-' '[-]'
-- 
1.7.12.1.406.g6ab07c4
Johannes Sixt· Oct 12, 2012, 07:22 UTC · re: Nguyễn Thái Ngọc Duy · lore

Re: [PATCH v4 12/12] t3070: disable two fnmatch tests that have different results on different libc

Am 10/10/2012 12:40, schrieb Nguyễn Thái Ngọc Duy:
> fnmatch on glibc-2.12.1 returns no match. glibc-2.15 returns ok.

There are many more cases that fail with the fnmatch() that we ship in compat/fnmatch. To test this on Linux, you have to remove the "#if defined _LIBC || !defined __GNU_LIBRARY__" brackets from compat/fnmatch/fnmatch.c and build with NO_FNMATCH.

-- Hannes
Nguyen Thai Ngoc Duy· Oct 12, 2012, 09:49 UTC · re: Johannes Sixt · lore

Re: [PATCH v4 12/12] t3070: disable two fnmatch tests that have different results on different libc

On Fri, Oct 12, 2012 at 2:22 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:
Show 7 quoted lines
> Am 10/10/2012 12:40, schrieb Nguyễn Thái Ngọc Duy:
>> fnmatch on glibc-2.12.1 returns no match. glibc-2.15 returns ok.
>
> There are many more cases that fail with the fnmatch() that we ship in
> compat/fnmatch. To test this on Linux, you have to remove the "#if defined
> _LIBC || !defined __GNU_LIBRARY__" brackets from compat/fnmatch/fnmatch.c
> and build with NO_FNMATCH.

Thanks. The point of checking against fnmatch is to make sure that wildmatch does not divert in behavior from fnmatch. For some corner cases that behavior is undefined, I think it is ok for different fnmatch versions to behave differently.

But some of failed tests make me worry about fnmatch. compat/fnmatch for example does not match '**/foo' with '/foo' (it does '*/foo' with '/foo'). 'A' is matched with '[[:digit:][:space:][:upper:]]' but not '[[:digit:][:upper:][:space:]]'. Perhaps we better off convert git to use wildmatch only to keep matching behavior more reliable. wildmatch does not support non-FNM_PATHNAME mode, but that should be easy to fix. The only downside I see is fnmatch may support locale while wildmatch does not.

-- 
Duy
Junio C Hamano· Oct 10, 2012, 23:48 UTC · re: Nguyễn Thái Ngọc Duy · lore

Re: [PATCH v4 00/12] Wildmatch v4

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
> Really small updates. I did not want to resend it this soon but this
> may fix the compile errors for Junio.
Thanks; queued and pushed out on 'pu'.  Comments later.
Junio C Hamano· Oct 11, 2012, 04:33 UTC · re: Junio C Hamano · lore

Re: [PATCH v4 00/12] Wildmatch v4

Junio C Hamano <gitster@pobox.com> writes:
> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
>
>> Really small updates. I did not want to resend it this soon but this
>> may fix the compile errors for Junio.
t3070 seems to break TAP,
    *** prove ***
    t3070-wildmatch.sh .. Failed 1/151 subtests 
    Test Summary Report
    -------------------
    t3070-wildmatch.sh (Wstat: 0 Tests: 150 Failed: 0)
      Parse errors: Tests out of sequence.  Found (76) but expected (75)
                    Tests out of sequence.  Found (77) but expected (76)
                    Tests out of sequence.  Found (78) but expected (77)
                    Tests out of sequence.  Found (79) but expected (78)
                    Tests out of sequence.  Found (80) but expected (79)
    Displayed the first 5 of 77 TAP syntax errors.
This probably is due to this part of the output:
    ok 72 - wildmatch 1 1 [ab] [\[:]ab]
    ok 73 - wildmatch 1 1 ?a?b \??\?b
    ok 74 - wildmatch 1 1 abc ^G^Hok 75 - wildmatch 0 0 foo 
    ok 76 - wildmatch 1 0 foo/bar/baz/to **/t[o]
    ok 77 - wildmatch 1 1 a1B [[:alpha:]][[:digit:]][[:upper:]]
Nguyen Thai Ngoc Duy· Oct 11, 2012, 11:56 UTC · re: Junio C Hamano · lore

Re: [PATCH v4 00/12] Wildmatch v4

On Thu, Oct 11, 2012 at 11:33 AM, Junio C Hamano <gitster@pobox.com> wrote:
Show 30 quoted lines
> Junio C Hamano <gitster@pobox.com> writes:
>
>> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
>>
>>> Really small updates. I did not want to resend it this soon but this
>>> may fix the compile errors for Junio.
>
> t3070 seems to break TAP,
>
>     *** prove ***
>     t3070-wildmatch.sh .. Failed 1/151 subtests
>
>     Test Summary Report
>     -------------------
>     t3070-wildmatch.sh (Wstat: 0 Tests: 150 Failed: 0)
>       Parse errors: Tests out of sequence.  Found (76) but expected (75)
>                     Tests out of sequence.  Found (77) but expected (76)
>                     Tests out of sequence.  Found (78) but expected (77)
>                     Tests out of sequence.  Found (79) but expected (78)
>                     Tests out of sequence.  Found (80) but expected (79)
>     Displayed the first 5 of 77 TAP syntax errors.
>
> This probably is due to this part of the output:
>
>     ok 72 - wildmatch 1 1 [ab] [\[:]ab]
>     ok 73 - wildmatch 1 1 ?a?b \??\?b
>     ok 74 - wildmatch 1 1 abc ^G^Hok 75 - wildmatch 0 0 foo
>     ok 76 - wildmatch 1 0 foo/bar/baz/to **/t[o]
>     ok 77 - wildmatch 1 1 a1B [[:alpha:]][[:digit:]][[:upper:]]
>

It seems to prove fine here with perl 5.12.3, Test-Harness-3.230, bash-4.0_p38. What version do you use?

-- 
Duy
Junio C Hamano· Oct 11, 2012, 16:09 UTC · re: Nguyen Thai Ngoc Duy · lore

Re: [PATCH v4 00/12] Wildmatch v4

Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:
Show 34 quoted lines
> On Thu, Oct 11, 2012 at 11:33 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> Junio C Hamano <gitster@pobox.com> writes:
>>
>>> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
>>>
>>>> Really small updates. I did not want to resend it this soon but this
>>>> may fix the compile errors for Junio.
>>
>> t3070 seems to break TAP,
>>
>>     *** prove ***
>>     t3070-wildmatch.sh .. Failed 1/151 subtests
>>
>>     Test Summary Report
>>     -------------------
>>     t3070-wildmatch.sh (Wstat: 0 Tests: 150 Failed: 0)
>>       Parse errors: Tests out of sequence.  Found (76) but expected (75)
>>                     Tests out of sequence.  Found (77) but expected (76)
>>                     Tests out of sequence.  Found (78) but expected (77)
>>                     Tests out of sequence.  Found (79) but expected (78)
>>                     Tests out of sequence.  Found (80) but expected (79)
>>     Displayed the first 5 of 77 TAP syntax errors.
>>
>> This probably is due to this part of the output:
>>
>>     ok 72 - wildmatch 1 1 [ab] [\[:]ab]
>>     ok 73 - wildmatch 1 1 ?a?b \??\?b
>>     ok 74 - wildmatch 1 1 abc ^G^Hok 75 - wildmatch 0 0 foo
>>     ok 76 - wildmatch 1 0 foo/bar/baz/to **/t[o]
>>     ok 77 - wildmatch 1 1 a1B [[:alpha:]][[:digit:]][[:upper:]]
>>
>
> It seems to prove fine here with perl 5.12.3, Test-Harness-3.230,
> bash-4.0_p38. What version do you use?

I seem to have Perl 5.10.1, Test::Harness 3.17, Dash 0.5.5.1. But as you can see above, what is _fed_ to the Perl-side is broken (notice the concatenation at the end of "ok 74"), so I do not think versions of Perl-side matters.

I suspect that this is the immediate culprit:
    match 1 1 'abc' '\a\b\c'

The symptom looks like that somebody is interpreting \a as BEL, \b as backspace, etc. when showing the "ok ..." line, no?

Junio C Hamano· Oct 11, 2012, 17:09 UTC · re: Junio C Hamano · lore

Re: [PATCH v4 00/12] Wildmatch v4

Junio C Hamano <gitster@pobox.com> writes:
Show 15 quoted lines
>>> This probably is due to this part of the output:
>>>
>>>     ok 72 - wildmatch 1 1 [ab] [\[:]ab]
>>>     ok 73 - wildmatch 1 1 ?a?b \??\?b
>>>     ok 74 - wildmatch 1 1 abc ^G^Hok 75 - wildmatch 0 0 foo
>>>     ok 76 - wildmatch 1 0 foo/bar/baz/to **/t[o]
>>>     ok 77 - wildmatch 1 1 a1B [[:alpha:]][[:digit:]][[:upper:]]
>> ...
>
> I suspect that this is the immediate culprit:
>
>     match 1 1 'abc' '\a\b\c'
>
> The symptom looks like that somebody is interpreting \a as BEL, \b
> as backspace, etc. when showing the "ok ..." line, no?
Ahh, it must be this bits from t/test-lib.sh
 t/test-lib.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
Show changes to diff +1 −1
diff --git i/t/test-lib.sh w/t/test-lib.sh
index 514282c..489bc80 100644
--- i/t/test-lib.sh
+++ w/t/test-lib.sh
@@ -230,7 +230,7 @@ else
 	say_color() {
 		test -z "$1" && test -n "$quiet" && return
 		shift
-		echo "$*"
+		printf "%s\n" "$*"
 	}
 fi
 
Junio C Hamano· Oct 11, 2012, 23:50 UTC · re: Junio C Hamano · lore

Re: [PATCH v4 00/12] Wildmatch v4

Junio C Hamano <gitster@pobox.com> writes:
Show 17 quoted lines
> Ahh, it must be this bits from t/test-lib.sh
>
>  t/test-lib.sh | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git i/t/test-lib.sh w/t/test-lib.sh
> index 514282c..489bc80 100644
> --- i/t/test-lib.sh
> +++ w/t/test-lib.sh
> @@ -230,7 +230,7 @@ else
>  	say_color() {
>  		test -z "$1" && test -n "$quiet" && return
>  		shift
> -		echo "$*"
> +		printf "%s\n" "$*"
>  	}
>  fi
I'll queue this fix separately before your series on 'pu'.
-- >8 --
Subject: [PATCH] test-lib: Fix say_color () not to interpret \a\b\c in the message

When running with color disabled (e.g. under prove to produce TAP output), say_color() helper function is defined to use echo to show the message. With a message that ends with "\c", echo is allowed to interpret it as "Do not end the line with LF".

Use printf "%s\n" to emit the message literally.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 t/test-lib.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
Show changes to t/test-lib.sh +1 −1
diff --git a/t/test-lib.sh b/t/test-lib.sh
index c0d04c4..280b3aa 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -169,7 +169,7 @@ else
 	say_color() {
 		test -z "$1" && test -n "$quiet" && return
 		shift
-		echo "$*"
+		printf "%s\n" "$*"
 	}
 fi
 
-- 
1.8.0.rc1.82.ga68bb49
Torsten Bögershausen· Oct 12, 2012, 16:44 UTC · re: Nguyen Thai Ngoc Duy · lore

Re: [PATCH v4 00/12] Wildmatch v4

On 11.10.12 13:56, Nguyen Thai Ngoc Duy wrote:
Show 35 quoted lines
> On Thu, Oct 11, 2012 at 11:33 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> Junio C Hamano <gitster@pobox.com> writes:
>>
>>> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
>>>
>>>> Really small updates. I did not want to resend it this soon but this
>>>> may fix the compile errors for Junio.
>>
>> t3070 seems to break TAP,
>>
>>     *** prove ***
>>     t3070-wildmatch.sh .. Failed 1/151 subtests
>>
>>     Test Summary Report
>>     -------------------
>>     t3070-wildmatch.sh (Wstat: 0 Tests: 150 Failed: 0)
>>       Parse errors: Tests out of sequence.  Found (76) but expected (75)
>>                     Tests out of sequence.  Found (77) but expected (76)
>>                     Tests out of sequence.  Found (78) but expected (77)
>>                     Tests out of sequence.  Found (79) but expected (78)
>>                     Tests out of sequence.  Found (80) but expected (79)
>>     Displayed the first 5 of 77 TAP syntax errors.
>>
>> This probably is due to this part of the output:
>>
>>     ok 72 - wildmatch 1 1 [ab] [\[:]ab]
>>     ok 73 - wildmatch 1 1 ?a?b \??\?b
>>     ok 74 - wildmatch 1 1 abc ^G^Hok 75 - wildmatch 0 0 foo
>>     ok 76 - wildmatch 1 0 foo/bar/baz/to **/t[o]
>>     ok 77 - wildmatch 1 1 a1B [[:alpha:]][[:digit:]][[:upper:]]
>>
> 
> It seems to prove fine here with perl 5.12.3, Test-Harness-3.230,
> bash-4.0_p38. What version do you use?
> 
Some problems even here (Mac OS) commit 2aeb6d4d7884f4c4425

not ok 61 - wildmatch 0 0 \ \ not ok 62 - wildmatch 0 0 /\ */\ not ok 69 - wildmatch 1 1 [ab] [[:]ab] not ok 71 - wildmatch 1 1 [ab] [[:digit]ab] not ok 80 - wildmatch 1 0 1 [[:digit:][:upper:][:space:]] not ok 81 - wildmatch 0 0 1 [[:digit:][:upper:][:spaci:]] not ok 88 - wildmatch 1 0 _ [[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]] not ok 89 - wildmatch 1 0 _ [[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]] not ok 93 - wildmatch 1 1 y [a-c[:digit:]x-z]

I have 2 perls, I'm not sure which versin is used by git these days.
 which perl
/opt/local/bin/perl
tb@birne:~/projects/git/git.pu/t> perl --version
This is perl, v5.8.9 built for darwin-2level

---------------- /usr/bin/perl --version

This is perl, v5.10.0 built for darwin-thread-multi-2level (with 2 registered patches, see perl -V for more detail) ----------

If I can do more debugging/testing please let me know /Torsten

Junio C Hamano· Oct 12, 2012, 17:05 UTC · re: Torsten Bögershausen · lore

Re: [PATCH v4 00/12] Wildmatch v4

Torsten Bögershausen <tboegi@web.de> writes:
Show 49 quoted lines
> On 11.10.12 13:56, Nguyen Thai Ngoc Duy wrote:
>> On Thu, Oct 11, 2012 at 11:33 AM, Junio C Hamano <gitster@pobox.com> wrote:
>>> Junio C Hamano <gitster@pobox.com> writes:
>>>
>>>> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:
>>>>
>>>>> Really small updates. I did not want to resend it this soon but this
>>>>> may fix the compile errors for Junio.
>>>
>>> t3070 seems to break TAP,
>>>
>>>     *** prove ***
>>>     t3070-wildmatch.sh .. Failed 1/151 subtests
>>>
>>>     Test Summary Report
>>>     -------------------
>>>     t3070-wildmatch.sh (Wstat: 0 Tests: 150 Failed: 0)
>>>       Parse errors: Tests out of sequence.  Found (76) but expected (75)
>>>                     Tests out of sequence.  Found (77) but expected (76)
>>>                     Tests out of sequence.  Found (78) but expected (77)
>>>                     Tests out of sequence.  Found (79) but expected (78)
>>>                     Tests out of sequence.  Found (80) but expected (79)
>>>     Displayed the first 5 of 77 TAP syntax errors.
>>>
>>> This probably is due to this part of the output:
>>>
>>>     ok 72 - wildmatch 1 1 [ab] [\[:]ab]
>>>     ok 73 - wildmatch 1 1 ?a?b \??\?b
>>>     ok 74 - wildmatch 1 1 abc ^G^Hok 75 - wildmatch 0 0 foo
>>>     ok 76 - wildmatch 1 0 foo/bar/baz/to **/t[o]
>>>     ok 77 - wildmatch 1 1 a1B [[:alpha:]][[:digit:]][[:upper:]]
>>>
>> 
>> It seems to prove fine here with perl 5.12.3, Test-Harness-3.230,
>> bash-4.0_p38. What version do you use?
>> 
> Some problems even here (Mac OS) commit 2aeb6d4d7884f4c4425
>
> not ok 61 - wildmatch 0 0 \ \
> not ok 62 - wildmatch 0 0 /\ */\
> not ok 69 - wildmatch 1 1 [ab] [[:]ab]
> not ok 71 - wildmatch 1 1 [ab] [[:digit]ab]
> not ok 80 - wildmatch 1 0 1 [[:digit:][:upper:][:space:]]
> not ok 81 - wildmatch 0 0 1 [[:digit:][:upper:][:spaci:]]
> not ok 88 - wildmatch 1 0 _ [[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]
> not ok 89 - wildmatch 1 0 _ [[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]
> not ok 93 - wildmatch 1 1 y [a-c[:digit:]x-z]
>
> I have 2 perls,...

The problem under discussion was that the output from passing tests were mangled and not interpreted correctly by a program that analyzes them, which happens to be written in Perl. Even in that context, it was pointless to ask for Perl versions, as it was clear that the input to that Perl-written program, i.e. output from the tests, were not formatted correctly to say "ok NN message".

If you are getting "not ok", I do not think you need to look at your Perl. The wildmatch tests are failing, and it is far more likely that the wildmatch code is failing in your environment, than the test harness that runs wildmatch code is failing.

Neither the wildmatch code itself (in C), nor the test harness that is giving you the above "not ok" (in shell) depends on Perl.

Thanks for a report, but the debugging needs to see how the matching code in mildmatch misbehaves.

I suspect it is another case where the platform fnmatch (from MacOS) is not behaving as these tests expect.

Nguyen, how about updating the match () shell function in 3070 so that it not just says not-ok, but indicates what failed (wildmatch failed, or wildmatch passed but fnmatch failed), at least when the test is run as "./t3070-*.sh -v -i"?

Torsten Bögershausen· Oct 12, 2012, 20:28 UTC · re: Junio C Hamano · lore

Re: [PATCH v4 00/12] Wildmatch v4

On 10/12/2012 07:05 PM, Junio C Hamano wrote:
Show 11 quoted lines
>> Some problems even here (Mac OS) commit 2aeb6d4d7884f4c4425
>>
>> not ok 61 - wildmatch 0 0 \ \
>> not ok 62 - wildmatch 0 0 /\ */\
>> not ok 69 - wildmatch 1 1 [ab] [[:]ab]
>> not ok 71 - wildmatch 1 1 [ab] [[:digit]ab]
>> not ok 80 - wildmatch 1 0 1 [[:digit:][:upper:][:space:]]
>> not ok 81 - wildmatch 0 0 1 [[:digit:][:upper:][:spaci:]]
>> not ok 88 - wildmatch 1 0 _ [[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]
>> not ok 89 - wildmatch 1 0 _ [[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]
>> not ok 93 - wildmatch 1 1 y [a-c[:digit:]x-z]
> Nguyen, how about updating the match () shell function in 3070 so
> that it not just says not-ok, but indicates what failed (wildmatch
> failed, or wildmatch passed but fnmatch failed), at least when the
> test is run as "./t3070-*.sh -v -i"?

It seams to be the fnmatch, since removing the fnmatch makes t3070 pass.

However, pullling in compat/fnmatch in the Makefile
  ifeq ($(uname_S),Darwin)
+       NO_FNMATCH = YesPlease
================
gives this result:
  ./t3070-wildmatch.sh 2>&1 | grep "not ok"
not ok 23 - wildmatch 1 1 a]b a[]]b
not ok 25 - wildmatch 1 1 a]b a[]-]b
not ok 43 - wildmatch 1 1 /foo **/foo
not ok 79 - wildmatch 1 1 A [[:digit:][:upper:][:space:]]
not ok 84 - wildmatch 1 1 . [[:digit:][:punct:][:space:]]
not ok 85 - wildmatch 1 1 5 [[:xdigit:]]
not ok 86 - wildmatch 1 1 f [[:xdigit:]]
not ok 87 - wildmatch 1 1 D [[:xdigit:]]
not ok 90 - wildmatch 1 1 . 
[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]
not ok 92 - wildmatch 1 1 b [a-c[:digit:]x-z]
not ok 95 - wildmatch 1 1 ] [\\-^]
not ok 98 - wildmatch 1 1 ] [\]]
(Confused)
Nguyen Thai Ngoc Duy· Oct 13, 2012, 03:49 UTC · re: Junio C Hamano · lore

Re: [PATCH v4 00/12] Wildmatch v4

On Fri, Oct 12, 2012 at 10:05:06AM -0700, Junio C Hamano wrote:
> Nguyen, how about updating the match () shell function in 3070 so
> that it not just says not-ok, but indicates what failed (wildmatch
> failed, or wildmatch passed but fnmatch failed), at least when the
> test is run as "./t3070-*.sh -v -i"?

You could squash this to the "Integrate wildmatch to git" patch, or just put it at the end of the series (I'll need to send a series update anyway). This splits fnmatch and wildmatch tests separately so we can easily identify which one fails.

-- 8< --
Show changes to t/t3070-wildmatch.sh +18 −11
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index c3ee729..4f97923 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -5,20 +5,28 @@ test_description='wildmatch tests'
 . ./test-lib.sh
 
 match() {
-    test_expect_success "wildmatch $*" "
-	if [ $1 = 1 ]; then
+    if [ $1 = 1 ]; then
+	test_expect_success "wildmatch:    match $3 $4" "
 	    test-wildmatch wildmatch '$3' '$4'
-	else
+	"
+    else
+	test_expect_success "wildmatch: no match $3 $4" "
 	    ! test-wildmatch wildmatch '$3' '$4'
-	fi &&
-	if [ $2 = 1 ]; then
+	"
+    fi
+    if [ $2 = 1 ]; then
+	test_expect_success "fnmatch:      match $3 $4" "
 	    test-wildmatch fnmatch '$3' '$4'
-	elif [ $2 = x ]; then
-	    true
-	else
+	"
+    elif [ $2 = 0 ]; then
+	test_expect_success "fnmatch:   no match $3 $4" "
 	    ! test-wildmatch fnmatch '$3' '$4'
-	fi
-    "
+	"
+#    else
+#	test_expect_success BROKEN_FNMATCH "fnmatch:       $3 $4" "
+#	    test-wildmatch fnmatch '$3' '$4'
+#	"
+    fi
 }
 
 # Basic wildmat features
-- 8< --
-- 
Duy

← back to recent threads