{"thread":{"id":"32405","subject":"[PATCH 3/3] Convert all fnmatch() calls to wildmatch()","startedAt":"2012-12-19T13:08:05Z","lastAt":"2012-12-20T13:34:06Z","messageCount":11,"participants":["Nguyễn Thái Ngọc Duy","Nguyen Thai Ngoc Duy","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"205188","messageId":"1355922488-20976-1-git-send-email-pclouds@gmail.com","threadId":"32405","inReplyTo":null,"subject":"[PATCH/WIP 0/3] Bye bye fnmatch()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-12-19T13:08:05Z","receivedAt":"2012-12-19T13:08:05Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"For those who have not followed, nd/wildmatch brings another\nfnmatch-like implementation which can nearly replace fnmatch.\nSystem fnmatch() seems to behave differently in some cases. It's\nbetter to stay away and use one implementation for all.\n\nI just wanted to see how much work there may be if we go this way. It\nturns out not much. I haven't checked my dowild() changes carefully.\nI may have left a bug in '[]' code. There are some minor issues I\nlike dependency on FNM_* macros or wildmatch.h should be incorporated\nback to git-compat-util.h. But the test suite passes for me. So it's\npromising.\n\nNguyễn Thái Ngọc Duy (3):\n  wildmatch: make dowild() take arbitrary flags\n  wildmatch: support \"no FNM_PATHNAME\" mode\n  Convert all fnmatch() calls to wildmatch()\n\n builtin/apply.c        |  3 ++-\n builtin/branch.c       |  3 ++-\n builtin/describe.c     |  3 ++-\n builtin/for-each-ref.c |  3 ++-\n builtin/ls-remote.c    |  3 ++-\n builtin/name-rev.c     |  3 ++-\n builtin/reflog.c       |  3 ++-\n builtin/replace.c      |  3 ++-\n builtin/show-branch.c  |  3 ++-\n builtin/tag.c          |  3 ++-\n diffcore-order.c       |  3 ++-\n dir.c                  |  6 +++---\n refs.c                 |  3 ++-\n t/t3070-wildmatch.sh   | 27 +++++++++++++++++++++++++++\n test-wildmatch.c       |  4 +++-\n tree-walk.c            |  5 +++--\n wildmatch.c            | 25 ++++++++++++++-----------\n 17 files changed, 74 insertions(+), 29 deletions(-)\n\n-- \n1.8.0.rc2.23.g1fb49df\n"},{"id":"205189","messageId":"1355922488-20976-2-git-send-email-pclouds@gmail.com","threadId":"32405","inReplyTo":"1355922488-20976-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 1/3] wildmatch: make dowild() take arbitrary flags","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-12-19T13:08:06Z","receivedAt":"2012-12-19T13:08:06Z","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 | 13 ++++++-------\n 1 file changed, 6 insertions(+), 7 deletions(-)\n\ndiff --git a/wildmatch.c b/wildmatch.c\nindex 3972e26..9586ed9 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -55,7 +55,7 @@ typedef unsigned char uchar;\n #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))\n \n /* Match pattern \"p\" against \"text\" */\n-static int dowild(const uchar *p, const uchar *text, int force_lower_case)\n+static int dowild(const uchar *p, const uchar *text, int flags)\n {\n \tuchar p_ch;\n \n@@ -64,9 +64,9 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case)\n \t\tuchar t_ch, prev_ch;\n \t\tif ((t_ch = *text) == '\\0' && p_ch != '*')\n \t\t\treturn ABORT_ALL;\n-\t\tif (force_lower_case && ISUPPER(t_ch))\n+\t\tif (flags & FNM_CASEFOLD && ISUPPER(t_ch))\n \t\t\tt_ch = tolower(t_ch);\n-\t\tif (force_lower_case && ISUPPER(p_ch))\n+\t\tif (flags & FNM_CASEFOLD && ISUPPER(p_ch))\n \t\t\tp_ch = tolower(p_ch);\n \t\tswitch (p_ch) {\n \t\tcase '\\\\':\n@@ -100,7 +100,7 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case)\n \t\t\t\t\t * both foo/bar and foo/a/bar.\n \t\t\t\t\t */\n \t\t\t\t\tif (p[0] == '/' &&\n-\t\t\t\t\t    dowild(p + 1, text, force_lower_case) == MATCH)\n+\t\t\t\t\t    dowild(p + 1, text, flags) == MATCH)\n \t\t\t\t\t\treturn MATCH;\n \t\t\t\t\tspecial = TRUE;\n \t\t\t\t} else\n@@ -119,7 +119,7 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case)\n \t\t\twhile (1) {\n \t\t\t\tif (t_ch == '\\0')\n \t\t\t\t\tbreak;\n-\t\t\t\tif ((matched = dowild(p, text,  force_lower_case)) != NOMATCH) {\n+\t\t\t\tif ((matched = dowild(p, text,  flags)) != NOMATCH) {\n \t\t\t\t\tif (!special || matched != ABORT_TO_STARSTAR)\n \t\t\t\t\t\treturn matched;\n \t\t\t\t} else if (!special && t_ch == '/')\n@@ -229,6 +229,5 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case)\n /* Match the \"pattern\" against the \"text\" string. */\n int wildmatch(const char *pattern, const char *text, int flags)\n {\n-\treturn dowild((const uchar*)pattern, (const uchar*)text,\n-\t\t      flags & FNM_CASEFOLD ? 1 :0);\n+\treturn dowild((const uchar*)pattern, (const uchar*)text, flags);\n }\n-- \n1.8.0.rc2.23.g1fb49df\n"},{"id":"205190","messageId":"1355922488-20976-3-git-send-email-pclouds@gmail.com","threadId":"32405","inReplyTo":"1355922488-20976-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 2/3] wildmatch: support \"no FNM_PATHNAME\" mode","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-12-19T13:08:07Z","receivedAt":"2012-12-19T13:08:07Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"By default wildmatch(,, 0) is equivalent with fnmatch(,, FNM_PATHNAME).\n\nThis patch makes wildmatch behave more like fnmatch: FNM_PATHNAME\nbehavior is always applied when FNM_PATHNAME is passed to\nwildmatch. Without FNM_PATHNAME, wildmatch accepts '/' in '?' and '[]'\nand treats '*' like '**' in the original version.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n The choice of name \"pathspec\" is not good. I couldn't think of\n anything appropriate and just did not care enough at this point.\n\n dir.c                |  2 +-\n t/t3070-wildmatch.sh | 27 +++++++++++++++++++++++++++\n test-wildmatch.c     |  4 +++-\n wildmatch.c          | 12 ++++++++----\n 4 files changed, 39 insertions(+), 6 deletions(-)\n\ndiff --git a/dir.c b/dir.c\nindex cb7328b..7bbd6f8 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -595,7 +595,7 @@ int match_pathname(const char *pathname, int pathlen,\n \t}\n \n \treturn wildmatch(pattern, name,\n-\t\t\t ignore_case ? FNM_CASEFOLD : 0) == 0;\n+\t\t\t FNM_PATHNAME | (ignore_case ? FNM_CASEFOLD : 0)) == 0;\n }\n \n /* Scan the list and let the last match determine the fate.\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nindex 3155eab..ca4ac46 100755\n--- a/t/t3070-wildmatch.sh\n+++ b/t/t3070-wildmatch.sh\n@@ -29,6 +29,18 @@ match() {\n     fi\n }\n \n+pathspec() {\n+    if [ $1 = 1 ]; then\n+\ttest_expect_success \"pathspec:    match '$2' '$3'\" \"\n+\t    test-wildmatch pathspec '$2' '$3'\n+\t\"\n+    else\n+\ttest_expect_success \"pathspec: no match '$2' '$3'\" \"\n+\t    ! test-wildmatch pathspec '$2' '$3'\n+\t\"\n+    fi\n+}\n+\n # Basic wildmat features\n match 1 1 foo foo\n match 0 0 foo bar\n@@ -192,4 +204,19 @@ match 0 0 'XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' 'XXX/*/\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+pathspec 1 foo foo\n+pathspec 0 foo fo\n+pathspec 1 foo/bar foo/bar\n+pathspec 1 foo/bar 'foo/*'\n+pathspec 1 foo/bba/arr 'foo/*'\n+pathspec 1 foo/bba/arr 'foo/**'\n+pathspec 1 foo/bba/arr 'foo*'\n+pathspec 1 foo/bba/arr 'foo**'\n+pathspec 1 foo/bba/arr 'foo/*arr'\n+pathspec 1 foo/bba/arr 'foo/**arr'\n+pathspec 0 foo/bba/arr 'foo/*z'\n+pathspec 0 foo/bba/arr 'foo/**z'\n+pathspec 1 foo/bar 'foo?bar'\n+pathspec 1 foo/bar 'foo[/]bar'\n+\n test_done\ndiff --git a/test-wildmatch.c b/test-wildmatch.c\nindex e384c8e..7fefa4f 100644\n--- a/test-wildmatch.c\n+++ b/test-wildmatch.c\n@@ -12,9 +12,11 @@ int main(int argc, char **argv)\n \t\t\targv[i] += 3;\n \t}\n \tif (!strcmp(argv[1], \"wildmatch\"))\n+\t\treturn !!wildmatch(argv[3], argv[2], FNM_PATHNAME);\n+\telse if (!strcmp(argv[1], \"pathspec\"))\n \t\treturn !!wildmatch(argv[3], argv[2], 0);\n \telse if (!strcmp(argv[1], \"iwildmatch\"))\n-\t\treturn !!wildmatch(argv[3], argv[2], FNM_CASEFOLD);\n+\t\treturn !!wildmatch(argv[3], argv[2], FNM_PATHNAME | FNM_CASEFOLD);\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 9586ed9..6aa034f 100644\n--- a/wildmatch.c\n+++ b/wildmatch.c\n@@ -80,14 +80,17 @@ static int dowild(const uchar *p, const uchar *text, int flags)\n \t\t\tcontinue;\n \t\tcase '?':\n \t\t\t/* Match anything but '/'. */\n-\t\t\tif (t_ch == '/')\n+\t\t\tif (flags & FNM_PATHNAME && t_ch == '/')\n \t\t\t\treturn NOMATCH;\n \t\t\tcontinue;\n \t\tcase '*':\n \t\t\tif (*++p == '*') {\n \t\t\t\tconst uchar *prev_p = p - 2;\n \t\t\t\twhile (*++p == '*') {}\n-\t\t\t\tif ((prev_p == text || *prev_p == '/') ||\n+\t\t\t\tif (!(flags & FNM_PATHNAME))\n+\t\t\t\t\t/* without FNM_PATHNAME, '*' == '**' */\n+\t\t\t\t\tspecial = TRUE;\n+\t\t\t\telse if ((prev_p == text || *prev_p == '/') ||\n \t\t\t\t    (*p == '\\0' || *p == '/' ||\n \t\t\t\t     (p[0] == '\\\\' && p[1] == '/'))) {\n \t\t\t\t\t/*\n@@ -106,7 +109,7 @@ static int dowild(const uchar *p, const uchar *text, int flags)\n \t\t\t\t} else\n \t\t\t\t\treturn ABORT_MALFORMED;\n \t\t\t} else\n-\t\t\t\tspecial = FALSE;\n+\t\t\t\tspecial = flags & FNM_PATHNAME ? FALSE: TRUE;\n \t\t\tif (*p == '\\0') {\n \t\t\t\t/* Trailing \"**\" matches everything.  Trailing \"*\" matches\n \t\t\t\t * only if there are no more slash characters. */\n@@ -217,7 +220,8 @@ static int dowild(const uchar *p, const uchar *text, int flags)\n \t\t\t\t} else if (t_ch == p_ch)\n \t\t\t\t\tmatched = TRUE;\n \t\t\t} while (prev_ch = p_ch, (p_ch = *++p) != ']');\n-\t\t\tif (matched == special || t_ch == '/')\n+\t\t\tif (matched == special ||\n+\t\t\t    (flags & FNM_PATHNAME && t_ch == '/'))\n \t\t\t\treturn NOMATCH;\n \t\t\tcontinue;\n \t\t}\n-- \n1.8.0.rc2.23.g1fb49df\n"},{"id":"205184","messageId":"1355922488-20976-4-git-send-email-pclouds@gmail.com","threadId":"32405","inReplyTo":"1355922488-20976-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 3/3] Convert all fnmatch() calls to wildmatch()","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-12-19T13:08:08Z","receivedAt":"2012-12-19T13:08:08Z","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 builtin/apply.c        | 3 ++-\n builtin/branch.c       | 3 ++-\n builtin/describe.c     | 3 ++-\n builtin/for-each-ref.c | 3 ++-\n builtin/ls-remote.c    | 3 ++-\n builtin/name-rev.c     | 3 ++-\n builtin/reflog.c       | 3 ++-\n builtin/replace.c      | 3 ++-\n builtin/show-branch.c  | 3 ++-\n builtin/tag.c          | 3 ++-\n diffcore-order.c       | 3 ++-\n dir.c                  | 4 ++--\n refs.c                 | 3 ++-\n tree-walk.c            | 5 +++--\n 14 files changed, 29 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex d2180b0..86ff51d 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -16,6 +16,7 @@\n #include \"dir.h\"\n #include \"diff.h\"\n #include \"parse-options.h\"\n+#include \"wildmatch.h\"\n \n /*\n  *  --check turns on checking that the working tree matches the\n@@ -3786,7 +3787,7 @@ static int use_patch(struct patch *p)\n \t/* See if it matches any of exclude/include rule */\n \tfor (i = 0; i < limit_by_name.nr; i++) {\n \t\tstruct string_list_item *it = &limit_by_name.items[i];\n-\t\tif (!fnmatch(it->string, pathname, 0))\n+\t\tif (!wildmatch(it->string, pathname, 0))\n \t\t\treturn (it->util != NULL);\n \t}\n \ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 0e060f2..81257c1 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -17,6 +17,7 @@\n #include \"revision.h\"\n #include \"string-list.h\"\n #include \"column.h\"\n+#include \"wildmatch.h\"\n \n static const char * const builtin_branch_usage[] = {\n \t\"git branch [options] [-r | -a] [--merged | --no-merged]\",\n@@ -286,7 +287,7 @@ static int match_patterns(const char **pattern, const char *refname)\n \tif (!*pattern)\n \t\treturn 1; /* no pattern always matches */\n \twhile (*pattern) {\n-\t\tif (!fnmatch(*pattern, refname, 0))\n+\t\tif (!wildmatch(*pattern, refname, 0))\n \t\t\treturn 1;\n \t\tpattern++;\n \t}\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex 9f63067..1f55802 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -7,6 +7,7 @@\n #include \"parse-options.h\"\n #include \"diff.h\"\n #include \"hash.h\"\n+#include \"wildmatch.h\"\n \n #define SEEN\t\t(1u<<0)\n #define MAX_TAGS\t(FLAG_BITS - 1)\n@@ -161,7 +162,7 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void\n \t\telse\n \t\t\tprio = 1;\n \n-\t\tif (pattern && fnmatch(pattern, path + 10, 0))\n+\t\tif (pattern && wildmatch(pattern, path + 10, 0))\n \t\t\tprio = 0;\n \t}\n \telse\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 0c5294e..85f87dd 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -9,6 +9,7 @@\n #include \"quote.h\"\n #include \"parse-options.h\"\n #include \"remote.h\"\n+#include \"wildmatch.h\"\n \n /* Quoting styles */\n #define QUOTE_NONE 0\n@@ -794,7 +795,7 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1, int f\n \t\t\t     refname[plen] == '/' ||\n \t\t\t     p[plen-1] == '/'))\n \t\t\t\tbreak;\n-\t\t\tif (!fnmatch(p, refname, FNM_PATHNAME))\n+\t\t\tif (!wildmatch(p, refname, FNM_PATHNAME))\n \t\t\t\tbreak;\n \t\t}\n \t\tif (!*pattern)\ndiff --git a/builtin/ls-remote.c b/builtin/ls-remote.c\nindex 41c88a9..8271fbb 100644\n--- a/builtin/ls-remote.c\n+++ b/builtin/ls-remote.c\n@@ -2,6 +2,7 @@\n #include \"cache.h\"\n #include \"transport.h\"\n #include \"remote.h\"\n+#include \"wildmatch.h\"\n \n static const char ls_remote_usage[] =\n \"git ls-remote [--heads] [--tags]  [-u <exec> | --upload-pack <exec>]\\n\"\n@@ -22,7 +23,7 @@ static int tail_match(const char **pattern, const char *path)\n \tif (snprintf(pathbuf, sizeof(pathbuf), \"/%s\", path) > sizeof(pathbuf))\n \t\treturn error(\"insanely long ref %.*s...\", 20, path);\n \twhile ((p = *(pattern++)) != NULL) {\n-\t\tif (!fnmatch(p, pathbuf, 0))\n+\t\tif (!wildmatch(p, pathbuf, 0))\n \t\t\treturn 1;\n \t}\n \treturn 0;\ndiff --git a/builtin/name-rev.c b/builtin/name-rev.c\nindex 1b37458..0cc3141 100644\n--- a/builtin/name-rev.c\n+++ b/builtin/name-rev.c\n@@ -4,6 +4,7 @@\n #include \"tag.h\"\n #include \"refs.h\"\n #include \"parse-options.h\"\n+#include \"wildmatch.h\"\n \n #define CUTOFF_DATE_SLOP 86400 /* one day */\n \n@@ -97,7 +98,7 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void\n \tif (data->tags_only && prefixcmp(path, \"refs/tags/\"))\n \t\treturn 0;\n \n-\tif (data->ref_filter && fnmatch(data->ref_filter, path, 0))\n+\tif (data->ref_filter && wildmatch(data->ref_filter, path, 0))\n \t\treturn 0;\n \n \twhile (o && o->type == OBJ_TAG) {\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 062d7da..39dd8b4 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -7,6 +7,7 @@\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"reachable.h\"\n+#include \"wildmatch.h\"\n \n /*\n  * reflog expire\n@@ -561,7 +562,7 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c\n \t\treturn; /* both given explicitly -- nothing to tweak */\n \n \tfor (ent = reflog_expire_cfg; ent; ent = ent->next) {\n-\t\tif (!fnmatch(ent->pattern, ref, 0)) {\n+\t\tif (!wildmatch(ent->pattern, ref, 0)) {\n \t\t\tif (!(slot & EXPIRE_TOTAL))\n \t\t\t\tcb->expire_total = ent->expire_total;\n \t\t\tif (!(slot & EXPIRE_UNREACH))\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex 4a8970e..7fdefa8 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -12,6 +12,7 @@\n #include \"builtin.h\"\n #include \"refs.h\"\n #include \"parse-options.h\"\n+#include \"wildmatch.h\"\n \n static const char * const git_replace_usage[] = {\n \t\"git replace [-f] <object> <replacement>\",\n@@ -25,7 +26,7 @@ static int show_reference(const char *refname, const unsigned char *sha1,\n {\n \tconst char *pattern = cb_data;\n \n-\tif (!fnmatch(pattern, refname, 0))\n+\tif (!wildmatch(pattern, refname, 0))\n \t\tprintf(\"%s\\n\", refname);\n \n \treturn 0;\ndiff --git a/builtin/show-branch.c b/builtin/show-branch.c\nindex a59e088..f4fa165 100644\n--- a/builtin/show-branch.c\n+++ b/builtin/show-branch.c\n@@ -4,6 +4,7 @@\n #include \"builtin.h\"\n #include \"color.h\"\n #include \"parse-options.h\"\n+#include \"wildmatch.h\"\n \n static const char* show_branch_usage[] = {\n     \"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]\",\n@@ -452,7 +453,7 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1, i\n \t\t\tslash--;\n \tif (!*tail)\n \t\treturn 0;\n-\tif (fnmatch(match_ref_pattern, tail, 0))\n+\tif (wildmatch(match_ref_pattern, tail, 0))\n \t\treturn 0;\n \tif (!prefixcmp(refname, \"refs/heads/\"))\n \t\treturn append_head_ref(refname, sha1, flag, cb_data);\ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex 7b1be85..89aead3 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -17,6 +17,7 @@\n #include \"gpg-interface.h\"\n #include \"sha1-array.h\"\n #include \"column.h\"\n+#include \"wildmatch.h\"\n \n static const char * const git_tag_usage[] = {\n \t\"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]\",\n@@ -42,7 +43,7 @@ static int match_pattern(const char **patterns, const char *ref)\n \tif (!*patterns)\n \t\treturn 1;\n \tfor (; *patterns; patterns++)\n-\t\tif (!fnmatch(*patterns, ref, 0))\n+\t\tif (!wildmatch(*patterns, ref, 0))\n \t\t\treturn 1;\n \treturn 0;\n }\ndiff --git a/diffcore-order.c b/diffcore-order.c\nindex 23e9385..21eb30c 100644\n--- a/diffcore-order.c\n+++ b/diffcore-order.c\n@@ -4,6 +4,7 @@\n #include \"cache.h\"\n #include \"diff.h\"\n #include \"diffcore.h\"\n+#include \"wildmatch.h\"\n \n static char **order;\n static int order_cnt;\n@@ -79,7 +80,7 @@ static int match_order(const char *path)\n \t\tstrcpy(p, path);\n \t\twhile (p[0]) {\n \t\t\tchar *cp;\n-\t\t\tif (!fnmatch(order[i], p, 0))\n+\t\t\tif (!wildmatch(order[i], p, 0))\n \t\t\t\treturn i;\n \t\t\tcp = strrchr(p, '/');\n \t\t\tif (!cp)\ndiff --git a/dir.c b/dir.c\nindex 7bbd6f8..c1339c4 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -32,7 +32,7 @@ int strncmp_icase(const char *a, const char *b, size_t count)\n \n int fnmatch_icase(const char *pattern, const char *string, int flags)\n {\n-\treturn fnmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0));\n+\treturn wildmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0));\n }\n \n static size_t common_prefix_len(const char **pathspec)\n@@ -231,7 +231,7 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,\n \t\t\treturn MATCHED_RECURSIVELY;\n \t}\n \n-\tif (item->use_wildcard && !fnmatch(match, name, 0))\n+\tif (item->use_wildcard && !wildmatch(match, name, 0))\n \t\treturn MATCHED_FNMATCH;\n \n \treturn 0;\ndiff --git a/refs.c b/refs.c\nindex da74a2b..47929d9 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -3,6 +3,7 @@\n #include \"object.h\"\n #include \"tag.h\"\n #include \"dir.h\"\n+#include \"wildmatch.h\"\n \n /*\n  * Make sure \"ref\" is something reasonable to have under \".git/refs/\";\n@@ -1188,7 +1189,7 @@ static int filter_refs(const char *refname, const unsigned char *sha1, int flags\n \t\t       void *data)\n {\n \tstruct ref_filter *filter = (struct ref_filter *)data;\n-\tif (fnmatch(filter->pattern, refname, 0))\n+\tif (wildmatch(filter->pattern, refname, 0))\n \t\treturn 0;\n \treturn filter->fn(refname, sha1, flags, filter->cb_data);\n }\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 492c7cd..c729e89 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -3,6 +3,7 @@\n #include \"unpack-trees.h\"\n #include \"dir.h\"\n #include \"tree.h\"\n+#include \"wildmatch.h\"\n \n static const char *get_mode(const char *str, unsigned int *modep)\n {\n@@ -627,7 +628,7 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,\n \t\t\t\treturn entry_interesting;\n \n \t\t\tif (item->use_wildcard) {\n-\t\t\t\tif (!fnmatch(match + baselen, entry->path, 0))\n+\t\t\t\tif (!wildmatch(match + baselen, entry->path, 0))\n \t\t\t\t\treturn entry_interesting;\n \n \t\t\t\t/*\n@@ -652,7 +653,7 @@ match_wildcards:\n \n \t\tstrbuf_add(base, entry->path, pathlen);\n \n-\t\tif (!fnmatch(match, base->buf + base_offset, 0)) {\n+\t\tif (!wildmatch(match, base->buf + base_offset, 0)) {\n \t\t\tstrbuf_setlen(base, base_offset + baselen);\n \t\t\treturn entry_interesting;\n \t\t}\n-- \n1.8.0.rc2.23.g1fb49df\n"},{"id":"205187","messageId":"CACsJy8CO7PdcVtK+CvmFn=BX-PBQUh8wwo=Y8Mk1-dZ+TGq7kA@mail.gmail.com","threadId":"32405","inReplyTo":"1355922488-20976-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH/WIP 0/3] Bye bye fnmatch()","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-12-19T13:21:28Z","receivedAt":"2012-12-19T13:21:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Dec 19, 2012 at 8:08 PM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> For those who have not followed, nd/wildmatch brings another\n> fnmatch-like implementation which can nearly replace fnmatch.\n> System fnmatch() seems to behave differently in some cases. It's\n> better to stay away and use one implementation for all.\n\nOh I forgot. Case-insensitive matching will be available to everybody.\nOn the other hand it'll be a lot more work to (implement and) use\nother FNM_* flags like FNM_PERIOD.\n-- \nDuy\n"},{"id":"205199","messageId":"7v4njiatp3.fsf@alter.siamese.dyndns.org","threadId":"32405","inReplyTo":"1355922488-20976-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 1/3] wildmatch: make dowild() take arbitrary flags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-12-19T16:32:40Z","receivedAt":"2012-12-19T16:32:40Z","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> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>  wildmatch.c | 13 ++++++-------\n>  1 file changed, 6 insertions(+), 7 deletions(-)\n>\n> diff --git a/wildmatch.c b/wildmatch.c\n> index 3972e26..9586ed9 100644\n> --- a/wildmatch.c\n> +++ b/wildmatch.c\n> @@ -55,7 +55,7 @@ typedef unsigned char uchar;\n>  #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))\n>  \n>  /* Match pattern \"p\" against \"text\" */\n> -static int dowild(const uchar *p, const uchar *text, int force_lower_case)\n> +static int dowild(const uchar *p, const uchar *text, int flags)\n\nIt may be better to declare a bitset like this unsigned.\n\n>  {\n>  \tuchar p_ch;\n>  \n> @@ -64,9 +64,9 @@ static int dowild(const uchar *p, const uchar *text, int force_lower_case)\n>  \t\tuchar t_ch, prev_ch;\n>  \t\tif ((t_ch = *text) == '\\0' && p_ch != '*')\n>  \t\t\treturn ABORT_ALL;\n> -\t\tif (force_lower_case && ISUPPER(t_ch))\n> +\t\tif (flags & FNM_CASEFOLD && ISUPPER(t_ch))\n\nPlease add parentheses around bitwise-AND that is used as a boolean,\ni.e.\n\n\tif ((flags & FNM_CASEFOLD) && ISUPPER(t_ch))\n\nLess chance of confusion.\n"},{"id":"205201","messageId":"7vlicu9cpk.fsf@alter.siamese.dyndns.org","threadId":"32405","inReplyTo":"1355922488-20976-3-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 2/3] wildmatch: support \"no FNM_PATHNAME\" mode","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-12-19T17:24:55Z","receivedAt":"2012-12-19T17:24:55Z","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> By default wildmatch(,, 0) is equivalent with fnmatch(,, FNM_PATHNAME).\n\nIs this stating a fact before or after the patch?\n\nI think it is more like:\n\n    So far, wildmatch() has always honoured directory boundary and\n    there was no way to turn it off.  Make it behave more like\n    fnmatch() by requiring all callers that want the FNM_PATHNAME\n    behaviour to pass that in the flags parameter.  Callers that do\n    not specify FNM_PATHNAME will get wildcards like ? and * in\n    their patterns matched against '/' and ...\n\n>  The choice of name \"pathspec\" is not good. I couldn't think of\n>  anything appropriate and just did not care enough at this point.\n\nWell, you should, before this series leaves the WIP state.  It seems\nthat all operating modes supported by test-wildmatch are named as\nsomethingmatch, so \"pathmatch\" may be a better candidate.\n\n> diff --git a/test-wildmatch.c b/test-wildmatch.c\n> index e384c8e..7fefa4f 100644\n> --- a/test-wildmatch.c\n> +++ b/test-wildmatch.c\n> @@ -12,9 +12,11 @@ int main(int argc, char **argv)\n>  \t\t\targv[i] += 3;\n>  \t}\n>  \tif (!strcmp(argv[1], \"wildmatch\"))\n> +\t\treturn !!wildmatch(argv[3], argv[2], FNM_PATHNAME);\n> +\telse if (!strcmp(argv[1], \"pathspec\"))\n>  \t\treturn !!wildmatch(argv[3], argv[2], 0);\n\n\"ipathmatch\" to pass only FNM_CASEFOLD may be a natural extension,\nbut I doubt we use that combination in the real life (yet).\n\nI am probably two step ahead of what is being done (read: this will\nbe a Git 2.0 topic, if not later), but I am wondering how we'd\nintegrate this new machinery well with the pathspec limited\ntraversal.\n\nTraditionally, when traversing a tree limited with a pathspec, say,\n\"Documentation/*.txt\", we used the simple part of the prefix and\nnoticed that any subdirectory whose name is not \"Documentation\" can\nnever match the pathspec and avoided descending into it.  As the\nuser can use \"**\" to expand to \"any levels of subdirectory\", I think\nthe pathspec limited traversal eventually can safely (and should)\nuse FNM_PATHNAME by default.  \n\nThat will allow people to say \"Documentation/**/*.txt\" to match both\n\"git.txt\" and \"howto/maintain-git.txt\", without resorting to the\nmore traditional !FNM_PATHNAME semantics \"Documentation/*.txt\" to\nmatch the latter (i.e. \"*\" matches \"howto/maintain-git\").\n\nWhen that happens, we should want to retain the same \"do not bother\nto descend into subdirectories that will never match\" optimization\nfor a pattern like \"Doc*tion/**/*.txt\".  Because of FNM_PATHNAME, we\ncan tell if a subdirectory is worth descending into by looking at\nthe not-so-simple prefix \"Doc*tion/\"; \"Documentation\" will match,\n\"Doc\" will not (because '*' won't match '/').\n\nWhich tells me that integrating this _well_ into the rest of the\nsystem is not just a matter of replacing fnmatch() with wildmatch().\n\nI also expect that wildmatch() would be much slower than fnmatch()\nespecially when doing its \"**\" magic, but I personally do not think\nit will be a showstopper.  If the user asks for a more powerful but\nexpensive operation, we are saving time for the user by doing a more\npowerful thing (reducing the need to postprocess the results) and\ncan afford to spend extra cycles.\n\nAs long as simpler patterns fnmatch() groks (namely, '?', '*', and\n'[class]' wildcards only) are not slowed down by replacing it with\nwildmatch(), that is, of course.\n"},{"id":"205205","messageId":"7v8v8tanyn.fsf@alter.siamese.dyndns.org","threadId":"32405","inReplyTo":"1355922488-20976-4-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 3/3] Convert all fnmatch() calls to wildmatch()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-12-19T18:36:32Z","receivedAt":"2012-12-19T18:36:32Z","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/tree-walk.c b/tree-walk.c\n> index 492c7cd..c729e89 100644\n> --- a/tree-walk.c\n> +++ b/tree-walk.c\n> @@ -3,6 +3,7 @@\n>  #include \"unpack-trees.h\"\n>  #include \"dir.h\"\n>  #include \"tree.h\"\n> +#include \"wildmatch.h\"\n>  \n>  static const char *get_mode(const char *str, unsigned int *modep)\n>  {\n> @@ -627,7 +628,7 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,\n>  \t\t\t\treturn entry_interesting;\n>  \n>  \t\t\tif (item->use_wildcard) {\n> -\t\t\t\tif (!fnmatch(match + baselen, entry->path, 0))\n> +\t\t\t\tif (!wildmatch(match + baselen, entry->path, 0))\n>  \t\t\t\t\treturn entry_interesting;\n\nThis and the change to dir.c obviously have interactions with\n8c6abbc (pathspec: apply \"*.c\" optimization from exclude,\n2012-11-24).\n\nI've already alluded to it in my response to 2/3, I guess.\n"},{"id":"205234","messageId":"CACsJy8C9Nhdq_xBAOxtmLcUnrUioAMvWCPk=sBZKOzFY2WR+iA@mail.gmail.com","threadId":"32405","inReplyTo":"7vlicu9cpk.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 2/3] wildmatch: support \"no FNM_PATHNAME\" mode","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-12-20T01:55:50Z","receivedAt":"2012-12-20T01:55:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Dec 20, 2012 at 12:24 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> When that happens, we should want to retain the same \"do not bother\n> to descend into subdirectories that will never match\" optimization\n> for a pattern like \"Doc*tion/**/*.txt\".  Because of FNM_PATHNAME, we\n> can tell if a subdirectory is worth descending into by looking at\n> the not-so-simple prefix \"Doc*tion/\"; \"Documentation\" will match,\n> \"Doc\" will not (because '*' won't match '/').\n>\n> Which tells me that integrating this _well_ into the rest of the\n> system is not just a matter of replacing fnmatch() with wildmatch().\n\nYeah, we open a door for more opportunities and a lot more headache.\n\n> I also expect that wildmatch() would be much slower than fnmatch()\n> especially when doing its \"**\" magic, but I personally do not think\n> it will be a showstopper.\n\nA potential showstopper is the lack of multibyte support. I don't know\nif people want that though.\n\n> If the user asks for a more powerful but\n> expensive operation, we are saving time for the user by doing a more\n> powerful thing (reducing the need to postprocess the results) and\n> can afford to spend extra cycles.\n\nIn some case we may be able to spend fewer cycles by preprocessing\npatterns first.\n\n> As long as simpler patterns fnmatch() groks (namely, '?', '*', and\n> '[class]' wildcards only) are not slowed down by replacing it with\n> wildmatch(), that is, of course.\n\nI'm concerned about performance vs fnmatch too. I'll probably write a\nsmall program to exercise both and measure it with glibc.\n-- \nDuy\n"},{"id":"205251","messageId":"CACsJy8B3jdE5h272d0-R-OUcS-Fput+7ADBT4UZEMf58b9=2eA@mail.gmail.com","threadId":"32405","inReplyTo":"7v8v8tanyn.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 3/3] Convert all fnmatch() calls to wildmatch()","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-12-20T12:36:06Z","receivedAt":"2012-12-20T12:36:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Dec 20, 2012 at 1:36 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> @@ -627,7 +628,7 @@ enum interesting tree_entry_interesting(const struct name_entry *entry,\n>>                               return entry_interesting;\n>>\n>>                       if (item->use_wildcard) {\n>> -                             if (!fnmatch(match + baselen, entry->path, 0))\n>> +                             if (!wildmatch(match + baselen, entry->path, 0))\n>>                                       return entry_interesting;\n>\n> This and the change to dir.c obviously have interactions with\n> 8c6abbc (pathspec: apply \"*.c\" optimization from exclude,\n> 2012-11-24).\n>\n> I've already alluded to it in my response to 2/3, I guess.\n\nConflict of plans. I did not expect myself to work on replacing\nfnmatch soon and git_fnmatch is an intermediate step to collect more\noptimizations and gradually replace fnmatch. Eventually git_fnmatch()\nwould become a wrapper of wildmatch, all the optimizations are pushed\ndown there. If we replace fnmatch->wildmatch first then there's little\nreason for the existence of git_fnmatch(). Maybe I should merge this\nwith the fnmatch elimination into a single series.\n-- \nDuy\n"},{"id":"205252","messageId":"CACsJy8AFAKEeeFMp1EF+=QyZydoyVRFqPdAKrZbPe2h4_bf=fg@mail.gmail.com","threadId":"32405","inReplyTo":"CACsJy8C9Nhdq_xBAOxtmLcUnrUioAMvWCPk=sBZKOzFY2WR+iA@mail.gmail.com","subject":"Re: [PATCH 2/3] wildmatch: support \"no FNM_PATHNAME\" mode","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2012-12-20T13:34:06Z","receivedAt":"2012-12-20T13:34:06Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Dec 20, 2012 at 8:55 AM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n>> As long as simpler patterns fnmatch() groks (namely, '?', '*', and\n>> '[class]' wildcards only) are not slowed down by replacing it with\n>> wildmatch(), that is, of course.\n>\n> I'm concerned about performance vs fnmatch too. I'll probably write a\n> small program to exercise both and measure it with glibc.\n\nI'll send out proper patches later this weekend, but unless I make big\nmistakes it seems wildmatch is faster than fnmatch from glibc 2.14.1\non x86-64 :) Maybe I did make mistakes. The test matches every line in\n/tmp/filelist.txt (which is ls-files from linux-2.6.git) 2000 times.\n\"pathname\" means FNM_PATHNAME.\n\n$ export LANG=C\n$ ./test-wildmatch-perf /tmp/filelist.txt 'Documentation/*' 2000\nwildmatch 1s 528394us\nfnmatch   2s 588396us\n$ ./test-wildmatch-perf /tmp/filelist.txt 'drivers/*' 2000\nwildmatch 1s 957243us\nfnmatch   3s 134839us\n$ ./test-wildmatch-perf /tmp/filelist.txt 'Documentation/*' 2000 pathname\nwildmatch 1s 548575us\nfnmatch   2s 629680us\n$ ./test-wildmatch-perf /tmp/filelist.txt 'drivers/*' 2000 pathname\nwildmatch 2s 142377us\nfnmatch   3s 318295us\n$ ./test-wildmatch-perf /tmp/filelist.txt '[Dd]ocu[Mn]entation/*' 2000\nwildmatch 1s 748505us\nfnmatch   3s 224414us\n$ ./test-wildmatch-perf /tmp/filelist.txt '[Dd]o?u[Mn]en?ati?n/*' 2000\nwildmatch 1s 743268us\nfnmatch   3s 278034us\n$ ./test-wildmatch-perf /tmp/filelist.txt '[Dd]o?u[Mn]en?ati?n/*' 2000 pathname\nwildmatch 1s 745151us\nfnmatch   3s 314127us\n$ ./test-wildmatch-perf /tmp/filelist.txt nonsense 2000 pathname\nwildmatch 1s 310727us\nfnmatch   2s 279123us\n-- \nDuy\n"}]}