{"thread":{"id":"31653","subject":"[PATCH 0/5] wildmatch series update","startedAt":"2012-09-26T11:25:36Z","lastAt":"2012-10-02T18:26:30Z","messageCount":9,"participants":["Nguyễn Thái Ngọc Duy","Junio C Hamano","Ævar Arnfjörð Bjarmason"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"199937","messageId":"1348658741-8146-1-git-send-email-pclouds@gmail.com","threadId":"31653","inReplyTo":null,"subject":"[PATCH 0/5] wildmatch series update","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-09-26T11:25:36Z","receivedAt":"2012-09-26T11:25:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"No functional changes. Just incorporate changes from Ramsay and Johannes.\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                           |   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 t/t3070-wildmatch.sh               |  27 +++\n t/t3070/wildtest.txt               | 165 +++++++++++++++++\n test-wildmatch.c                   | 208 ++++++++++++++++++++++\n wildmatch.c                        | 355 +++++++++++++++++++++++++++++++++++++\n wildmatch.h                        |   6 +\n 12 files changed, 803 insertions(+), 2 deletions(-)\n create mode 100755 t/t3070-wildmatch.sh\n create mode 100644 t/t3070/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.1.406.g6ab07c4\n"},{"id":"199938","messageId":"1348658741-8146-2-git-send-email-pclouds@gmail.com","threadId":"31653","inReplyTo":"1348658741-8146-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-26T11:25:37Z","receivedAt":"2012-09-26T11:25:37Z","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/wildtest.txt\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n t/t3070/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/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/wildtest.txt b/t/t3070/wildtest.txt\nnew file mode 100644\nindex 0000000..42c1678\n--- /dev/null\n+++ b/t/t3070/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.1.406.g6ab07c4\n"},{"id":"199939","messageId":"1348658741-8146-3-git-send-email-pclouds@gmail.com","threadId":"31653","inReplyTo":"1348658741-8146-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-26T11:25:38Z","receivedAt":"2012-09-26T11:25:38Z","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>\nSigned-off-by: Junio C Hamano <gitster@pobox.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.1.406.g6ab07c4\n"},{"id":"199940","messageId":"1348658741-8146-4-git-send-email-pclouds@gmail.com","threadId":"31653","inReplyTo":"1348658741-8146-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-26T11:25:39Z","receivedAt":"2012-09-26T11:25:39Z","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>\nSigned-off-by: Junio C Hamano <gitster@pobox.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.1.406.g6ab07c4\n"},{"id":"199941","messageId":"1348658741-8146-5-git-send-email-pclouds@gmail.com","threadId":"31653","inReplyTo":"1348658741-8146-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-26T11:25:40Z","receivedAt":"2012-09-26T11:25:40Z","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; the\ndependency on libpopt in the original has been replaced with the use\nof our parse-options. Global variables in test-wildmatch are marked\nstatic to avoid sparse warnings.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n .gitignore           |  1 +\n Makefile             |  3 ++\n t/t3070-wildmatch.sh | 27 ++++++++++++++++\n test-wildmatch.c     | 88 ++++++++++++++++++++++------------------------------\n wildmatch.c          | 26 +++++-----------\n 5 files changed, 75 insertions(+), 70 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 26b697d..d6235e6 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -504,6 +504,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@@ -676,6 +677,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@@ -807,6 +809,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\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nnew file mode 100755\nindex 0000000..c4da26c\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/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..bb726c8 100644\n--- a/test-wildmatch.c\n+++ b/test-wildmatch.c\n@@ -19,34 +19,38 @@\n \n /*#define COMPARE_WITH_FNMATCH*/\n \n-#define WILD_TEST_ITERATIONS\n-#include \"lib/wildmatch.c\"\n+#include \"cache.h\"\n+#include \"parse-options.h\"\n+#include \"wildmatch.h\"\n \n-#include <popt.h>\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 #ifdef COMPARE_WITH_FNMATCH\n #include <fnmatch.h>\n \n-int fnmatch_errors = 0;\n+static int fnmatch_errors = 0;\n #endif\n \n-int wildmatch_errors = 0;\n-char number_separator = ',';\n+static int wildmatch_errors = 0;\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+static int explode_mod = 0;\n+static int empties_mod = 0;\n+static int empty_at_start = 0;\n+static int empty_at_end = 0;\n+static char *empties;\n+\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@@ -100,51 +104,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 c7f7f9f..f153f8a 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@@ -53,10 +59,6 @@\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@@ -64,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@@ -289,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@@ -300,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@@ -319,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-- \n1.7.12.1.406.g6ab07c4\n"},{"id":"199942","messageId":"1348658741-8146-6-git-send-email-pclouds@gmail.com","threadId":"31653","inReplyTo":"1348658741-8146-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-26T11:25:41Z","receivedAt":"2012-09-26T11:25:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\nSigned-off-by: Junio C Hamano <gitster@pobox.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 b52efb5..15ebaa1 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@@ -663,7 +664,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 240bf0c..92cda82 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@@ -573,7 +574,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.1.406.g6ab07c4\n"},{"id":"200029","messageId":"7vfw63qqy0.fsf@alter.siamese.dyndns.org","threadId":"31653","inReplyTo":"1348658741-8146-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 0/5] wildmatch series update","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-09-27T18:08:23Z","receivedAt":"2012-09-27T18:08:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thanks; will re-queue.\n"},{"id":"200320","messageId":"CACBZZX6CLZAcUG2GjBt4brF9dGr1Zvj1HMTV=U-AXFZhzR=39Q@mail.gmail.com","threadId":"31653","inReplyTo":"1348658741-8146-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 1/5] Import wildmatch from rsync","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2012-10-02T14:07:00Z","receivedAt":"2012-10-02T14:07:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"On Wed, Sep 26, 2012 at 1:25 PM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> These files are from rsync.git commit\n> f92f5b166e3019db42bc7fe1aa2f1a9178cd215d, which was the last commit\n> before rsync turned GPL-3. All files are imported as-is and\n> no-op. Adaptation is done in a separate patch.\n\nPerhaps Wayne Davison (added to CC) wouldn't mind giving us permission\nto use the subsequent changes to these files under the GPLv2?\n\nrsync.git $ git --no-pager log --pretty=\"%h %an %s\\n\" --reverse\nf92f5b166e3019db42bc7fe1aa2f1a9178cd215d.. -- '*wild*'\n4fd842f Wayne Davison Switching to GPL 3.\\n\n8e41b68 Wayne Davison Tweaking the license text a bit more.\\n\nd3d07a5 Wayne Davison Include 2008 in the copyright years.\\n\nadc2476 Wayne Davison Output numbers in 3-digit groups by default\n(e.g. 1,234,567). Also improved the human-readable output functions,\nincluding adding the ability to output negative numbers.\\n\nb3bf9b9 Wayne Davison Update the copyright year.\\n\nfd91c3b Wayne Davison Fix two unused-variable compiler warnings.\\n\n\n\n>\n> rsync.git           ->  git.git\n> lib/wildmatch.[ch]      wildmatch.[ch]\n> wildtest.c              test-wildmatch.c\n> wildtest.txt            t/t3070/wildtest.txt\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  t/t3070/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/wildtest.txt\n>  create mode 100644 test-wildmatch.c\n>  create mode 100644 wildmatch.c\n>  create mode 100644 wildmatch.h\n>\n> diff --git a/t/t3070/wildtest.txt b/t/t3070/wildtest.txt\n> new file mode 100644\n> index 0000000..42c1678\n> --- /dev/null\n> +++ b/t/t3070/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                        foo\n> +0 1 foo                        bar\n> +1 1 ''                 \"\"\n> +1 1 foo                        ???\n> +0 1 foo                        ??\n> +1 1 foo                        *\n> +1 1 foo                        f*\n> +0 1 foo                        *f\n> +1 1 foo                        *foo*\n> +1 1 foobar             *ob*a*r*\n> +1 1 aaaaaaabababab     *ab\n> +1 1 foo*               foo\\*\n> +0 1 foobar             foo\\*bar\n> +1 1 f\\oo               f\\\\oo\n> +1 1 ball               *[al]?\n> +0 1 ten                        [ten]\n> +1 1 ten                        **[!te]\n> +0 1 ten                        **[!ten]\n> +1 1 ten                        t[a-g]n\n> +0 1 ten                        t[!a-g]n\n> +1 1 ton                        t[!a-g]n\n> +1 1 ton                        t[^a-g]n\n> +1 1 a]b                        a[]]b\n> +1 1 a-b                        a[]-]b\n> +1 1 a]b                        a[]-]b\n> +0 1 aab                        a[]-]b\n> +1 1 aab                        a[]a-]b\n> +1 1 ]                  ]\n> +\n> +# Extended slash-matching features\n> +0 1 foo/baz/bar                foo*bar\n> +1 1 foo/baz/bar                foo**bar\n> +0 1 foo/bar            foo?bar\n> +0 1 foo/bar            foo[/]bar\n> +0 1 foo/bar            f[^eiu][^eiu][^eiu][^eiu][^eiu]r\n> +1 1 foo-bar            f[^eiu][^eiu][^eiu][^eiu][^eiu]r\n> +0 1 foo                        **/foo\n> +1 1 /foo               **/foo\n> +1 1 bar/baz/foo                **/foo\n> +0 1 bar/baz/foo                */foo\n> +0 0 foo/bar/baz                **/bar*\n> +1 1 deep/foo/bar/baz   **/bar/*\n> +0 1 deep/foo/bar/baz/  **/bar/*\n> +1 1 deep/foo/bar/baz/  **/bar/**\n> +0 1 deep/foo/bar       **/bar/*\n> +1 1 deep/foo/bar/      **/bar/**\n> +1 1 foo/bar/baz                **/bar**\n> +1 1 foo/bar/baz/x      */bar/**\n> +0 0 deep/foo/bar/baz/x */bar/**\n> +1 1 deep/foo/bar/baz/x **/bar/*/*\n> +\n> +# Various additional tests\n> +0 1 acrt               a[c-c]st\n> +1 1 acrt               a[c-c]rt\n> +0 1 ]                  [!]-]\n> +1 1 a                  [!]-]\n> +0 1 ''                 \\\n> +0 1 \\                  \\\n> +0 1 /\\                 */\\\n> +1 1 /\\                 */\\\\\n> +1 1 foo                        foo\n> +1 1 @foo               @foo\n> +0 1 foo                        @foo\n> +1 1 [ab]               \\[ab]\n> +1 1 [ab]               [[]ab]\n> +1 1 [ab]               [[:]ab]\n> +0 1 [ab]               [[::]ab]\n> +1 1 [ab]               [[:digit]ab]\n> +1 1 [ab]               [\\[:]ab]\n> +1 1 ?a?b               \\??\\?b\n> +1 1 abc                        \\a\\b\\c\n> +0 1 foo                        ''\n> +1 1 foo/bar/baz/to     **/t[o]\n> +\n> +# Character class tests\n> +1 1 a1B                [[:alpha:]][[:digit:]][[:upper:]]\n> +0 1 a          [[:digit:][:upper:][:space:]]\n> +1 1 A          [[:digit:][:upper:][:space:]]\n> +1 1 1          [[:digit:][:upper:][:space:]]\n> +0 1 1          [[:digit:][:upper:][:spaci:]]\n> +1 1 ' '                [[:digit:][:upper:][:space:]]\n> +0 1 .          [[:digit:][:upper:][:space:]]\n> +1 1 .          [[:digit:][:punct:][:space:]]\n> +1 1 5          [[:xdigit:]]\n> +1 1 f          [[:xdigit:]]\n> +1 1 D          [[:xdigit:]]\n> +1 1 _          [[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]\n> +#1 1 …         [^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]\n> +1 1            [^[:alnum:][:alpha:][:blank:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]\n> +1 1 .          [^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]\n> +1 1 5          [a-c[:digit:]x-z]\n> +1 1 b          [a-c[:digit:]x-z]\n> +1 1 y          [a-c[:digit:]x-z]\n> +0 1 q          [a-c[:digit:]x-z]\n> +\n> +# Additional tests, including some malformed wildmats\n> +1 1 ]          [\\\\-^]\n> +0 1 [          [\\\\-^]\n> +1 1 -          [\\-_]\n> +1 1 ]          [\\]]\n> +0 1 \\]         [\\]]\n> +0 1 \\          [\\]]\n> +0 1 ab         a[]b\n> +0 1 a[]b       a[]b\n> +0 1 ab[                ab[\n> +0 1 ab         [!\n> +0 1 ab         [-\n> +1 1 -          [-]\n> +0 1 -          [a-\n> +0 1 -          [!a-\n> +1 1 -          [--A]\n> +1 1 5          [--A]\n> +1 1 ' '                '[ --]'\n> +1 1 $          '[ --]'\n> +1 1 -          '[ --]'\n> +0 1 0          '[ --]'\n> +1 1 -          [---]\n> +1 1 -          [------]\n> +0 1 j          [a-e-n]\n> +1 1 -          [a-e-n]\n> +1 1 a          [!------]\n> +0 1 [          []-a]\n> +1 1 ^          []-a]\n> +0 1 ^          [!]-a]\n> +1 1 [          [!]-a]\n> +1 1 ^          [a^bc]\n> +1 1 -b]                [a-]b]\n> +0 1 \\          [\\]\n> +1 1 \\          [\\\\]\n> +0 1 \\          [!\\\\]\n> +1 1 G          [A-\\\\]\n> +0 1 aaabbb     b*a\n> +0 1 aabcaa     *ba*\n> +1 1 ,          [,]\n> +1 1 ,          [\\\\,]\n> +1 1 \\          [\\\\,]\n> +1 1 -          [,-.]\n> +0 1 +          [,-.]\n> +0 1 -.]                [,-.]\n> +1 1 2          [\\1-\\3]\n> +1 1 3          [\\1-\\3]\n> +0 1 4          [\\1-\\3]\n> +1 1 \\          [[-\\]]\n> +1 1 [          [[-\\]]\n> +1 1 ]          [[-\\]]\n> +0 1 -          [[-\\]]\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  -*-*-*-*-*-*-12-*-*-*-m-*-*-*\n> +0 1 -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1  -*-*-*-*-*-*-12-*-*-*-m-*-*-*\n> +0 1 -adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1  -*-*-*-*-*-*-12-*-*-*-m-*-*-*\n> +1 1 /adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1  /*/*/*/*/*/*/12/*/*/*/m/*/*/*\n> +0 1 /adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1  /*/*/*/*/*/*/12/*/*/*/m/*/*/*\n> +1 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt              **/*a*b*g*n*t\n> +0 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz             **/*a*b*g*n*t\n> diff --git a/test-wildmatch.c b/test-wildmatch.c\n> new file mode 100644\n> index 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> +        bool same_as_fnmatch,\n> +#endif\n> +        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> +       char buf[MAXPATHLEN*2], *texts[MAXPATHLEN];\n> +       int pos = 0, cnt = 0, ndx = 0, len = strlen(text);\n> +\n> +       if (empty_at_start)\n> +           texts[ndx++] = \"\";\n> +       /* An empty string must turn into at least one empty array item. */\n> +       while (1) {\n> +           texts[ndx] = buf + ndx * (explode_mod + 1);\n> +           strlcpy(texts[ndx++], text + pos, explode_mod + 1);\n> +           if (pos + explode_mod >= len)\n> +               break;\n> +           pos += explode_mod;\n> +           if (!(++cnt % empties_mod))\n> +               texts[ndx++] = \"\";\n> +       }\n> +       if (empty_at_end)\n> +           texts[ndx++] = \"\";\n> +       texts[ndx] = NULL;\n> +       matched = wildmatch_array(pattern, (const char**)texts, 0);\n> +    } else\n> +       matched = wildmatch(pattern, text);\n> +#ifdef COMPARE_WITH_FNMATCH\n> +    fn_matched = !fnmatch(pattern, text, flags);\n> +#endif\n> +    if (matched != matches) {\n> +       printf(\"wildmatch failure on line %d:\\n  %s\\n  %s\\n  expected %s match\\n\",\n> +              line, text, pattern, matches? \"a\" : \"NO\");\n> +       wildmatch_errors++;\n> +    }\n> +#ifdef COMPARE_WITH_FNMATCH\n> +    if (fn_matched != (matches ^ !same_as_fnmatch)) {\n> +       printf(\"fnmatch disagreement on line %d:\\n  %s\\n  %s\\n  expected %s match\\n\",\n> +              line, text, pattern, matches ^ !same_as_fnmatch? \"a\" : \"NO\");\n> +       fnmatch_errors++;\n> +    }\n> +#endif\n> +    if (output_iterations) {\n> +       printf(\"%d: \\\"%s\\\" iterations = %d\\n\", line, pattern,\n> +              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> +                                   long_options, 0);\n> +\n> +    while ((opt = poptGetNextOpt(pc)) != -1) {\n> +       switch (opt) {\n> +         case 'e':\n> +           arg = poptGetOptArg(pc);\n> +           empties_mod = atoi(arg);\n> +           if (strchr(arg, 's'))\n> +               empty_at_start = 1;\n> +           if (strchr(arg, 'e'))\n> +               empty_at_end = 1;\n> +           if (!explode_mod)\n> +               explode_mod = 1024;\n> +           break;\n> +         default:\n> +           fprintf(stderr, \"%s: %s\\n\",\n> +                   poptBadOption(pc, POPT_BADOPTION_NOALIAS),\n> +                   poptStrerror(opt));\n> +           exit(1);\n> +       }\n> +    }\n> +\n> +    if (explode_mod && !empties_mod)\n> +       empties_mod = 1024;\n> +\n> +    argv = (char**)poptGetArgs(pc);\n> +    if (!argv || argv[1]) {\n> +       fprintf(stderr, \"Usage: wildtest [OPTIONS] TESTFILE\\n\");\n> +       exit(1);\n> +    }\n> +\n> +    if ((fp = fopen(*argv, \"r\")) == NULL) {\n> +       fprintf(stderr, \"Unable to open %s\\n\", *argv);\n> +       exit(1);\n> +    }\n> +\n> +    line = 0;\n> +    while (fgets(buf, sizeof buf, fp)) {\n> +       line++;\n> +       if (*buf == '#' || *buf == '\\n')\n> +           continue;\n> +       for (s = buf, i = 0; i <= 1; i++) {\n> +           if (*s == '1')\n> +               flag[i] = 1;\n> +           else if (*s == '0')\n> +               flag[i] = 0;\n> +           else\n> +               flag[i] = -1;\n> +           if (*++s != ' ' && *s != '\\t')\n> +               flag[i] = -1;\n> +           if (flag[i] < 0) {\n> +               fprintf(stderr, \"Invalid flag syntax on line %d of %s:\\n%s\",\n> +                       line, *argv, buf);\n> +               exit(1);\n> +           }\n> +           while (*++s == ' ' || *s == '\\t') {}\n> +       }\n> +       for (i = 0; i <= 1; i++) {\n> +           if (*s == '\\'' || *s == '\"' || *s == '`') {\n> +               char quote = *s++;\n> +               string[i] = s;\n> +               while (*s && *s != quote) s++;\n> +               if (!*s) {\n> +                   fprintf(stderr, \"Unmatched quote on line %d of %s:\\n%s\",\n> +                           line, *argv, buf);\n> +                   exit(1);\n> +               }\n> +               end[i] = s;\n> +           }\n> +           else {\n> +               if (!*s || *s == '\\n') {\n> +                   fprintf(stderr, \"Not enough strings on line %d of %s:\\n%s\",\n> +                           line, *argv, buf);\n> +                   exit(1);\n> +               }\n> +               string[i] = s;\n> +               while (*++s && *s != ' ' && *s != '\\t' && *s != '\\n') {}\n> +               end[i] = s;\n> +           }\n> +           while (*++s == ' ' || *s == '\\t') {}\n> +       }\n> +       *end[0] = *end[1] = '\\0';\n> +       run_test(line, flag[0],\n> +#ifdef COMPARE_WITH_FNMATCH\n> +                flag[1],\n> +#endif\n> +                string[0], string[1]);\n> +    }\n> +\n> +    if (!wildmatch_errors)\n> +       fputs(\"No\", stdout);\n> +    else\n> +       printf(\"%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> +       fputs(\"No\", stdout);\n> +    else\n> +       printf(\"%d\", fnmatch_errors);\n> +    printf(\" fnmatch error%s found.\\n\", fnmatch_errors == 1? \"\" : \"s\");\n> +\n> +#endif\n> +\n> +    return 0;\n> +}\n> diff --git a/wildmatch.c b/wildmatch.c\n> new file mode 100644\n> index 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   '!'\n> +#define NEGATE_CLASS2  '^'\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> +                                   && *(class) == *(litmatch) \\\n> +                                   && 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> +       int matched, special;\n> +       uchar t_ch, prev_ch;\n> +       while ((t_ch = *text) == '\\0') {\n> +           if (*a == NULL) {\n> +               if (p_ch != '*')\n> +                   return ABORT_ALL;\n> +               break;\n> +           }\n> +           text = *a++;\n> +       }\n> +       if (force_lower_case && ISUPPER(t_ch))\n> +           t_ch = tolower(t_ch);\n> +       switch (p_ch) {\n> +         case '\\\\':\n> +           /* Literal match with following character.  Note that the test\n> +            * in \"default\" handles the p[1] == '\\0' failure case. */\n> +           p_ch = *++p;\n> +           /* FALLTHROUGH */\n> +         default:\n> +           if (t_ch != p_ch)\n> +               return FALSE;\n> +           continue;\n> +         case '?':\n> +           /* Match anything but '/'. */\n> +           if (t_ch == '/')\n> +               return FALSE;\n> +           continue;\n> +         case '*':\n> +           if (*++p == '*') {\n> +               while (*++p == '*') {}\n> +               special = TRUE;\n> +           } else\n> +               special = FALSE;\n> +           if (*p == '\\0') {\n> +               /* Trailing \"**\" matches everything.  Trailing \"*\" matches\n> +                * only if there are no more slash characters. */\n> +               if (!special) {\n> +                   do {\n> +                       if (strchr((char*)text, '/') != NULL)\n> +                           return FALSE;\n> +                   } while ((text = *a++) != NULL);\n> +               }\n> +               return TRUE;\n> +           }\n> +           while (1) {\n> +               if (t_ch == '\\0') {\n> +                   if ((text = *a++) == NULL)\n> +                       break;\n> +                   t_ch = *text;\n> +                   continue;\n> +               }\n> +               if ((matched = dowild(p, text, a)) != FALSE) {\n> +                   if (!special || matched != ABORT_TO_STARSTAR)\n> +                       return matched;\n> +               } else if (!special && t_ch == '/')\n> +                   return ABORT_TO_STARSTAR;\n> +               t_ch = *++text;\n> +           }\n> +           return ABORT_ALL;\n> +         case '[':\n> +           p_ch = *++p;\n> +#ifdef NEGATE_CLASS2\n> +           if (p_ch == NEGATE_CLASS2)\n> +               p_ch = NEGATE_CLASS;\n> +#endif\n> +           /* Assign literal TRUE/FALSE because of \"matched\" comparison. */\n> +           special = p_ch == NEGATE_CLASS? TRUE : FALSE;\n> +           if (special) {\n> +               /* Inverted character class. */\n> +               p_ch = *++p;\n> +           }\n> +           prev_ch = 0;\n> +           matched = FALSE;\n> +           do {\n> +               if (!p_ch)\n> +                   return ABORT_ALL;\n> +               if (p_ch == '\\\\') {\n> +                   p_ch = *++p;\n> +                   if (!p_ch)\n> +                       return ABORT_ALL;\n> +                   if (t_ch == p_ch)\n> +                       matched = TRUE;\n> +               } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {\n> +                   p_ch = *++p;\n> +                   if (p_ch == '\\\\') {\n> +                       p_ch = *++p;\n> +                       if (!p_ch)\n> +                           return ABORT_ALL;\n> +                   }\n> +                   if (t_ch <= p_ch && t_ch >= prev_ch)\n> +                       matched = TRUE;\n> +                   p_ch = 0; /* This makes \"prev_ch\" get set to 0. */\n> +               } else if (p_ch == '[' && p[1] == ':') {\n> +                   const uchar *s;\n> +                   int i;\n> +                   for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/\n> +                   if (!p_ch)\n> +                       return ABORT_ALL;\n> +                   i = p - s - 1;\n> +                   if (i < 0 || p[-1] != ':') {\n> +                       /* Didn't find \":]\", so treat like a normal set. */\n> +                       p = s - 2;\n> +                       p_ch = '[';\n> +                       if (t_ch == p_ch)\n> +                           matched = TRUE;\n> +                       continue;\n> +                   }\n> +                   if (CC_EQ(s,i, \"alnum\")) {\n> +                       if (ISALNUM(t_ch))\n> +                           matched = TRUE;\n> +                   } else if (CC_EQ(s,i, \"alpha\")) {\n> +                       if (ISALPHA(t_ch))\n> +                           matched = TRUE;\n> +                   } else if (CC_EQ(s,i, \"blank\")) {\n> +                       if (ISBLANK(t_ch))\n> +                           matched = TRUE;\n> +                   } else if (CC_EQ(s,i, \"cntrl\")) {\n> +                       if (ISCNTRL(t_ch))\n> +                           matched = TRUE;\n> +                   } else if (CC_EQ(s,i, \"digit\")) {\n> +                       if (ISDIGIT(t_ch))\n> +                           matched = TRUE;\n> +                   } else if (CC_EQ(s,i, \"graph\")) {\n> +                       if (ISGRAPH(t_ch))\n> +                           matched = TRUE;\n> +                   } else if (CC_EQ(s,i, \"lower\")) {\n> +                       if (ISLOWER(t_ch))\n> +                           matched = TRUE;\n> +                   } else if (CC_EQ(s,i, \"print\")) {\n> +                       if (ISPRINT(t_ch))\n> +                           matched = TRUE;\n> +                   } else if (CC_EQ(s,i, \"punct\")) {\n> +                       if (ISPUNCT(t_ch))\n> +                           matched = TRUE;\n> +                   } else if (CC_EQ(s,i, \"space\")) {\n> +                       if (ISSPACE(t_ch))\n> +                           matched = TRUE;\n> +                   } else if (CC_EQ(s,i, \"upper\")) {\n> +                       if (ISUPPER(t_ch))\n> +                           matched = TRUE;\n> +                   } else if (CC_EQ(s,i, \"xdigit\")) {\n> +                       if (ISXDIGIT(t_ch))\n> +                           matched = TRUE;\n> +                   } else /* malformed [:class:] string */\n> +                       return ABORT_ALL;\n> +                   p_ch = 0; /* This makes \"prev_ch\" get set to 0. */\n> +               } else if (t_ch == p_ch)\n> +                   matched = TRUE;\n> +           } while (prev_ch = p_ch, (p_ch = *++p) != ']');\n> +           if (matched == special || t_ch == '/')\n> +               return FALSE;\n> +           continue;\n> +       }\n> +    }\n> +\n> +    do {\n> +       if (*text)\n> +           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> +       while (*text == '\\0') {\n> +           if ((text = *a++) == NULL)\n> +               return FALSE;\n> +       }\n> +       if (*text != *s)\n> +           return FALSE;\n> +    }\n> +\n> +    do {\n> +       if (*text)\n> +           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> +           a++;\n> +\n> +    while (a != first_a) {\n> +       const uchar *s = *--a;\n> +       s += strlen((char*)s);\n> +       while (--s >= *a) {\n> +           if (*s == '/' && !--count) {\n> +               *a_ptr = a+1;\n> +               return s+1;\n> +           }\n> +       }\n> +    }\n> +\n> +    if (count == 1) {\n> +       *a_ptr = a+1;\n> +       return *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> +       text = trailing_N_elements(&a, where);\n> +    else\n> +       text = *a++;\n> +    if (!text)\n> +       return FALSE;\n> +\n> +    if ((matched = dowild(p, text, a)) != TRUE && where < 0\n> +     && matched != ABORT_ALL) {\n> +       while (1) {\n> +           if (*text == '\\0') {\n> +               if ((text = (uchar*)*a++) == NULL)\n> +                   return FALSE;\n> +               continue;\n> +           }\n> +           if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE\n> +            && matched != ABORT_TO_STARSTAR)\n> +               break;\n> +       }\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> +       text = trailing_N_elements(&a, where);\n> +    else\n> +       text = *a++;\n> +    if (!text)\n> +       return FALSE;\n> +\n> +    return doliteral(s, text, a) == TRUE;\n> +}\n> diff --git a/wildmatch.h b/wildmatch.h\n> new file mode 100644\n> index 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> --\n> 1.7.12.1.406.g6ab07c4\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"200352","messageId":"7vfw5w91d5.fsf@alter.siamese.dyndns.org","threadId":"31653","inReplyTo":"CAHSx_SsnSAV7SVLRnAFvS7AmdRgPkPX3NEM+6HTjfW5r8hXeig@mail.gmail.com","subject":"Re: [PATCH 1/5] Import wildmatch from rsync","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-10-02T18:26:30Z","receivedAt":"2012-10-02T18:26:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Wayne Davison <wayned@samba.org> writes:\n\n> On Tue, Oct 2, 2012 at 7:07 AM, Ævar Arnfjörð Bjarmason <avarab@gmail.com>wrote:\n>\n>> Perhaps Wayne Davison (added to CC) wouldn't mind giving us permission to\n>> use the subsequent changes to these files under the GPLv2?\n>\n> There have been no changes in the wildmat files since the GPL v3\n> changeover.  If there were (and I made them), I'd have been happy to let\n> you have them under GPL v2.\n\nSuper ;-)\n\nThanks.\n"}]}