{"thread":{"id":"31539","subject":"[PATCH 0/5] Support matching \"**\" in .gitattributes and .gitignore","startedAt":"2012-09-15T12:01:59Z","lastAt":"2012-09-25T07:01:55Z","messageCount":34,"participants":["Nguyễn Thái Ngọc Duy","Philip Oakley","Junio C Hamano","Nguyen Thai Ngoc Duy","Jeff King","Wesley J. Landaker","Jan Engelhardt","Johannes Sixt"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"199093","messageId":"1347710524-15404-1-git-send-email-pclouds@gmail.com","threadId":"31539","inReplyTo":null,"subject":"[PATCH 0/5] Support matching \"**\" in .gitattributes and .gitignore","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-15T12:01:59Z","receivedAt":"2012-09-15T12:01:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"A while back I posted a proof of concept using wildmatch() from rsync.\nBack then the obstacle was case-insensitive matching support. I did not\nrealize that there was iwildmatch() that does exactly that.\n\nSo here again a series that is probably ready for consumption. Now\npatterns that contain slashes will always go through (i)wildmatch().\nfnmatch() is only used for basename matching.\n\nNguyễn Thái Ngọc Duy (5):\n  Import wildmatch from rsync\n  compat/wildmatch: remove static variable force_lower_case\n  compat/wildmatch: fix case-insensitive matching\n  Integrate wildmatch to git\n  Support \"**\" in .gitignore and .gitattributes patterns using\n    wildmatch()\n\n Documentation/gitignore.txt    |   3 +\n Makefile                       |   6 +\n attr.c                         |   4 +-\n compat/wildmatch.c             | 373 +++++++++++++++++++++++++++++++++++++++++\n compat/wildmatch.h             |   6 +\n dir.c                          |   5 +-\n t/t3070-wildmatch.sh           |  27 +++\n t/t3070-wildmatch/wildtest.txt | 165 ++++++++++++++++++\n test-wildmatch.c               | 228 +++++++++++++++++++++++++\n 9 files changed, 815 insertions(+), 2 deletions(-)\n create mode 100644 compat/wildmatch.c\n create mode 100644 compat/wildmatch.h\n create mode 100755 t/t3070-wildmatch.sh\n create mode 100644 t/t3070-wildmatch/wildtest.txt\n create mode 100644 test-wildmatch.c\n\n-- \n1.7.12.403.gce5cf6f.dirty\n"},{"id":"199098","messageId":"1347710524-15404-2-git-send-email-pclouds@gmail.com","threadId":"31539","inReplyTo":"1347710524-15404-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 1/5] Import wildmatch from rsync","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-15T12:02:00Z","receivedAt":"2012-09-15T12:02:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"These files are from rsync.git commit\nd51a3adb4fca3e6b1b046c6e570828f3bca8fe36. The commit is GPL-3. However\nwildmatch.[ch] have not changed since rsync turned to GPL-3.\n\nrsync.git           ->  git.git\nlib/wildmatch.[ch]      compat/wildmatch.[ch]\nwildtest.c              test-wildmatch.c\nwildtest.txt            t/t3070-wildmatch/wildtest.txt\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n compat/wildmatch.c             | 368 +++++++++++++++++++++++++++++++++++++++++\n compat/wildmatch.h             |   6 +\n t/t3070-wildmatch/wildtest.txt | 165 ++++++++++++++++++\n test-wildmatch.c               | 222 +++++++++++++++++++++++++\n 4 files changed, 761 insertions(+)\n create mode 100644 compat/wildmatch.c\n create mode 100644 compat/wildmatch.h\n create mode 100644 t/t3070-wildmatch/wildtest.txt\n create mode 100644 test-wildmatch.c\n\ndiff --git a/compat/wildmatch.c b/compat/wildmatch.c\nnew file mode 100644\nindex 0000000..f3a1731\n--- /dev/null\n+++ b/compat/wildmatch.c\n@@ -0,0 +1,368 @@\n+/*\n+**  Do shell-style pattern matching for ?, \\, [], and * characters.\n+**  It is 8bit clean.\n+**\n+**  Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.\n+**  Rich $alz is now <rsalz@bbn.com>.\n+**\n+**  Modified by Wayne Davison to special-case '/' matching, to make '**'\n+**  work differently than '*', and to fix the character-class code.\n+*/\n+\n+#include \"rsync.h\"\n+\n+/* What character marks an inverted character class? */\n+#define NEGATE_CLASS\t'!'\n+#define NEGATE_CLASS2\t'^'\n+\n+#define FALSE 0\n+#define TRUE 1\n+#define ABORT_ALL -1\n+#define ABORT_TO_STARSTAR -2\n+\n+#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \\\n+\t\t\t\t    && *(class) == *(litmatch) \\\n+\t\t\t\t    && strncmp((char*)class, litmatch, len) == 0)\n+\n+#if defined STDC_HEADERS || !defined isascii\n+# define ISASCII(c) 1\n+#else\n+# define ISASCII(c) isascii(c)\n+#endif\n+\n+#ifdef isblank\n+# define ISBLANK(c) (ISASCII(c) && isblank(c))\n+#else\n+# define ISBLANK(c) ((c) == ' ' || (c) == '\\t')\n+#endif\n+\n+#ifdef isgraph\n+# define ISGRAPH(c) (ISASCII(c) && isgraph(c))\n+#else\n+# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))\n+#endif\n+\n+#define ISPRINT(c) (ISASCII(c) && isprint(c))\n+#define ISDIGIT(c) (ISASCII(c) && isdigit(c))\n+#define ISALNUM(c) (ISASCII(c) && isalnum(c))\n+#define ISALPHA(c) (ISASCII(c) && isalpha(c))\n+#define ISCNTRL(c) (ISASCII(c) && iscntrl(c))\n+#define ISLOWER(c) (ISASCII(c) && islower(c))\n+#define ISPUNCT(c) (ISASCII(c) && ispunct(c))\n+#define ISSPACE(c) (ISASCII(c) && isspace(c))\n+#define ISUPPER(c) (ISASCII(c) && isupper(c))\n+#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))\n+\n+#ifdef WILD_TEST_ITERATIONS\n+int wildmatch_iteration_count;\n+#endif\n+\n+static int force_lower_case = 0;\n+\n+/* Match pattern \"p\" against the a virtually-joined string consisting\n+ * of \"text\" and any strings in array \"a\". */\n+static int dowild(const uchar *p, const uchar *text, const uchar*const *a)\n+{\n+    uchar p_ch;\n+\n+#ifdef WILD_TEST_ITERATIONS\n+    wildmatch_iteration_count++;\n+#endif\n+\n+    for ( ; (p_ch = *p) != '\\0'; text++, p++) {\n+\tint matched, special;\n+\tuchar t_ch, prev_ch;\n+\twhile ((t_ch = *text) == '\\0') {\n+\t    if (*a == NULL) {\n+\t\tif (p_ch != '*')\n+\t\t    return ABORT_ALL;\n+\t\tbreak;\n+\t    }\n+\t    text = *a++;\n+\t}\n+\tif (force_lower_case && ISUPPER(t_ch))\n+\t    t_ch = tolower(t_ch);\n+\tswitch (p_ch) {\n+\t  case '\\\\':\n+\t    /* Literal match with following character.  Note that the test\n+\t     * in \"default\" handles the p[1] == '\\0' failure case. */\n+\t    p_ch = *++p;\n+\t    /* FALLTHROUGH */\n+\t  default:\n+\t    if (t_ch != p_ch)\n+\t\treturn FALSE;\n+\t    continue;\n+\t  case '?':\n+\t    /* Match anything but '/'. */\n+\t    if (t_ch == '/')\n+\t\treturn FALSE;\n+\t    continue;\n+\t  case '*':\n+\t    if (*++p == '*') {\n+\t\twhile (*++p == '*') {}\n+\t\tspecial = TRUE;\n+\t    } else\n+\t\tspecial = FALSE;\n+\t    if (*p == '\\0') {\n+\t\t/* Trailing \"**\" matches everything.  Trailing \"*\" matches\n+\t\t * only if there are no more slash characters. */\n+\t\tif (!special) {\n+\t\t    do {\n+\t\t\tif (strchr((char*)text, '/') != NULL)\n+\t\t\t    return FALSE;\n+\t\t    } while ((text = *a++) != NULL);\n+\t\t}\n+\t\treturn TRUE;\n+\t    }\n+\t    while (1) {\n+\t\tif (t_ch == '\\0') {\n+\t\t    if ((text = *a++) == NULL)\n+\t\t\tbreak;\n+\t\t    t_ch = *text;\n+\t\t    continue;\n+\t\t}\n+\t\tif ((matched = dowild(p, text, a)) != FALSE) {\n+\t\t    if (!special || matched != ABORT_TO_STARSTAR)\n+\t\t\treturn matched;\n+\t\t} else if (!special && t_ch == '/')\n+\t\t    return ABORT_TO_STARSTAR;\n+\t\tt_ch = *++text;\n+\t    }\n+\t    return ABORT_ALL;\n+\t  case '[':\n+\t    p_ch = *++p;\n+#ifdef NEGATE_CLASS2\n+\t    if (p_ch == NEGATE_CLASS2)\n+\t\tp_ch = NEGATE_CLASS;\n+#endif\n+\t    /* Assign literal TRUE/FALSE because of \"matched\" comparison. */\n+\t    special = p_ch == NEGATE_CLASS? TRUE : FALSE;\n+\t    if (special) {\n+\t\t/* Inverted character class. */\n+\t\tp_ch = *++p;\n+\t    }\n+\t    prev_ch = 0;\n+\t    matched = FALSE;\n+\t    do {\n+\t\tif (!p_ch)\n+\t\t    return ABORT_ALL;\n+\t\tif (p_ch == '\\\\') {\n+\t\t    p_ch = *++p;\n+\t\t    if (!p_ch)\n+\t\t\treturn ABORT_ALL;\n+\t\t    if (t_ch == p_ch)\n+\t\t\tmatched = TRUE;\n+\t\t} else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {\n+\t\t    p_ch = *++p;\n+\t\t    if (p_ch == '\\\\') {\n+\t\t\tp_ch = *++p;\n+\t\t\tif (!p_ch)\n+\t\t\t    return ABORT_ALL;\n+\t\t    }\n+\t\t    if (t_ch <= p_ch && t_ch >= prev_ch)\n+\t\t\tmatched = TRUE;\n+\t\t    p_ch = 0; /* This makes \"prev_ch\" get set to 0. */\n+\t\t} else if (p_ch == '[' && p[1] == ':') {\n+\t\t    const uchar *s;\n+\t\t    int i;\n+\t\t    for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/\n+\t\t    if (!p_ch)\n+\t\t\treturn ABORT_ALL;\n+\t\t    i = p - s - 1;\n+\t\t    if (i < 0 || p[-1] != ':') {\n+\t\t\t/* Didn't find \":]\", so treat like a normal set. */\n+\t\t\tp = s - 2;\n+\t\t\tp_ch = '[';\n+\t\t\tif (t_ch == p_ch)\n+\t\t\t    matched = TRUE;\n+\t\t\tcontinue;\n+\t\t    }\n+\t\t    if (CC_EQ(s,i, \"alnum\")) {\n+\t\t\tif (ISALNUM(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"alpha\")) {\n+\t\t\tif (ISALPHA(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"blank\")) {\n+\t\t\tif (ISBLANK(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"cntrl\")) {\n+\t\t\tif (ISCNTRL(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"digit\")) {\n+\t\t\tif (ISDIGIT(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"graph\")) {\n+\t\t\tif (ISGRAPH(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"lower\")) {\n+\t\t\tif (ISLOWER(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"print\")) {\n+\t\t\tif (ISPRINT(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"punct\")) {\n+\t\t\tif (ISPUNCT(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"space\")) {\n+\t\t\tif (ISSPACE(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"upper\")) {\n+\t\t\tif (ISUPPER(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"xdigit\")) {\n+\t\t\tif (ISXDIGIT(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else /* malformed [:class:] string */\n+\t\t\treturn ABORT_ALL;\n+\t\t    p_ch = 0; /* This makes \"prev_ch\" get set to 0. */\n+\t\t} else if (t_ch == p_ch)\n+\t\t    matched = TRUE;\n+\t    } while (prev_ch = p_ch, (p_ch = *++p) != ']');\n+\t    if (matched == special || t_ch == '/')\n+\t\treturn FALSE;\n+\t    continue;\n+\t}\n+    }\n+\n+    do {\n+\tif (*text)\n+\t    return FALSE;\n+    } while ((text = *a++) != NULL);\n+\n+    return TRUE;\n+}\n+\n+/* Match literal string \"s\" against the a virtually-joined string consisting\n+ * of \"text\" and any strings in array \"a\". */\n+static int doliteral(const uchar *s, const uchar *text, const uchar*const *a)\n+{\n+    for ( ; *s != '\\0'; text++, s++) {\n+\twhile (*text == '\\0') {\n+\t    if ((text = *a++) == NULL)\n+\t\treturn FALSE;\n+\t}\n+\tif (*text != *s)\n+\t    return FALSE;\n+    }\n+\n+    do {\n+\tif (*text)\n+\t    return FALSE;\n+    } while ((text = *a++) != NULL);\n+\n+    return TRUE;\n+}\n+\n+/* Return the last \"count\" path elements from the concatenated string.\n+ * We return a string pointer to the start of the string, and update the\n+ * array pointer-pointer to point to any remaining string elements. */\n+static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)\n+{\n+    const uchar*const *a = *a_ptr;\n+    const uchar*const *first_a = a;\n+\n+    while (*a)\n+\t    a++;\n+\n+    while (a != first_a) {\n+\tconst uchar *s = *--a;\n+\ts += strlen((char*)s);\n+\twhile (--s >= *a) {\n+\t    if (*s == '/' && !--count) {\n+\t\t*a_ptr = a+1;\n+\t\treturn s+1;\n+\t    }\n+\t}\n+    }\n+\n+    if (count == 1) {\n+\t*a_ptr = a+1;\n+\treturn *a;\n+    }\n+\n+    return NULL;\n+}\n+\n+/* Match the \"pattern\" against the \"text\" string. */\n+int wildmatch(const char *pattern, const char *text)\n+{\n+    static const uchar *nomore[1]; /* A NULL pointer. */\n+#ifdef WILD_TEST_ITERATIONS\n+    wildmatch_iteration_count = 0;\n+#endif\n+    return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;\n+}\n+\n+/* Match the \"pattern\" against the forced-to-lower-case \"text\" string. */\n+int iwildmatch(const char *pattern, const char *text)\n+{\n+    static const uchar *nomore[1]; /* A NULL pointer. */\n+    int ret;\n+#ifdef WILD_TEST_ITERATIONS\n+    wildmatch_iteration_count = 0;\n+#endif\n+    force_lower_case = 1;\n+    ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;\n+    force_lower_case = 0;\n+    return ret;\n+}\n+\n+/* Match pattern \"p\" against the a virtually-joined string consisting\n+ * of all the pointers in array \"texts\" (which has a NULL pointer at the\n+ * end).  The int \"where\" can be 0 (normal matching), > 0 (match only\n+ * the trailing N slash-separated filename components of \"texts\"), or < 0\n+ * (match the \"pattern\" at the start or after any slash in \"texts\"). */\n+int wildmatch_array(const char *pattern, const char*const *texts, int where)\n+{\n+    const uchar *p = (const uchar*)pattern;\n+    const uchar*const *a = (const uchar*const*)texts;\n+    const uchar *text;\n+    int matched;\n+\n+#ifdef WILD_TEST_ITERATIONS\n+    wildmatch_iteration_count = 0;\n+#endif\n+\n+    if (where > 0)\n+\ttext = trailing_N_elements(&a, where);\n+    else\n+\ttext = *a++;\n+    if (!text)\n+\treturn FALSE;\n+\n+    if ((matched = dowild(p, text, a)) != TRUE && where < 0\n+     && matched != ABORT_ALL) {\n+\twhile (1) {\n+\t    if (*text == '\\0') {\n+\t\tif ((text = (uchar*)*a++) == NULL)\n+\t\t    return FALSE;\n+\t\tcontinue;\n+\t    }\n+\t    if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE\n+\t     && matched != ABORT_TO_STARSTAR)\n+\t\tbreak;\n+\t}\n+    }\n+    return matched == TRUE;\n+}\n+\n+/* Match literal string \"s\" against the a virtually-joined string consisting\n+ * of all the pointers in array \"texts\" (which has a NULL pointer at the\n+ * end).  The int \"where\" can be 0 (normal matching), or > 0 (match\n+ * only the trailing N slash-separated filename components of \"texts\"). */\n+int litmatch_array(const char *string, const char*const *texts, int where)\n+{\n+    const uchar *s = (const uchar*)string;\n+    const uchar*const *a = (const uchar* const*)texts;\n+    const uchar *text;\n+\n+    if (where > 0)\n+\ttext = trailing_N_elements(&a, where);\n+    else\n+\ttext = *a++;\n+    if (!text)\n+\treturn FALSE;\n+\n+    return doliteral(s, text, a) == TRUE;\n+}\ndiff --git a/compat/wildmatch.h b/compat/wildmatch.h\nnew file mode 100644\nindex 0000000..e7f1a35\n--- /dev/null\n+++ b/compat/wildmatch.h\n@@ -0,0 +1,6 @@\n+/* wildmatch.h */\n+\n+int wildmatch(const char *pattern, const char *text);\n+int iwildmatch(const char *pattern, const char *text);\n+int wildmatch_array(const char *pattern, const char*const *texts, int where);\n+int litmatch_array(const char *string, const char*const *texts, int where);\ndiff --git a/t/t3070-wildmatch/wildtest.txt b/t/t3070-wildmatch/wildtest.txt\nnew file mode 100644\nindex 0000000..42c1678\n--- /dev/null\n+++ b/t/t3070-wildmatch/wildtest.txt\n@@ -0,0 +1,165 @@\n+# Input is in the following format (all items white-space separated):\n+#\n+# The first two items are 1 or 0 indicating if the wildmat call is expected to\n+# succeed and if fnmatch works the same way as wildmat, respectively.  After\n+# that is a text string for the match, and a pattern string.  Strings can be\n+# quoted (if desired) in either double or single quotes, as well as backticks.\n+#\n+# MATCH FNMATCH_SAME \"text to match\" 'pattern to use'\n+\n+# Basic wildmat features\n+1 1 foo\t\t\tfoo\n+0 1 foo\t\t\tbar\n+1 1 ''\t\t\t\"\"\n+1 1 foo\t\t\t???\n+0 1 foo\t\t\t??\n+1 1 foo\t\t\t*\n+1 1 foo\t\t\tf*\n+0 1 foo\t\t\t*f\n+1 1 foo\t\t\t*foo*\n+1 1 foobar\t\t*ob*a*r*\n+1 1 aaaaaaabababab\t*ab\n+1 1 foo*\t\tfoo\\*\n+0 1 foobar\t\tfoo\\*bar\n+1 1 f\\oo\t\tf\\\\oo\n+1 1 ball\t\t*[al]?\n+0 1 ten\t\t\t[ten]\n+1 1 ten\t\t\t**[!te]\n+0 1 ten\t\t\t**[!ten]\n+1 1 ten\t\t\tt[a-g]n\n+0 1 ten\t\t\tt[!a-g]n\n+1 1 ton\t\t\tt[!a-g]n\n+1 1 ton\t\t\tt[^a-g]n\n+1 1 a]b\t\t\ta[]]b\n+1 1 a-b\t\t\ta[]-]b\n+1 1 a]b\t\t\ta[]-]b\n+0 1 aab\t\t\ta[]-]b\n+1 1 aab\t\t\ta[]a-]b\n+1 1 ]\t\t\t]\n+\n+# Extended slash-matching features\n+0 1 foo/baz/bar\t\tfoo*bar\n+1 1 foo/baz/bar\t\tfoo**bar\n+0 1 foo/bar\t\tfoo?bar\n+0 1 foo/bar\t\tfoo[/]bar\n+0 1 foo/bar\t\tf[^eiu][^eiu][^eiu][^eiu][^eiu]r\n+1 1 foo-bar\t\tf[^eiu][^eiu][^eiu][^eiu][^eiu]r\n+0 1 foo\t\t\t**/foo\n+1 1 /foo\t\t**/foo\n+1 1 bar/baz/foo\t\t**/foo\n+0 1 bar/baz/foo\t\t*/foo\n+0 0 foo/bar/baz\t\t**/bar*\n+1 1 deep/foo/bar/baz\t**/bar/*\n+0 1 deep/foo/bar/baz/\t**/bar/*\n+1 1 deep/foo/bar/baz/\t**/bar/**\n+0 1 deep/foo/bar\t**/bar/*\n+1 1 deep/foo/bar/\t**/bar/**\n+1 1 foo/bar/baz\t\t**/bar**\n+1 1 foo/bar/baz/x\t*/bar/**\n+0 0 deep/foo/bar/baz/x\t*/bar/**\n+1 1 deep/foo/bar/baz/x\t**/bar/*/*\n+\n+# Various additional tests\n+0 1 acrt\t\ta[c-c]st\n+1 1 acrt\t\ta[c-c]rt\n+0 1 ]\t\t\t[!]-]\n+1 1 a\t\t\t[!]-]\n+0 1 ''\t\t\t\\\n+0 1 \\\t\t\t\\\n+0 1 /\\\t\t\t*/\\\n+1 1 /\\\t\t\t*/\\\\\n+1 1 foo\t\t\tfoo\n+1 1 @foo\t\t@foo\n+0 1 foo\t\t\t@foo\n+1 1 [ab]\t\t\\[ab]\n+1 1 [ab]\t\t[[]ab]\n+1 1 [ab]\t\t[[:]ab]\n+0 1 [ab]\t\t[[::]ab]\n+1 1 [ab]\t\t[[:digit]ab]\n+1 1 [ab]\t\t[\\[:]ab]\n+1 1 ?a?b\t\t\\??\\?b\n+1 1 abc\t\t\t\\a\\b\\c\n+0 1 foo\t\t\t''\n+1 1 foo/bar/baz/to\t**/t[o]\n+\n+# Character class tests\n+1 1 a1B\t\t[[:alpha:]][[:digit:]][[:upper:]]\n+0 1 a\t\t[[:digit:][:upper:][:space:]]\n+1 1 A\t\t[[:digit:][:upper:][:space:]]\n+1 1 1\t\t[[:digit:][:upper:][:space:]]\n+0 1 1\t\t[[:digit:][:upper:][:spaci:]]\n+1 1 ' '\t\t[[:digit:][:upper:][:space:]]\n+0 1 .\t\t[[:digit:][:upper:][:space:]]\n+1 1 .\t\t[[:digit:][:punct:][:space:]]\n+1 1 5\t\t[[:xdigit:]]\n+1 1 f\t\t[[:xdigit:]]\n+1 1 D\t\t[[:xdigit:]]\n+1 1 _\t\t[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]\n+#1 1 �\t\t[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]\n+1 1 \t\t[^[:alnum:][:alpha:][:blank:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]\n+1 1 .\t\t[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]\n+1 1 5\t\t[a-c[:digit:]x-z]\n+1 1 b\t\t[a-c[:digit:]x-z]\n+1 1 y\t\t[a-c[:digit:]x-z]\n+0 1 q\t\t[a-c[:digit:]x-z]\n+\n+# Additional tests, including some malformed wildmats\n+1 1 ]\t\t[\\\\-^]\n+0 1 [\t\t[\\\\-^]\n+1 1 -\t\t[\\-_]\n+1 1 ]\t\t[\\]]\n+0 1 \\]\t\t[\\]]\n+0 1 \\\t\t[\\]]\n+0 1 ab\t\ta[]b\n+0 1 a[]b\ta[]b\n+0 1 ab[\t\tab[\n+0 1 ab\t\t[!\n+0 1 ab\t\t[-\n+1 1 -\t\t[-]\n+0 1 -\t\t[a-\n+0 1 -\t\t[!a-\n+1 1 -\t\t[--A]\n+1 1 5\t\t[--A]\n+1 1 ' '\t\t'[ --]'\n+1 1 $\t\t'[ --]'\n+1 1 -\t\t'[ --]'\n+0 1 0\t\t'[ --]'\n+1 1 -\t\t[---]\n+1 1 -\t\t[------]\n+0 1 j\t\t[a-e-n]\n+1 1 -\t\t[a-e-n]\n+1 1 a\t\t[!------]\n+0 1 [\t\t[]-a]\n+1 1 ^\t\t[]-a]\n+0 1 ^\t\t[!]-a]\n+1 1 [\t\t[!]-a]\n+1 1 ^\t\t[a^bc]\n+1 1 -b]\t\t[a-]b]\n+0 1 \\\t\t[\\]\n+1 1 \\\t\t[\\\\]\n+0 1 \\\t\t[!\\\\]\n+1 1 G\t\t[A-\\\\]\n+0 1 aaabbb\tb*a\n+0 1 aabcaa\t*ba*\n+1 1 ,\t\t[,]\n+1 1 ,\t\t[\\\\,]\n+1 1 \\\t\t[\\\\,]\n+1 1 -\t\t[,-.]\n+0 1 +\t\t[,-.]\n+0 1 -.]\t\t[,-.]\n+1 1 2\t\t[\\1-\\3]\n+1 1 3\t\t[\\1-\\3]\n+0 1 4\t\t[\\1-\\3]\n+1 1 \\\t\t[[-\\]]\n+1 1 [\t\t[[-\\]]\n+1 1 ]\t\t[[-\\]]\n+0 1 -\t\t[[-\\]]\n+\n+# Test recursion and the abort code (use \"wildtest -i\" to see iteration counts)\n+1 1 -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1\t-*-*-*-*-*-*-12-*-*-*-m-*-*-*\n+0 1 -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1\t-*-*-*-*-*-*-12-*-*-*-m-*-*-*\n+0 1 -adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1\t-*-*-*-*-*-*-12-*-*-*-m-*-*-*\n+1 1 /adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1\t/*/*/*/*/*/*/12/*/*/*/m/*/*/*\n+0 1 /adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1\t/*/*/*/*/*/*/12/*/*/*/m/*/*/*\n+1 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt\t\t**/*a*b*g*n*t\n+0 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz\t\t**/*a*b*g*n*t\ndiff --git a/test-wildmatch.c b/test-wildmatch.c\nnew file mode 100644\nindex 0000000..88585c2\n--- /dev/null\n+++ b/test-wildmatch.c\n@@ -0,0 +1,222 @@\n+/*\n+ * Test suite for the wildmatch code.\n+ *\n+ * Copyright (C) 2003-2009 Wayne Davison\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License as published by\n+ * the Free Software Foundation; either version 3 of the License, or\n+ * (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License along\n+ * with this program; if not, visit the http://fsf.org website.\n+ */\n+\n+/*#define COMPARE_WITH_FNMATCH*/\n+\n+#define WILD_TEST_ITERATIONS\n+#include \"lib/wildmatch.c\"\n+\n+#include <popt.h>\n+\n+#ifdef COMPARE_WITH_FNMATCH\n+#include <fnmatch.h>\n+\n+int fnmatch_errors = 0;\n+#endif\n+\n+int wildmatch_errors = 0;\n+char number_separator = ',';\n+\n+typedef char bool;\n+\n+int output_iterations = 0;\n+int explode_mod = 0;\n+int empties_mod = 0;\n+int empty_at_start = 0;\n+int empty_at_end = 0;\n+\n+static struct poptOption long_options[] = {\n+  /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */\n+  {\"iterations\",     'i', POPT_ARG_NONE,   &output_iterations, 0, 0, 0},\n+  {\"empties\",        'e', POPT_ARG_STRING, 0, 'e', 0, 0},\n+  {\"explode\",        'x', POPT_ARG_INT,    &explode_mod, 0, 0, 0},\n+  {0,0,0,0, 0, 0, 0}\n+};\n+\n+/* match just at the start of string (anchored tests) */\n+static void\n+run_test(int line, bool matches,\n+#ifdef COMPARE_WITH_FNMATCH\n+\t bool same_as_fnmatch,\n+#endif\n+\t const char *text, const char *pattern)\n+{\n+    bool matched;\n+#ifdef COMPARE_WITH_FNMATCH\n+    bool fn_matched;\n+    int flags = strstr(pattern, \"**\")? 0 : FNM_PATHNAME;\n+#endif\n+\n+    if (explode_mod) {\n+\tchar buf[MAXPATHLEN*2], *texts[MAXPATHLEN];\n+\tint pos = 0, cnt = 0, ndx = 0, len = strlen(text);\n+\n+\tif (empty_at_start)\n+\t    texts[ndx++] = \"\";\n+\t/* An empty string must turn into at least one empty array item. */\n+\twhile (1) {\n+\t    texts[ndx] = buf + ndx * (explode_mod + 1);\n+\t    strlcpy(texts[ndx++], text + pos, explode_mod + 1);\n+\t    if (pos + explode_mod >= len)\n+\t\tbreak;\n+\t    pos += explode_mod;\n+\t    if (!(++cnt % empties_mod))\n+\t\ttexts[ndx++] = \"\";\n+\t}\n+\tif (empty_at_end)\n+\t    texts[ndx++] = \"\";\n+\ttexts[ndx] = NULL;\n+\tmatched = wildmatch_array(pattern, (const char**)texts, 0);\n+    } else\n+\tmatched = wildmatch(pattern, text);\n+#ifdef COMPARE_WITH_FNMATCH\n+    fn_matched = !fnmatch(pattern, text, flags);\n+#endif\n+    if (matched != matches) {\n+\tprintf(\"wildmatch failure on line %d:\\n  %s\\n  %s\\n  expected %s match\\n\",\n+\t       line, text, pattern, matches? \"a\" : \"NO\");\n+\twildmatch_errors++;\n+    }\n+#ifdef COMPARE_WITH_FNMATCH\n+    if (fn_matched != (matches ^ !same_as_fnmatch)) {\n+\tprintf(\"fnmatch disagreement on line %d:\\n  %s\\n  %s\\n  expected %s match\\n\",\n+\t       line, text, pattern, matches ^ !same_as_fnmatch? \"a\" : \"NO\");\n+\tfnmatch_errors++;\n+    }\n+#endif\n+    if (output_iterations) {\n+\tprintf(\"%d: \\\"%s\\\" iterations = %d\\n\", line, pattern,\n+\t       wildmatch_iteration_count);\n+    }\n+}\n+\n+int\n+main(int argc, char **argv)\n+{\n+    char buf[2048], *s, *string[2], *end[2];\n+    const char *arg;\n+    FILE *fp;\n+    int opt, line, i, flag[2];\n+    poptContext pc = poptGetContext(\"wildtest\", argc, (const char**)argv,\n+\t\t\t\t    long_options, 0);\n+\n+    while ((opt = poptGetNextOpt(pc)) != -1) {\n+\tswitch (opt) {\n+\t  case 'e':\n+\t    arg = poptGetOptArg(pc);\n+\t    empties_mod = atoi(arg);\n+\t    if (strchr(arg, 's'))\n+\t\tempty_at_start = 1;\n+\t    if (strchr(arg, 'e'))\n+\t\tempty_at_end = 1;\n+\t    if (!explode_mod)\n+\t\texplode_mod = 1024;\n+\t    break;\n+\t  default:\n+\t    fprintf(stderr, \"%s: %s\\n\",\n+\t\t    poptBadOption(pc, POPT_BADOPTION_NOALIAS),\n+\t\t    poptStrerror(opt));\n+\t    exit(1);\n+\t}\n+    }\n+\n+    if (explode_mod && !empties_mod)\n+\tempties_mod = 1024;\n+\n+    argv = (char**)poptGetArgs(pc);\n+    if (!argv || argv[1]) {\n+\tfprintf(stderr, \"Usage: wildtest [OPTIONS] TESTFILE\\n\");\n+\texit(1);\n+    }\n+\n+    if ((fp = fopen(*argv, \"r\")) == NULL) {\n+\tfprintf(stderr, \"Unable to open %s\\n\", *argv);\n+\texit(1);\n+    }\n+\n+    line = 0;\n+    while (fgets(buf, sizeof buf, fp)) {\n+\tline++;\n+\tif (*buf == '#' || *buf == '\\n')\n+\t    continue;\n+\tfor (s = buf, i = 0; i <= 1; i++) {\n+\t    if (*s == '1')\n+\t\tflag[i] = 1;\n+\t    else if (*s == '0')\n+\t\tflag[i] = 0;\n+\t    else\n+\t\tflag[i] = -1;\n+\t    if (*++s != ' ' && *s != '\\t')\n+\t\tflag[i] = -1;\n+\t    if (flag[i] < 0) {\n+\t\tfprintf(stderr, \"Invalid flag syntax on line %d of %s:\\n%s\",\n+\t\t\tline, *argv, buf);\n+\t\texit(1);\n+\t    }\n+\t    while (*++s == ' ' || *s == '\\t') {}\n+\t}\n+\tfor (i = 0; i <= 1; i++) {\n+\t    if (*s == '\\'' || *s == '\"' || *s == '`') {\n+\t\tchar quote = *s++;\n+\t\tstring[i] = s;\n+\t\twhile (*s && *s != quote) s++;\n+\t\tif (!*s) {\n+\t\t    fprintf(stderr, \"Unmatched quote on line %d of %s:\\n%s\",\n+\t\t\t    line, *argv, buf);\n+\t\t    exit(1);\n+\t\t}\n+\t\tend[i] = s;\n+\t    }\n+\t    else {\n+\t\tif (!*s || *s == '\\n') {\n+\t\t    fprintf(stderr, \"Not enough strings on line %d of %s:\\n%s\",\n+\t\t\t    line, *argv, buf);\n+\t\t    exit(1);\n+\t\t}\n+\t\tstring[i] = s;\n+\t\twhile (*++s && *s != ' ' && *s != '\\t' && *s != '\\n') {}\n+\t\tend[i] = s;\n+\t    }\n+\t    while (*++s == ' ' || *s == '\\t') {}\n+\t}\n+\t*end[0] = *end[1] = '\\0';\n+\trun_test(line, flag[0],\n+#ifdef COMPARE_WITH_FNMATCH\n+\t\t flag[1],\n+#endif\n+\t\t string[0], string[1]);\n+    }\n+\n+    if (!wildmatch_errors)\n+\tfputs(\"No\", stdout);\n+    else\n+\tprintf(\"%d\", wildmatch_errors);\n+    printf(\" wildmatch error%s found.\\n\", wildmatch_errors == 1? \"\" : \"s\");\n+\n+#ifdef COMPARE_WITH_FNMATCH\n+    if (!fnmatch_errors)\n+\tfputs(\"No\", stdout);\n+    else\n+\tprintf(\"%d\", fnmatch_errors);\n+    printf(\" fnmatch error%s found.\\n\", fnmatch_errors == 1? \"\" : \"s\");\n+\n+#endif\n+\n+    return 0;\n+}\n-- \n1.7.12.403.gce5cf6f.dirty\n"},{"id":"199095","messageId":"1347710524-15404-3-git-send-email-pclouds@gmail.com","threadId":"31539","inReplyTo":"1347710524-15404-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 2/5] compat/wildmatch: remove static variable force_lower_case","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-15T12:02:01Z","receivedAt":"2012-09-15T12:02:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"One place less to worry about thread safety\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n compat/wildmatch.c | 17 +++++++----------\n 1 file changed, 7 insertions(+), 10 deletions(-)\n\ndiff --git a/compat/wildmatch.c b/compat/wildmatch.c\nindex f3a1731..e824eb2 100644\n--- a/compat/wildmatch.c\n+++ b/compat/wildmatch.c\n@@ -57,11 +57,10 @@\n int wildmatch_iteration_count;\n #endif\n \n-static int force_lower_case = 0;\n-\n /* Match pattern \"p\" against the a virtually-joined string consisting\n  * of \"text\" and any strings in array \"a\". */\n-static int dowild(const uchar *p, const uchar *text, const uchar*const *a)\n+static int dowild(const uchar *p, const uchar *text,\n+\t\t  const uchar*const *a, int force_lower_case)\n {\n     uchar p_ch;\n \n@@ -121,7 +120,7 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a)\n \t\t    t_ch = *text;\n \t\t    continue;\n \t\t}\n-\t\tif ((matched = dowild(p, text, a)) != FALSE) {\n+\t\tif ((matched = dowild(p, text, a, force_lower_case)) != FALSE) {\n \t\t    if (!special || matched != ABORT_TO_STARSTAR)\n \t\t\treturn matched;\n \t\t} else if (!special && t_ch == '/')\n@@ -291,7 +290,7 @@ int wildmatch(const char *pattern, const char *text)\n #ifdef WILD_TEST_ITERATIONS\n     wildmatch_iteration_count = 0;\n #endif\n-    return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;\n+    return dowild((const uchar*)pattern, (const uchar*)text, nomore, 0) == TRUE;\n }\n \n /* Match the \"pattern\" against the forced-to-lower-case \"text\" string. */\n@@ -302,9 +301,7 @@ int iwildmatch(const char *pattern, const char *text)\n #ifdef WILD_TEST_ITERATIONS\n     wildmatch_iteration_count = 0;\n #endif\n-    force_lower_case = 1;\n-    ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;\n-    force_lower_case = 0;\n+    ret = dowild((const uchar*)pattern, (const uchar*)text, nomore, 1) == TRUE;\n     return ret;\n }\n \n@@ -331,7 +328,7 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where)\n     if (!text)\n \treturn FALSE;\n \n-    if ((matched = dowild(p, text, a)) != TRUE && where < 0\n+    if ((matched = dowild(p, text, a, 0)) != TRUE && where < 0\n      && matched != ABORT_ALL) {\n \twhile (1) {\n \t    if (*text == '\\0') {\n@@ -339,7 +336,7 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where)\n \t\t    return FALSE;\n \t\tcontinue;\n \t    }\n-\t    if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE\n+\t    if (*text++ == '/' && (matched = dowild(p, text, a, 0)) != FALSE\n \t     && matched != ABORT_TO_STARSTAR)\n \t\tbreak;\n \t}\n-- \n1.7.12.403.gce5cf6f.dirty\n"},{"id":"199099","messageId":"1347710524-15404-4-git-send-email-pclouds@gmail.com","threadId":"31539","inReplyTo":"1347710524-15404-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 3/5] compat/wildmatch: fix case-insensitive matching","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-15T12:02:02Z","receivedAt":"2012-09-15T12:02:02Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"dowild() does case insensitive matching by lower-casing the text. That\nmeans lower case letters in patterns imply case-insensitive matching,\nbut upper case means exact matching.\n\nWe do not want that subtlety. Lower case pattern too so iwildmatch()\nalways does what we expect it to do.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n compat/wildmatch.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/compat/wildmatch.c b/compat/wildmatch.c\nindex e824eb2..c7f7f9f 100644\n--- a/compat/wildmatch.c\n+++ b/compat/wildmatch.c\n@@ -81,6 +81,8 @@ static int dowild(const uchar *p, const uchar *text,\n \t}\n \tif (force_lower_case && ISUPPER(t_ch))\n \t    t_ch = tolower(t_ch);\n+\tif (force_lower_case && ISUPPER(p_ch))\n+\t    p_ch = tolower(p_ch);\n \tswitch (p_ch) {\n \t  case '\\\\':\n \t    /* Literal match with following character.  Note that the test\n-- \n1.7.12.403.gce5cf6f.dirty\n"},{"id":"199096","messageId":"1347710524-15404-5-git-send-email-pclouds@gmail.com","threadId":"31539","inReplyTo":"1347710524-15404-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 4/5] Integrate wildmatch to git","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-15T12:02:03Z","receivedAt":"2012-09-15T12:02:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This makes wildmatch.c part of libgit.a and builds test-wildmatch\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Makefile             |  6 ++++++\n compat/wildmatch.c   |  8 +++++++-\n t/t3070-wildmatch.sh | 27 +++++++++++++++++++++++++++\n test-wildmatch.c     |  8 +++++++-\n 4 files changed, 47 insertions(+), 2 deletions(-)\n create mode 100755 t/t3070-wildmatch.sh\n\ndiff --git a/Makefile b/Makefile\nindex 56301dc..c3608e6 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -511,6 +511,7 @@ TEST_PROGRAMS_NEED_X += test-sha1\n TEST_PROGRAMS_NEED_X += test-sigchain\n TEST_PROGRAMS_NEED_X += test-subprocess\n TEST_PROGRAMS_NEED_X += test-svn-fe\n+TEST_PROGRAMS_NEED_X += test-wildmatch\n \n TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))\n \n@@ -605,6 +606,7 @@ LIB_H += compat/mingw.h\n LIB_H += compat/obstack.h\n LIB_H += compat/precompose_utf8.h\n LIB_H += compat/terminal.h\n+LIB_H += compat/wildmatch.h\n LIB_H += compat/win32/dirent.h\n LIB_H += compat/win32/poll.h\n LIB_H += compat/win32/pthread.h\n@@ -709,6 +711,7 @@ LIB_OBJS += combine-diff.o\n LIB_OBJS += commit.o\n LIB_OBJS += compat/obstack.o\n LIB_OBJS += compat/terminal.o\n+LIB_OBJS += compat/wildmatch.o\n LIB_OBJS += config.o\n LIB_OBJS += connect.o\n LIB_OBJS += connected.o\n@@ -2586,6 +2589,9 @@ test-svn-fe$X: vcs-svn/lib.a\n test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)\n \n+test-wildmatch$X: test-wildmatch.o GIT-LDFLAGS\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) -lpopt\n+\n check-sha1:: test-sha1$X\n \t./test-sha1.sh\n \ndiff --git a/compat/wildmatch.c b/compat/wildmatch.c\nindex c7f7f9f..625cb0c 100644\n--- a/compat/wildmatch.c\n+++ b/compat/wildmatch.c\n@@ -9,7 +9,13 @@\n **  work differently than '*', and to fix the character-class code.\n */\n \n-#include \"rsync.h\"\n+#include <stddef.h>\n+#include <ctype.h>\n+#include <string.h>\n+\n+#include \"wildmatch.h\"\n+\n+typedef unsigned char uchar;\n \n /* What character marks an inverted character class? */\n #define NEGATE_CLASS\t'!'\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nnew file mode 100755\nindex 0000000..7fb63ff\n--- /dev/null\n+++ b/t/t3070-wildmatch.sh\n@@ -0,0 +1,27 @@\n+#!/bin/sh\n+\n+test_description='wildmatch tests'\n+\n+. ./test-lib.sh\n+\n+test_wildmatch() {\n+    test_expect_success \"wildmatch $*\" \"\n+\ttest-wildmatch $* ../t3070-wildmatch/wildtest.txt >actual &&\n+\techo 'No wildmatch errors found.' >expected &&\n+\ttest_cmp expected actual\n+    \"\n+}\n+\n+test_wildmatch -x1\n+test_wildmatch -x1 -e1\n+test_wildmatch -x1 -else\n+test_wildmatch -x2\n+test_wildmatch -x2 -ese\n+test_wildmatch -x3\n+test_wildmatch -x3 -e1\n+test_wildmatch -x4\n+test_wildmatch -x4 -e2e\n+test_wildmatch -x5\n+test_wildmatch -x5 -es\n+\n+test_done\ndiff --git a/test-wildmatch.c b/test-wildmatch.c\nindex 88585c2..2c506a0 100644\n--- a/test-wildmatch.c\n+++ b/test-wildmatch.c\n@@ -20,7 +20,13 @@\n /*#define COMPARE_WITH_FNMATCH*/\n \n #define WILD_TEST_ITERATIONS\n-#include \"lib/wildmatch.c\"\n+#include \"compat/wildmatch.c\"\n+\n+#define MAXPATHLEN 1024\n+#ifdef NO_STRLCPY\n+#include \"compat/strlcpy.c\"\n+#define strlcpy gitstrlcpy\n+#endif\n \n #include <popt.h>\n \n-- \n1.7.12.403.gce5cf6f.dirty\n"},{"id":"199094","messageId":"1347710524-15404-6-git-send-email-pclouds@gmail.com","threadId":"31539","inReplyTo":"1347710524-15404-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 5/5] Support \"**\" in .gitignore and .gitattributes patterns using wildmatch()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-15T12:02:04Z","receivedAt":"2012-09-15T12:02:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/gitignore.txt | 3 +++\n attr.c                      | 4 +++-\n dir.c                       | 5 ++++-\n 3 files changed, 10 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt\nindex c1f692a..eb81d31 100644\n--- a/Documentation/gitignore.txt\n+++ b/Documentation/gitignore.txt\n@@ -93,6 +93,9 @@ PATTERN FORMAT\n    For example, \"Documentation/{asterisk}.html\" matches\n    \"Documentation/git.html\" but not \"Documentation/ppc/ppc.html\"\n    or \"tools/perf/Documentation/perf.html\".\n++\n+Contrary to fnmatch(3), git matches \"**\" to anything including\n+slashes, similar to rsync(1).\n \n  - A leading slash matches the beginning of the pathname.\n    For example, \"/{asterisk}.c\" matches \"cat-file.c\" but not\ndiff --git a/attr.c b/attr.c\nindex 3430faf..f06ad95 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -12,6 +12,7 @@\n #include \"exec_cmd.h\"\n #include \"attr.h\"\n #include \"dir.h\"\n+#include \"compat/wildmatch.h\"\n \n const char git_attr__true[] = \"(builtin)true\";\n const char git_attr__false[] = \"\\0(builtin)false\";\n@@ -666,7 +667,8 @@ static int path_matches(const char *pathname, int pathlen,\n \t\treturn 0;\n \tif (baselen != 0)\n \t\tbaselen++;\n-\treturn fnmatch_icase(pattern, pathname + baselen, FNM_PATHNAME) == 0;\n+\treturn (ignore_case && iwildmatch(pattern, pathname + baselen)) ||\n+\t\t(!ignore_case && wildmatch(pattern, pathname + baselen));\n }\n \n static int macroexpand_one(int attr_nr, int rem);\ndiff --git a/dir.c b/dir.c\nindex 4868339..c17f9ff 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -8,6 +8,7 @@\n #include \"cache.h\"\n #include \"dir.h\"\n #include \"refs.h\"\n+#include \"compat/wildmatch.h\"\n \n struct path_simplify {\n \tint len;\n@@ -575,7 +576,9 @@ int excluded_from_list(const char *pathname,\n \t\t\tnamelen -= prefix;\n \t\t}\n \n-\t\tif (!namelen || !fnmatch_icase(exclude, name, FNM_PATHNAME))\n+\t\tif (!namelen ||\n+\t\t    ((ignore_case && iwildmatch(exclude, name)) ||\n+\t\t     (!ignore_case && wildmatch(exclude, name))))\n \t\t\treturn to_exclude;\n \t}\n \treturn -1; /* undecided */\n-- \n1.7.12.403.gce5cf6f.dirty\n"},{"id":"199102","messageId":"F93DBBE1DDAA44A28C9020F9A58FD825@PhilipOakley","threadId":"31539","inReplyTo":"1347710524-15404-1-git-send-email-pclouds@gmail.com","subject":"How to create the \" [PATCH 0/5]\" first email?","fromName":"Philip Oakley","fromEmail":"philipoakley@iee.org","sentAt":"2012-09-15T14:27:02Z","receivedAt":"2012-09-15T14:27:02Z","isPatch":true,"sender":{"key":"philipoakley@iee.email","avatar":"https://avatars.githubusercontent.com/u/914343?v=4"},"body":"The git format-patch allows numbered patches to be created, however I'm\nnot sure how folk generate the initial \"0/n\" patch. Which of the various\noptions should I be using?\n\nAlso, being on windows, the various 'mbox' and 'MUA' discussions are new\nto me, so the format-patch step where :-\n\n\"Typically it will be placed in a MUA’s drafts folder, edited to add\ntimely commentary that should not go in the changelog after the three\ndashes, and then sent as a message whose body, in our example, starts\nwith \"arch/arm config files were…\". On the receiving end, readers can\nsave interesting patches in a UNIX mailbox and apply them with\ngit-am(1).\"\n\nhides a multitude of implicit knowledge steps. Is there an extended\ndescription of what that would mean from a platform independent\nviewpoint? e.g. if the patches are separte files and an mbox is one\nconsolidated file, how to get from one to the other so that 'it' can be\nsent by 'git send-mail'.\n\nI'm also missing an understanding of the preparation stage where one\ntries to tidy up the various commit messages becaue they weren't\nexplicit, specfic nor concise enough, so I suspect that there is an\nimplicit `git format-patch` <-> `git am` loop of sharpening the mbox\npatches before submission to the list that I'm missing. Has this \ndescribed somewhere?\n\nPhilip Oakley\n"},{"id":"199105","messageId":"7vzk4rz00y.fsf@alter.siamese.dyndns.org","threadId":"31539","inReplyTo":"F93DBBE1DDAA44A28C9020F9A58FD825@PhilipOakley","subject":"Re: How to create the \" [PATCH 0/5]\" first email?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-15T17:08:45Z","receivedAt":"2012-09-15T17:08:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Philip Oakley\" <philipoakley@iee.org> writes:\n\n> \"Typically it will be placed in a MUA’s drafts folder, edited to add\n> timely commentary that should not go in the changelog after the three\n> dashes, and then sent as a message whose body, in our example, starts\n> with \"arch/arm config files were…\". On the receiving end, readers can\n> save interesting patches in a UNIX mailbox and apply them with\n> git-am(1).\"\n>\n> hides a multitude of implicit knowledge steps. Is there an extended\n> description of what that would mean from a platform independent\n> viewpoint? e.g. if the patches are separte files and an mbox is one\n> consolidated file, how to get from one to the other so that 'it' can be\n> sent by 'git send-mail'.\n\nIf you plan to use \"git send-email\" to send the final results out,\nyou should consider \"git send-email\" as your \"MUA\" in the quoted\nparagraph.  And that will be very platform independent viewpoint to\nsee things from.\n\n\"git format-patch -o my-series/ --cover-letter ...\"  would treat\n\"my-series/\" directory as \"MUA's drafts folder\" and prepares the\nmessages you would want to send out, and you can proof-read and edit\nthe files in there before telling your \"MUA\" to send them out, with\n\"git send-email ... my-series/*.patch\" or something.\n\n> I'm also missing an understanding of the preparation stage where one\n> tries to tidy up the various commit messages becaue they weren't\n> explicit, specfic nor concise enough,...\n\nMany people usually do \"rebase -i\" until perfection and then a\nsingle final invocation of \"format-patch\".  Of course, the \"final\"\ncan and should further be proof-read and it is fine to do typofixes\nin the format-patch output files without going back to the commits\nbefore sending them out.\n\n> ...so I suspect that there is an\n> implicit `git format-patch` <-> `git am` loop of sharpening the mbox\n> patches before submission to the list that I'm missing. Has this \n> described somewhere?\n"},{"id":"199136","messageId":"7v627ezcm9.fsf@alter.siamese.dyndns.org","threadId":"31539","inReplyTo":"1347710524-15404-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 1/5] Import wildmatch from rsync","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-16T06:49:02Z","receivedAt":"2012-09-16T06:49:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> These files are from rsync.git commit\n> d51a3adb4fca3e6b1b046c6e570828f3bca8fe36. The commit is GPL-3. However\n> wildmatch.[ch] have not changed since rsync turned to GPL-3.\n\nI'd like see the last version of rsync that was GPLv2 hunted down\nand the copy from that version lifted from it.  If you _know_ they\nare identical, then you should be able to do that, no?\n\n>  compat/wildmatch.c             | 368 +++++++++++++++++++++++++++++++++++++++++\n>  compat/wildmatch.h             |   6 +\n\nAs this will be linked on all platforms, it shouldn't be in compat/\ndirectory.  Unlike xdiff/ that has many files, this can live at the\ntop-level, just like kwset we borrowed from GNU grep does.\n\n> diff --git a/compat/wildmatch.c b/compat/wildmatch.c\n> new file mode 100644\n> index 0000000..f3a1731\n> --- /dev/null\n> +++ b/compat/wildmatch.c\n> @@ -0,0 +1,368 @@\n> +/*\n> +**  Do shell-style pattern matching for ?, \\, [], and * characters.\n> +**  It is 8bit clean.\n> +**\n> +**  Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.\n> +**  Rich $alz is now <rsalz@bbn.com>.\n> +**\n> +**  Modified by Wayne Davison to special-case '/' matching, to make '**'\n> +**  work differently than '*', and to fix the character-class code.\n> +*/\n> +\n> +#include \"rsync.h\"\n\nHuh???\n\nAhh.  The approach you took is \"This patch imports the pristine copy\nto make it easier to verify the provenance, and the plan is to make\nit usable with separate follow-up patches.\", I agree that is a very\nsensible thing to do.\n\nBut please say so in the commit log message.\n"},{"id":"199160","messageId":"1347809265-2457-1-git-send-email-pclouds@gmail.com","threadId":"31539","inReplyTo":"1347710524-15404-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 0/5] Support matching \"**\" in .gitattributes and .gitignore","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-16T15:27:40Z","receivedAt":"2012-09-16T15:27:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Changes in v2:\n\n - Correct commit sha-1 in the 1/5 to the last GPL-2 rsync commit.\n   State it is pristine import. Move wildmatch.[ch] to top dir.\n - Add tests to 5/5 (I forgot to test that the integration should run\n   well, in addition to wildmatch() working in general)\n\nNote that there might be a regression because wildmatch() does not\ncare about LC_CTYPE when dealing with character classes, while libc's\nfnmatch might. But it's probably not worth bothering about because we\nmatch against path names, which do not necessarily follow LC_TYPE\nbut the file system's encoding.\n\nNguyễn Thái Ngọc Duy (5):\n  Import wildmatch from rsync\n  compat/wildmatch: remove static variable force_lower_case\n  compat/wildmatch: fix case-insensitive matching\n  Integrate wildmatch to git\n  Support \"**\" in .gitignore and .gitattributes patterns using\n    wildmatch()\n\n .gitignore                         |   1 +\n Documentation/gitignore.txt        |   3 +\n Makefile                           |   6 +\n attr.c                             |   4 +-\n dir.c                              |   5 +-\n t/t0003-attributes.sh              |  17 ++\n t/t3001-ls-files-others-exclude.sh |  11 ++\n t/t3070-wildmatch.sh               |  27 +++\n t/t3070-wildmatch/wildtest.txt     | 165 ++++++++++++++++\n test-wildmatch.c                   | 228 +++++++++++++++++++++++\n wildmatch.c                        | 373 +++++++++++++++++++++++++++++++++++++\n wildmatch.h                        |   6 +\n 12 files changed, 844 insertions(+), 2 deletions(-)\n create mode 100755 t/t3070-wildmatch.sh\n create mode 100644 t/t3070-wildmatch/wildtest.txt\n create mode 100644 test-wildmatch.c\n create mode 100644 wildmatch.c\n create mode 100644 wildmatch.h\n\n-- \n1.7.12.403.gce5cf6f.dirty\n"},{"id":"199159","messageId":"1347809265-2457-2-git-send-email-pclouds@gmail.com","threadId":"31539","inReplyTo":"1347809265-2457-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 1/5] Import wildmatch from rsync","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-16T15:27:41Z","receivedAt":"2012-09-16T15:27:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"These files are from rsync.git commit\nf92f5b166e3019db42bc7fe1aa2f1a9178cd215d, which was the last commit\nbefore rsync turned GPL-3. All files are imported as-is and\nno-op. Adaptation is done in a separate patch.\n\nrsync.git           ->  git.git\nlib/wildmatch.[ch]      wildmatch.[ch]\nwildtest.c              test-wildmatch.c\nwildtest.txt            t/t3070-wildmatch/wildtest.txt\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t3070-wildmatch/wildtest.txt | 165 ++++++++++++++++++\n test-wildmatch.c               | 222 +++++++++++++++++++++++++\n wildmatch.c                    | 368 +++++++++++++++++++++++++++++++++++++++++\n wildmatch.h                    |   6 +\n 4 files changed, 761 insertions(+)\n create mode 100644 t/t3070-wildmatch/wildtest.txt\n create mode 100644 test-wildmatch.c\n create mode 100644 wildmatch.c\n create mode 100644 wildmatch.h\n\ndiff --git a/t/t3070-wildmatch/wildtest.txt b/t/t3070-wildmatch/wildtest.txt\nnew file mode 100644\nindex 0000000..42c1678\n--- /dev/null\n+++ b/t/t3070-wildmatch/wildtest.txt\n@@ -0,0 +1,165 @@\n+# Input is in the following format (all items white-space separated):\n+#\n+# The first two items are 1 or 0 indicating if the wildmat call is expected to\n+# succeed and if fnmatch works the same way as wildmat, respectively.  After\n+# that is a text string for the match, and a pattern string.  Strings can be\n+# quoted (if desired) in either double or single quotes, as well as backticks.\n+#\n+# MATCH FNMATCH_SAME \"text to match\" 'pattern to use'\n+\n+# Basic wildmat features\n+1 1 foo\t\t\tfoo\n+0 1 foo\t\t\tbar\n+1 1 ''\t\t\t\"\"\n+1 1 foo\t\t\t???\n+0 1 foo\t\t\t??\n+1 1 foo\t\t\t*\n+1 1 foo\t\t\tf*\n+0 1 foo\t\t\t*f\n+1 1 foo\t\t\t*foo*\n+1 1 foobar\t\t*ob*a*r*\n+1 1 aaaaaaabababab\t*ab\n+1 1 foo*\t\tfoo\\*\n+0 1 foobar\t\tfoo\\*bar\n+1 1 f\\oo\t\tf\\\\oo\n+1 1 ball\t\t*[al]?\n+0 1 ten\t\t\t[ten]\n+1 1 ten\t\t\t**[!te]\n+0 1 ten\t\t\t**[!ten]\n+1 1 ten\t\t\tt[a-g]n\n+0 1 ten\t\t\tt[!a-g]n\n+1 1 ton\t\t\tt[!a-g]n\n+1 1 ton\t\t\tt[^a-g]n\n+1 1 a]b\t\t\ta[]]b\n+1 1 a-b\t\t\ta[]-]b\n+1 1 a]b\t\t\ta[]-]b\n+0 1 aab\t\t\ta[]-]b\n+1 1 aab\t\t\ta[]a-]b\n+1 1 ]\t\t\t]\n+\n+# Extended slash-matching features\n+0 1 foo/baz/bar\t\tfoo*bar\n+1 1 foo/baz/bar\t\tfoo**bar\n+0 1 foo/bar\t\tfoo?bar\n+0 1 foo/bar\t\tfoo[/]bar\n+0 1 foo/bar\t\tf[^eiu][^eiu][^eiu][^eiu][^eiu]r\n+1 1 foo-bar\t\tf[^eiu][^eiu][^eiu][^eiu][^eiu]r\n+0 1 foo\t\t\t**/foo\n+1 1 /foo\t\t**/foo\n+1 1 bar/baz/foo\t\t**/foo\n+0 1 bar/baz/foo\t\t*/foo\n+0 0 foo/bar/baz\t\t**/bar*\n+1 1 deep/foo/bar/baz\t**/bar/*\n+0 1 deep/foo/bar/baz/\t**/bar/*\n+1 1 deep/foo/bar/baz/\t**/bar/**\n+0 1 deep/foo/bar\t**/bar/*\n+1 1 deep/foo/bar/\t**/bar/**\n+1 1 foo/bar/baz\t\t**/bar**\n+1 1 foo/bar/baz/x\t*/bar/**\n+0 0 deep/foo/bar/baz/x\t*/bar/**\n+1 1 deep/foo/bar/baz/x\t**/bar/*/*\n+\n+# Various additional tests\n+0 1 acrt\t\ta[c-c]st\n+1 1 acrt\t\ta[c-c]rt\n+0 1 ]\t\t\t[!]-]\n+1 1 a\t\t\t[!]-]\n+0 1 ''\t\t\t\\\n+0 1 \\\t\t\t\\\n+0 1 /\\\t\t\t*/\\\n+1 1 /\\\t\t\t*/\\\\\n+1 1 foo\t\t\tfoo\n+1 1 @foo\t\t@foo\n+0 1 foo\t\t\t@foo\n+1 1 [ab]\t\t\\[ab]\n+1 1 [ab]\t\t[[]ab]\n+1 1 [ab]\t\t[[:]ab]\n+0 1 [ab]\t\t[[::]ab]\n+1 1 [ab]\t\t[[:digit]ab]\n+1 1 [ab]\t\t[\\[:]ab]\n+1 1 ?a?b\t\t\\??\\?b\n+1 1 abc\t\t\t\\a\\b\\c\n+0 1 foo\t\t\t''\n+1 1 foo/bar/baz/to\t**/t[o]\n+\n+# Character class tests\n+1 1 a1B\t\t[[:alpha:]][[:digit:]][[:upper:]]\n+0 1 a\t\t[[:digit:][:upper:][:space:]]\n+1 1 A\t\t[[:digit:][:upper:][:space:]]\n+1 1 1\t\t[[:digit:][:upper:][:space:]]\n+0 1 1\t\t[[:digit:][:upper:][:spaci:]]\n+1 1 ' '\t\t[[:digit:][:upper:][:space:]]\n+0 1 .\t\t[[:digit:][:upper:][:space:]]\n+1 1 .\t\t[[:digit:][:punct:][:space:]]\n+1 1 5\t\t[[:xdigit:]]\n+1 1 f\t\t[[:xdigit:]]\n+1 1 D\t\t[[:xdigit:]]\n+1 1 _\t\t[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]\n+#1 1 �\t\t[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]\n+1 1 \t\t[^[:alnum:][:alpha:][:blank:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]\n+1 1 .\t\t[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]\n+1 1 5\t\t[a-c[:digit:]x-z]\n+1 1 b\t\t[a-c[:digit:]x-z]\n+1 1 y\t\t[a-c[:digit:]x-z]\n+0 1 q\t\t[a-c[:digit:]x-z]\n+\n+# Additional tests, including some malformed wildmats\n+1 1 ]\t\t[\\\\-^]\n+0 1 [\t\t[\\\\-^]\n+1 1 -\t\t[\\-_]\n+1 1 ]\t\t[\\]]\n+0 1 \\]\t\t[\\]]\n+0 1 \\\t\t[\\]]\n+0 1 ab\t\ta[]b\n+0 1 a[]b\ta[]b\n+0 1 ab[\t\tab[\n+0 1 ab\t\t[!\n+0 1 ab\t\t[-\n+1 1 -\t\t[-]\n+0 1 -\t\t[a-\n+0 1 -\t\t[!a-\n+1 1 -\t\t[--A]\n+1 1 5\t\t[--A]\n+1 1 ' '\t\t'[ --]'\n+1 1 $\t\t'[ --]'\n+1 1 -\t\t'[ --]'\n+0 1 0\t\t'[ --]'\n+1 1 -\t\t[---]\n+1 1 -\t\t[------]\n+0 1 j\t\t[a-e-n]\n+1 1 -\t\t[a-e-n]\n+1 1 a\t\t[!------]\n+0 1 [\t\t[]-a]\n+1 1 ^\t\t[]-a]\n+0 1 ^\t\t[!]-a]\n+1 1 [\t\t[!]-a]\n+1 1 ^\t\t[a^bc]\n+1 1 -b]\t\t[a-]b]\n+0 1 \\\t\t[\\]\n+1 1 \\\t\t[\\\\]\n+0 1 \\\t\t[!\\\\]\n+1 1 G\t\t[A-\\\\]\n+0 1 aaabbb\tb*a\n+0 1 aabcaa\t*ba*\n+1 1 ,\t\t[,]\n+1 1 ,\t\t[\\\\,]\n+1 1 \\\t\t[\\\\,]\n+1 1 -\t\t[,-.]\n+0 1 +\t\t[,-.]\n+0 1 -.]\t\t[,-.]\n+1 1 2\t\t[\\1-\\3]\n+1 1 3\t\t[\\1-\\3]\n+0 1 4\t\t[\\1-\\3]\n+1 1 \\\t\t[[-\\]]\n+1 1 [\t\t[[-\\]]\n+1 1 ]\t\t[[-\\]]\n+0 1 -\t\t[[-\\]]\n+\n+# Test recursion and the abort code (use \"wildtest -i\" to see iteration counts)\n+1 1 -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1\t-*-*-*-*-*-*-12-*-*-*-m-*-*-*\n+0 1 -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1\t-*-*-*-*-*-*-12-*-*-*-m-*-*-*\n+0 1 -adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1\t-*-*-*-*-*-*-12-*-*-*-m-*-*-*\n+1 1 /adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1\t/*/*/*/*/*/*/12/*/*/*/m/*/*/*\n+0 1 /adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1\t/*/*/*/*/*/*/12/*/*/*/m/*/*/*\n+1 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt\t\t**/*a*b*g*n*t\n+0 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz\t\t**/*a*b*g*n*t\ndiff --git a/test-wildmatch.c b/test-wildmatch.c\nnew file mode 100644\nindex 0000000..88585c2\n--- /dev/null\n+++ b/test-wildmatch.c\n@@ -0,0 +1,222 @@\n+/*\n+ * Test suite for the wildmatch code.\n+ *\n+ * Copyright (C) 2003-2009 Wayne Davison\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License as published by\n+ * the Free Software Foundation; either version 3 of the License, or\n+ * (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License along\n+ * with this program; if not, visit the http://fsf.org website.\n+ */\n+\n+/*#define COMPARE_WITH_FNMATCH*/\n+\n+#define WILD_TEST_ITERATIONS\n+#include \"lib/wildmatch.c\"\n+\n+#include <popt.h>\n+\n+#ifdef COMPARE_WITH_FNMATCH\n+#include <fnmatch.h>\n+\n+int fnmatch_errors = 0;\n+#endif\n+\n+int wildmatch_errors = 0;\n+char number_separator = ',';\n+\n+typedef char bool;\n+\n+int output_iterations = 0;\n+int explode_mod = 0;\n+int empties_mod = 0;\n+int empty_at_start = 0;\n+int empty_at_end = 0;\n+\n+static struct poptOption long_options[] = {\n+  /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */\n+  {\"iterations\",     'i', POPT_ARG_NONE,   &output_iterations, 0, 0, 0},\n+  {\"empties\",        'e', POPT_ARG_STRING, 0, 'e', 0, 0},\n+  {\"explode\",        'x', POPT_ARG_INT,    &explode_mod, 0, 0, 0},\n+  {0,0,0,0, 0, 0, 0}\n+};\n+\n+/* match just at the start of string (anchored tests) */\n+static void\n+run_test(int line, bool matches,\n+#ifdef COMPARE_WITH_FNMATCH\n+\t bool same_as_fnmatch,\n+#endif\n+\t const char *text, const char *pattern)\n+{\n+    bool matched;\n+#ifdef COMPARE_WITH_FNMATCH\n+    bool fn_matched;\n+    int flags = strstr(pattern, \"**\")? 0 : FNM_PATHNAME;\n+#endif\n+\n+    if (explode_mod) {\n+\tchar buf[MAXPATHLEN*2], *texts[MAXPATHLEN];\n+\tint pos = 0, cnt = 0, ndx = 0, len = strlen(text);\n+\n+\tif (empty_at_start)\n+\t    texts[ndx++] = \"\";\n+\t/* An empty string must turn into at least one empty array item. */\n+\twhile (1) {\n+\t    texts[ndx] = buf + ndx * (explode_mod + 1);\n+\t    strlcpy(texts[ndx++], text + pos, explode_mod + 1);\n+\t    if (pos + explode_mod >= len)\n+\t\tbreak;\n+\t    pos += explode_mod;\n+\t    if (!(++cnt % empties_mod))\n+\t\ttexts[ndx++] = \"\";\n+\t}\n+\tif (empty_at_end)\n+\t    texts[ndx++] = \"\";\n+\ttexts[ndx] = NULL;\n+\tmatched = wildmatch_array(pattern, (const char**)texts, 0);\n+    } else\n+\tmatched = wildmatch(pattern, text);\n+#ifdef COMPARE_WITH_FNMATCH\n+    fn_matched = !fnmatch(pattern, text, flags);\n+#endif\n+    if (matched != matches) {\n+\tprintf(\"wildmatch failure on line %d:\\n  %s\\n  %s\\n  expected %s match\\n\",\n+\t       line, text, pattern, matches? \"a\" : \"NO\");\n+\twildmatch_errors++;\n+    }\n+#ifdef COMPARE_WITH_FNMATCH\n+    if (fn_matched != (matches ^ !same_as_fnmatch)) {\n+\tprintf(\"fnmatch disagreement on line %d:\\n  %s\\n  %s\\n  expected %s match\\n\",\n+\t       line, text, pattern, matches ^ !same_as_fnmatch? \"a\" : \"NO\");\n+\tfnmatch_errors++;\n+    }\n+#endif\n+    if (output_iterations) {\n+\tprintf(\"%d: \\\"%s\\\" iterations = %d\\n\", line, pattern,\n+\t       wildmatch_iteration_count);\n+    }\n+}\n+\n+int\n+main(int argc, char **argv)\n+{\n+    char buf[2048], *s, *string[2], *end[2];\n+    const char *arg;\n+    FILE *fp;\n+    int opt, line, i, flag[2];\n+    poptContext pc = poptGetContext(\"wildtest\", argc, (const char**)argv,\n+\t\t\t\t    long_options, 0);\n+\n+    while ((opt = poptGetNextOpt(pc)) != -1) {\n+\tswitch (opt) {\n+\t  case 'e':\n+\t    arg = poptGetOptArg(pc);\n+\t    empties_mod = atoi(arg);\n+\t    if (strchr(arg, 's'))\n+\t\tempty_at_start = 1;\n+\t    if (strchr(arg, 'e'))\n+\t\tempty_at_end = 1;\n+\t    if (!explode_mod)\n+\t\texplode_mod = 1024;\n+\t    break;\n+\t  default:\n+\t    fprintf(stderr, \"%s: %s\\n\",\n+\t\t    poptBadOption(pc, POPT_BADOPTION_NOALIAS),\n+\t\t    poptStrerror(opt));\n+\t    exit(1);\n+\t}\n+    }\n+\n+    if (explode_mod && !empties_mod)\n+\tempties_mod = 1024;\n+\n+    argv = (char**)poptGetArgs(pc);\n+    if (!argv || argv[1]) {\n+\tfprintf(stderr, \"Usage: wildtest [OPTIONS] TESTFILE\\n\");\n+\texit(1);\n+    }\n+\n+    if ((fp = fopen(*argv, \"r\")) == NULL) {\n+\tfprintf(stderr, \"Unable to open %s\\n\", *argv);\n+\texit(1);\n+    }\n+\n+    line = 0;\n+    while (fgets(buf, sizeof buf, fp)) {\n+\tline++;\n+\tif (*buf == '#' || *buf == '\\n')\n+\t    continue;\n+\tfor (s = buf, i = 0; i <= 1; i++) {\n+\t    if (*s == '1')\n+\t\tflag[i] = 1;\n+\t    else if (*s == '0')\n+\t\tflag[i] = 0;\n+\t    else\n+\t\tflag[i] = -1;\n+\t    if (*++s != ' ' && *s != '\\t')\n+\t\tflag[i] = -1;\n+\t    if (flag[i] < 0) {\n+\t\tfprintf(stderr, \"Invalid flag syntax on line %d of %s:\\n%s\",\n+\t\t\tline, *argv, buf);\n+\t\texit(1);\n+\t    }\n+\t    while (*++s == ' ' || *s == '\\t') {}\n+\t}\n+\tfor (i = 0; i <= 1; i++) {\n+\t    if (*s == '\\'' || *s == '\"' || *s == '`') {\n+\t\tchar quote = *s++;\n+\t\tstring[i] = s;\n+\t\twhile (*s && *s != quote) s++;\n+\t\tif (!*s) {\n+\t\t    fprintf(stderr, \"Unmatched quote on line %d of %s:\\n%s\",\n+\t\t\t    line, *argv, buf);\n+\t\t    exit(1);\n+\t\t}\n+\t\tend[i] = s;\n+\t    }\n+\t    else {\n+\t\tif (!*s || *s == '\\n') {\n+\t\t    fprintf(stderr, \"Not enough strings on line %d of %s:\\n%s\",\n+\t\t\t    line, *argv, buf);\n+\t\t    exit(1);\n+\t\t}\n+\t\tstring[i] = s;\n+\t\twhile (*++s && *s != ' ' && *s != '\\t' && *s != '\\n') {}\n+\t\tend[i] = s;\n+\t    }\n+\t    while (*++s == ' ' || *s == '\\t') {}\n+\t}\n+\t*end[0] = *end[1] = '\\0';\n+\trun_test(line, flag[0],\n+#ifdef COMPARE_WITH_FNMATCH\n+\t\t flag[1],\n+#endif\n+\t\t string[0], string[1]);\n+    }\n+\n+    if (!wildmatch_errors)\n+\tfputs(\"No\", stdout);\n+    else\n+\tprintf(\"%d\", wildmatch_errors);\n+    printf(\" wildmatch error%s found.\\n\", wildmatch_errors == 1? \"\" : \"s\");\n+\n+#ifdef COMPARE_WITH_FNMATCH\n+    if (!fnmatch_errors)\n+\tfputs(\"No\", stdout);\n+    else\n+\tprintf(\"%d\", fnmatch_errors);\n+    printf(\" fnmatch error%s found.\\n\", fnmatch_errors == 1? \"\" : \"s\");\n+\n+#endif\n+\n+    return 0;\n+}\ndiff --git a/wildmatch.c b/wildmatch.c\nnew file mode 100644\nindex 0000000..f3a1731\n--- /dev/null\n+++ b/wildmatch.c\n@@ -0,0 +1,368 @@\n+/*\n+**  Do shell-style pattern matching for ?, \\, [], and * characters.\n+**  It is 8bit clean.\n+**\n+**  Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.\n+**  Rich $alz is now <rsalz@bbn.com>.\n+**\n+**  Modified by Wayne Davison to special-case '/' matching, to make '**'\n+**  work differently than '*', and to fix the character-class code.\n+*/\n+\n+#include \"rsync.h\"\n+\n+/* What character marks an inverted character class? */\n+#define NEGATE_CLASS\t'!'\n+#define NEGATE_CLASS2\t'^'\n+\n+#define FALSE 0\n+#define TRUE 1\n+#define ABORT_ALL -1\n+#define ABORT_TO_STARSTAR -2\n+\n+#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \\\n+\t\t\t\t    && *(class) == *(litmatch) \\\n+\t\t\t\t    && strncmp((char*)class, litmatch, len) == 0)\n+\n+#if defined STDC_HEADERS || !defined isascii\n+# define ISASCII(c) 1\n+#else\n+# define ISASCII(c) isascii(c)\n+#endif\n+\n+#ifdef isblank\n+# define ISBLANK(c) (ISASCII(c) && isblank(c))\n+#else\n+# define ISBLANK(c) ((c) == ' ' || (c) == '\\t')\n+#endif\n+\n+#ifdef isgraph\n+# define ISGRAPH(c) (ISASCII(c) && isgraph(c))\n+#else\n+# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))\n+#endif\n+\n+#define ISPRINT(c) (ISASCII(c) && isprint(c))\n+#define ISDIGIT(c) (ISASCII(c) && isdigit(c))\n+#define ISALNUM(c) (ISASCII(c) && isalnum(c))\n+#define ISALPHA(c) (ISASCII(c) && isalpha(c))\n+#define ISCNTRL(c) (ISASCII(c) && iscntrl(c))\n+#define ISLOWER(c) (ISASCII(c) && islower(c))\n+#define ISPUNCT(c) (ISASCII(c) && ispunct(c))\n+#define ISSPACE(c) (ISASCII(c) && isspace(c))\n+#define ISUPPER(c) (ISASCII(c) && isupper(c))\n+#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))\n+\n+#ifdef WILD_TEST_ITERATIONS\n+int wildmatch_iteration_count;\n+#endif\n+\n+static int force_lower_case = 0;\n+\n+/* Match pattern \"p\" against the a virtually-joined string consisting\n+ * of \"text\" and any strings in array \"a\". */\n+static int dowild(const uchar *p, const uchar *text, const uchar*const *a)\n+{\n+    uchar p_ch;\n+\n+#ifdef WILD_TEST_ITERATIONS\n+    wildmatch_iteration_count++;\n+#endif\n+\n+    for ( ; (p_ch = *p) != '\\0'; text++, p++) {\n+\tint matched, special;\n+\tuchar t_ch, prev_ch;\n+\twhile ((t_ch = *text) == '\\0') {\n+\t    if (*a == NULL) {\n+\t\tif (p_ch != '*')\n+\t\t    return ABORT_ALL;\n+\t\tbreak;\n+\t    }\n+\t    text = *a++;\n+\t}\n+\tif (force_lower_case && ISUPPER(t_ch))\n+\t    t_ch = tolower(t_ch);\n+\tswitch (p_ch) {\n+\t  case '\\\\':\n+\t    /* Literal match with following character.  Note that the test\n+\t     * in \"default\" handles the p[1] == '\\0' failure case. */\n+\t    p_ch = *++p;\n+\t    /* FALLTHROUGH */\n+\t  default:\n+\t    if (t_ch != p_ch)\n+\t\treturn FALSE;\n+\t    continue;\n+\t  case '?':\n+\t    /* Match anything but '/'. */\n+\t    if (t_ch == '/')\n+\t\treturn FALSE;\n+\t    continue;\n+\t  case '*':\n+\t    if (*++p == '*') {\n+\t\twhile (*++p == '*') {}\n+\t\tspecial = TRUE;\n+\t    } else\n+\t\tspecial = FALSE;\n+\t    if (*p == '\\0') {\n+\t\t/* Trailing \"**\" matches everything.  Trailing \"*\" matches\n+\t\t * only if there are no more slash characters. */\n+\t\tif (!special) {\n+\t\t    do {\n+\t\t\tif (strchr((char*)text, '/') != NULL)\n+\t\t\t    return FALSE;\n+\t\t    } while ((text = *a++) != NULL);\n+\t\t}\n+\t\treturn TRUE;\n+\t    }\n+\t    while (1) {\n+\t\tif (t_ch == '\\0') {\n+\t\t    if ((text = *a++) == NULL)\n+\t\t\tbreak;\n+\t\t    t_ch = *text;\n+\t\t    continue;\n+\t\t}\n+\t\tif ((matched = dowild(p, text, a)) != FALSE) {\n+\t\t    if (!special || matched != ABORT_TO_STARSTAR)\n+\t\t\treturn matched;\n+\t\t} else if (!special && t_ch == '/')\n+\t\t    return ABORT_TO_STARSTAR;\n+\t\tt_ch = *++text;\n+\t    }\n+\t    return ABORT_ALL;\n+\t  case '[':\n+\t    p_ch = *++p;\n+#ifdef NEGATE_CLASS2\n+\t    if (p_ch == NEGATE_CLASS2)\n+\t\tp_ch = NEGATE_CLASS;\n+#endif\n+\t    /* Assign literal TRUE/FALSE because of \"matched\" comparison. */\n+\t    special = p_ch == NEGATE_CLASS? TRUE : FALSE;\n+\t    if (special) {\n+\t\t/* Inverted character class. */\n+\t\tp_ch = *++p;\n+\t    }\n+\t    prev_ch = 0;\n+\t    matched = FALSE;\n+\t    do {\n+\t\tif (!p_ch)\n+\t\t    return ABORT_ALL;\n+\t\tif (p_ch == '\\\\') {\n+\t\t    p_ch = *++p;\n+\t\t    if (!p_ch)\n+\t\t\treturn ABORT_ALL;\n+\t\t    if (t_ch == p_ch)\n+\t\t\tmatched = TRUE;\n+\t\t} else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {\n+\t\t    p_ch = *++p;\n+\t\t    if (p_ch == '\\\\') {\n+\t\t\tp_ch = *++p;\n+\t\t\tif (!p_ch)\n+\t\t\t    return ABORT_ALL;\n+\t\t    }\n+\t\t    if (t_ch <= p_ch && t_ch >= prev_ch)\n+\t\t\tmatched = TRUE;\n+\t\t    p_ch = 0; /* This makes \"prev_ch\" get set to 0. */\n+\t\t} else if (p_ch == '[' && p[1] == ':') {\n+\t\t    const uchar *s;\n+\t\t    int i;\n+\t\t    for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/\n+\t\t    if (!p_ch)\n+\t\t\treturn ABORT_ALL;\n+\t\t    i = p - s - 1;\n+\t\t    if (i < 0 || p[-1] != ':') {\n+\t\t\t/* Didn't find \":]\", so treat like a normal set. */\n+\t\t\tp = s - 2;\n+\t\t\tp_ch = '[';\n+\t\t\tif (t_ch == p_ch)\n+\t\t\t    matched = TRUE;\n+\t\t\tcontinue;\n+\t\t    }\n+\t\t    if (CC_EQ(s,i, \"alnum\")) {\n+\t\t\tif (ISALNUM(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"alpha\")) {\n+\t\t\tif (ISALPHA(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"blank\")) {\n+\t\t\tif (ISBLANK(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"cntrl\")) {\n+\t\t\tif (ISCNTRL(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"digit\")) {\n+\t\t\tif (ISDIGIT(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"graph\")) {\n+\t\t\tif (ISGRAPH(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"lower\")) {\n+\t\t\tif (ISLOWER(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"print\")) {\n+\t\t\tif (ISPRINT(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"punct\")) {\n+\t\t\tif (ISPUNCT(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"space\")) {\n+\t\t\tif (ISSPACE(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"upper\")) {\n+\t\t\tif (ISUPPER(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else if (CC_EQ(s,i, \"xdigit\")) {\n+\t\t\tif (ISXDIGIT(t_ch))\n+\t\t\t    matched = TRUE;\n+\t\t    } else /* malformed [:class:] string */\n+\t\t\treturn ABORT_ALL;\n+\t\t    p_ch = 0; /* This makes \"prev_ch\" get set to 0. */\n+\t\t} else if (t_ch == p_ch)\n+\t\t    matched = TRUE;\n+\t    } while (prev_ch = p_ch, (p_ch = *++p) != ']');\n+\t    if (matched == special || t_ch == '/')\n+\t\treturn FALSE;\n+\t    continue;\n+\t}\n+    }\n+\n+    do {\n+\tif (*text)\n+\t    return FALSE;\n+    } while ((text = *a++) != NULL);\n+\n+    return TRUE;\n+}\n+\n+/* Match literal string \"s\" against the a virtually-joined string consisting\n+ * of \"text\" and any strings in array \"a\". */\n+static int doliteral(const uchar *s, const uchar *text, const uchar*const *a)\n+{\n+    for ( ; *s != '\\0'; text++, s++) {\n+\twhile (*text == '\\0') {\n+\t    if ((text = *a++) == NULL)\n+\t\treturn FALSE;\n+\t}\n+\tif (*text != *s)\n+\t    return FALSE;\n+    }\n+\n+    do {\n+\tif (*text)\n+\t    return FALSE;\n+    } while ((text = *a++) != NULL);\n+\n+    return TRUE;\n+}\n+\n+/* Return the last \"count\" path elements from the concatenated string.\n+ * We return a string pointer to the start of the string, and update the\n+ * array pointer-pointer to point to any remaining string elements. */\n+static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)\n+{\n+    const uchar*const *a = *a_ptr;\n+    const uchar*const *first_a = a;\n+\n+    while (*a)\n+\t    a++;\n+\n+    while (a != first_a) {\n+\tconst uchar *s = *--a;\n+\ts += strlen((char*)s);\n+\twhile (--s >= *a) {\n+\t    if (*s == '/' && !--count) {\n+\t\t*a_ptr = a+1;\n+\t\treturn s+1;\n+\t    }\n+\t}\n+    }\n+\n+    if (count == 1) {\n+\t*a_ptr = a+1;\n+\treturn *a;\n+    }\n+\n+    return NULL;\n+}\n+\n+/* Match the \"pattern\" against the \"text\" string. */\n+int wildmatch(const char *pattern, const char *text)\n+{\n+    static const uchar *nomore[1]; /* A NULL pointer. */\n+#ifdef WILD_TEST_ITERATIONS\n+    wildmatch_iteration_count = 0;\n+#endif\n+    return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;\n+}\n+\n+/* Match the \"pattern\" against the forced-to-lower-case \"text\" string. */\n+int iwildmatch(const char *pattern, const char *text)\n+{\n+    static const uchar *nomore[1]; /* A NULL pointer. */\n+    int ret;\n+#ifdef WILD_TEST_ITERATIONS\n+    wildmatch_iteration_count = 0;\n+#endif\n+    force_lower_case = 1;\n+    ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;\n+    force_lower_case = 0;\n+    return ret;\n+}\n+\n+/* Match pattern \"p\" against the a virtually-joined string consisting\n+ * of all the pointers in array \"texts\" (which has a NULL pointer at the\n+ * end).  The int \"where\" can be 0 (normal matching), > 0 (match only\n+ * the trailing N slash-separated filename components of \"texts\"), or < 0\n+ * (match the \"pattern\" at the start or after any slash in \"texts\"). */\n+int wildmatch_array(const char *pattern, const char*const *texts, int where)\n+{\n+    const uchar *p = (const uchar*)pattern;\n+    const uchar*const *a = (const uchar*const*)texts;\n+    const uchar *text;\n+    int matched;\n+\n+#ifdef WILD_TEST_ITERATIONS\n+    wildmatch_iteration_count = 0;\n+#endif\n+\n+    if (where > 0)\n+\ttext = trailing_N_elements(&a, where);\n+    else\n+\ttext = *a++;\n+    if (!text)\n+\treturn FALSE;\n+\n+    if ((matched = dowild(p, text, a)) != TRUE && where < 0\n+     && matched != ABORT_ALL) {\n+\twhile (1) {\n+\t    if (*text == '\\0') {\n+\t\tif ((text = (uchar*)*a++) == NULL)\n+\t\t    return FALSE;\n+\t\tcontinue;\n+\t    }\n+\t    if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE\n+\t     && matched != ABORT_TO_STARSTAR)\n+\t\tbreak;\n+\t}\n+    }\n+    return matched == TRUE;\n+}\n+\n+/* Match literal string \"s\" against the a virtually-joined string consisting\n+ * of all the pointers in array \"texts\" (which has a NULL pointer at the\n+ * end).  The int \"where\" can be 0 (normal matching), or > 0 (match\n+ * only the trailing N slash-separated filename components of \"texts\"). */\n+int litmatch_array(const char *string, const char*const *texts, int where)\n+{\n+    const uchar *s = (const uchar*)string;\n+    const uchar*const *a = (const uchar* const*)texts;\n+    const uchar *text;\n+\n+    if (where > 0)\n+\ttext = trailing_N_elements(&a, where);\n+    else\n+\ttext = *a++;\n+    if (!text)\n+\treturn FALSE;\n+\n+    return doliteral(s, text, a) == TRUE;\n+}\ndiff --git a/wildmatch.h b/wildmatch.h\nnew file mode 100644\nindex 0000000..e7f1a35\n--- /dev/null\n+++ b/wildmatch.h\n@@ -0,0 +1,6 @@\n+/* wildmatch.h */\n+\n+int wildmatch(const char *pattern, const char *text);\n+int iwildmatch(const char *pattern, const char *text);\n+int wildmatch_array(const char *pattern, const char*const *texts, int where);\n+int litmatch_array(const char *string, const char*const *texts, int where);\n-- \n1.7.12.403.gce5cf6f.dirty\n"},{"id":"199161","messageId":"1347809265-2457-3-git-send-email-pclouds@gmail.com","threadId":"31539","inReplyTo":"1347809265-2457-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 2/5] compat/wildmatch: remove static variable force_lower_case","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-16T15:27:42Z","receivedAt":"2012-09-16T15:27:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"One place less to worry about thread safety\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n wildmatch.c | 17 +++++++----------\n 1 file changed, 7 insertions(+), 10 deletions(-)\n\ndiff --git a/wildmatch.c b/wildmatch.c\nindex f3a1731..e824eb2 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -57,11 +57,10 @@\n int wildmatch_iteration_count;\n #endif\n \n-static int force_lower_case = 0;\n-\n /* Match pattern \"p\" against the a virtually-joined string consisting\n  * of \"text\" and any strings in array \"a\". */\n-static int dowild(const uchar *p, const uchar *text, const uchar*const *a)\n+static int dowild(const uchar *p, const uchar *text,\n+\t\t  const uchar*const *a, int force_lower_case)\n {\n     uchar p_ch;\n \n@@ -121,7 +120,7 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a)\n \t\t    t_ch = *text;\n \t\t    continue;\n \t\t}\n-\t\tif ((matched = dowild(p, text, a)) != FALSE) {\n+\t\tif ((matched = dowild(p, text, a, force_lower_case)) != FALSE) {\n \t\t    if (!special || matched != ABORT_TO_STARSTAR)\n \t\t\treturn matched;\n \t\t} else if (!special && t_ch == '/')\n@@ -291,7 +290,7 @@ int wildmatch(const char *pattern, const char *text)\n #ifdef WILD_TEST_ITERATIONS\n     wildmatch_iteration_count = 0;\n #endif\n-    return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;\n+    return dowild((const uchar*)pattern, (const uchar*)text, nomore, 0) == TRUE;\n }\n \n /* Match the \"pattern\" against the forced-to-lower-case \"text\" string. */\n@@ -302,9 +301,7 @@ int iwildmatch(const char *pattern, const char *text)\n #ifdef WILD_TEST_ITERATIONS\n     wildmatch_iteration_count = 0;\n #endif\n-    force_lower_case = 1;\n-    ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;\n-    force_lower_case = 0;\n+    ret = dowild((const uchar*)pattern, (const uchar*)text, nomore, 1) == TRUE;\n     return ret;\n }\n \n@@ -331,7 +328,7 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where)\n     if (!text)\n \treturn FALSE;\n \n-    if ((matched = dowild(p, text, a)) != TRUE && where < 0\n+    if ((matched = dowild(p, text, a, 0)) != TRUE && where < 0\n      && matched != ABORT_ALL) {\n \twhile (1) {\n \t    if (*text == '\\0') {\n@@ -339,7 +336,7 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where)\n \t\t    return FALSE;\n \t\tcontinue;\n \t    }\n-\t    if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE\n+\t    if (*text++ == '/' && (matched = dowild(p, text, a, 0)) != FALSE\n \t     && matched != ABORT_TO_STARSTAR)\n \t\tbreak;\n \t}\n-- \n1.7.12.403.gce5cf6f.dirty\n"},{"id":"199164","messageId":"1347809265-2457-4-git-send-email-pclouds@gmail.com","threadId":"31539","inReplyTo":"1347809265-2457-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 3/5] compat/wildmatch: fix case-insensitive matching","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-16T15:27:43Z","receivedAt":"2012-09-16T15:27:43Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"dowild() does case insensitive matching by lower-casing the text. That\nmeans lower case letters in patterns imply case-insensitive matching,\nbut upper case means exact matching.\n\nWe do not want that subtlety. Lower case pattern too so iwildmatch()\nalways does what we expect it to do.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n wildmatch.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/wildmatch.c b/wildmatch.c\nindex e824eb2..c7f7f9f 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -81,6 +81,8 @@ static int dowild(const uchar *p, const uchar *text,\n \t}\n \tif (force_lower_case && ISUPPER(t_ch))\n \t    t_ch = tolower(t_ch);\n+\tif (force_lower_case && ISUPPER(p_ch))\n+\t    p_ch = tolower(p_ch);\n \tswitch (p_ch) {\n \t  case '\\\\':\n \t    /* Literal match with following character.  Note that the test\n-- \n1.7.12.403.gce5cf6f.dirty\n"},{"id":"199163","messageId":"1347809265-2457-5-git-send-email-pclouds@gmail.com","threadId":"31539","inReplyTo":"1347809265-2457-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 4/5] Integrate wildmatch to git","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-16T15:27:44Z","receivedAt":"2012-09-16T15:27:44Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This makes wildmatch.c part of libgit.a and builds test-wildmatch\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore           |  1 +\n Makefile             |  6 ++++++\n t/t3070-wildmatch.sh | 27 +++++++++++++++++++++++++++\n test-wildmatch.c     |  8 +++++++-\n wildmatch.c          |  8 +++++++-\n 5 files changed, 48 insertions(+), 2 deletions(-)\n create mode 100755 t/t3070-wildmatch.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex 68fe464..54b1b3b 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -196,6 +196,7 @@\n /test-sigchain\n /test-subprocess\n /test-svn-fe\n+/test-wildmatch\n /common-cmds.h\n *.tar.gz\n *.dsc\ndiff --git a/Makefile b/Makefile\nindex 56301dc..745e88c 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -511,6 +511,7 @@ TEST_PROGRAMS_NEED_X += test-sha1\n TEST_PROGRAMS_NEED_X += test-sigchain\n TEST_PROGRAMS_NEED_X += test-subprocess\n TEST_PROGRAMS_NEED_X += test-svn-fe\n+TEST_PROGRAMS_NEED_X += test-wildmatch\n \n TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))\n \n@@ -683,6 +684,7 @@ LIB_H += userdiff.h\n LIB_H += utf8.h\n LIB_H += varint.h\n LIB_H += walker.h\n+LIB_H += wildmatch.h\n LIB_H += wt-status.h\n LIB_H += xdiff-interface.h\n LIB_H += xdiff/xdiff.h\n@@ -814,6 +816,7 @@ LIB_OBJS += utf8.o\n LIB_OBJS += varint.o\n LIB_OBJS += version.o\n LIB_OBJS += walker.o\n+LIB_OBJS += wildmatch.o\n LIB_OBJS += wrapper.o\n LIB_OBJS += write_or_die.o\n LIB_OBJS += ws.o\n@@ -2586,6 +2589,9 @@ test-svn-fe$X: vcs-svn/lib.a\n test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)\n \n+test-wildmatch$X: test-wildmatch.o GIT-LDFLAGS\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) -lpopt\n+\n check-sha1:: test-sha1$X\n \t./test-sha1.sh\n \ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nnew file mode 100755\nindex 0000000..7fb63ff\n--- /dev/null\n+++ b/t/t3070-wildmatch.sh\n@@ -0,0 +1,27 @@\n+#!/bin/sh\n+\n+test_description='wildmatch tests'\n+\n+. ./test-lib.sh\n+\n+test_wildmatch() {\n+    test_expect_success \"wildmatch $*\" \"\n+\ttest-wildmatch $* ../t3070-wildmatch/wildtest.txt >actual &&\n+\techo 'No wildmatch errors found.' >expected &&\n+\ttest_cmp expected actual\n+    \"\n+}\n+\n+test_wildmatch -x1\n+test_wildmatch -x1 -e1\n+test_wildmatch -x1 -else\n+test_wildmatch -x2\n+test_wildmatch -x2 -ese\n+test_wildmatch -x3\n+test_wildmatch -x3 -e1\n+test_wildmatch -x4\n+test_wildmatch -x4 -e2e\n+test_wildmatch -x5\n+test_wildmatch -x5 -es\n+\n+test_done\ndiff --git a/test-wildmatch.c b/test-wildmatch.c\nindex 88585c2..828188a 100644\n--- a/test-wildmatch.c\n+++ b/test-wildmatch.c\n@@ -20,7 +20,13 @@\n /*#define COMPARE_WITH_FNMATCH*/\n \n #define WILD_TEST_ITERATIONS\n-#include \"lib/wildmatch.c\"\n+#include \"wildmatch.c\"\n+\n+#define MAXPATHLEN 1024\n+#ifdef NO_STRLCPY\n+#include \"compat/strlcpy.c\"\n+#define strlcpy gitstrlcpy\n+#endif\n \n #include <popt.h>\n \ndiff --git a/wildmatch.c b/wildmatch.c\nindex c7f7f9f..625cb0c 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -9,7 +9,13 @@\n **  work differently than '*', and to fix the character-class code.\n */\n \n-#include \"rsync.h\"\n+#include <stddef.h>\n+#include <ctype.h>\n+#include <string.h>\n+\n+#include \"wildmatch.h\"\n+\n+typedef unsigned char uchar;\n \n /* What character marks an inverted character class? */\n #define NEGATE_CLASS\t'!'\n-- \n1.7.12.403.gce5cf6f.dirty\n"},{"id":"199162","messageId":"1347809265-2457-6-git-send-email-pclouds@gmail.com","threadId":"31539","inReplyTo":"1347809265-2457-1-git-send-email-pclouds@gmail.com","subject":"[PATCH v2 5/5] Support \"**\" in .gitignore and .gitattributes patterns using wildmatch()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-16T15:27:45Z","receivedAt":"2012-09-16T15:27:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/gitignore.txt        |  3 +++\n attr.c                             |  4 +++-\n dir.c                              |  5 ++++-\n t/t0003-attributes.sh              | 17 +++++++++++++++++\n t/t3001-ls-files-others-exclude.sh | 11 +++++++++++\n 5 files changed, 38 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt\nindex c1f692a..eb81d31 100644\n--- a/Documentation/gitignore.txt\n+++ b/Documentation/gitignore.txt\n@@ -93,6 +93,9 @@ PATTERN FORMAT\n    For example, \"Documentation/{asterisk}.html\" matches\n    \"Documentation/git.html\" but not \"Documentation/ppc/ppc.html\"\n    or \"tools/perf/Documentation/perf.html\".\n++\n+Contrary to fnmatch(3), git matches \"**\" to anything including\n+slashes, similar to rsync(1).\n \n  - A leading slash matches the beginning of the pathname.\n    For example, \"/{asterisk}.c\" matches \"cat-file.c\" but not\ndiff --git a/attr.c b/attr.c\nindex 3430faf..2cea18c 100644\n--- a/attr.c\n+++ b/attr.c\n@@ -12,6 +12,7 @@\n #include \"exec_cmd.h\"\n #include \"attr.h\"\n #include \"dir.h\"\n+#include \"wildmatch.h\"\n \n const char git_attr__true[] = \"(builtin)true\";\n const char git_attr__false[] = \"\\0(builtin)false\";\n@@ -666,7 +667,8 @@ static int path_matches(const char *pathname, int pathlen,\n \t\treturn 0;\n \tif (baselen != 0)\n \t\tbaselen++;\n-\treturn fnmatch_icase(pattern, pathname + baselen, FNM_PATHNAME) == 0;\n+\treturn (ignore_case && iwildmatch(pattern, pathname + baselen)) ||\n+\t\t(!ignore_case && wildmatch(pattern, pathname + baselen));\n }\n \n static int macroexpand_one(int attr_nr, int rem);\ndiff --git a/dir.c b/dir.c\nindex 4868339..55ab2b3 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -8,6 +8,7 @@\n #include \"cache.h\"\n #include \"dir.h\"\n #include \"refs.h\"\n+#include \"wildmatch.h\"\n \n struct path_simplify {\n \tint len;\n@@ -575,7 +576,9 @@ int excluded_from_list(const char *pathname,\n \t\t\tnamelen -= prefix;\n \t\t}\n \n-\t\tif (!namelen || !fnmatch_icase(exclude, name, FNM_PATHNAME))\n+\t\tif (!namelen ||\n+\t\t    ((ignore_case && iwildmatch(exclude, name)) ||\n+\t\t     (!ignore_case && wildmatch(exclude, name))))\n \t\t\treturn to_exclude;\n \t}\n \treturn -1; /* undecided */\ndiff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh\nindex febc45c..6c3c554 100755\n--- a/t/t0003-attributes.sh\n+++ b/t/t0003-attributes.sh\n@@ -232,4 +232,21 @@ test_expect_success 'bare repository: test info/attributes' '\n \tattr_check subdir/a/i unspecified\n '\n \n+test_expect_success '\"**\" test' '\n+\tcd .. &&\n+\techo \"**/f foo=bar\" >.gitattributes &&\n+\tcat <<\\EOF >expect &&\n+f: foo: unspecified\n+a/f: foo: bar\n+a/b/f: foo: bar\n+a/b/c/f: foo: bar\n+EOF\n+\tgit check-attr foo -- \"f\" >actual 2>err &&\n+\tgit check-attr foo -- \"a/f\" >>actual 2>>err &&\n+\tgit check-attr foo -- \"a/b/f\" >>actual 2>>err &&\n+\tgit check-attr foo -- \"a/b/c/f\" >>actual 2>>err &&\n+\ttest_cmp expect actual &&\n+\ttest_line_count = 0 err\n+'\n+\n test_done\ndiff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh\nindex c8fe978..67c8bcf 100755\n--- a/t/t3001-ls-files-others-exclude.sh\n+++ b/t/t3001-ls-files-others-exclude.sh\n@@ -214,4 +214,15 @@ test_expect_success 'subdirectory ignore (l1)' '\n \ttest_cmp expect actual\n '\n \n+\n+test_expect_success 'ls-files with \"**\" patterns' '\n+\tcat <<\\EOF >expect &&\n+one/a.1\n+one/two/a.1\n+three/a.1\n+EOF\n+\tgit ls-files -o -i --exclude \"**/a.1\" >actual\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n1.7.12.403.gce5cf6f.dirty\n"},{"id":"199188","messageId":"7vpq5lw6z8.fsf@alter.siamese.dyndns.org","threadId":"31539","inReplyTo":"1347809265-2457-5-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH v2 4/5] Integrate wildmatch to git","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-17T05:31:23Z","receivedAt":"2012-09-17T05:31:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Am I missing some includes?\n\ntest-wildmatch.c:50: error: array type has incomplete element type\ntest-wildmatch.c:52: error: 'POPT_ARG_NONE' undeclared here (not in a function)\ntest-wildmatch.c:53: error: 'POPT_ARG_STRING' undeclared here (not in a function)\ntest-wildmatch.c:54: error: 'POPT_ARG_INT' undeclared here (not in a function)\ntest-wildmatch.c: In function 'main':\ntest-wildmatch.c:122: error: 'poptContext' undeclared (first use in this function)\ntest-wildmatch.c:122: error: (Each undeclared identifier is reported only once\ntest-wildmatch.c:122: error: for each function it appears in.)\ntest-wildmatch.c:122: error: expected ';' before 'pc'\ncc1: warnings being treated as errors\ntest-wildmatch.c:125: error: implicit declaration of function 'poptGetNextOpt'\ntest-wildmatch.c:125: error: 'pc' undeclared (first use in this function)\ntest-wildmatch.c:128: error: implicit declaration of function 'poptGetOptArg'\ntest-wildmatch.c:139: error: implicit declaration of function 'poptBadOption'\ntest-wildmatch.c:139: error: 'POPT_BADOPTION_NOALIAS' undeclared (first use in this function)\ntest-wildmatch.c:140: error: implicit declaration of function 'poptStrerror'\ntest-wildmatch.c:148: error: implicit declaration of function 'poptGetArgs'\n"},{"id":"199192","messageId":"7vd31lw5vy.fsf@alter.siamese.dyndns.org","threadId":"31539","inReplyTo":"7vpq5lw6z8.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 4/5] Integrate wildmatch to git","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-17T05:54:57Z","receivedAt":"2012-09-17T05:54:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Am I missing some includes?\n>\n> test-wildmatch.c:50: error: array type has incomplete element type\n> test-wildmatch.c:52: error: 'POPT_ARG_NONE' undeclared here (not in a function)\n> test-wildmatch.c:53: error: 'POPT_ARG_STRING' undeclared here (not in a function)\n> test-wildmatch.c:54: error: 'POPT_ARG_INT' undeclared here (not in a function)\n> test-wildmatch.c: In function 'main':\n> test-wildmatch.c:122: error: 'poptContext' undeclared (first use in this function)\n> test-wildmatch.c:122: error: (Each undeclared identifier is reported only once\n> test-wildmatch.c:122: error: for each function it appears in.)\n> test-wildmatch.c:122: error: expected ';' before 'pc'\n> cc1: warnings being treated as errors\n> test-wildmatch.c:125: error: implicit declaration of function 'poptGetNextOpt'\n> test-wildmatch.c:125: error: 'pc' undeclared (first use in this function)\n> test-wildmatch.c:128: error: implicit declaration of function 'poptGetOptArg'\n> test-wildmatch.c:139: error: implicit declaration of function 'poptBadOption'\n> test-wildmatch.c:139: error: 'POPT_BADOPTION_NOALIAS' undeclared (first use in this function)\n> test-wildmatch.c:140: error: implicit declaration of function 'poptStrerror'\n> test-wildmatch.c:148: error: implicit declaration of function 'poptGetArgs'\n\nYeah, popt.h it is.  It is a bit distasteful that we have a build\ndependency only to build test-* helper on something that we do not\neven have runtime dependency on.\n"},{"id":"199193","messageId":"CACsJy8Bwf_Xm_dbo1YtV9nynBy=V0_tPnPHR8bskjuHe9hpLsQ@mail.gmail.com","threadId":"31539","inReplyTo":"7vd31lw5vy.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 4/5] Integrate wildmatch to git","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-17T05:57:13Z","receivedAt":"2012-09-17T05:57:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Sep 17, 2012 at 12:54 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Yeah, popt.h it is.  It is a bit distasteful that we have a build\n> dependency only to build test-* helper on something that we do not\n> even have runtime dependency on.\n\nYep. I don't know how popular libpopt is. But if it's undesired, we\ncould rewrite test-wildmatch.c to use parse-options.c instead. We fork\nfrom rsync anyway due to license conflict so we don't have to keep it\nclose to upstream any more.\n-- \nDuy\n"},{"id":"199222","messageId":"20120917124020.GA21798@lanh","threadId":"31539","inReplyTo":"7vd31lw5vy.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 4/5] Integrate wildmatch to git","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-17T12:40:20Z","receivedAt":"2012-09-17T12:40:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sun, Sep 16, 2012 at 10:54:57PM -0700, Junio C Hamano wrote:\n> Yeah, popt.h it is.  It is a bit distasteful that we have a build\n> dependency only to build test-* helper on something that we do not\n> even have runtime dependency on.\n\nPerhaps this squash-in? It kills libpopt and removes the \"#include\n\"wildmatch.c\"\". Now we really do test what libgit.a carries.\n\n-- 8< --\ndiff --git a/Makefile b/Makefile\nindex 745e88c..093ab9c 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -2589,9 +2589,6 @@ test-svn-fe$X: vcs-svn/lib.a\n test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)\n \n-test-wildmatch$X: test-wildmatch.o GIT-LDFLAGS\n-\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) -lpopt\n-\n check-sha1:: test-sha1$X\n \t./test-sha1.sh\n \ndiff --git a/test-wildmatch.c b/test-wildmatch.c\nindex 828188a..b94921b 100644\n--- a/test-wildmatch.c\n+++ b/test-wildmatch.c\n@@ -19,17 +19,18 @@\n \n /*#define COMPARE_WITH_FNMATCH*/\n \n-#define WILD_TEST_ITERATIONS\n-#include \"wildmatch.c\"\n+#include \"cache.h\"\n+#include \"parse-options.h\"\n+#include \"wildmatch.h\"\n \n+#ifndef MAXPATHLEN\n #define MAXPATHLEN 1024\n+#endif\n #ifdef NO_STRLCPY\n #include \"compat/strlcpy.c\"\n #define strlcpy gitstrlcpy\n #endif\n \n-#include <popt.h>\n-\n #ifdef COMPARE_WITH_FNMATCH\n #include <fnmatch.h>\n \n@@ -41,18 +42,16 @@ char number_separator = ',';\n \n typedef char bool;\n \n-int output_iterations = 0;\n int explode_mod = 0;\n int empties_mod = 0;\n int empty_at_start = 0;\n int empty_at_end = 0;\n+char *empties;\n \n-static struct poptOption long_options[] = {\n-  /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */\n-  {\"iterations\",     'i', POPT_ARG_NONE,   &output_iterations, 0, 0, 0},\n-  {\"empties\",        'e', POPT_ARG_STRING, 0, 'e', 0, 0},\n-  {\"explode\",        'x', POPT_ARG_INT,    &explode_mod, 0, 0, 0},\n-  {0,0,0,0, 0, 0, 0}\n+static struct option options[] = {\n+  OPT_STRING('e', \"empties\", &empties, \"\", \"\"),\n+  OPT_INTEGER('x', \"explode\", &explode_mod, \"\"),\n+  OPT_END(),\n };\n \n /* match just at the start of string (anchored tests) */\n@@ -106,51 +105,33 @@ run_test(int line, bool matches,\n \tfnmatch_errors++;\n     }\n #endif\n-    if (output_iterations) {\n-\tprintf(\"%d: \\\"%s\\\" iterations = %d\\n\", line, pattern,\n-\t       wildmatch_iteration_count);\n-    }\n }\n \n int\n main(int argc, char **argv)\n {\n     char buf[2048], *s, *string[2], *end[2];\n-    const char *arg;\n     FILE *fp;\n-    int opt, line, i, flag[2];\n-    poptContext pc = poptGetContext(\"wildtest\", argc, (const char**)argv,\n-\t\t\t\t    long_options, 0);\n-\n-    while ((opt = poptGetNextOpt(pc)) != -1) {\n-\tswitch (opt) {\n-\t  case 'e':\n-\t    arg = poptGetOptArg(pc);\n-\t    empties_mod = atoi(arg);\n-\t    if (strchr(arg, 's'))\n-\t\tempty_at_start = 1;\n-\t    if (strchr(arg, 'e'))\n-\t\tempty_at_end = 1;\n-\t    if (!explode_mod)\n-\t\texplode_mod = 1024;\n-\t    break;\n-\t  default:\n-\t    fprintf(stderr, \"%s: %s\\n\",\n-\t\t    poptBadOption(pc, POPT_BADOPTION_NOALIAS),\n-\t\t    poptStrerror(opt));\n-\t    exit(1);\n-\t}\n+    int line, i, flag[2];\n+    const char *help[] = { NULL };\n+\n+    argc = parse_options(argc, (const char **)argv, \"\", options, help, 0);\n+    if (argc != 1)\n+\t    die(\"redundant options\");\n+    if (empties) {\n+\tconst char *arg = empties;\n+\tempties_mod = atoi(arg);\n+\tif (strchr(empties, 's'))\n+\t    empty_at_start = 1;\n+\tif (strchr(arg, 'e'))\n+\t    empty_at_end = 1;\n+\tif (!explode_mod)\n+\t    explode_mod = 1024;\n     }\n \n     if (explode_mod && !empties_mod)\n \tempties_mod = 1024;\n \n-    argv = (char**)poptGetArgs(pc);\n-    if (!argv || argv[1]) {\n-\tfprintf(stderr, \"Usage: wildtest [OPTIONS] TESTFILE\\n\");\n-\texit(1);\n-    }\n-\n     if ((fp = fopen(*argv, \"r\")) == NULL) {\n \tfprintf(stderr, \"Unable to open %s\\n\", *argv);\n \texit(1);\ndiff --git a/wildmatch.c b/wildmatch.c\nindex 625cb0c..f153f8a 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -59,10 +59,6 @@ typedef unsigned char uchar;\n #define ISUPPER(c) (ISASCII(c) && isupper(c))\n #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))\n \n-#ifdef WILD_TEST_ITERATIONS\n-int wildmatch_iteration_count;\n-#endif\n-\n /* Match pattern \"p\" against the a virtually-joined string consisting\n  * of \"text\" and any strings in array \"a\". */\n static int dowild(const uchar *p, const uchar *text,\n@@ -70,10 +66,6 @@ static int dowild(const uchar *p, const uchar *text,\n {\n     uchar p_ch;\n \n-#ifdef WILD_TEST_ITERATIONS\n-    wildmatch_iteration_count++;\n-#endif\n-\n     for ( ; (p_ch = *p) != '\\0'; text++, p++) {\n \tint matched, special;\n \tuchar t_ch, prev_ch;\n@@ -295,9 +287,6 @@ static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)\n int wildmatch(const char *pattern, const char *text)\n {\n     static const uchar *nomore[1]; /* A NULL pointer. */\n-#ifdef WILD_TEST_ITERATIONS\n-    wildmatch_iteration_count = 0;\n-#endif\n     return dowild((const uchar*)pattern, (const uchar*)text, nomore, 0) == TRUE;\n }\n \n@@ -306,9 +295,6 @@ int iwildmatch(const char *pattern, const char *text)\n {\n     static const uchar *nomore[1]; /* A NULL pointer. */\n     int ret;\n-#ifdef WILD_TEST_ITERATIONS\n-    wildmatch_iteration_count = 0;\n-#endif\n     ret = dowild((const uchar*)pattern, (const uchar*)text, nomore, 1) == TRUE;\n     return ret;\n }\n@@ -325,10 +311,6 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where)\n     const uchar *text;\n     int matched;\n \n-#ifdef WILD_TEST_ITERATIONS\n-    wildmatch_iteration_count = 0;\n-#endif\n-\n     if (where > 0)\n \ttext = trailing_N_elements(&a, where);\n     else\n-- 8< --\n"},{"id":"199255","messageId":"7vtxuwva67.fsf@alter.siamese.dyndns.org","threadId":"31539","inReplyTo":"20120917124020.GA21798@lanh","subject":"Re: [PATCH v2 4/5] Integrate wildmatch to git","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-17T17:20:00Z","receivedAt":"2012-09-17T17:20:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> On Sun, Sep 16, 2012 at 10:54:57PM -0700, Junio C Hamano wrote:\n>> Yeah, popt.h it is.  It is a bit distasteful that we have a build\n>> dependency only to build test-* helper on something that we do not\n>> even have runtime dependency on.\n>\n> Perhaps this squash-in? It kills libpopt and removes the \"#include\n> \"wildmatch.c\"\". Now we really do test what libgit.a carries.\n\nSounds fine to me.  Thanks; will take a look.\n\n>\n> -- 8< --\n> diff --git a/Makefile b/Makefile\n> index 745e88c..093ab9c 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -2589,9 +2589,6 @@ test-svn-fe$X: vcs-svn/lib.a\n>  test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS)\n>  \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)\n>  \n> -test-wildmatch$X: test-wildmatch.o GIT-LDFLAGS\n> -\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) -lpopt\n> -\n>  check-sha1:: test-sha1$X\n>  \t./test-sha1.sh\n>  \n> diff --git a/test-wildmatch.c b/test-wildmatch.c\n> index 828188a..b94921b 100644\n> --- a/test-wildmatch.c\n> +++ b/test-wildmatch.c\n> @@ -19,17 +19,18 @@\n>  \n>  /*#define COMPARE_WITH_FNMATCH*/\n>  \n> -#define WILD_TEST_ITERATIONS\n> -#include \"wildmatch.c\"\n> +#include \"cache.h\"\n> +#include \"parse-options.h\"\n> +#include \"wildmatch.h\"\n>  \n> +#ifndef MAXPATHLEN\n>  #define MAXPATHLEN 1024\n> +#endif\n>  #ifdef NO_STRLCPY\n>  #include \"compat/strlcpy.c\"\n>  #define strlcpy gitstrlcpy\n>  #endif\n>  \n> -#include <popt.h>\n> -\n>  #ifdef COMPARE_WITH_FNMATCH\n>  #include <fnmatch.h>\n>  \n> @@ -41,18 +42,16 @@ char number_separator = ',';\n>  \n>  typedef char bool;\n>  \n> -int output_iterations = 0;\n>  int explode_mod = 0;\n>  int empties_mod = 0;\n>  int empty_at_start = 0;\n>  int empty_at_end = 0;\n> +char *empties;\n>  \n> -static struct poptOption long_options[] = {\n> -  /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */\n> -  {\"iterations\",     'i', POPT_ARG_NONE,   &output_iterations, 0, 0, 0},\n> -  {\"empties\",        'e', POPT_ARG_STRING, 0, 'e', 0, 0},\n> -  {\"explode\",        'x', POPT_ARG_INT,    &explode_mod, 0, 0, 0},\n> -  {0,0,0,0, 0, 0, 0}\n> +static struct option options[] = {\n> +  OPT_STRING('e', \"empties\", &empties, \"\", \"\"),\n> +  OPT_INTEGER('x', \"explode\", &explode_mod, \"\"),\n> +  OPT_END(),\n>  };\n>  \n>  /* match just at the start of string (anchored tests) */\n> @@ -106,51 +105,33 @@ run_test(int line, bool matches,\n>  \tfnmatch_errors++;\n>      }\n>  #endif\n> -    if (output_iterations) {\n> -\tprintf(\"%d: \\\"%s\\\" iterations = %d\\n\", line, pattern,\n> -\t       wildmatch_iteration_count);\n> -    }\n>  }\n>  \n>  int\n>  main(int argc, char **argv)\n>  {\n>      char buf[2048], *s, *string[2], *end[2];\n> -    const char *arg;\n>      FILE *fp;\n> -    int opt, line, i, flag[2];\n> -    poptContext pc = poptGetContext(\"wildtest\", argc, (const char**)argv,\n> -\t\t\t\t    long_options, 0);\n> -\n> -    while ((opt = poptGetNextOpt(pc)) != -1) {\n> -\tswitch (opt) {\n> -\t  case 'e':\n> -\t    arg = poptGetOptArg(pc);\n> -\t    empties_mod = atoi(arg);\n> -\t    if (strchr(arg, 's'))\n> -\t\tempty_at_start = 1;\n> -\t    if (strchr(arg, 'e'))\n> -\t\tempty_at_end = 1;\n> -\t    if (!explode_mod)\n> -\t\texplode_mod = 1024;\n> -\t    break;\n> -\t  default:\n> -\t    fprintf(stderr, \"%s: %s\\n\",\n> -\t\t    poptBadOption(pc, POPT_BADOPTION_NOALIAS),\n> -\t\t    poptStrerror(opt));\n> -\t    exit(1);\n> -\t}\n> +    int line, i, flag[2];\n> +    const char *help[] = { NULL };\n> +\n> +    argc = parse_options(argc, (const char **)argv, \"\", options, help, 0);\n> +    if (argc != 1)\n> +\t    die(\"redundant options\");\n> +    if (empties) {\n> +\tconst char *arg = empties;\n> +\tempties_mod = atoi(arg);\n> +\tif (strchr(empties, 's'))\n> +\t    empty_at_start = 1;\n> +\tif (strchr(arg, 'e'))\n> +\t    empty_at_end = 1;\n> +\tif (!explode_mod)\n> +\t    explode_mod = 1024;\n>      }\n>  \n>      if (explode_mod && !empties_mod)\n>  \tempties_mod = 1024;\n>  \n> -    argv = (char**)poptGetArgs(pc);\n> -    if (!argv || argv[1]) {\n> -\tfprintf(stderr, \"Usage: wildtest [OPTIONS] TESTFILE\\n\");\n> -\texit(1);\n> -    }\n> -\n>      if ((fp = fopen(*argv, \"r\")) == NULL) {\n>  \tfprintf(stderr, \"Unable to open %s\\n\", *argv);\n>  \texit(1);\n> diff --git a/wildmatch.c b/wildmatch.c\n> index 625cb0c..f153f8a 100644\n> --- a/wildmatch.c\n> +++ b/wildmatch.c\n> @@ -59,10 +59,6 @@ typedef unsigned char uchar;\n>  #define ISUPPER(c) (ISASCII(c) && isupper(c))\n>  #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))\n>  \n> -#ifdef WILD_TEST_ITERATIONS\n> -int wildmatch_iteration_count;\n> -#endif\n> -\n>  /* Match pattern \"p\" against the a virtually-joined string consisting\n>   * of \"text\" and any strings in array \"a\". */\n>  static int dowild(const uchar *p, const uchar *text,\n> @@ -70,10 +66,6 @@ static int dowild(const uchar *p, const uchar *text,\n>  {\n>      uchar p_ch;\n>  \n> -#ifdef WILD_TEST_ITERATIONS\n> -    wildmatch_iteration_count++;\n> -#endif\n> -\n>      for ( ; (p_ch = *p) != '\\0'; text++, p++) {\n>  \tint matched, special;\n>  \tuchar t_ch, prev_ch;\n> @@ -295,9 +287,6 @@ static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)\n>  int wildmatch(const char *pattern, const char *text)\n>  {\n>      static const uchar *nomore[1]; /* A NULL pointer. */\n> -#ifdef WILD_TEST_ITERATIONS\n> -    wildmatch_iteration_count = 0;\n> -#endif\n>      return dowild((const uchar*)pattern, (const uchar*)text, nomore, 0) == TRUE;\n>  }\n>  \n> @@ -306,9 +295,6 @@ int iwildmatch(const char *pattern, const char *text)\n>  {\n>      static const uchar *nomore[1]; /* A NULL pointer. */\n>      int ret;\n> -#ifdef WILD_TEST_ITERATIONS\n> -    wildmatch_iteration_count = 0;\n> -#endif\n>      ret = dowild((const uchar*)pattern, (const uchar*)text, nomore, 1) == TRUE;\n>      return ret;\n>  }\n> @@ -325,10 +311,6 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where)\n>      const uchar *text;\n>      int matched;\n>  \n> -#ifdef WILD_TEST_ITERATIONS\n> -    wildmatch_iteration_count = 0;\n> -#endif\n> -\n>      if (where > 0)\n>  \ttext = trailing_N_elements(&a, where);\n>      else\n> -- 8< --\n"},{"id":"199306","messageId":"98180D4AA94C468887029389CC535D4F@PhilipOakley","threadId":"31539","inReplyTo":"7vzk4rz00y.fsf@alter.siamese.dyndns.org","subject":"Re: How to create the \" [PATCH 0/5]\" first email?","fromName":"Philip Oakley","fromEmail":"philipoakley@iee.org","sentAt":"2012-09-17T22:55:58Z","receivedAt":"2012-09-17T22:55:58Z","isPatch":true,"sender":{"key":"philipoakley@iee.email","avatar":"https://avatars.githubusercontent.com/u/914343?v=4"},"body":"From: \"Junio C Hamano\" <gitster@pobox.com>\n> \"Philip Oakley\" <philipoakley@iee.org> writes:\n>\n>> \"Typically it will be placed in a MUA’s drafts folder, edited to add\n>> timely commentary that should not go in the changelog after the three\n>> dashes, and then sent as a message whose body, in our example, starts\n>> with \"arch/arm config files were…\". On the receiving end, readers \n>> can\n>> save interesting patches in a UNIX mailbox and apply them with\n>> git-am(1).\"\n>>\n>> hides a multitude of implicit knowledge steps. Is there an extended\n>> description of what that would mean from a platform independent\n>> viewpoint? e.g. if the patches are separte files and an mbox is one\n>> consolidated file, how to get from one to the other so that 'it' can \n>> be\n>> sent by 'git send-mail'.\n>\n> If you plan to use \"git send-email\" to send the final results out,\n> you should consider \"git send-email\" as your \"MUA\" in the quoted\n> paragraph.  And that will be very platform independent viewpoint to\n> see things from.\n\nOn git for windows (msysgit)n there were a couple of other steps I had \nto do https://git.wiki.kernel.org/index.php/MSysGit:UsingSendEmail as \nOutlooklook Express isn't really a suitable MUA ;-)  [noted for other \nlist readers]\n\n>\n> \"git format-patch -o my-series/ --cover-letter ...\"  would treat\n> \"my-series/\" directory as \"MUA's drafts folder\" and prepares the\n> messages you would want to send out, and you can proof-read and edit\n> the files in there before telling your \"MUA\" to send them out, with\n> \"git send-email ... my-series/*.patch\" or something.\n>\n\nI hadn't picked up from the man page that the --cover-letter would do \nthe [PATCH 0/n] - should it?\n\n>> I'm also missing an understanding of the preparation stage where one\n>> tries to tidy up the various commit messages becaue they weren't\n>> explicit, specfic nor concise enough,...\n>\n> Many people usually do \"rebase -i\" until perfection and then a\n> single final invocation of \"format-patch\".  Of course, the \"final\"\n> can and should further be proof-read and it is fine to do typofixes\n> in the format-patch output files without going back to the commits\n> before sending them out.\n\nI did an initial rebase to correct a few obvious mistakes (e.g. an extra \nfile that had crept in), but then, after some false starts, used\n    git format-patch pu --stdout > fix_Docs.patch\nto get a single file I could inspect and refine for both the commit \nmessages and content.\n\nI then applied it (using git am) to a temp branch to see what it \nproduced, and could repeat the cycle until the patches looked right.\n>\n>> ...so I suspect that there is an\n>> implicit `git format-patch` <-> `git am` loop of sharpening the mbox\n>> patches before submission to the list that I'm missing. Has this\n>> described somewhere?\n> --\n\nHowever, when it came to creating the series, with comments, I couldn't \nsee a way of having my comments within my local commits, but preparing a \npatch series that would properly include the '---' separator.\n\nIs there a way of getting format-patch to change some line break \nsequence (within the commit message) to the '---' three dashes patch \nbreak suitable for submission to the list?\n\nPhilip\n"},{"id":"199308","messageId":"7vfw6gqkfg.fsf@alter.siamese.dyndns.org","threadId":"31539","inReplyTo":"98180D4AA94C468887029389CC535D4F@PhilipOakley","subject":"Re: How to create the \" [PATCH 0/5]\" first email?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-17T23:49:39Z","receivedAt":"2012-09-17T23:49:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Philip Oakley\" <philipoakley@iee.org> writes:\n\n> I then applied it (using git am) to a temp branch to see what it\n> produced, and could repeat the cycle until the patches looked right.\n\nThat's another obvious and valid way to prepare your series.  It all\ndepends on how comfortable you are to directly edit patches.  Some\npeople fear it.  Some don't.  Some can do it with their eyes closed ;-).\n\n> However, when it came to creating the series, with comments, I\n> couldn't see a way of having my comments within my local commits, but\n> preparing a patch series that would properly include the '---'\n> separator.\n\nAn unofficial trick that works is to write the\n\n    ---\n\n     * This is an additional comment\n\n\nyourself when running \"git commit\".  That will be propagated to the\noutput from format-patch.  You will have another \"---\" in front of\nthe diffstat, but nobody is hurt by that.\n\nBut when doing a big series that deserves a cover letter [PATCH 0/n],\nyou will use editor on the output from format-patch anyway, and I\nfind it simpler to do the follow-on comments at that point myself.\n\nPersonal preferences vary, so whatever makes you feel comfortable\nwith and works well for you is good.\n"},{"id":"199312","messageId":"20120918001522.GA10283@sigill.intra.peff.net","threadId":"31539","inReplyTo":"7vfw6gqkfg.fsf@alter.siamese.dyndns.org","subject":"Re: How to create the \" [PATCH 0/5]\" first email?","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-09-18T00:15:22Z","receivedAt":"2012-09-18T00:15:22Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Sep 17, 2012 at 04:49:39PM -0700, Junio C Hamano wrote:\n\n> > However, when it came to creating the series, with comments, I\n> > couldn't see a way of having my comments within my local commits, but\n> > preparing a patch series that would properly include the '---'\n> > separator.\n> \n> An unofficial trick that works is to write the\n> \n>     ---\n> \n>      * This is an additional comment\n> \n> \n> yourself when running \"git commit\".  That will be propagated to the\n> output from format-patch.  You will have another \"---\" in front of\n> the diffstat, but nobody is hurt by that.\n\nBut note that using \"format-patch -s\" will break; it puts the sign-off\nbelow the \"---\".\n\n> But when doing a big series that deserves a cover letter [PATCH 0/n],\n> you will use editor on the output from format-patch anyway, and I\n> find it simpler to do the follow-on comments at that point myself.\n\nMe too (actually, I load it all into mutt and then comment on each as I\nsend them out, but it amounts to the same thing, seeing as how my MUA\njust invokes $EDITOR when I edit a mail).\n\n-Peff\n"},{"id":"199313","messageId":"7v7grsqi98.fsf@alter.siamese.dyndns.org","threadId":"31539","inReplyTo":"20120918001522.GA10283@sigill.intra.peff.net","subject":"Re: How to create the \" [PATCH 0/5]\" first email?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-18T00:36:35Z","receivedAt":"2012-09-18T00:36:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Mon, Sep 17, 2012 at 04:49:39PM -0700, Junio C Hamano wrote:\n>\n>> > However, when it came to creating the series, with comments, I\n>> > couldn't see a way of having my comments within my local commits, but\n>> > preparing a patch series that would properly include the '---'\n>> > separator.\n>> \n>> An unofficial trick that works is to write the\n>> \n>>     ---\n>> \n>>      * This is an additional comment\n>> \n>> \n>> yourself when running \"git commit\".  That will be propagated to the\n>> output from format-patch.  You will have another \"---\" in front of\n>> the diffstat, but nobody is hurt by that.\n>\n> But note that using \"format-patch -s\" will break; it puts the sign-off\n> below the \"---\".\n\nI think \"format-patch -s\" is a workflow mistake in the first place.\nYou should be doing the sign-off the commit when you commit in the\nfirst place.  It is not like \"I cannot sign off because I think it\nis still iffy\" or anything.\n\nBut your point still stands; \"commit -s\" will not see through that\nofficial trick either ;-).\n\n>> But when doing a big series that deserves a cover letter [PATCH 0/n],\n>> you will use editor on the output from format-patch anyway, and I\n>> find it simpler to do the follow-on comments at that point myself.\n>\n> Me too (actually, I load it all into mutt and then comment on each as I\n> send them out, but it amounts to the same thing, seeing as how my MUA\n> just invokes $EDITOR when I edit a mail).\n"},{"id":"199385","messageId":"20120918185530.GB20498@sigill.intra.peff.net","threadId":"31539","inReplyTo":"7v7grsqi98.fsf@alter.siamese.dyndns.org","subject":"Re: How to create the \" [PATCH 0/5]\" first email?","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-09-18T18:55:30Z","receivedAt":"2012-09-18T18:55:30Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Sep 17, 2012 at 05:36:35PM -0700, Junio C Hamano wrote:\n\n> > But note that using \"format-patch -s\" will break; it puts the sign-off\n> > below the \"---\".\n> \n> I think \"format-patch -s\" is a workflow mistake in the first place.\n> You should be doing the sign-off the commit when you commit in the\n> first place.  It is not like \"I cannot sign off because I think it\n> is still iffy\" or anything.\n\nIt is mostly \"I do not signoff because typing '-s' each time I commit is\na pain\". I know it is supposed to be a conscious decision, but I think\nit is a little silly. Since everything I do for git.git is going to be\nsigned-off, either I will automate it, or I will end up typing it\nwithout thinking.  Whenever I make a conscious decision, it is to\n_withhold_ signoff, since that is the exceptional case.\n\nBut even without that, I still think format-patch is a reasonable time\nto do it. It is the time when I proof-read my commit message and patch\nin its final form, and think \"do I really want to send this?\". That\nseems to me like a reasonable time to make such a conscious decision to\nsignoff (or not).\n\n> But your point still stands; \"commit -s\" will not see through that\n> official trick either ;-).\n\nYes. :)\n\n-Peff\n"},{"id":"199387","messageId":"7vsjafm9hd.fsf@alter.siamese.dyndns.org","threadId":"31539","inReplyTo":"20120918185530.GB20498@sigill.intra.peff.net","subject":"Re: How to create the \" [PATCH 0/5]\" first email?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-18T19:11:58Z","receivedAt":"2012-09-18T19:11:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> But even without that, I still think format-patch is a reasonable time\n> to do it. It is the time when I proof-read my commit message and patch\n> in its final form, and think \"do I really want to send this?\".\n\nBut it is not like \"I cannot sign off because I think it is still\niffy\".\n\n> seems to me like a reasonable time to make such a conscious decision to\n> signoff (or not).\n>\n>> But your point still stands; \"commit -s\" will not see through that\n>> official trick either ;-).\n>\n> Yes. :)\n\nActually, no.  \"commit -s\" does not have any need to see through it.\n\n\t... hack hack hack ...\n        $ git commit -a -s\n        ... editor opens, you see your Sign-off at the end, with\n        ... the cursor sitting on the first line\n        ... edit the title, move to the line below the Sign-off,\n        ... and do the \"---\\n\\n * comment\" thing.\n\nAnd this survives \"rebase -i\" (but not \"format-patch | am\" for\nobvious reasons).\n\nSo I take it back.  The time you do the \"git commit\" for the very\nfirst time for this change that may need to be rerolled number of\ntimes is the right time to say \"-s\".\n"},{"id":"199390","messageId":"20120918191653.GA21187@sigill.intra.peff.net","threadId":"31539","inReplyTo":"7vsjafm9hd.fsf@alter.siamese.dyndns.org","subject":"Re: How to create the \" [PATCH 0/5]\" first email?","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-09-18T19:16:53Z","receivedAt":"2012-09-18T19:16:53Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Sep 18, 2012 at 12:11:58PM -0700, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > But even without that, I still think format-patch is a reasonable time\n> > to do it. It is the time when I proof-read my commit message and patch\n> > in its final form, and think \"do I really want to send this?\".\n> \n> But it is not like \"I cannot sign off because I think it is still\n> iffy\".\n\nNo, that is not the particular reason in my case, but I think I\nexplained other reasons why \"format-patch -s\" is not a wrong workflow.\n\n> >> But your point still stands; \"commit -s\" will not see through that\n> >> official trick either ;-).\n> >\n> > Yes. :)\n> \n> Actually, no.  \"commit -s\" does not have any need to see through it.\n> \n> \t... hack hack hack ...\n>         $ git commit -a -s\n>         ... editor opens, you see your Sign-off at the end, with\n>         ... the cursor sitting on the first line\n>         ... edit the title, move to the line below the Sign-off,\n>         ... and do the \"---\\n\\n * comment\" thing.\n> \n> And this survives \"rebase -i\" (but not \"format-patch | am\" for\n> obvious reasons).\n\nYes, if your particular workflow is to signoff the very first time you\ncommit. But it would not work for:\n\n ... hack hack hack ...\n $ git commit -a\n   ... make a note after \"---\" ...\n\n ... hack hack hack ...\n ... OK, looks good, ready to signoff ...\n $ git commit --amend -s\n\nSo it can work, but it is workflow dependent, and in general is a little\nflaky with the automagic signoff. You may want to signoff later for a\nvariety of reasons, not the least of which is that you forgot to type\n\"-s\" the first time.\n\n> So I take it back.  The time you do the \"git commit\" for the very\n> first time for this change that may need to be rerolled number of\n> times is the right time to say \"-s\".\n\nIf you remember to type it. :)\n\n-Peff\n"},{"id":"199396","messageId":"7vboh3m7tz.fsf@alter.siamese.dyndns.org","threadId":"31539","inReplyTo":"20120918191653.GA21187@sigill.intra.peff.net","subject":"Re: How to create the \" [PATCH 0/5]\" first email?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-18T19:47:36Z","receivedAt":"2012-09-18T19:47:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Tue, Sep 18, 2012 at 12:11:58PM -0700, Junio C Hamano wrote:\n>\n>> Jeff King <peff@peff.net> writes:\n>> \n>> > But even without that, I still think format-patch is a reasonable time\n>> > to do it. It is the time when I proof-read my commit message and patch\n>> > in its final form, and think \"do I really want to send this?\".\n>> \n>> But it is not like \"I cannot sign off because I think it is still\n>> iffy\".\n>\n> No, that is not the particular reason in my case, but I think I\n> explained other reasons why \"format-patch -s\" is not a wrong workflow.\n\nThen I didn't read it.  What does \"do I really want to send this?\"\nhave anything to do with DCO in any case?\n"},{"id":"199400","messageId":"62292D2F2B354D74AC637CD1DCBBCC72@PhilipOakley","threadId":"31539","inReplyTo":"7vboh3m7tz.fsf@alter.siamese.dyndns.org","subject":"Re: How to create the \" [PATCH 0/5]\" first email?","fromName":"Philip Oakley","fromEmail":"philipoakley@iee.org","sentAt":"2012-09-18T20:10:26Z","receivedAt":"2012-09-18T20:10:26Z","isPatch":true,"sender":{"key":"philipoakley@iee.email","avatar":"https://avatars.githubusercontent.com/u/914343?v=4"},"body":"From: \"Junio C Hamano\" <gitster@pobox.com>\n> have anything to do with DCO in any case?\n\nJunio,\nWhat's DCO an abbreviation of?\nPhilip\n"},{"id":"199401","messageId":"20120918201609.GA27240@sigill.intra.peff.net","threadId":"31539","inReplyTo":"7vboh3m7tz.fsf@alter.siamese.dyndns.org","subject":"Re: How to create the \" [PATCH 0/5]\" first email?","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-09-18T20:16:09Z","receivedAt":"2012-09-18T20:16:09Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Sep 18, 2012 at 12:47:36PM -0700, Junio C Hamano wrote:\n\n> >> Jeff King <peff@peff.net> writes:\n> >> \n> >> > But even without that, I still think format-patch is a reasonable time\n> >> > to do it. It is the time when I proof-read my commit message and patch\n> >> > in its final form, and think \"do I really want to send this?\".\n> >> \n> >> But it is not like \"I cannot sign off because I think it is still\n> >> iffy\".\n> >\n> > No, that is not the particular reason in my case, but I think I\n> > explained other reasons why \"format-patch -s\" is not a wrong workflow.\n> \n> Then I didn't read it.  What does \"do I really want to send this?\"\n> have anything to do with DCO in any case?\n\nBecause it is an excellent time to think about \"am I willing and able to\nagree to the DCO?\" As I said, for me personally working on git.git, that\nis not generally an issue. But I think it is perfectly reasonable for\nsomebody to work and commit in isolation, and then only decide on the\nDCO during the sending phase (perhaps because they need to clear it with\ntheir company's legal department or some such). In other words, \"it is\niffy at the time of commit\" might be exactly the reason for some people.\n\nIf you are responding to my \"that is not the particular reason in my\ncase\", I will paraphrase the reason I gave earlier: I find it annoying\nand pointless to type \"-s\" on every commit. We do not have\ncommit.signoff, but we do have format.signoff.\n\n-Peff\n"},{"id":"199402","messageId":"20120918201624.GB27240@sigill.intra.peff.net","threadId":"31539","inReplyTo":"62292D2F2B354D74AC637CD1DCBBCC72@PhilipOakley","subject":"Re: How to create the \" [PATCH 0/5]\" first email?","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-09-18T20:16:24Z","receivedAt":"2012-09-18T20:16:24Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Sep 18, 2012 at 09:10:26PM +0100, Philip Oakley wrote:\n\n> From: \"Junio C Hamano\" <gitster@pobox.com>\n> >have anything to do with DCO in any case?\n> \n> What's DCO an abbreviation of?\n\nDeveloper's Certificate of Origin. See SubmittingPatches.\n\n-Peff\n"},{"id":"199407","messageId":"201209181442.29010.wjl@icecavern.net","threadId":"31539","inReplyTo":"7vfw6gqkfg.fsf@alter.siamese.dyndns.org","subject":"Re: How to create the \" [PATCH 0/5]\" first email?","fromName":"Wesley J. Landaker","fromEmail":"wjl@icecavern.net","sentAt":"2012-09-18T20:42:28Z","receivedAt":"2012-09-18T20:42:28Z","isPatch":true,"sender":{"key":"wjl@icecavern.net","avatar":"https://avatars.githubusercontent.com/u/67229?v=4"},"body":"On Monday, September 17, 2012 17:49:39 Junio C Hamano wrote:\n> \"Philip Oakley\" <philipoakley@iee.org> writes:\n> > I then applied it (using git am) to a temp branch to see what it\n> > produced, and could repeat the cycle until the patches looked right.\n> \n> That's another obvious and valid way to prepare your series.  It all\n> depends on how comfortable you are to directly edit patches.  Some\n> people fear it.  Some don't.  Some can do it with their eyes closed ;-).\n> \n> > However, when it came to creating the series, with comments, I\n> > couldn't see a way of having my comments within my local commits, but\n> > preparing a patch series that would properly include the '---'\n> > separator.\n> \n> An unofficial trick that works is to write the\n> \n>     ---\n> \n>      * This is an additional comment\n> \n> \n> yourself when running \"git commit\".  That will be propagated to the\n> output from format-patch.  You will have another \"---\" in front of\n> the diffstat, but nobody is hurt by that.\n\nOne thing I have done is to add the additional comments I want with \"git \nnotes\", then give the \"--notes\" option to format-patch or send-email.\n\nUnfortunately, this sticks the notes right into the commit message section, \nbecause the \"--notes\" option is actually a diff option, not something \nformat-patch knows about, so you have to make sure to manually move it.\n\nBut even so, I've found it a a nice way to track comments.\n"},{"id":"199770","messageId":"alpine.LNX.2.01.1209231401500.9625@nerf07.vanv.qr","threadId":"31539","inReplyTo":"7vzk4rz00y.fsf@alter.siamese.dyndns.org","subject":"Re: How to create the \" [PATCH 0/5]\" first email?","fromName":"Jan Engelhardt","fromEmail":"jengelh@inai.de","sentAt":"2012-09-23T12:03:33Z","receivedAt":"2012-09-23T12:03:33Z","isPatch":true,"sender":{"key":"jengelh@inai.de","avatar":"https://avatars.githubusercontent.com/u/8861948?v=4"},"body":"\nOn Saturday 2012-09-15 19:08, Junio C Hamano wrote:\n>\n>If you plan to use \"git send-email\" to send the final results out,\n>you should consider \"git send-email\" as your \"MUA\" in the quoted\n>paragraph.  And that will be very platform independent viewpoint to\n>see things from.\n>\n>\"git format-patch -o my-series/ --cover-letter ...\"  would treat\n>\"my-series/\" directory as \"MUA's drafts folder\" and prepares the\n>messages you would want to send out, and you can proof-read and edit\n>the files in there before telling your \"MUA\" to send them out, with\n>\"git send-email ... my-series/*.patch\" or something.\n\nOne can also send [0/n] with a normal MUA, and then use\n\n git send-email --in-reply-to '<messageidof0@yourhost.no>' commitrange\n\n\nIt's not like 0/n has to be emitted at the same second 1/n is :)\n"},{"id":"199882","messageId":"506156E3.4060508@viscovery.net","threadId":"31539","inReplyTo":"1347809265-2457-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 6/5] side-step a make rule that builds t3070-wildmatch","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2012-09-25T07:01:55Z","receivedAt":"2012-09-25T07:01:55Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"From: Johannes Sixt <j6t@kdbg.org>\n\nRunning \"make t3070-wildmatch\" aborts with this error:\n\n   $ make t3070-wildmatch\n   *** t3070-wildmatch.sh ***\n   # passed all 11 test(s)\n   1..11\n   cat t3070-wildmatch.sh >t3070-wildmatch\n   /bin/sh.exe: t3070-wildmatch: File exists\n   make: *** [t3070-wildmatch] Error 1\n\nIt seems that there is a make rule that attempts to transform a *.sh file\ninto a file without the extension. The rule fails because t3070-wildmatch\nis a directory. Rename it to t3070.\n\nOf course, nobody would run exactly \"make t3070-wildmatch\". But it is\ncommon (at least for me) to run \"make t[3-9]*\" or similar, which would\ninclude t3070-wildmatch. With this patch, it includes t3070, but that does\nnot hurt because make just skips it with \"Nothing to be done for `t3070'.\"\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n Feel free to squash this patch if you re-roll the series.\n\n t/t3070-wildmatch.sh                      | 2 +-\n t/{t3070-wildmatch => t3070}/wildtest.txt | 0\n 2 files changed, 1 insertion(+), 1 deletion(-)\n rename t/{t3070-wildmatch => t3070}/wildtest.txt (100%)\n\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nindex 7fb63ff..c4da26c 100755\n--- a/t/t3070-wildmatch.sh\n+++ b/t/t3070-wildmatch.sh\n@@ -6,7 +6,7 @@ test_description='wildmatch tests'\n\n test_wildmatch() {\n     test_expect_success \"wildmatch $*\" \"\n-\ttest-wildmatch $* ../t3070-wildmatch/wildtest.txt >actual &&\n+\ttest-wildmatch $* ../t3070/wildtest.txt >actual &&\n \techo 'No wildmatch errors found.' >expected &&\n \ttest_cmp expected actual\n     \"\ndiff --git a/t/t3070-wildmatch/wildtest.txt b/t/t3070/wildtest.txt\nsimilarity index 100%\nrename from t/t3070-wildmatch/wildtest.txt\nrename to t/t3070/wildtest.txt\n-- \n1.7.12.1.1626.gf25df0e\n"}]}