From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: git@vger.kernel.org
Cc: "Junio C Hamano" <gitster@pobox.com>,
"Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
Subject: [PATCH v2 1/5] Import wildmatch from rsync
Date: Sun, 16 Sep 2012 22:27:41 +0700 [thread overview]
Message-ID: <1347809265-2457-2-git-send-email-pclouds@gmail.com> (raw)
In-Reply-To: <1347809265-2457-1-git-send-email-pclouds@gmail.com>
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=UTF-8, Size: 22865 bytes --]
These files are from rsync.git commit
f92f5b166e3019db42bc7fe1aa2f1a9178cd215d, which was the last commit
before rsync turned GPL-3. All files are imported as-is and
no-op. Adaptation is done in a separate patch.
rsync.git -> git.git
lib/wildmatch.[ch] wildmatch.[ch]
wildtest.c test-wildmatch.c
wildtest.txt t/t3070-wildmatch/wildtest.txt
Signed-off-by: Nguyá»
n Thái Ngá»c Duy <pclouds@gmail.com>
---
t/t3070-wildmatch/wildtest.txt | 165 ++++++++++++++++++
test-wildmatch.c | 222 +++++++++++++++++++++++++
wildmatch.c | 368 +++++++++++++++++++++++++++++++++++++++++
wildmatch.h | 6 +
4 files changed, 761 insertions(+)
create mode 100644 t/t3070-wildmatch/wildtest.txt
create mode 100644 test-wildmatch.c
create mode 100644 wildmatch.c
create mode 100644 wildmatch.h
diff --git a/t/t3070-wildmatch/wildtest.txt b/t/t3070-wildmatch/wildtest.txt
new file mode 100644
index 0000000..42c1678
--- /dev/null
+++ b/t/t3070-wildmatch/wildtest.txt
@@ -0,0 +1,165 @@
+# Input is in the following format (all items white-space separated):
+#
+# The first two items are 1 or 0 indicating if the wildmat call is expected to
+# succeed and if fnmatch works the same way as wildmat, respectively. After
+# that is a text string for the match, and a pattern string. Strings can be
+# quoted (if desired) in either double or single quotes, as well as backticks.
+#
+# MATCH FNMATCH_SAME "text to match" 'pattern to use'
+
+# Basic wildmat features
+1 1 foo foo
+0 1 foo bar
+1 1 '' ""
+1 1 foo ???
+0 1 foo ??
+1 1 foo *
+1 1 foo f*
+0 1 foo *f
+1 1 foo *foo*
+1 1 foobar *ob*a*r*
+1 1 aaaaaaabababab *ab
+1 1 foo* foo\*
+0 1 foobar foo\*bar
+1 1 f\oo f\\oo
+1 1 ball *[al]?
+0 1 ten [ten]
+1 1 ten **[!te]
+0 1 ten **[!ten]
+1 1 ten t[a-g]n
+0 1 ten t[!a-g]n
+1 1 ton t[!a-g]n
+1 1 ton t[^a-g]n
+1 1 a]b a[]]b
+1 1 a-b a[]-]b
+1 1 a]b a[]-]b
+0 1 aab a[]-]b
+1 1 aab a[]a-]b
+1 1 ] ]
+
+# Extended slash-matching features
+0 1 foo/baz/bar foo*bar
+1 1 foo/baz/bar foo**bar
+0 1 foo/bar foo?bar
+0 1 foo/bar foo[/]bar
+0 1 foo/bar f[^eiu][^eiu][^eiu][^eiu][^eiu]r
+1 1 foo-bar f[^eiu][^eiu][^eiu][^eiu][^eiu]r
+0 1 foo **/foo
+1 1 /foo **/foo
+1 1 bar/baz/foo **/foo
+0 1 bar/baz/foo */foo
+0 0 foo/bar/baz **/bar*
+1 1 deep/foo/bar/baz **/bar/*
+0 1 deep/foo/bar/baz/ **/bar/*
+1 1 deep/foo/bar/baz/ **/bar/**
+0 1 deep/foo/bar **/bar/*
+1 1 deep/foo/bar/ **/bar/**
+1 1 foo/bar/baz **/bar**
+1 1 foo/bar/baz/x */bar/**
+0 0 deep/foo/bar/baz/x */bar/**
+1 1 deep/foo/bar/baz/x **/bar/*/*
+
+# Various additional tests
+0 1 acrt a[c-c]st
+1 1 acrt a[c-c]rt
+0 1 ] [!]-]
+1 1 a [!]-]
+0 1 '' \
+0 1 \ \
+0 1 /\ */\
+1 1 /\ */\\
+1 1 foo foo
+1 1 @foo @foo
+0 1 foo @foo
+1 1 [ab] \[ab]
+1 1 [ab] [[]ab]
+1 1 [ab] [[:]ab]
+0 1 [ab] [[::]ab]
+1 1 [ab] [[:digit]ab]
+1 1 [ab] [\[:]ab]
+1 1 ?a?b \??\?b
+1 1 abc \a\b\c
+0 1 foo ''
+1 1 foo/bar/baz/to **/t[o]
+
+# Character class tests
+1 1 a1B [[:alpha:]][[:digit:]][[:upper:]]
+0 1 a [[:digit:][:upper:][:space:]]
+1 1 A [[:digit:][:upper:][:space:]]
+1 1 1 [[:digit:][:upper:][:space:]]
+0 1 1 [[:digit:][:upper:][:spaci:]]
+1 1 ' ' [[:digit:][:upper:][:space:]]
+0 1 . [[:digit:][:upper:][:space:]]
+1 1 . [[:digit:][:punct:][:space:]]
+1 1 5 [[:xdigit:]]
+1 1 f [[:xdigit:]]
+1 1 D [[:xdigit:]]
+1 1 _ [[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]
+#1 1
[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]
+1 1 \x7f [^[:alnum:][:alpha:][:blank:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]
+1 1 . [^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]
+1 1 5 [a-c[:digit:]x-z]
+1 1 b [a-c[:digit:]x-z]
+1 1 y [a-c[:digit:]x-z]
+0 1 q [a-c[:digit:]x-z]
+
+# Additional tests, including some malformed wildmats
+1 1 ] [\\-^]
+0 1 [ [\\-^]
+1 1 - [\-_]
+1 1 ] [\]]
+0 1 \] [\]]
+0 1 \ [\]]
+0 1 ab a[]b
+0 1 a[]b a[]b
+0 1 ab[ ab[
+0 1 ab [!
+0 1 ab [-
+1 1 - [-]
+0 1 - [a-
+0 1 - [!a-
+1 1 - [--A]
+1 1 5 [--A]
+1 1 ' ' '[ --]'
+1 1 $ '[ --]'
+1 1 - '[ --]'
+0 1 0 '[ --]'
+1 1 - [---]
+1 1 - [------]
+0 1 j [a-e-n]
+1 1 - [a-e-n]
+1 1 a [!------]
+0 1 [ []-a]
+1 1 ^ []-a]
+0 1 ^ [!]-a]
+1 1 [ [!]-a]
+1 1 ^ [a^bc]
+1 1 -b] [a-]b]
+0 1 \ [\]
+1 1 \ [\\]
+0 1 \ [!\\]
+1 1 G [A-\\]
+0 1 aaabbb b*a
+0 1 aabcaa *ba*
+1 1 , [,]
+1 1 , [\\,]
+1 1 \ [\\,]
+1 1 - [,-.]
+0 1 + [,-.]
+0 1 -.] [,-.]
+1 1 2 [\1-\3]
+1 1 3 [\1-\3]
+0 1 4 [\1-\3]
+1 1 \ [[-\]]
+1 1 [ [[-\]]
+1 1 ] [[-\]]
+0 1 - [[-\]]
+
+# Test recursion and the abort code (use "wildtest -i" to see iteration counts)
+1 1 -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1 -*-*-*-*-*-*-12-*-*-*-m-*-*-*
+0 1 -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1 -*-*-*-*-*-*-12-*-*-*-m-*-*-*
+0 1 -adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1 -*-*-*-*-*-*-12-*-*-*-m-*-*-*
+1 1 /adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1 /*/*/*/*/*/*/12/*/*/*/m/*/*/*
+0 1 /adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1 /*/*/*/*/*/*/12/*/*/*/m/*/*/*
+1 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt **/*a*b*g*n*t
+0 1 abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz **/*a*b*g*n*t
diff --git a/test-wildmatch.c b/test-wildmatch.c
new file mode 100644
index 0000000..88585c2
--- /dev/null
+++ b/test-wildmatch.c
@@ -0,0 +1,222 @@
+/*
+ * Test suite for the wildmatch code.
+ *
+ * Copyright (C) 2003-2009 Wayne Davison
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, visit the http://fsf.org website.
+ */
+
+/*#define COMPARE_WITH_FNMATCH*/
+
+#define WILD_TEST_ITERATIONS
+#include "lib/wildmatch.c"
+
+#include <popt.h>
+
+#ifdef COMPARE_WITH_FNMATCH
+#include <fnmatch.h>
+
+int fnmatch_errors = 0;
+#endif
+
+int wildmatch_errors = 0;
+char number_separator = ',';
+
+typedef char bool;
+
+int output_iterations = 0;
+int explode_mod = 0;
+int empties_mod = 0;
+int empty_at_start = 0;
+int empty_at_end = 0;
+
+static struct poptOption long_options[] = {
+ /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
+ {"iterations", 'i', POPT_ARG_NONE, &output_iterations, 0, 0, 0},
+ {"empties", 'e', POPT_ARG_STRING, 0, 'e', 0, 0},
+ {"explode", 'x', POPT_ARG_INT, &explode_mod, 0, 0, 0},
+ {0,0,0,0, 0, 0, 0}
+};
+
+/* match just at the start of string (anchored tests) */
+static void
+run_test(int line, bool matches,
+#ifdef COMPARE_WITH_FNMATCH
+ bool same_as_fnmatch,
+#endif
+ const char *text, const char *pattern)
+{
+ bool matched;
+#ifdef COMPARE_WITH_FNMATCH
+ bool fn_matched;
+ int flags = strstr(pattern, "**")? 0 : FNM_PATHNAME;
+#endif
+
+ if (explode_mod) {
+ char buf[MAXPATHLEN*2], *texts[MAXPATHLEN];
+ int pos = 0, cnt = 0, ndx = 0, len = strlen(text);
+
+ if (empty_at_start)
+ texts[ndx++] = "";
+ /* An empty string must turn into at least one empty array item. */
+ while (1) {
+ texts[ndx] = buf + ndx * (explode_mod + 1);
+ strlcpy(texts[ndx++], text + pos, explode_mod + 1);
+ if (pos + explode_mod >= len)
+ break;
+ pos += explode_mod;
+ if (!(++cnt % empties_mod))
+ texts[ndx++] = "";
+ }
+ if (empty_at_end)
+ texts[ndx++] = "";
+ texts[ndx] = NULL;
+ matched = wildmatch_array(pattern, (const char**)texts, 0);
+ } else
+ matched = wildmatch(pattern, text);
+#ifdef COMPARE_WITH_FNMATCH
+ fn_matched = !fnmatch(pattern, text, flags);
+#endif
+ if (matched != matches) {
+ printf("wildmatch failure on line %d:\n %s\n %s\n expected %s match\n",
+ line, text, pattern, matches? "a" : "NO");
+ wildmatch_errors++;
+ }
+#ifdef COMPARE_WITH_FNMATCH
+ if (fn_matched != (matches ^ !same_as_fnmatch)) {
+ printf("fnmatch disagreement on line %d:\n %s\n %s\n expected %s match\n",
+ line, text, pattern, matches ^ !same_as_fnmatch? "a" : "NO");
+ fnmatch_errors++;
+ }
+#endif
+ if (output_iterations) {
+ printf("%d: \"%s\" iterations = %d\n", line, pattern,
+ wildmatch_iteration_count);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ char buf[2048], *s, *string[2], *end[2];
+ const char *arg;
+ FILE *fp;
+ int opt, line, i, flag[2];
+ poptContext pc = poptGetContext("wildtest", argc, (const char**)argv,
+ long_options, 0);
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'e':
+ arg = poptGetOptArg(pc);
+ empties_mod = atoi(arg);
+ if (strchr(arg, 's'))
+ empty_at_start = 1;
+ if (strchr(arg, 'e'))
+ empty_at_end = 1;
+ if (!explode_mod)
+ explode_mod = 1024;
+ break;
+ default:
+ fprintf(stderr, "%s: %s\n",
+ poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+ poptStrerror(opt));
+ exit(1);
+ }
+ }
+
+ if (explode_mod && !empties_mod)
+ empties_mod = 1024;
+
+ argv = (char**)poptGetArgs(pc);
+ if (!argv || argv[1]) {
+ fprintf(stderr, "Usage: wildtest [OPTIONS] TESTFILE\n");
+ exit(1);
+ }
+
+ if ((fp = fopen(*argv, "r")) == NULL) {
+ fprintf(stderr, "Unable to open %s\n", *argv);
+ exit(1);
+ }
+
+ line = 0;
+ while (fgets(buf, sizeof buf, fp)) {
+ line++;
+ if (*buf == '#' || *buf == '\n')
+ continue;
+ for (s = buf, i = 0; i <= 1; i++) {
+ if (*s == '1')
+ flag[i] = 1;
+ else if (*s == '0')
+ flag[i] = 0;
+ else
+ flag[i] = -1;
+ if (*++s != ' ' && *s != '\t')
+ flag[i] = -1;
+ if (flag[i] < 0) {
+ fprintf(stderr, "Invalid flag syntax on line %d of %s:\n%s",
+ line, *argv, buf);
+ exit(1);
+ }
+ while (*++s == ' ' || *s == '\t') {}
+ }
+ for (i = 0; i <= 1; i++) {
+ if (*s == '\'' || *s == '"' || *s == '`') {
+ char quote = *s++;
+ string[i] = s;
+ while (*s && *s != quote) s++;
+ if (!*s) {
+ fprintf(stderr, "Unmatched quote on line %d of %s:\n%s",
+ line, *argv, buf);
+ exit(1);
+ }
+ end[i] = s;
+ }
+ else {
+ if (!*s || *s == '\n') {
+ fprintf(stderr, "Not enough strings on line %d of %s:\n%s",
+ line, *argv, buf);
+ exit(1);
+ }
+ string[i] = s;
+ while (*++s && *s != ' ' && *s != '\t' && *s != '\n') {}
+ end[i] = s;
+ }
+ while (*++s == ' ' || *s == '\t') {}
+ }
+ *end[0] = *end[1] = '\0';
+ run_test(line, flag[0],
+#ifdef COMPARE_WITH_FNMATCH
+ flag[1],
+#endif
+ string[0], string[1]);
+ }
+
+ if (!wildmatch_errors)
+ fputs("No", stdout);
+ else
+ printf("%d", wildmatch_errors);
+ printf(" wildmatch error%s found.\n", wildmatch_errors == 1? "" : "s");
+
+#ifdef COMPARE_WITH_FNMATCH
+ if (!fnmatch_errors)
+ fputs("No", stdout);
+ else
+ printf("%d", fnmatch_errors);
+ printf(" fnmatch error%s found.\n", fnmatch_errors == 1? "" : "s");
+
+#endif
+
+ return 0;
+}
diff --git a/wildmatch.c b/wildmatch.c
new file mode 100644
index 0000000..f3a1731
--- /dev/null
+++ b/wildmatch.c
@@ -0,0 +1,368 @@
+/*
+** Do shell-style pattern matching for ?, \, [], and * characters.
+** It is 8bit clean.
+**
+** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
+** Rich $alz is now <rsalz@bbn.com>.
+**
+** Modified by Wayne Davison to special-case '/' matching, to make '**'
+** work differently than '*', and to fix the character-class code.
+*/
+
+#include "rsync.h"
+
+/* What character marks an inverted character class? */
+#define NEGATE_CLASS '!'
+#define NEGATE_CLASS2 '^'
+
+#define FALSE 0
+#define TRUE 1
+#define ABORT_ALL -1
+#define ABORT_TO_STARSTAR -2
+
+#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \
+ && *(class) == *(litmatch) \
+ && strncmp((char*)class, litmatch, len) == 0)
+
+#if defined STDC_HEADERS || !defined isascii
+# define ISASCII(c) 1
+#else
+# define ISASCII(c) isascii(c)
+#endif
+
+#ifdef isblank
+# define ISBLANK(c) (ISASCII(c) && isblank(c))
+#else
+# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+
+#ifdef isgraph
+# define ISGRAPH(c) (ISASCII(c) && isgraph(c))
+#else
+# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))
+#endif
+
+#define ISPRINT(c) (ISASCII(c) && isprint(c))
+#define ISDIGIT(c) (ISASCII(c) && isdigit(c))
+#define ISALNUM(c) (ISASCII(c) && isalnum(c))
+#define ISALPHA(c) (ISASCII(c) && isalpha(c))
+#define ISCNTRL(c) (ISASCII(c) && iscntrl(c))
+#define ISLOWER(c) (ISASCII(c) && islower(c))
+#define ISPUNCT(c) (ISASCII(c) && ispunct(c))
+#define ISSPACE(c) (ISASCII(c) && isspace(c))
+#define ISUPPER(c) (ISASCII(c) && isupper(c))
+#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
+
+#ifdef WILD_TEST_ITERATIONS
+int wildmatch_iteration_count;
+#endif
+
+static int force_lower_case = 0;
+
+/* Match pattern "p" against the a virtually-joined string consisting
+ * of "text" and any strings in array "a". */
+static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
+{
+ uchar p_ch;
+
+#ifdef WILD_TEST_ITERATIONS
+ wildmatch_iteration_count++;
+#endif
+
+ for ( ; (p_ch = *p) != '\0'; text++, p++) {
+ int matched, special;
+ uchar t_ch, prev_ch;
+ while ((t_ch = *text) == '\0') {
+ if (*a == NULL) {
+ if (p_ch != '*')
+ return ABORT_ALL;
+ break;
+ }
+ text = *a++;
+ }
+ if (force_lower_case && ISUPPER(t_ch))
+ t_ch = tolower(t_ch);
+ switch (p_ch) {
+ case '\\':
+ /* Literal match with following character. Note that the test
+ * in "default" handles the p[1] == '\0' failure case. */
+ p_ch = *++p;
+ /* FALLTHROUGH */
+ default:
+ if (t_ch != p_ch)
+ return FALSE;
+ continue;
+ case '?':
+ /* Match anything but '/'. */
+ if (t_ch == '/')
+ return FALSE;
+ continue;
+ case '*':
+ if (*++p == '*') {
+ while (*++p == '*') {}
+ special = TRUE;
+ } else
+ special = FALSE;
+ if (*p == '\0') {
+ /* Trailing "**" matches everything. Trailing "*" matches
+ * only if there are no more slash characters. */
+ if (!special) {
+ do {
+ if (strchr((char*)text, '/') != NULL)
+ return FALSE;
+ } while ((text = *a++) != NULL);
+ }
+ return TRUE;
+ }
+ while (1) {
+ if (t_ch == '\0') {
+ if ((text = *a++) == NULL)
+ break;
+ t_ch = *text;
+ continue;
+ }
+ if ((matched = dowild(p, text, a)) != FALSE) {
+ if (!special || matched != ABORT_TO_STARSTAR)
+ return matched;
+ } else if (!special && t_ch == '/')
+ return ABORT_TO_STARSTAR;
+ t_ch = *++text;
+ }
+ return ABORT_ALL;
+ case '[':
+ p_ch = *++p;
+#ifdef NEGATE_CLASS2
+ if (p_ch == NEGATE_CLASS2)
+ p_ch = NEGATE_CLASS;
+#endif
+ /* Assign literal TRUE/FALSE because of "matched" comparison. */
+ special = p_ch == NEGATE_CLASS? TRUE : FALSE;
+ if (special) {
+ /* Inverted character class. */
+ p_ch = *++p;
+ }
+ prev_ch = 0;
+ matched = FALSE;
+ do {
+ if (!p_ch)
+ return ABORT_ALL;
+ if (p_ch == '\\') {
+ p_ch = *++p;
+ if (!p_ch)
+ return ABORT_ALL;
+ if (t_ch == p_ch)
+ matched = TRUE;
+ } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {
+ p_ch = *++p;
+ if (p_ch == '\\') {
+ p_ch = *++p;
+ if (!p_ch)
+ return ABORT_ALL;
+ }
+ if (t_ch <= p_ch && t_ch >= prev_ch)
+ matched = TRUE;
+ p_ch = 0; /* This makes "prev_ch" get set to 0. */
+ } else if (p_ch == '[' && p[1] == ':') {
+ const uchar *s;
+ int i;
+ for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/
+ if (!p_ch)
+ return ABORT_ALL;
+ i = p - s - 1;
+ if (i < 0 || p[-1] != ':') {
+ /* Didn't find ":]", so treat like a normal set. */
+ p = s - 2;
+ p_ch = '[';
+ if (t_ch == p_ch)
+ matched = TRUE;
+ continue;
+ }
+ if (CC_EQ(s,i, "alnum")) {
+ if (ISALNUM(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "alpha")) {
+ if (ISALPHA(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "blank")) {
+ if (ISBLANK(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "cntrl")) {
+ if (ISCNTRL(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "digit")) {
+ if (ISDIGIT(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "graph")) {
+ if (ISGRAPH(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "lower")) {
+ if (ISLOWER(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "print")) {
+ if (ISPRINT(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "punct")) {
+ if (ISPUNCT(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "space")) {
+ if (ISSPACE(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "upper")) {
+ if (ISUPPER(t_ch))
+ matched = TRUE;
+ } else if (CC_EQ(s,i, "xdigit")) {
+ if (ISXDIGIT(t_ch))
+ matched = TRUE;
+ } else /* malformed [:class:] string */
+ return ABORT_ALL;
+ p_ch = 0; /* This makes "prev_ch" get set to 0. */
+ } else if (t_ch == p_ch)
+ matched = TRUE;
+ } while (prev_ch = p_ch, (p_ch = *++p) != ']');
+ if (matched == special || t_ch == '/')
+ return FALSE;
+ continue;
+ }
+ }
+
+ do {
+ if (*text)
+ return FALSE;
+ } while ((text = *a++) != NULL);
+
+ return TRUE;
+}
+
+/* Match literal string "s" against the a virtually-joined string consisting
+ * of "text" and any strings in array "a". */
+static int doliteral(const uchar *s, const uchar *text, const uchar*const *a)
+{
+ for ( ; *s != '\0'; text++, s++) {
+ while (*text == '\0') {
+ if ((text = *a++) == NULL)
+ return FALSE;
+ }
+ if (*text != *s)
+ return FALSE;
+ }
+
+ do {
+ if (*text)
+ return FALSE;
+ } while ((text = *a++) != NULL);
+
+ return TRUE;
+}
+
+/* Return the last "count" path elements from the concatenated string.
+ * We return a string pointer to the start of the string, and update the
+ * array pointer-pointer to point to any remaining string elements. */
+static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)
+{
+ const uchar*const *a = *a_ptr;
+ const uchar*const *first_a = a;
+
+ while (*a)
+ a++;
+
+ while (a != first_a) {
+ const uchar *s = *--a;
+ s += strlen((char*)s);
+ while (--s >= *a) {
+ if (*s == '/' && !--count) {
+ *a_ptr = a+1;
+ return s+1;
+ }
+ }
+ }
+
+ if (count == 1) {
+ *a_ptr = a+1;
+ return *a;
+ }
+
+ return NULL;
+}
+
+/* Match the "pattern" against the "text" string. */
+int wildmatch(const char *pattern, const char *text)
+{
+ static const uchar *nomore[1]; /* A NULL pointer. */
+#ifdef WILD_TEST_ITERATIONS
+ wildmatch_iteration_count = 0;
+#endif
+ return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
+}
+
+/* Match the "pattern" against the forced-to-lower-case "text" string. */
+int iwildmatch(const char *pattern, const char *text)
+{
+ static const uchar *nomore[1]; /* A NULL pointer. */
+ int ret;
+#ifdef WILD_TEST_ITERATIONS
+ wildmatch_iteration_count = 0;
+#endif
+ force_lower_case = 1;
+ ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
+ force_lower_case = 0;
+ return ret;
+}
+
+/* Match pattern "p" against the a virtually-joined string consisting
+ * of all the pointers in array "texts" (which has a NULL pointer at the
+ * end). The int "where" can be 0 (normal matching), > 0 (match only
+ * the trailing N slash-separated filename components of "texts"), or < 0
+ * (match the "pattern" at the start or after any slash in "texts"). */
+int wildmatch_array(const char *pattern, const char*const *texts, int where)
+{
+ const uchar *p = (const uchar*)pattern;
+ const uchar*const *a = (const uchar*const*)texts;
+ const uchar *text;
+ int matched;
+
+#ifdef WILD_TEST_ITERATIONS
+ wildmatch_iteration_count = 0;
+#endif
+
+ if (where > 0)
+ text = trailing_N_elements(&a, where);
+ else
+ text = *a++;
+ if (!text)
+ return FALSE;
+
+ if ((matched = dowild(p, text, a)) != TRUE && where < 0
+ && matched != ABORT_ALL) {
+ while (1) {
+ if (*text == '\0') {
+ if ((text = (uchar*)*a++) == NULL)
+ return FALSE;
+ continue;
+ }
+ if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE
+ && matched != ABORT_TO_STARSTAR)
+ break;
+ }
+ }
+ return matched == TRUE;
+}
+
+/* Match literal string "s" against the a virtually-joined string consisting
+ * of all the pointers in array "texts" (which has a NULL pointer at the
+ * end). The int "where" can be 0 (normal matching), or > 0 (match
+ * only the trailing N slash-separated filename components of "texts"). */
+int litmatch_array(const char *string, const char*const *texts, int where)
+{
+ const uchar *s = (const uchar*)string;
+ const uchar*const *a = (const uchar* const*)texts;
+ const uchar *text;
+
+ if (where > 0)
+ text = trailing_N_elements(&a, where);
+ else
+ text = *a++;
+ if (!text)
+ return FALSE;
+
+ return doliteral(s, text, a) == TRUE;
+}
diff --git a/wildmatch.h b/wildmatch.h
new file mode 100644
index 0000000..e7f1a35
--- /dev/null
+++ b/wildmatch.h
@@ -0,0 +1,6 @@
+/* wildmatch.h */
+
+int wildmatch(const char *pattern, const char *text);
+int iwildmatch(const char *pattern, const char *text);
+int wildmatch_array(const char *pattern, const char*const *texts, int where);
+int litmatch_array(const char *string, const char*const *texts, int where);
--
1.7.12.403.gce5cf6f.dirty
next prev parent reply other threads:[~2012-09-16 15:27 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-09-15 12:01 [PATCH 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
2012-09-15 12:02 ` [PATCH 1/5] Import wildmatch from rsync Nguyễn Thái Ngọc Duy
2012-09-16 6:49 ` Junio C Hamano
2012-09-15 12:02 ` [PATCH 2/5] compat/wildmatch: remove static variable force_lower_case Nguyễn Thái Ngọc Duy
2012-09-15 12:02 ` [PATCH 3/5] compat/wildmatch: fix case-insensitive matching Nguyễn Thái Ngọc Duy
2012-09-15 12:02 ` [PATCH 4/5] Integrate wildmatch to git Nguyễn Thái Ngọc Duy
2012-09-15 12:02 ` [PATCH 5/5] Support "**" in .gitignore and .gitattributes patterns using wildmatch() Nguyễn Thái Ngọc Duy
2012-09-15 14:27 ` How to create the " [PATCH 0/5]" first email? Philip Oakley
2012-09-15 17:08 ` Junio C Hamano
2012-09-17 22:55 ` Philip Oakley
2012-09-17 23:49 ` Junio C Hamano
2012-09-18 0:15 ` Jeff King
2012-09-18 0:36 ` Junio C Hamano
2012-09-18 18:55 ` Jeff King
2012-09-18 19:11 ` Junio C Hamano
2012-09-18 19:16 ` Jeff King
2012-09-18 19:47 ` Junio C Hamano
2012-09-18 20:10 ` Philip Oakley
2012-09-18 20:16 ` Jeff King
2012-09-18 20:16 ` Jeff King
2012-09-18 20:42 ` Wesley J. Landaker
2012-09-23 12:03 ` Jan Engelhardt
2012-09-16 15:27 ` [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy
2012-09-16 15:27 ` Nguyễn Thái Ngọc Duy [this message]
2012-09-16 15:27 ` [PATCH v2 2/5] compat/wildmatch: remove static variable force_lower_case Nguyễn Thái Ngọc Duy
2012-09-16 15:27 ` [PATCH v2 3/5] compat/wildmatch: fix case-insensitive matching Nguyễn Thái Ngọc Duy
2012-09-16 15:27 ` [PATCH v2 4/5] Integrate wildmatch to git Nguyễn Thái Ngọc Duy
2012-09-17 5:31 ` Junio C Hamano
2012-09-17 5:54 ` Junio C Hamano
2012-09-17 5:57 ` Nguyen Thai Ngoc Duy
2012-09-17 12:40 ` Nguyen Thai Ngoc Duy
2012-09-17 17:20 ` Junio C Hamano
2012-09-16 15:27 ` [PATCH v2 5/5] Support "**" in .gitignore and .gitattributes patterns using wildmatch() Nguyễn Thái Ngọc Duy
2012-09-25 7:01 ` [PATCH 6/5] side-step a make rule that builds t3070-wildmatch Johannes Sixt
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1347809265-2457-2-git-send-email-pclouds@gmail.com \
--to=pclouds@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.