{"thread":{"id":"31763","subject":"[PATCH 0/8] wildmatch take 3","startedAt":"2012-10-09T03:08:59Z","lastAt":"2012-10-10T05:47:54Z","messageCount":15,"participants":["Nguyễn Thái Ngọc Duy","Michael Haggerty","Junio C Hamano","Nguyen Thai Ngoc Duy"],"isPatch":true,"patchVersion":1,"patchTotal":8},"messages":[{"id":"200803","messageId":"1349752147-13314-1-git-send-email-pclouds@gmail.com","threadId":"31763","inReplyTo":null,"subject":"[PATCH 0/8] wildmatch take 3","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-09T03:08:59Z","receivedAt":"2012-10-09T03:08:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Still experimental but the semantics is getting better, I think.\nPlease point out what you think still does not make sense. Quote from\nthe last patch:\n\n  Two consecutive asterisks (\"`**`\") in patterns matched against\n  full pathname may have special meaning:\n  \n   - A leading \"`**`\" followed by a slash means match in all\n     directories. For example, \"`**/foo`\" matches file or directory\n     \"`foo`\" anywhere, the same as pattern \"`foo`\". \"**/foo/bar\"\n     matches file or directory \"`bar`\" anywhere that is directly\n     under directory \"`foo`\".\n  \n   - A trailing \"/**\" matches everything inside. For example,\n     \"abc/**\" is equivalent to \"`/abc/`\".\n  \n   - A slash followed by two consecutive asterisks then a slash\n     matches zero or more directories. For example, \"`a/**/b`\"\n     matches \"`a/b`\", \"`a/x/b`\", \"`a/x/y/b`\" and so on.\n  \n   - Consecutive asterisks otherwise are treated like normal\n     asterisk wildcards.\n\n\"abc/**\" and \"abc/*\" are different in attr (the former matches all\nfiles, the latter only files directly under abc) while they are the\nsame for ignore. I don't like these subtleties but I don't\nthink we have a choice unless we rework attr matching machinery.\n\nTests t3070.100 and .101 fail on some machine and not on other due to\n(I guess) fnmatch behavior changes. Maybe we should fallback on\ncompat/fnmatch in such cases for consistent behavior.\n\nThere are problems with asciidoc and \"`**/`\" but that's not something\nwe need to care now.\n\nOn top of master (and a small conflict with nd/attr-match-optim-more)\n\nNguyễn Thái Ngọc Duy (8):\n  Import wildmatch from rsync\n  wildmatch: remove unnecessary functions\n  Integrate wildmatch to git\n  wildmatch: remove static variable force_lower_case\n  wildmatch: fix case-insensitive matching\n  wildmatch: adjust \"**\" behavior\n  wildmatch: make /**/ match zero or more directories\n  Support \"**\" wildcard in .gitignore and .gitattributes\n\n .gitignore                         |   1 +\n Documentation/gitignore.txt        |  19 +++\n Makefile                           |   3 +\n attr.c                             |   4 +-\n dir.c                              |   4 +-\n t/t0003-attributes.sh              |  38 ++++++\n t/t3001-ls-files-others-exclude.sh |  19 +++\n t/t3070-wildmatch.sh               | 184 ++++++++++++++++++++++++++++\n test-wildmatch.c                   |  14 +++\n wildmatch.c                        | 245 +++++++++++++++++++++++++++++++++++++\n wildmatch.h                        |   3 +\n 11 files changed, 532 insertions(+), 2 deletions(-)\n create mode 100755 t/t3070-wildmatch.sh\n create mode 100644 test-wildmatch.c\n create mode 100644 wildmatch.c\n create mode 100644 wildmatch.h\n\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"200804","messageId":"1349752147-13314-2-git-send-email-pclouds@gmail.com","threadId":"31763","inReplyTo":"1349752147-13314-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 1/8] Import wildmatch from rsync","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-09T03:09:00Z","receivedAt":"2012-10-09T03:09: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\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.txt            t/t3070/wildtest.txt\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t3070/wildtest.txt | 165 +++++++++++++++++++++++\n wildmatch.c          | 368 +++++++++++++++++++++++++++++++++++++++++++++++++++\n wildmatch.h          |   6 +\n 3 files changed, 539 insertions(+)\n create mode 100644 t/t3070/wildtest.txt\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/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.8.0.rc0.29.g1fdd78f\n"},{"id":"200805","messageId":"1349752147-13314-3-git-send-email-pclouds@gmail.com","threadId":"31763","inReplyTo":"1349752147-13314-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 2/8] wildmatch: remove unnecessary functions","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-09T03:09:01Z","receivedAt":"2012-10-09T03:09:01Z","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 wildmatch.c | 161 ++++--------------------------------------------------------\n wildmatch.h |   2 -\n 2 files changed, 9 insertions(+), 154 deletions(-)\n\ndiff --git a/wildmatch.c b/wildmatch.c\nindex f3a1731..71dba76 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -53,33 +53,19 @@\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+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-\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 ((t_ch = *text) == '\\0' && p_ch != '*')\n+\t\treturn ABORT_ALL;\n \tif (force_lower_case && ISUPPER(t_ch))\n \t    t_ch = tolower(t_ch);\n \tswitch (p_ch) {\n@@ -107,21 +93,15 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a)\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\tif (t_ch == '\\0')\n+\t\t    break;\n+\t\tif ((matched = dowild(p, text)) != FALSE) {\n \t\t    if (!special || matched != ABORT_TO_STARSTAR)\n \t\t\treturn matched;\n \t\t} else if (!special && t_ch == '/')\n@@ -225,144 +205,21 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a)\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+    return *text ? FALSE : TRUE;\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+    return dowild((const uchar*)pattern, (const uchar*)text) == 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+    ret = dowild((const uchar*)pattern, (const uchar*)text) == 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\nindex e7f1a35..562faa3 100644\n--- a/wildmatch.h\n+++ b/wildmatch.h\n@@ -2,5 +2,3 @@\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.8.0.rc0.29.g1fdd78f\n"},{"id":"200806","messageId":"1349752147-13314-4-git-send-email-pclouds@gmail.com","threadId":"31763","inReplyTo":"1349752147-13314-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 3/8] Integrate wildmatch to git","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-09T03:09:02Z","receivedAt":"2012-10-09T03:09:02Z","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>\n---\n .gitignore           |   1 +\n Makefile             |   3 +\n t/t3070-wildmatch.sh | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++\n t/t3070/wildtest.txt | 165 -----------------------------------------------\n test-wildmatch.c     |  14 ++++\n wildmatch.c          |   8 ++-\n 6 files changed, 203 insertions(+), 166 deletions(-)\n create mode 100755 t/t3070-wildmatch.sh\n delete mode 100644 t/t3070/wildtest.txt\n create mode 100644 test-wildmatch.c\n\ndiff --git a/.gitignore b/.gitignore\nindex a188a82..37c3507 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -197,6 +197,7 @@\n /test-string-list\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 8413606..9a97379 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -523,6 +523,7 @@ TEST_PROGRAMS_NEED_X += test-sigchain\n TEST_PROGRAMS_NEED_X += test-string-list\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@@ -695,6 +696,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@@ -826,6 +828,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..bb92f8d\n--- /dev/null\n+++ b/t/t3070-wildmatch.sh\n@@ -0,0 +1,178 @@\n+#!/bin/sh\n+\n+test_description='wildmatch tests'\n+\n+. ./test-lib.sh\n+\n+match() {\n+    test_expect_success \"wildmatch $*\" \"\n+\tif [ $1 = 1 ]; then\n+\t    test-wildmatch wildmatch '$3' '$4'\n+\telse\n+\t    ! test-wildmatch wildmatch '$3' '$4'\n+\tfi &&\n+\tif [ $2 = 1 ]; then\n+\t    test-wildmatch fnmatch '$3' '$4'\n+\telse\n+\t    ! test-wildmatch fnmatch '$3' '$4'\n+\tfi\n+    \"\n+}\n+\n+# Basic wildmat features\n+match 1 1 foo foo\n+match 0 0 foo bar\n+match 1 1 '' \"\"\n+match 1 1 foo '???'\n+match 0 0 foo '??'\n+match 1 1 foo '*'\n+match 1 1 foo 'f*'\n+match 0 0 foo '*f'\n+match 1 1 foo '*foo*'\n+match 1 1 foobar '*ob*a*r*'\n+match 1 1 aaaaaaabababab '*ab'\n+match 1 1 'foo*' 'foo\\*'\n+match 0 0 foobar 'foo\\*bar'\n+match 1 1 'f\\oo' 'f\\\\oo'\n+match 1 1 ball '*[al]?'\n+match 0 0 ten '[ten]'\n+match 1 1 ten '**[!te]'\n+match 0 0 ten '**[!ten]'\n+match 1 1 ten 't[a-g]n'\n+match 0 0 ten 't[!a-g]n'\n+match 1 1 ton 't[!a-g]n'\n+match 1 1 ton 't[^a-g]n'\n+match 1 1 'a]b' 'a[]]b'\n+match 1 1 a-b 'a[]-]b'\n+match 1 1 'a]b' 'a[]-]b'\n+match 0 0 aab 'a[]-]b'\n+match 1 1 aab 'a[]a-]b'\n+match 1 1 ']' ']'\n+\n+# Extended slash-matching features\n+match 0 0 'foo/baz/bar' 'foo*bar'\n+match 1 0 'foo/baz/bar' 'foo**bar'\n+match 0 0 'foo/bar' 'foo?bar'\n+match 0 0 'foo/bar' 'foo[/]bar'\n+match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n+match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n+match 0 0 'foo' '**/foo'\n+match 1 1 '/foo' '**/foo'\n+match 1 0 'bar/baz/foo' '**/foo'\n+match 0 0 'bar/baz/foo' '*/foo'\n+match 0 0 'foo/bar/baz' '**/bar*'\n+match 1 0 'deep/foo/bar/baz' '**/bar/*'\n+match 0 0 'deep/foo/bar/baz/' '**/bar/*'\n+match 1 0 'deep/foo/bar/baz/' '**/bar/**'\n+match 0 0 'deep/foo/bar' '**/bar/*'\n+match 1 0 'deep/foo/bar/' '**/bar/**'\n+match 1 0 'foo/bar/baz' '**/bar**'\n+match 1 0 'foo/bar/baz/x' '*/bar/**'\n+match 0 0 'deep/foo/bar/baz/x' '*/bar/**'\n+match 1 0 'deep/foo/bar/baz/x' '**/bar/*/*'\n+\n+# Various additional tests\n+match 0 0 'acrt' 'a[c-c]st'\n+match 1 1 'acrt' 'a[c-c]rt'\n+match 0 0 ']' '[!]-]'\n+match 1 1 'a' '[!]-]'\n+match 0 0 '' '\\'\n+match 0 0 '\\' '\\'\n+match 0 0 '/\\' '*/\\'\n+match 1 1 '/\\' '*/\\\\'\n+match 1 1 'foo' 'foo'\n+match 1 1 '@foo' '@foo'\n+match 0 0 'foo' '@foo'\n+match 1 1 '[ab]' '\\[ab]'\n+match 1 1 '[ab]' '[[]ab]'\n+match 1 1 '[ab]' '[[:]ab]'\n+match 0 0 '[ab]' '[[::]ab]'\n+match 1 1 '[ab]' '[[:digit]ab]'\n+match 1 1 '[ab]' '[\\[:]ab]'\n+match 1 1 '?a?b' '\\??\\?b'\n+match 1 1 'abc' '\\a\\b\\c'\n+match 0 0 'foo' ''\n+match 1 0 'foo/bar/baz/to' '**/t[o]'\n+\n+# Character class tests\n+match 1 1 'a1B' '[[:alpha:]][[:digit:]][[:upper:]]'\n+match 0 0 'a' '[[:digit:][:upper:][:space:]]'\n+match 1 1 'A' '[[:digit:][:upper:][:space:]]'\n+match 1 0 '1' '[[:digit:][:upper:][:space:]]'\n+match 0 0 '1' '[[:digit:][:upper:][:spaci:]]'\n+match 1 1 ' ' '[[:digit:][:upper:][:space:]]'\n+match 0 0 '.' '[[:digit:][:upper:][:space:]]'\n+match 1 1 '.' '[[:digit:][:punct:][:space:]]'\n+match 1 1 '5' '[[:xdigit:]]'\n+match 1 1 'f' '[[:xdigit:]]'\n+match 1 1 'D' '[[:xdigit:]]'\n+match 1 0 '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]'\n+match 1 0 '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]'\n+match 1 1 '.' '[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]'\n+match 1 1 '5' '[a-c[:digit:]x-z]'\n+match 1 1 'b' '[a-c[:digit:]x-z]'\n+match 1 1 'y' '[a-c[:digit:]x-z]'\n+match 0 0 'q' '[a-c[:digit:]x-z]'\n+\n+# Additional tests, including some malformed wildmats\n+match 1 1 ']' '[\\\\-^]'\n+match 0 0 '[' '[\\\\-^]'\n+match 1 1 '-' '[\\-_]'\n+match 1 1 ']' '[\\]]'\n+match 0 0 '\\]' '[\\]]'\n+match 0 0 '\\' '[\\]]'\n+match 0 0 'ab' 'a[]b'\n+match 0 1 'a[]b' 'a[]b'\n+match 0 1 'ab[' 'ab['\n+match 0 0 'ab' '[!'\n+match 0 0 'ab' '[-'\n+match 1 1 '-' '[-]'\n+match 0 0 '-' '[a-'\n+match 0 0 '-' '[!a-'\n+match 1 1 '-' '[--A]'\n+match 1 1 '5' '[--A]'\n+match 1 1 ' ' '[ --]'\n+match 1 1 '$' '[ --]'\n+match 1 1 '-' '[ --]'\n+match 0 0 '0' '[ --]'\n+match 1 1 '-' '[---]'\n+match 1 1 '-' '[------]'\n+match 0 0 'j' '[a-e-n]'\n+match 1 1 '-' '[a-e-n]'\n+match 1 1 'a' '[!------]'\n+match 0 0 '[' '[]-a]'\n+match 1 1 '^' '[]-a]'\n+match 0 0 '^' '[!]-a]'\n+match 1 1 '[' '[!]-a]'\n+match 1 1 '^' '[a^bc]'\n+match 1 1 '-b]' '[a-]b]'\n+match 0 0 '\\' '[\\]'\n+match 1 1 '\\' '[\\\\]'\n+match 0 0 '\\' '[!\\\\]'\n+match 1 1 'G' '[A-\\\\]'\n+match 0 0 'aaabbb' 'b*a'\n+match 0 0 'aabcaa' '*ba*'\n+match 1 1 ',' '[,]'\n+match 1 1 ',' '[\\\\,]'\n+match 1 1 '\\' '[\\\\,]'\n+match 1 1 '-' '[,-.]'\n+match 0 0 '+' '[,-.]'\n+match 0 0 '-.]' '[,-.]'\n+match 1 1 '2' '[\\1-\\3]'\n+match 1 1 '3' '[\\1-\\3]'\n+match 0 0 '4' '[\\1-\\3]'\n+match 1 1 '\\' '[[-\\]]'\n+match 1 1 '[' '[[-\\]]'\n+match 1 1 ']' '[[-\\]]'\n+match 0 0 '-' '[[-\\]]'\n+\n+# Test recursion and the abort code (use \"wildtest -i\" to see iteration counts)\n+match 1 1 '-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n+match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n+match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*'\n+match 1 1 '/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n+match 0 0 '/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' '/*/*/*/*/*/*/12/*/*/*/m/*/*/*'\n+match 1 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt' '**/*a*b*g*n*t'\n+match 0 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz' '**/*a*b*g*n*t'\n+\n+test_done\ndiff --git a/t/t3070/wildtest.txt b/t/t3070/wildtest.txt\ndeleted file mode 100644\nindex 42c1678..0000000\n--- a/t/t3070/wildtest.txt\n+++ /dev/null\n@@ -1,165 +0,0 @@\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..08962d5\n--- /dev/null\n+++ b/test-wildmatch.c\n@@ -0,0 +1,14 @@\n+#include \"cache.h\"\n+#include \"wildmatch.h\"\n+\n+int main(int argc, char **argv)\n+{\n+\tif (!strcmp(argv[1], \"wildmatch\"))\n+\t\treturn wildmatch(argv[3], argv[2]) ? 0 : 1;\n+\telse if (!strcmp(argv[1], \"iwildmatch\"))\n+\t\treturn iwildmatch(argv[3], argv[2]) ? 0 : 1;\n+\telse if (!strcmp(argv[1], \"fnmatch\"))\n+\t\treturn fnmatch(argv[3], argv[2], FNM_PATHNAME);\n+\telse\n+\t\treturn 1;\n+}\ndiff --git a/wildmatch.c b/wildmatch.c\nindex 71dba76..7b64a6b 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.8.0.rc0.29.g1fdd78f\n"},{"id":"200807","messageId":"1349752147-13314-5-git-send-email-pclouds@gmail.com","threadId":"31763","inReplyTo":"1349752147-13314-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 4/8] wildmatch: remove static variable force_lower_case","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-09T03:09:03Z","receivedAt":"2012-10-09T03:09:03Z","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. Also combine wildmatch\nand iwildmatch into one.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n test-wildmatch.c |  4 ++--\n wildmatch.c      | 23 ++++++-----------------\n wildmatch.h      |  3 +--\n 3 files changed, 9 insertions(+), 21 deletions(-)\n\ndiff --git a/test-wildmatch.c b/test-wildmatch.c\nindex 08962d5..5c18cf8 100644\n--- a/test-wildmatch.c\n+++ b/test-wildmatch.c\n@@ -4,9 +4,9 @@\n int main(int argc, char **argv)\n {\n \tif (!strcmp(argv[1], \"wildmatch\"))\n-\t\treturn wildmatch(argv[3], argv[2]) ? 0 : 1;\n+\t\treturn wildmatch(argv[3], argv[2], 0) ? 0 : 1;\n \telse if (!strcmp(argv[1], \"iwildmatch\"))\n-\t\treturn iwildmatch(argv[3], argv[2]) ? 0 : 1;\n+\t\treturn wildmatch(argv[3], argv[2], FNM_CASEFOLD) ? 0 : 1;\n \telse if (!strcmp(argv[1], \"fnmatch\"))\n \t\treturn fnmatch(argv[3], argv[2], FNM_PATHNAME);\n \telse\ndiff --git a/wildmatch.c b/wildmatch.c\nindex 7b64a6b..2382873 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -11,8 +11,8 @@\n \n #include <stddef.h>\n #include <ctype.h>\n-#include <string.h>\n \n+#include \"cache.h\"\n #include \"wildmatch.h\"\n \n typedef unsigned char uchar;\n@@ -59,11 +59,9 @@ typedef unsigned char uchar;\n #define ISUPPER(c) (ISASCII(c) && isupper(c))\n #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))\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)\n+static int dowild(const uchar *p, const uchar *text, int force_lower_case)\n {\n     uchar p_ch;\n \n@@ -107,7 +105,7 @@ static int dowild(const uchar *p, const uchar *text)\n \t    while (1) {\n \t\tif (t_ch == '\\0')\n \t\t    break;\n-\t\tif ((matched = dowild(p, text)) != FALSE) {\n+\t\tif ((matched = dowild(p, text, 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@@ -215,17 +213,8 @@ static int dowild(const uchar *p, const uchar *text)\n }\n \n /* Match the \"pattern\" against the \"text\" string. */\n-int wildmatch(const char *pattern, const char *text)\n-{\n-    return dowild((const uchar*)pattern, (const uchar*)text) == 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+int wildmatch(const char *pattern, const char *text, int flags)\n {\n-    int ret;\n-    force_lower_case = 1;\n-    ret = dowild((const uchar*)pattern, (const uchar*)text) == TRUE;\n-    force_lower_case = 0;\n-    return ret;\n+    return dowild((const uchar*)pattern, (const uchar*)text,\n+\t\t  flags & FNM_CASEFOLD ? 1 : 0) == TRUE;\n }\ndiff --git a/wildmatch.h b/wildmatch.h\nindex 562faa3..e974f9a 100644\n--- a/wildmatch.h\n+++ b/wildmatch.h\n@@ -1,4 +1,3 @@\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(const char *pattern, const char *text, int flags);\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"200808","messageId":"1349752147-13314-6-git-send-email-pclouds@gmail.com","threadId":"31763","inReplyTo":"1349752147-13314-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 5/8] wildmatch: fix case-insensitive matching","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-09T03:09:04Z","receivedAt":"2012-10-09T03:09:04Z","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 2382873..fdb8cb1 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -72,6 +72,8 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case)\n \t\treturn ABORT_ALL;\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.8.0.rc0.29.g1fdd78f\n"},{"id":"200809","messageId":"1349752147-13314-7-git-send-email-pclouds@gmail.com","threadId":"31763","inReplyTo":"1349752147-13314-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 6/8] wildmatch: adjust \"**\" behavior","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-09T03:09:05Z","receivedAt":"2012-10-09T03:09:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Standard wildmatch() sees consecutive asterisks as \"*\" that can also\nmatch slashes. But that may be hard to explain to users as\n\"abc/**/def\" can match \"abcdef\", \"abcxyzdef\", \"abc/def\", \"abc/x/def\",\n\"abc/x/y/def\"...\n\nThis patch changes wildmatch so that users can do\n\n- \"**/def\" -> all paths ending with file/directory 'def'\n- \"abc/**\" - equivalent to \"/abc/\"\n- \"abc/**/def\" -> \"abc/x/def\", \"abc/x/y/def\"...\n- other \"**\" cases are downgraded to normal \"*\"\n\nBasically the magic of \"**\" only remains if it's wrapped around by\nslashes.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t3070-wildmatch.sh | 2 +-\n wildmatch.c          | 8 +++++++-\n 2 files changed, 8 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nindex bb92f8d..d320f84 100755\n--- a/t/t3070-wildmatch.sh\n+++ b/t/t3070-wildmatch.sh\n@@ -51,7 +51,7 @@ match 1 1 ']' ']'\n \n # Extended slash-matching features\n match 0 0 'foo/baz/bar' 'foo*bar'\n-match 1 0 'foo/baz/bar' 'foo**bar'\n+match 0 0 'foo/baz/bar' 'foo**bar'\n match 0 0 'foo/bar' 'foo?bar'\n match 0 0 'foo/bar' 'foo[/]bar'\n match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\ndiff --git a/wildmatch.c b/wildmatch.c\nindex fdb8cb1..1b39346 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -91,8 +91,14 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case)\n \t    continue;\n \t  case '*':\n \t    if (*++p == '*') {\n+\t\tconst uchar *prev_p = p - 2;\n \t\twhile (*++p == '*') {}\n-\t\tspecial = TRUE;\n+\t\tif ((prev_p == text || *prev_p == '/') ||\n+\t\t    (*p == '\\0' || *p == '/' ||\n+\t\t     (p[0] == '\\\\' && p[1] == '/'))) {\n+\t\t    special = TRUE;\n+\t\t} else\n+\t\t    special = FALSE;\n \t    } else\n \t\tspecial = FALSE;\n \t    if (*p == '\\0') {\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"200810","messageId":"1349752147-13314-8-git-send-email-pclouds@gmail.com","threadId":"31763","inReplyTo":"1349752147-13314-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 7/8] wildmatch: make /**/ match zero or more directories","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-09T03:09:06Z","receivedAt":"2012-10-09T03:09:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\"foo/**/bar\" matches \"foo/x/bar\", \"foo/x/y/bar\"... but not\n\"foo/bar\". We make a special case, when foo/**/ is detected (and\n\"foo/\" part is already matched), try matching \"bar\" with the rest of\nthe string.\n\n\"Match one or more directories\" semantics can be easily achieved using\n\"foo/*/**/bar\".\n\nThis also makes \"**/foo\" match \"foo\" in addition to \"x/foo\",\n\"x/y/foo\"..\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n t/t3070-wildmatch.sh |  8 +++++++-\n wildmatch.c          | 17 +++++++++++++++++\n 2 files changed, 24 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nindex d320f84..a247a36 100755\n--- a/t/t3070-wildmatch.sh\n+++ b/t/t3070-wildmatch.sh\n@@ -52,11 +52,17 @@ match 1 1 ']' ']'\n # Extended slash-matching features\n match 0 0 'foo/baz/bar' 'foo*bar'\n match 0 0 'foo/baz/bar' 'foo**bar'\n+match 1 1 'foo/baz/bar' 'foo/**/bar'\n+match 1 0 'foo/baz/bar' 'foo/**/**/bar'\n+match 1 0 'foo/b/a/z/bar' 'foo/**/bar'\n+match 1 0 'foo/b/a/z/bar' 'foo/**/**/bar'\n+match 1 0 'foo/bar' 'foo/**/bar'\n+match 1 0 'foo/bar' 'foo/**/**/bar'\n match 0 0 'foo/bar' 'foo?bar'\n match 0 0 'foo/bar' 'foo[/]bar'\n match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r'\n-match 0 0 'foo' '**/foo'\n+match 1 0 'foo' '**/foo'\n match 1 1 '/foo' '**/foo'\n match 1 0 'bar/baz/foo' '**/foo'\n match 0 0 'bar/baz/foo' '*/foo'\ndiff --git a/wildmatch.c b/wildmatch.c\nindex 1b39346..4069b2d 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -96,6 +96,23 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case)\n \t\tif ((prev_p == text || *prev_p == '/') ||\n \t\t    (*p == '\\0' || *p == '/' ||\n \t\t     (p[0] == '\\\\' && p[1] == '/'))) {\n+\t\t\t/*\n+\t\t\t * Assuming we already match 'foo/' and are at\n+\t\t\t * <star star slash>, just assume it matches\n+\t\t\t * nothing and go ahead match the rest of the\n+\t\t\t * pattern with the remaining string. This\n+\t\t\t * helps make foo/<*><*>/bar (<> because\n+\t\t\t * otherwise it breaks C comment syntax) match\n+\t\t\t * both foo/bar and foo/a/bar.\n+\t\t\t *\n+\t\t\t * Crazy patterns like /<*><*>/<*><*>/ are\n+\t\t\t * treated like /<*><*>/. But undefined\n+\t\t\t * behavior is even appropriate for people\n+\t\t\t * writing such a pattern.\n+\t\t\t */\n+\t\t\tif (p[0] == '/' &&\n+\t\t\t    dowild(p + 1, text, force_lower_case) == TRUE)\n+\t\t\t\treturn TRUE;\n \t\t    special = TRUE;\n \t\t} else\n \t\t    special = FALSE;\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"200811","messageId":"1349752147-13314-9-git-send-email-pclouds@gmail.com","threadId":"31763","inReplyTo":"1349752147-13314-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 8/8] Support \"**\" wildcard in .gitignore and .gitattributes","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-09T03:09:07Z","receivedAt":"2012-10-09T03:09:07Z","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        | 19 +++++++++++++++++++\n attr.c                             |  4 +++-\n dir.c                              |  4 +++-\n t/t0003-attributes.sh              | 38 ++++++++++++++++++++++++++++++++++++++\n t/t3001-ls-files-others-exclude.sh | 19 +++++++++++++++++++\n 5 files changed, 82 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt\nindex 96639e0..5a9c9f7 100644\n--- a/Documentation/gitignore.txt\n+++ b/Documentation/gitignore.txt\n@@ -104,6 +104,25 @@ PATTERN FORMAT\n    For example, \"/{asterisk}.c\" matches \"cat-file.c\" but not\n    \"mozilla-sha1/sha1.c\".\n \n+Two consecutive asterisks (\"`**`\") in patterns matched against\n+full pathname may have special meaning:\n+\n+ - A leading \"`**`\" followed by a slash means match in all\n+   directories. For example, \"`**/foo`\" matches file or directory\n+   \"`foo`\" anywhere, the same as pattern \"`foo`\". \"**/foo/bar\"\n+   matches file or directory \"`bar`\" anywhere that is directly\n+   under directory \"`foo`\".\n+\n+ - A trailing \"/**\" matches everything inside. For example,\n+   \"abc/**\" is equivalent to \"`/abc/`\".\n+\n+ - A slash followed by two consecutive asterisks then a slash\n+   matches zero or more directories. For example, \"`a/**/b`\"\n+   matches \"`a/b`\", \"`a/x/b`\", \"`a/x/y/b`\" and so on.\n+\n+ - Consecutive asterisks otherwise are treated like normal\n+   asterisk wildcards.\n+\n NOTES\n -----\n \ndiff --git a/attr.c b/attr.c\nindex 887a9ae..e85e5ed 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 wildmatch(pattern, pathname + baselen,\n+\t\t\t ignore_case ? FNM_CASEFOLD : 0);\n }\n \n static int macroexpand_one(int attr_nr, int rem);\ndiff --git a/dir.c b/dir.c\nindex 4868339..dc721c0 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,8 @@ 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    wildmatch(exclude, name, ignore_case ? FNM_CASEFOLD : 0))\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..67a5694 100755\n--- a/t/t0003-attributes.sh\n+++ b/t/t0003-attributes.sh\n@@ -232,4 +232,42 @@ 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: bar\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_expect_success '\"**\" with no slashes test' '\n+\techo \"a**f foo=bar\" >.gitattributes &&\n+\tgit check-attr foo -- \"f\" >actual &&\n+\tcat <<\\EOF >expect &&\n+f: foo: unspecified\n+af: foo: bar\n+axf: foo: bar\n+a/f: foo: unspecified\n+a/b/f: foo: unspecified\n+a/b/c/f: foo: unspecified\n+EOF\n+\tgit check-attr foo -- \"f\" >actual 2>err &&\n+\tgit check-attr foo -- \"af\" >>actual 2>err &&\n+\tgit check-attr foo -- \"axf\" >>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..278315d 100755\n--- a/t/t3001-ls-files-others-exclude.sh\n+++ b/t/t3001-ls-files-others-exclude.sh\n@@ -214,4 +214,23 @@ 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+a.1\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+\n+test_expect_success 'ls-files with \"**\" patterns and no slashes' '\n+\t: >expect &&\n+\tgit ls-files -o -i --exclude \"one**a.1\" >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n1.8.0.rc0.29.g1fdd78f\n"},{"id":"200823","messageId":"5073D8FF.8080503@alum.mit.edu","threadId":"31763","inReplyTo":"1349752147-13314-9-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 8/8] Support \"**\" wildcard in .gitignore and .gitattributes","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2012-10-09T07:57:51Z","receivedAt":"2012-10-09T07:57:51Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"I like how this series is going and it's going to be a nice new feature.\n Some comments...\n\nIt would be helpful if you would use\n\n    --subject-prefix='PATCH v3'\n\netc. to help spectators keep track of the different versions of your\npatch series.\n\nOn 10/09/2012 05:09 AM, Nguyễn Thái Ngọc Duy wrote:\n> \n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  Documentation/gitignore.txt        | 19 +++++++++++++++++++\n>  attr.c                             |  4 +++-\n>  dir.c                              |  4 +++-\n>  t/t0003-attributes.sh              | 38 ++++++++++++++++++++++++++++++++++++++\n>  t/t3001-ls-files-others-exclude.sh | 19 +++++++++++++++++++\n>  5 files changed, 82 insertions(+), 2 deletions(-)\n> \n> diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt\n> index 96639e0..5a9c9f7 100644\n> --- a/Documentation/gitignore.txt\n> +++ b/Documentation/gitignore.txt\n> @@ -104,6 +104,25 @@ PATTERN FORMAT\n>     For example, \"/{asterisk}.c\" matches \"cat-file.c\" but not\n>     \"mozilla-sha1/sha1.c\".\n>  \n> +Two consecutive asterisks (\"`**`\") in patterns matched against\n> +full pathname may have special meaning:\n> +\n> + - A leading \"`**`\" followed by a slash means match in all\n> +   directories. For example, \"`**/foo`\" matches file or directory\n> +   \"`foo`\" anywhere, the same as pattern \"`foo`\". \"**/foo/bar\"\n> +   matches file or directory \"`bar`\" anywhere that is directly\n> +   under directory \"`foo`\".\n> +\n> + - A trailing \"/**\" matches everything inside. For example,\n> +   \"abc/**\" is equivalent to \"`/abc/`\".\n\nIt seems odd that you add a leading slash in this example.  I assume\nthat is because of the rule that a pattern containing a slash is\nconsidered anchored at the current directory.  But I find it confusing\nbecause the addition of the leading slash is not part of the rule you\nare trying to illustrate here, and is therefore a distraction.  I\nsuggest that you write either\n\n- A trailing \"/**\" matches everything inside. For example,\n  \"/abc/**\" is equivalent to \"`/abc/`\".\n\nor\n\n- A trailing \"/**\" matches everything inside. For example,\n  \"abc/**\" is equivalent to \"`abc/`\" (which is also equivalent\n  to \"`/abc/`\").\n\n> +\n> + - A slash followed by two consecutive asterisks then a slash\n> +   matches zero or more directories. For example, \"`a/**/b`\"\n> +   matches \"`a/b`\", \"`a/x/b`\", \"`a/x/y/b`\" and so on.\n> +\n> + - Consecutive asterisks otherwise are treated like normal\n> +   asterisk wildcards.\n> +\n\nI don't like the last rule.  (1) This construct is superfluous; why\nwouldn't the user just use a single asterisk?  (2) Allowing this\nconstruct means that it could appear in .gitignore files, creating\nunnecessary confusion: extrapolating from the other meanings of \"**\"\nusers would expect that it would somehow match slashes.  (3) It is\nconceivable (though admittedly unlikely) that we might want to assign a\ndistinct meaning to this construct in the future, and accepting it now\nas a different way to spell \"*\" would prevent such a change.\n\nPerhaps this rule was intended for backwards compatibility?\n\nI think it would be preferable to say that other uses of consecutive\nasterisks are undefined, and probably make them trigger a warning.\n\n>  NOTES\n>  -----\n>  \n> diff --git a/attr.c b/attr.c\n> index 887a9ae..e85e5ed 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 wildmatch(pattern, pathname + baselen,\n> +\t\t\t ignore_case ? FNM_CASEFOLD : 0);\n>  }\n>  \n>  static int macroexpand_one(int attr_nr, int rem);\n> diff --git a/dir.c b/dir.c\n> index 4868339..dc721c0 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,8 @@ 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    wildmatch(exclude, name, ignore_case ? FNM_CASEFOLD : 0))\n>  \t\t\treturn to_exclude;\n>  \t}\n>  \treturn -1; /* undecided */\n> diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh\n> index febc45c..67a5694 100755\n> --- a/t/t0003-attributes.sh\n> +++ b/t/t0003-attributes.sh\n> @@ -232,4 +232,42 @@ 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: bar\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_expect_success '\"**\" with no slashes test' '\n> +\techo \"a**f foo=bar\" >.gitattributes &&\n> +\tgit check-attr foo -- \"f\" >actual &&\n> +\tcat <<\\EOF >expect &&\n> +f: foo: unspecified\n> +af: foo: bar\n> +axf: foo: bar\n> +a/f: foo: unspecified\n> +a/b/f: foo: unspecified\n> +a/b/c/f: foo: unspecified\n> +EOF\n> +\tgit check-attr foo -- \"f\" >actual 2>err &&\n> +\tgit check-attr foo -- \"af\" >>actual 2>err &&\n> +\tgit check-attr foo -- \"axf\" >>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\n> diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh\n> index c8fe978..278315d 100755\n> --- a/t/t3001-ls-files-others-exclude.sh\n> +++ b/t/t3001-ls-files-others-exclude.sh\n> @@ -214,4 +214,23 @@ 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> +a.1\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> +\n> +test_expect_success 'ls-files with \"**\" patterns and no slashes' '\n> +\t: >expect &&\n> +\tgit ls-files -o -i --exclude \"one**a.1\" >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n>  test_done\n> \n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"200859","messageId":"7vtxu3e5jl.fsf@alter.siamese.dyndns.org","threadId":"31763","inReplyTo":"1349752147-13314-5-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 4/8] wildmatch: remove static variable force_lower_case","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-10-09T20:47:42Z","receivedAt":"2012-10-09T20:47:42Z","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> diff --git a/wildmatch.c b/wildmatch.c\n> index 7b64a6b..2382873 100644\n> --- a/wildmatch.c\n> +++ b/wildmatch.c\n> @@ -11,8 +11,8 @@\n>  \n>  #include <stddef.h>\n>  #include <ctype.h>\n> -#include <string.h>\n>  \n> +#include \"cache.h\"\n>  #include \"wildmatch.h\"\n\nThis is wrong; the includes from the system headers should have\nbeen removed in the previous step where the series \"integrated\"\nwildmatch to git, after which point the first include any C source\nthat is not at the platform-compatibility layer should be cache.h\nor git-compat-util.h.\n"},{"id":"200874","messageId":"CACsJy8BOcH8cXje5osL4oNP1YkJ8HadUOYBek3uhEbySBey9Kw@mail.gmail.com","threadId":"31763","inReplyTo":"7vtxu3e5jl.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 4/8] wildmatch: remove static variable force_lower_case","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-10T05:14:50Z","receivedAt":"2012-10-10T05:14:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Oct 10, 2012 at 3:47 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> diff --git a/wildmatch.c b/wildmatch.c\n>> index 7b64a6b..2382873 100644\n>> --- a/wildmatch.c\n>> +++ b/wildmatch.c\n>> @@ -11,8 +11,8 @@\n>>\n>>  #include <stddef.h>\n>>  #include <ctype.h>\n>> -#include <string.h>\n>>\n>> +#include \"cache.h\"\n>>  #include \"wildmatch.h\"\n>\n> This is wrong; the includes from the system headers should have\n> been removed in the previous step where the series \"integrated\"\n> wildmatch to git, after which point the first include any C source\n> that is not at the platform-compatibility layer should be cache.h\n> or git-compat-util.h.\n\nGit's ctype does not seem to be complete for wildmatch's use so\nctype.h is required. But that can be easily fixed later on.\n-- \nDuy\n"},{"id":"200876","messageId":"7v1uh6evv7.fsf@alter.siamese.dyndns.org","threadId":"31763","inReplyTo":"CACsJy8BOcH8cXje5osL4oNP1YkJ8HadUOYBek3uhEbySBey9Kw@mail.gmail.com","subject":"Re: [PATCH 4/8] wildmatch: remove static variable force_lower_case","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-10-10T05:31:24Z","receivedAt":"2012-10-10T05:31:24Z","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> Git's ctype does not seem to be complete for wildmatch's use so\n> ctype.h is required. But that can be easily fixed later on.\n\nUntil \"later on\", I cannot even compile the series.\n"},{"id":"200878","messageId":"CACsJy8AW4bCrXZtNtpYGT4=Z72u=Ge2OuX1sG3xs_jUx9gRfPw@mail.gmail.com","threadId":"31763","inReplyTo":"5073D8FF.8080503@alum.mit.edu","subject":"Re: [PATCH 8/8] Support \"**\" wildcard in .gitignore and .gitattributes","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-10T05:40:51Z","receivedAt":"2012-10-10T05:40:51Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Oct 9, 2012 at 2:57 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:\n>> + - A trailing \"/**\" matches everything inside. For example,\n>> +   \"abc/**\" is equivalent to \"`/abc/`\".\n>\n> It seems odd that you add a leading slash in this example.  I assume\n> that is because of the rule that a pattern containing a slash is\n> considered anchored at the current directory. But I find it confusing\n> because the addition of the leading slash is not part of the rule you\n> are trying to illustrate here, and is therefore a distraction.  I\n> suggest that you write either\n>\n> - A trailing \"/**\" matches everything inside. For example,\n>   \"/abc/**\" is equivalent to \"`/abc/`\".\n>\n> or\n>\n> - A trailing \"/**\" matches everything inside. For example,\n>   \"abc/**\" is equivalent to \"`abc/`\" (which is also equivalent\n>   to \"`/abc/`\").\n\nThe tricky thing in .gitignore is that the last '/' alone does not\nimply anchor. So \"abc/\" means match _directory_ abc anywhere in\nworktree. So the former is probably better. I should also add a note\nhere (or in gitattributes.txt) about the difference between \"/abc/*\"\nand \"/abc/**\". The former assigns attributes to all files directly\nunder abc (e.g. depth 1), the latter infinite depth.\n\n>> + - A slash followed by two consecutive asterisks then a slash\n>> +   matches zero or more directories. For example, \"`a/**/b`\"\n>> +   matches \"`a/b`\", \"`a/x/b`\", \"`a/x/y/b`\" and so on.\n>> +\n>> + - Consecutive asterisks otherwise are treated like normal\n>> +   asterisk wildcards.\n>> +\n>\n> I don't like the last rule.  (1) This construct is superfluous; why\n> wouldn't the user just use a single asterisk?  (2) Allowing this\n> construct means that it could appear in .gitignore files, creating\n> unnecessary confusion: extrapolating from the other meanings of \"**\"\n> users would expect that it would somehow match slashes.  (3) It is\n> conceivable (though admittedly unlikely) that we might want to assign a\n> distinct meaning to this construct in the future, and accepting it now\n> as a different way to spell \"*\" would prevent such a change.\n>\n> Perhaps this rule was intended for backwards compatibility?\n\nWe break backwards compatibility already. Existing \"**/\" or \"/**\"\npatterns now behave differently.\n\n> I think it would be preferable to say that other uses of consecutive\n> asterisks are undefined, and probably make them trigger a warning.\n\nInstead of undefined, we can reject the pattern as \"broken\". I have to\ncheck how fnmatch/wildmatch deals with broken patterns (it must do).\nIf it returns a different code for broken patterns, then we can warn\nusers, which is not limited in just \"**\" breakage.\n-- \nDuy\n"},{"id":"200880","messageId":"CACsJy8BALFdRJw2sDBPStgxYaqy8d374MGo+S1CAdVnj4FtecA@mail.gmail.com","threadId":"31763","inReplyTo":"7v1uh6evv7.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 4/8] wildmatch: remove static variable force_lower_case","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-10-10T05:47:54Z","receivedAt":"2012-10-10T05:47:54Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Oct 10, 2012 at 12:31 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>\n>> Git's ctype does not seem to be complete for wildmatch's use so\n>> ctype.h is required. But that can be easily fixed later on.\n>\n> Until \"later on\", I cannot even compile the series.\n\nSo that's why you noticed this patch :) It builds fine here. I'll fix\nup and send an update later.\n-- \nDuy\n"}]}