* [PATCH 0/5] Support matching "**" in .gitattributes and .gitignore @ 2012-09-15 12:01 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 ` (6 more replies) 0 siblings, 7 replies; 34+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-09-15 12:01 UTC (permalink / raw) To: git; +Cc: Nguyễn Thái Ngọc Duy A while back I posted a proof of concept using wildmatch() from rsync. Back then the obstacle was case-insensitive matching support. I did not realize that there was iwildmatch() that does exactly that. So here again a series that is probably ready for consumption. Now patterns that contain slashes will always go through (i)wildmatch(). fnmatch() is only used for basename matching. Nguyễn Thái Ngọc Duy (5): Import wildmatch from rsync compat/wildmatch: remove static variable force_lower_case compat/wildmatch: fix case-insensitive matching Integrate wildmatch to git Support "**" in .gitignore and .gitattributes patterns using wildmatch() Documentation/gitignore.txt | 3 + Makefile | 6 + attr.c | 4 +- compat/wildmatch.c | 373 +++++++++++++++++++++++++++++++++++++++++ compat/wildmatch.h | 6 + dir.c | 5 +- t/t3070-wildmatch.sh | 27 +++ t/t3070-wildmatch/wildtest.txt | 165 ++++++++++++++++++ test-wildmatch.c | 228 +++++++++++++++++++++++++ 9 files changed, 815 insertions(+), 2 deletions(-) create mode 100644 compat/wildmatch.c create mode 100644 compat/wildmatch.h create mode 100755 t/t3070-wildmatch.sh create mode 100644 t/t3070-wildmatch/wildtest.txt create mode 100644 test-wildmatch.c -- 1.7.12.403.gce5cf6f.dirty ^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH 1/5] Import wildmatch from rsync 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 ` 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 ` (5 subsequent siblings) 6 siblings, 1 reply; 34+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-09-15 12:02 UTC (permalink / raw) To: git; +Cc: Nguyễn Thái Ngọc Duy [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1: Type: text/plain; charset=UTF-8, Size: 22884 bytes --] These files are from rsync.git commit d51a3adb4fca3e6b1b046c6e570828f3bca8fe36. The commit is GPL-3. However wildmatch.[ch] have not changed since rsync turned to GPL-3. rsync.git -> git.git lib/wildmatch.[ch] compat/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> --- compat/wildmatch.c | 368 +++++++++++++++++++++++++++++++++++++++++ compat/wildmatch.h | 6 + t/t3070-wildmatch/wildtest.txt | 165 ++++++++++++++++++ test-wildmatch.c | 222 +++++++++++++++++++++++++ 4 files changed, 761 insertions(+) create mode 100644 compat/wildmatch.c create mode 100644 compat/wildmatch.h create mode 100644 t/t3070-wildmatch/wildtest.txt create mode 100644 test-wildmatch.c diff --git a/compat/wildmatch.c b/compat/wildmatch.c new file mode 100644 index 0000000..f3a1731 --- /dev/null +++ b/compat/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/compat/wildmatch.h b/compat/wildmatch.h new file mode 100644 index 0000000..e7f1a35 --- /dev/null +++ b/compat/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); 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; +} -- 1.7.12.403.gce5cf6f.dirty ^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: [PATCH 1/5] Import wildmatch from rsync 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 0 siblings, 0 replies; 34+ messages in thread From: Junio C Hamano @ 2012-09-16 6:49 UTC (permalink / raw) To: Nguyễn Thái Ngọc Duy; +Cc: git Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes: > These files are from rsync.git commit > d51a3adb4fca3e6b1b046c6e570828f3bca8fe36. The commit is GPL-3. However > wildmatch.[ch] have not changed since rsync turned to GPL-3. I'd like see the last version of rsync that was GPLv2 hunted down and the copy from that version lifted from it. If you _know_ they are identical, then you should be able to do that, no? > compat/wildmatch.c | 368 +++++++++++++++++++++++++++++++++++++++++ > compat/wildmatch.h | 6 + As this will be linked on all platforms, it shouldn't be in compat/ directory. Unlike xdiff/ that has many files, this can live at the top-level, just like kwset we borrowed from GNU grep does. > diff --git a/compat/wildmatch.c b/compat/wildmatch.c > new file mode 100644 > index 0000000..f3a1731 > --- /dev/null > +++ b/compat/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" Huh??? Ahh. The approach you took is "This patch imports the pristine copy to make it easier to verify the provenance, and the plan is to make it usable with separate follow-up patches.", I agree that is a very sensible thing to do. But please say so in the commit log message. ^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH 2/5] compat/wildmatch: remove static variable force_lower_case 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-15 12:02 ` 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 ` (4 subsequent siblings) 6 siblings, 0 replies; 34+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-09-15 12:02 UTC (permalink / raw) To: git; +Cc: Nguyễn Thái Ngọc Duy One place less to worry about thread safety Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- compat/wildmatch.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/compat/wildmatch.c b/compat/wildmatch.c index f3a1731..e824eb2 100644 --- a/compat/wildmatch.c +++ b/compat/wildmatch.c @@ -57,11 +57,10 @@ 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) +static int dowild(const uchar *p, const uchar *text, + const uchar*const *a, int force_lower_case) { uchar p_ch; @@ -121,7 +120,7 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a) t_ch = *text; continue; } - if ((matched = dowild(p, text, a)) != FALSE) { + if ((matched = dowild(p, text, a, force_lower_case)) != FALSE) { if (!special || matched != ABORT_TO_STARSTAR) return matched; } else if (!special && t_ch == '/') @@ -291,7 +290,7 @@ int wildmatch(const char *pattern, const char *text) #ifdef WILD_TEST_ITERATIONS wildmatch_iteration_count = 0; #endif - return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE; + return dowild((const uchar*)pattern, (const uchar*)text, nomore, 0) == TRUE; } /* Match the "pattern" against the forced-to-lower-case "text" string. */ @@ -302,9 +301,7 @@ int iwildmatch(const char *pattern, const char *text) #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; + ret = dowild((const uchar*)pattern, (const uchar*)text, nomore, 1) == TRUE; return ret; } @@ -331,7 +328,7 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where) if (!text) return FALSE; - if ((matched = dowild(p, text, a)) != TRUE && where < 0 + if ((matched = dowild(p, text, a, 0)) != TRUE && where < 0 && matched != ABORT_ALL) { while (1) { if (*text == '\0') { @@ -339,7 +336,7 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where) return FALSE; continue; } - if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE + if (*text++ == '/' && (matched = dowild(p, text, a, 0)) != FALSE && matched != ABORT_TO_STARSTAR) break; } -- 1.7.12.403.gce5cf6f.dirty ^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 3/5] compat/wildmatch: fix case-insensitive matching 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-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 ` 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 ` (3 subsequent siblings) 6 siblings, 0 replies; 34+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-09-15 12:02 UTC (permalink / raw) To: git; +Cc: Nguyễn Thái Ngọc Duy dowild() does case insensitive matching by lower-casing the text. That means lower case letters in patterns imply case-insensitive matching, but upper case means exact matching. We do not want that subtlety. Lower case pattern too so iwildmatch() always does what we expect it to do. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- compat/wildmatch.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compat/wildmatch.c b/compat/wildmatch.c index e824eb2..c7f7f9f 100644 --- a/compat/wildmatch.c +++ b/compat/wildmatch.c @@ -81,6 +81,8 @@ static int dowild(const uchar *p, const uchar *text, } if (force_lower_case && ISUPPER(t_ch)) t_ch = tolower(t_ch); + if (force_lower_case && ISUPPER(p_ch)) + p_ch = tolower(p_ch); switch (p_ch) { case '\\': /* Literal match with following character. Note that the test -- 1.7.12.403.gce5cf6f.dirty ^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 4/5] Integrate wildmatch to git 2012-09-15 12:01 [PATCH 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy ` (2 preceding siblings ...) 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 ` 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 ` (2 subsequent siblings) 6 siblings, 0 replies; 34+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-09-15 12:02 UTC (permalink / raw) To: git; +Cc: Nguyễn Thái Ngọc Duy This makes wildmatch.c part of libgit.a and builds test-wildmatch Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- Makefile | 6 ++++++ compat/wildmatch.c | 8 +++++++- t/t3070-wildmatch.sh | 27 +++++++++++++++++++++++++++ test-wildmatch.c | 8 +++++++- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100755 t/t3070-wildmatch.sh diff --git a/Makefile b/Makefile index 56301dc..c3608e6 100644 --- a/Makefile +++ b/Makefile @@ -511,6 +511,7 @@ TEST_PROGRAMS_NEED_X += test-sha1 TEST_PROGRAMS_NEED_X += test-sigchain TEST_PROGRAMS_NEED_X += test-subprocess TEST_PROGRAMS_NEED_X += test-svn-fe +TEST_PROGRAMS_NEED_X += test-wildmatch TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X)) @@ -605,6 +606,7 @@ LIB_H += compat/mingw.h LIB_H += compat/obstack.h LIB_H += compat/precompose_utf8.h LIB_H += compat/terminal.h +LIB_H += compat/wildmatch.h LIB_H += compat/win32/dirent.h LIB_H += compat/win32/poll.h LIB_H += compat/win32/pthread.h @@ -709,6 +711,7 @@ LIB_OBJS += combine-diff.o LIB_OBJS += commit.o LIB_OBJS += compat/obstack.o LIB_OBJS += compat/terminal.o +LIB_OBJS += compat/wildmatch.o LIB_OBJS += config.o LIB_OBJS += connect.o LIB_OBJS += connected.o @@ -2586,6 +2589,9 @@ test-svn-fe$X: vcs-svn/lib.a test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS) +test-wildmatch$X: test-wildmatch.o GIT-LDFLAGS + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) -lpopt + check-sha1:: test-sha1$X ./test-sha1.sh diff --git a/compat/wildmatch.c b/compat/wildmatch.c index c7f7f9f..625cb0c 100644 --- a/compat/wildmatch.c +++ b/compat/wildmatch.c @@ -9,7 +9,13 @@ ** work differently than '*', and to fix the character-class code. */ -#include "rsync.h" +#include <stddef.h> +#include <ctype.h> +#include <string.h> + +#include "wildmatch.h" + +typedef unsigned char uchar; /* What character marks an inverted character class? */ #define NEGATE_CLASS '!' diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh new file mode 100755 index 0000000..7fb63ff --- /dev/null +++ b/t/t3070-wildmatch.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +test_description='wildmatch tests' + +. ./test-lib.sh + +test_wildmatch() { + test_expect_success "wildmatch $*" " + test-wildmatch $* ../t3070-wildmatch/wildtest.txt >actual && + echo 'No wildmatch errors found.' >expected && + test_cmp expected actual + " +} + +test_wildmatch -x1 +test_wildmatch -x1 -e1 +test_wildmatch -x1 -else +test_wildmatch -x2 +test_wildmatch -x2 -ese +test_wildmatch -x3 +test_wildmatch -x3 -e1 +test_wildmatch -x4 +test_wildmatch -x4 -e2e +test_wildmatch -x5 +test_wildmatch -x5 -es + +test_done diff --git a/test-wildmatch.c b/test-wildmatch.c index 88585c2..2c506a0 100644 --- a/test-wildmatch.c +++ b/test-wildmatch.c @@ -20,7 +20,13 @@ /*#define COMPARE_WITH_FNMATCH*/ #define WILD_TEST_ITERATIONS -#include "lib/wildmatch.c" +#include "compat/wildmatch.c" + +#define MAXPATHLEN 1024 +#ifdef NO_STRLCPY +#include "compat/strlcpy.c" +#define strlcpy gitstrlcpy +#endif #include <popt.h> -- 1.7.12.403.gce5cf6f.dirty ^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 5/5] Support "**" in .gitignore and .gitattributes patterns using wildmatch() 2012-09-15 12:01 [PATCH 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy ` (3 preceding siblings ...) 2012-09-15 12:02 ` [PATCH 4/5] Integrate wildmatch to git Nguyễn Thái Ngọc Duy @ 2012-09-15 12:02 ` 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-16 15:27 ` [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy 6 siblings, 0 replies; 34+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-09-15 12:02 UTC (permalink / raw) To: git; +Cc: Nguyễn Thái Ngọc Duy Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- Documentation/gitignore.txt | 3 +++ attr.c | 4 +++- dir.c | 5 ++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt index c1f692a..eb81d31 100644 --- a/Documentation/gitignore.txt +++ b/Documentation/gitignore.txt @@ -93,6 +93,9 @@ PATTERN FORMAT For example, "Documentation/{asterisk}.html" matches "Documentation/git.html" but not "Documentation/ppc/ppc.html" or "tools/perf/Documentation/perf.html". ++ +Contrary to fnmatch(3), git matches "**" to anything including +slashes, similar to rsync(1). - A leading slash matches the beginning of the pathname. For example, "/{asterisk}.c" matches "cat-file.c" but not diff --git a/attr.c b/attr.c index 3430faf..f06ad95 100644 --- a/attr.c +++ b/attr.c @@ -12,6 +12,7 @@ #include "exec_cmd.h" #include "attr.h" #include "dir.h" +#include "compat/wildmatch.h" const char git_attr__true[] = "(builtin)true"; const char git_attr__false[] = "\0(builtin)false"; @@ -666,7 +667,8 @@ static int path_matches(const char *pathname, int pathlen, return 0; if (baselen != 0) baselen++; - return fnmatch_icase(pattern, pathname + baselen, FNM_PATHNAME) == 0; + return (ignore_case && iwildmatch(pattern, pathname + baselen)) || + (!ignore_case && wildmatch(pattern, pathname + baselen)); } static int macroexpand_one(int attr_nr, int rem); diff --git a/dir.c b/dir.c index 4868339..c17f9ff 100644 --- a/dir.c +++ b/dir.c @@ -8,6 +8,7 @@ #include "cache.h" #include "dir.h" #include "refs.h" +#include "compat/wildmatch.h" struct path_simplify { int len; @@ -575,7 +576,9 @@ int excluded_from_list(const char *pathname, namelen -= prefix; } - if (!namelen || !fnmatch_icase(exclude, name, FNM_PATHNAME)) + if (!namelen || + ((ignore_case && iwildmatch(exclude, name)) || + (!ignore_case && wildmatch(exclude, name)))) return to_exclude; } return -1; /* undecided */ -- 1.7.12.403.gce5cf6f.dirty ^ permalink raw reply related [flat|nested] 34+ messages in thread
* How to create the " [PATCH 0/5]" first email? 2012-09-15 12:01 [PATCH 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy ` (4 preceding siblings ...) 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 ` Philip Oakley 2012-09-15 17:08 ` Junio C Hamano 2012-09-16 15:27 ` [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy 6 siblings, 1 reply; 34+ messages in thread From: Philip Oakley @ 2012-09-15 14:27 UTC (permalink / raw) To: Git List The git format-patch allows numbered patches to be created, however I'm not sure how folk generate the initial "0/n" patch. Which of the various options should I be using? Also, being on windows, the various 'mbox' and 'MUA' discussions are new to me, so the format-patch step where :- "Typically it will be placed in a MUA’s drafts folder, edited to add timely commentary that should not go in the changelog after the three dashes, and then sent as a message whose body, in our example, starts with "arch/arm config files were…". On the receiving end, readers can save interesting patches in a UNIX mailbox and apply them with git-am(1)." hides a multitude of implicit knowledge steps. Is there an extended description of what that would mean from a platform independent viewpoint? e.g. if the patches are separte files and an mbox is one consolidated file, how to get from one to the other so that 'it' can be sent by 'git send-mail'. I'm also missing an understanding of the preparation stage where one tries to tidy up the various commit messages becaue they weren't explicit, specfic nor concise enough, so I suspect that there is an implicit `git format-patch` <-> `git am` loop of sharpening the mbox patches before submission to the list that I'm missing. Has this described somewhere? Philip Oakley ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to create the " [PATCH 0/5]" first email? 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-23 12:03 ` Jan Engelhardt 0 siblings, 2 replies; 34+ messages in thread From: Junio C Hamano @ 2012-09-15 17:08 UTC (permalink / raw) To: Philip Oakley; +Cc: Git List "Philip Oakley" <philipoakley@iee.org> writes: > "Typically it will be placed in a MUA’s drafts folder, edited to add > timely commentary that should not go in the changelog after the three > dashes, and then sent as a message whose body, in our example, starts > with "arch/arm config files were…". On the receiving end, readers can > save interesting patches in a UNIX mailbox and apply them with > git-am(1)." > > hides a multitude of implicit knowledge steps. Is there an extended > description of what that would mean from a platform independent > viewpoint? e.g. if the patches are separte files and an mbox is one > consolidated file, how to get from one to the other so that 'it' can be > sent by 'git send-mail'. If you plan to use "git send-email" to send the final results out, you should consider "git send-email" as your "MUA" in the quoted paragraph. And that will be very platform independent viewpoint to see things from. "git format-patch -o my-series/ --cover-letter ..." would treat "my-series/" directory as "MUA's drafts folder" and prepares the messages you would want to send out, and you can proof-read and edit the files in there before telling your "MUA" to send them out, with "git send-email ... my-series/*.patch" or something. > I'm also missing an understanding of the preparation stage where one > tries to tidy up the various commit messages becaue they weren't > explicit, specfic nor concise enough,... Many people usually do "rebase -i" until perfection and then a single final invocation of "format-patch". Of course, the "final" can and should further be proof-read and it is fine to do typofixes in the format-patch output files without going back to the commits before sending them out. > ...so I suspect that there is an > implicit `git format-patch` <-> `git am` loop of sharpening the mbox > patches before submission to the list that I'm missing. Has this > described somewhere? ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to create the " [PATCH 0/5]" first email? 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-23 12:03 ` Jan Engelhardt 1 sibling, 1 reply; 34+ messages in thread From: Philip Oakley @ 2012-09-17 22:55 UTC (permalink / raw) To: Junio C Hamano; +Cc: Git List From: "Junio C Hamano" <gitster@pobox.com> > "Philip Oakley" <philipoakley@iee.org> writes: > >> "Typically it will be placed in a MUA’s drafts folder, edited to add >> timely commentary that should not go in the changelog after the three >> dashes, and then sent as a message whose body, in our example, starts >> with "arch/arm config files were…". On the receiving end, readers >> can >> save interesting patches in a UNIX mailbox and apply them with >> git-am(1)." >> >> hides a multitude of implicit knowledge steps. Is there an extended >> description of what that would mean from a platform independent >> viewpoint? e.g. if the patches are separte files and an mbox is one >> consolidated file, how to get from one to the other so that 'it' can >> be >> sent by 'git send-mail'. > > If you plan to use "git send-email" to send the final results out, > you should consider "git send-email" as your "MUA" in the quoted > paragraph. And that will be very platform independent viewpoint to > see things from. On git for windows (msysgit)n there were a couple of other steps I had to do https://git.wiki.kernel.org/index.php/MSysGit:UsingSendEmail as Outlooklook Express isn't really a suitable MUA ;-) [noted for other list readers] > > "git format-patch -o my-series/ --cover-letter ..." would treat > "my-series/" directory as "MUA's drafts folder" and prepares the > messages you would want to send out, and you can proof-read and edit > the files in there before telling your "MUA" to send them out, with > "git send-email ... my-series/*.patch" or something. > I hadn't picked up from the man page that the --cover-letter would do the [PATCH 0/n] - should it? >> I'm also missing an understanding of the preparation stage where one >> tries to tidy up the various commit messages becaue they weren't >> explicit, specfic nor concise enough,... > > Many people usually do "rebase -i" until perfection and then a > single final invocation of "format-patch". Of course, the "final" > can and should further be proof-read and it is fine to do typofixes > in the format-patch output files without going back to the commits > before sending them out. I did an initial rebase to correct a few obvious mistakes (e.g. an extra file that had crept in), but then, after some false starts, used git format-patch pu --stdout > fix_Docs.patch to get a single file I could inspect and refine for both the commit messages and content. I then applied it (using git am) to a temp branch to see what it produced, and could repeat the cycle until the patches looked right. > >> ...so I suspect that there is an >> implicit `git format-patch` <-> `git am` loop of sharpening the mbox >> patches before submission to the list that I'm missing. Has this >> described somewhere? > -- However, when it came to creating the series, with comments, I couldn't see a way of having my comments within my local commits, but preparing a patch series that would properly include the '---' separator. Is there a way of getting format-patch to change some line break sequence (within the commit message) to the '---' three dashes patch break suitable for submission to the list? Philip ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to create the " [PATCH 0/5]" first email? 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 20:42 ` Wesley J. Landaker 0 siblings, 2 replies; 34+ messages in thread From: Junio C Hamano @ 2012-09-17 23:49 UTC (permalink / raw) To: Philip Oakley; +Cc: Git List "Philip Oakley" <philipoakley@iee.org> writes: > I then applied it (using git am) to a temp branch to see what it > produced, and could repeat the cycle until the patches looked right. That's another obvious and valid way to prepare your series. It all depends on how comfortable you are to directly edit patches. Some people fear it. Some don't. Some can do it with their eyes closed ;-). > However, when it came to creating the series, with comments, I > couldn't see a way of having my comments within my local commits, but > preparing a patch series that would properly include the '---' > separator. An unofficial trick that works is to write the --- * This is an additional comment yourself when running "git commit". That will be propagated to the output from format-patch. You will have another "---" in front of the diffstat, but nobody is hurt by that. But when doing a big series that deserves a cover letter [PATCH 0/n], you will use editor on the output from format-patch anyway, and I find it simpler to do the follow-on comments at that point myself. Personal preferences vary, so whatever makes you feel comfortable with and works well for you is good. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to create the " [PATCH 0/5]" first email? 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 20:42 ` Wesley J. Landaker 1 sibling, 1 reply; 34+ messages in thread From: Jeff King @ 2012-09-18 0:15 UTC (permalink / raw) To: Junio C Hamano; +Cc: Philip Oakley, Git List On Mon, Sep 17, 2012 at 04:49:39PM -0700, Junio C Hamano wrote: > > However, when it came to creating the series, with comments, I > > couldn't see a way of having my comments within my local commits, but > > preparing a patch series that would properly include the '---' > > separator. > > An unofficial trick that works is to write the > > --- > > * This is an additional comment > > > yourself when running "git commit". That will be propagated to the > output from format-patch. You will have another "---" in front of > the diffstat, but nobody is hurt by that. But note that using "format-patch -s" will break; it puts the sign-off below the "---". > But when doing a big series that deserves a cover letter [PATCH 0/n], > you will use editor on the output from format-patch anyway, and I > find it simpler to do the follow-on comments at that point myself. Me too (actually, I load it all into mutt and then comment on each as I send them out, but it amounts to the same thing, seeing as how my MUA just invokes $EDITOR when I edit a mail). -Peff ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to create the " [PATCH 0/5]" first email? 2012-09-18 0:15 ` Jeff King @ 2012-09-18 0:36 ` Junio C Hamano 2012-09-18 18:55 ` Jeff King 0 siblings, 1 reply; 34+ messages in thread From: Junio C Hamano @ 2012-09-18 0:36 UTC (permalink / raw) To: Jeff King; +Cc: Philip Oakley, Git List Jeff King <peff@peff.net> writes: > On Mon, Sep 17, 2012 at 04:49:39PM -0700, Junio C Hamano wrote: > >> > However, when it came to creating the series, with comments, I >> > couldn't see a way of having my comments within my local commits, but >> > preparing a patch series that would properly include the '---' >> > separator. >> >> An unofficial trick that works is to write the >> >> --- >> >> * This is an additional comment >> >> >> yourself when running "git commit". That will be propagated to the >> output from format-patch. You will have another "---" in front of >> the diffstat, but nobody is hurt by that. > > But note that using "format-patch -s" will break; it puts the sign-off > below the "---". I think "format-patch -s" is a workflow mistake in the first place. You should be doing the sign-off the commit when you commit in the first place. It is not like "I cannot sign off because I think it is still iffy" or anything. But your point still stands; "commit -s" will not see through that official trick either ;-). >> But when doing a big series that deserves a cover letter [PATCH 0/n], >> you will use editor on the output from format-patch anyway, and I >> find it simpler to do the follow-on comments at that point myself. > > Me too (actually, I load it all into mutt and then comment on each as I > send them out, but it amounts to the same thing, seeing as how my MUA > just invokes $EDITOR when I edit a mail). ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to create the " [PATCH 0/5]" first email? 2012-09-18 0:36 ` Junio C Hamano @ 2012-09-18 18:55 ` Jeff King 2012-09-18 19:11 ` Junio C Hamano 0 siblings, 1 reply; 34+ messages in thread From: Jeff King @ 2012-09-18 18:55 UTC (permalink / raw) To: Junio C Hamano; +Cc: Philip Oakley, Git List On Mon, Sep 17, 2012 at 05:36:35PM -0700, Junio C Hamano wrote: > > But note that using "format-patch -s" will break; it puts the sign-off > > below the "---". > > I think "format-patch -s" is a workflow mistake in the first place. > You should be doing the sign-off the commit when you commit in the > first place. It is not like "I cannot sign off because I think it > is still iffy" or anything. It is mostly "I do not signoff because typing '-s' each time I commit is a pain". I know it is supposed to be a conscious decision, but I think it is a little silly. Since everything I do for git.git is going to be signed-off, either I will automate it, or I will end up typing it without thinking. Whenever I make a conscious decision, it is to _withhold_ signoff, since that is the exceptional case. But even without that, I still think format-patch is a reasonable time to do it. It is the time when I proof-read my commit message and patch in its final form, and think "do I really want to send this?". That seems to me like a reasonable time to make such a conscious decision to signoff (or not). > But your point still stands; "commit -s" will not see through that > official trick either ;-). Yes. :) -Peff ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to create the " [PATCH 0/5]" first email? 2012-09-18 18:55 ` Jeff King @ 2012-09-18 19:11 ` Junio C Hamano 2012-09-18 19:16 ` Jeff King 0 siblings, 1 reply; 34+ messages in thread From: Junio C Hamano @ 2012-09-18 19:11 UTC (permalink / raw) To: Jeff King; +Cc: Philip Oakley, Git List Jeff King <peff@peff.net> writes: > But even without that, I still think format-patch is a reasonable time > to do it. It is the time when I proof-read my commit message and patch > in its final form, and think "do I really want to send this?". But it is not like "I cannot sign off because I think it is still iffy". > seems to me like a reasonable time to make such a conscious decision to > signoff (or not). > >> But your point still stands; "commit -s" will not see through that >> official trick either ;-). > > Yes. :) Actually, no. "commit -s" does not have any need to see through it. ... hack hack hack ... $ git commit -a -s ... editor opens, you see your Sign-off at the end, with ... the cursor sitting on the first line ... edit the title, move to the line below the Sign-off, ... and do the "---\n\n * comment" thing. And this survives "rebase -i" (but not "format-patch | am" for obvious reasons). So I take it back. The time you do the "git commit" for the very first time for this change that may need to be rerolled number of times is the right time to say "-s". ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to create the " [PATCH 0/5]" first email? 2012-09-18 19:11 ` Junio C Hamano @ 2012-09-18 19:16 ` Jeff King 2012-09-18 19:47 ` Junio C Hamano 0 siblings, 1 reply; 34+ messages in thread From: Jeff King @ 2012-09-18 19:16 UTC (permalink / raw) To: Junio C Hamano; +Cc: Philip Oakley, Git List On Tue, Sep 18, 2012 at 12:11:58PM -0700, Junio C Hamano wrote: > Jeff King <peff@peff.net> writes: > > > But even without that, I still think format-patch is a reasonable time > > to do it. It is the time when I proof-read my commit message and patch > > in its final form, and think "do I really want to send this?". > > But it is not like "I cannot sign off because I think it is still > iffy". No, that is not the particular reason in my case, but I think I explained other reasons why "format-patch -s" is not a wrong workflow. > >> But your point still stands; "commit -s" will not see through that > >> official trick either ;-). > > > > Yes. :) > > Actually, no. "commit -s" does not have any need to see through it. > > ... hack hack hack ... > $ git commit -a -s > ... editor opens, you see your Sign-off at the end, with > ... the cursor sitting on the first line > ... edit the title, move to the line below the Sign-off, > ... and do the "---\n\n * comment" thing. > > And this survives "rebase -i" (but not "format-patch | am" for > obvious reasons). Yes, if your particular workflow is to signoff the very first time you commit. But it would not work for: ... hack hack hack ... $ git commit -a ... make a note after "---" ... ... hack hack hack ... ... OK, looks good, ready to signoff ... $ git commit --amend -s So it can work, but it is workflow dependent, and in general is a little flaky with the automagic signoff. You may want to signoff later for a variety of reasons, not the least of which is that you forgot to type "-s" the first time. > So I take it back. The time you do the "git commit" for the very > first time for this change that may need to be rerolled number of > times is the right time to say "-s". If you remember to type it. :) -Peff ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to create the " [PATCH 0/5]" first email? 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 0 siblings, 2 replies; 34+ messages in thread From: Junio C Hamano @ 2012-09-18 19:47 UTC (permalink / raw) To: Jeff King; +Cc: Philip Oakley, Git List Jeff King <peff@peff.net> writes: > On Tue, Sep 18, 2012 at 12:11:58PM -0700, Junio C Hamano wrote: > >> Jeff King <peff@peff.net> writes: >> >> > But even without that, I still think format-patch is a reasonable time >> > to do it. It is the time when I proof-read my commit message and patch >> > in its final form, and think "do I really want to send this?". >> >> But it is not like "I cannot sign off because I think it is still >> iffy". > > No, that is not the particular reason in my case, but I think I > explained other reasons why "format-patch -s" is not a wrong workflow. Then I didn't read it. What does "do I really want to send this?" have anything to do with DCO in any case? ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to create the " [PATCH 0/5]" first email? 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 1 sibling, 1 reply; 34+ messages in thread From: Philip Oakley @ 2012-09-18 20:10 UTC (permalink / raw) To: Junio C Hamano, Jeff King; +Cc: Git List From: "Junio C Hamano" <gitster@pobox.com> > have anything to do with DCO in any case? Junio, What's DCO an abbreviation of? Philip ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to create the " [PATCH 0/5]" first email? 2012-09-18 20:10 ` Philip Oakley @ 2012-09-18 20:16 ` Jeff King 0 siblings, 0 replies; 34+ messages in thread From: Jeff King @ 2012-09-18 20:16 UTC (permalink / raw) To: Philip Oakley; +Cc: Junio C Hamano, Git List On Tue, Sep 18, 2012 at 09:10:26PM +0100, Philip Oakley wrote: > From: "Junio C Hamano" <gitster@pobox.com> > >have anything to do with DCO in any case? > > What's DCO an abbreviation of? Developer's Certificate of Origin. See SubmittingPatches. -Peff ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to create the " [PATCH 0/5]" first email? 2012-09-18 19:47 ` Junio C Hamano 2012-09-18 20:10 ` Philip Oakley @ 2012-09-18 20:16 ` Jeff King 1 sibling, 0 replies; 34+ messages in thread From: Jeff King @ 2012-09-18 20:16 UTC (permalink / raw) To: Junio C Hamano; +Cc: Philip Oakley, Git List On Tue, Sep 18, 2012 at 12:47:36PM -0700, Junio C Hamano wrote: > >> Jeff King <peff@peff.net> writes: > >> > >> > But even without that, I still think format-patch is a reasonable time > >> > to do it. It is the time when I proof-read my commit message and patch > >> > in its final form, and think "do I really want to send this?". > >> > >> But it is not like "I cannot sign off because I think it is still > >> iffy". > > > > No, that is not the particular reason in my case, but I think I > > explained other reasons why "format-patch -s" is not a wrong workflow. > > Then I didn't read it. What does "do I really want to send this?" > have anything to do with DCO in any case? Because it is an excellent time to think about "am I willing and able to agree to the DCO?" As I said, for me personally working on git.git, that is not generally an issue. But I think it is perfectly reasonable for somebody to work and commit in isolation, and then only decide on the DCO during the sending phase (perhaps because they need to clear it with their company's legal department or some such). In other words, "it is iffy at the time of commit" might be exactly the reason for some people. If you are responding to my "that is not the particular reason in my case", I will paraphrase the reason I gave earlier: I find it annoying and pointless to type "-s" on every commit. We do not have commit.signoff, but we do have format.signoff. -Peff ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to create the " [PATCH 0/5]" first email? 2012-09-17 23:49 ` Junio C Hamano 2012-09-18 0:15 ` Jeff King @ 2012-09-18 20:42 ` Wesley J. Landaker 1 sibling, 0 replies; 34+ messages in thread From: Wesley J. Landaker @ 2012-09-18 20:42 UTC (permalink / raw) To: Junio C Hamano; +Cc: Philip Oakley, Git List On Monday, September 17, 2012 17:49:39 Junio C Hamano wrote: > "Philip Oakley" <philipoakley@iee.org> writes: > > I then applied it (using git am) to a temp branch to see what it > > produced, and could repeat the cycle until the patches looked right. > > That's another obvious and valid way to prepare your series. It all > depends on how comfortable you are to directly edit patches. Some > people fear it. Some don't. Some can do it with their eyes closed ;-). > > > However, when it came to creating the series, with comments, I > > couldn't see a way of having my comments within my local commits, but > > preparing a patch series that would properly include the '---' > > separator. > > An unofficial trick that works is to write the > > --- > > * This is an additional comment > > > yourself when running "git commit". That will be propagated to the > output from format-patch. You will have another "---" in front of > the diffstat, but nobody is hurt by that. One thing I have done is to add the additional comments I want with "git notes", then give the "--notes" option to format-patch or send-email. Unfortunately, this sticks the notes right into the commit message section, because the "--notes" option is actually a diff option, not something format-patch knows about, so you have to make sure to manually move it. But even so, I've found it a a nice way to track comments. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to create the " [PATCH 0/5]" first email? 2012-09-15 17:08 ` Junio C Hamano 2012-09-17 22:55 ` Philip Oakley @ 2012-09-23 12:03 ` Jan Engelhardt 1 sibling, 0 replies; 34+ messages in thread From: Jan Engelhardt @ 2012-09-23 12:03 UTC (permalink / raw) To: Junio C Hamano; +Cc: Philip Oakley, Git List On Saturday 2012-09-15 19:08, Junio C Hamano wrote: > >If you plan to use "git send-email" to send the final results out, >you should consider "git send-email" as your "MUA" in the quoted >paragraph. And that will be very platform independent viewpoint to >see things from. > >"git format-patch -o my-series/ --cover-letter ..." would treat >"my-series/" directory as "MUA's drafts folder" and prepares the >messages you would want to send out, and you can proof-read and edit >the files in there before telling your "MUA" to send them out, with >"git send-email ... my-series/*.patch" or something. One can also send [0/n] with a normal MUA, and then use git send-email --in-reply-to '<messageidof0@yourhost.no>' commitrange It's not like 0/n has to be emitted at the same second 1/n is :) ^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore 2012-09-15 12:01 [PATCH 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy ` (5 preceding siblings ...) 2012-09-15 14:27 ` How to create the " [PATCH 0/5]" first email? Philip Oakley @ 2012-09-16 15:27 ` Nguyễn Thái Ngọc Duy 2012-09-16 15:27 ` [PATCH v2 1/5] Import wildmatch from rsync Nguyễn Thái Ngọc Duy ` (5 more replies) 6 siblings, 6 replies; 34+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-09-16 15:27 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy Changes in v2: - Correct commit sha-1 in the 1/5 to the last GPL-2 rsync commit. State it is pristine import. Move wildmatch.[ch] to top dir. - Add tests to 5/5 (I forgot to test that the integration should run well, in addition to wildmatch() working in general) Note that there might be a regression because wildmatch() does not care about LC_CTYPE when dealing with character classes, while libc's fnmatch might. But it's probably not worth bothering about because we match against path names, which do not necessarily follow LC_TYPE but the file system's encoding. Nguyễn Thái Ngọc Duy (5): Import wildmatch from rsync compat/wildmatch: remove static variable force_lower_case compat/wildmatch: fix case-insensitive matching Integrate wildmatch to git Support "**" in .gitignore and .gitattributes patterns using wildmatch() .gitignore | 1 + Documentation/gitignore.txt | 3 + Makefile | 6 + attr.c | 4 +- dir.c | 5 +- t/t0003-attributes.sh | 17 ++ t/t3001-ls-files-others-exclude.sh | 11 ++ t/t3070-wildmatch.sh | 27 +++ t/t3070-wildmatch/wildtest.txt | 165 ++++++++++++++++ test-wildmatch.c | 228 +++++++++++++++++++++++ wildmatch.c | 373 +++++++++++++++++++++++++++++++++++++ wildmatch.h | 6 + 12 files changed, 844 insertions(+), 2 deletions(-) create mode 100755 t/t3070-wildmatch.sh create mode 100644 t/t3070-wildmatch/wildtest.txt create mode 100644 test-wildmatch.c create mode 100644 wildmatch.c create mode 100644 wildmatch.h -- 1.7.12.403.gce5cf6f.dirty ^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v2 1/5] Import wildmatch from rsync 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 2012-09-16 15:27 ` [PATCH v2 2/5] compat/wildmatch: remove static variable force_lower_case Nguyễn Thái Ngọc Duy ` (4 subsequent siblings) 5 siblings, 0 replies; 34+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-09-16 15:27 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy [-- 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 ^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v2 2/5] compat/wildmatch: remove static variable force_lower_case 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 ` [PATCH v2 1/5] Import wildmatch from rsync Nguyễn Thái Ngọc Duy @ 2012-09-16 15:27 ` 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 ` (3 subsequent siblings) 5 siblings, 0 replies; 34+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-09-16 15:27 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy One place less to worry about thread safety Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- wildmatch.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/wildmatch.c b/wildmatch.c index f3a1731..e824eb2 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -57,11 +57,10 @@ 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) +static int dowild(const uchar *p, const uchar *text, + const uchar*const *a, int force_lower_case) { uchar p_ch; @@ -121,7 +120,7 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a) t_ch = *text; continue; } - if ((matched = dowild(p, text, a)) != FALSE) { + if ((matched = dowild(p, text, a, force_lower_case)) != FALSE) { if (!special || matched != ABORT_TO_STARSTAR) return matched; } else if (!special && t_ch == '/') @@ -291,7 +290,7 @@ int wildmatch(const char *pattern, const char *text) #ifdef WILD_TEST_ITERATIONS wildmatch_iteration_count = 0; #endif - return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE; + return dowild((const uchar*)pattern, (const uchar*)text, nomore, 0) == TRUE; } /* Match the "pattern" against the forced-to-lower-case "text" string. */ @@ -302,9 +301,7 @@ int iwildmatch(const char *pattern, const char *text) #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; + ret = dowild((const uchar*)pattern, (const uchar*)text, nomore, 1) == TRUE; return ret; } @@ -331,7 +328,7 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where) if (!text) return FALSE; - if ((matched = dowild(p, text, a)) != TRUE && where < 0 + if ((matched = dowild(p, text, a, 0)) != TRUE && where < 0 && matched != ABORT_ALL) { while (1) { if (*text == '\0') { @@ -339,7 +336,7 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where) return FALSE; continue; } - if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE + if (*text++ == '/' && (matched = dowild(p, text, a, 0)) != FALSE && matched != ABORT_TO_STARSTAR) break; } -- 1.7.12.403.gce5cf6f.dirty ^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v2 3/5] compat/wildmatch: fix case-insensitive matching 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 ` [PATCH v2 1/5] Import wildmatch from rsync Nguyễn Thái Ngọc Duy 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 ` 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 ` (2 subsequent siblings) 5 siblings, 0 replies; 34+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-09-16 15:27 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy dowild() does case insensitive matching by lower-casing the text. That means lower case letters in patterns imply case-insensitive matching, but upper case means exact matching. We do not want that subtlety. Lower case pattern too so iwildmatch() always does what we expect it to do. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- wildmatch.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wildmatch.c b/wildmatch.c index e824eb2..c7f7f9f 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -81,6 +81,8 @@ static int dowild(const uchar *p, const uchar *text, } if (force_lower_case && ISUPPER(t_ch)) t_ch = tolower(t_ch); + if (force_lower_case && ISUPPER(p_ch)) + p_ch = tolower(p_ch); switch (p_ch) { case '\\': /* Literal match with following character. Note that the test -- 1.7.12.403.gce5cf6f.dirty ^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v2 4/5] Integrate wildmatch to git 2012-09-16 15:27 ` [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy ` (2 preceding siblings ...) 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 ` Nguyễn Thái Ngọc Duy 2012-09-17 5:31 ` 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 5 siblings, 1 reply; 34+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-09-16 15:27 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy This makes wildmatch.c part of libgit.a and builds test-wildmatch Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- .gitignore | 1 + Makefile | 6 ++++++ t/t3070-wildmatch.sh | 27 +++++++++++++++++++++++++++ test-wildmatch.c | 8 +++++++- wildmatch.c | 8 +++++++- 5 files changed, 48 insertions(+), 2 deletions(-) create mode 100755 t/t3070-wildmatch.sh diff --git a/.gitignore b/.gitignore index 68fe464..54b1b3b 100644 --- a/.gitignore +++ b/.gitignore @@ -196,6 +196,7 @@ /test-sigchain /test-subprocess /test-svn-fe +/test-wildmatch /common-cmds.h *.tar.gz *.dsc diff --git a/Makefile b/Makefile index 56301dc..745e88c 100644 --- a/Makefile +++ b/Makefile @@ -511,6 +511,7 @@ TEST_PROGRAMS_NEED_X += test-sha1 TEST_PROGRAMS_NEED_X += test-sigchain TEST_PROGRAMS_NEED_X += test-subprocess TEST_PROGRAMS_NEED_X += test-svn-fe +TEST_PROGRAMS_NEED_X += test-wildmatch TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X)) @@ -683,6 +684,7 @@ LIB_H += userdiff.h LIB_H += utf8.h LIB_H += varint.h LIB_H += walker.h +LIB_H += wildmatch.h LIB_H += wt-status.h LIB_H += xdiff-interface.h LIB_H += xdiff/xdiff.h @@ -814,6 +816,7 @@ LIB_OBJS += utf8.o LIB_OBJS += varint.o LIB_OBJS += version.o LIB_OBJS += walker.o +LIB_OBJS += wildmatch.o LIB_OBJS += wrapper.o LIB_OBJS += write_or_die.o LIB_OBJS += ws.o @@ -2586,6 +2589,9 @@ test-svn-fe$X: vcs-svn/lib.a test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS) +test-wildmatch$X: test-wildmatch.o GIT-LDFLAGS + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) -lpopt + check-sha1:: test-sha1$X ./test-sha1.sh diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh new file mode 100755 index 0000000..7fb63ff --- /dev/null +++ b/t/t3070-wildmatch.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +test_description='wildmatch tests' + +. ./test-lib.sh + +test_wildmatch() { + test_expect_success "wildmatch $*" " + test-wildmatch $* ../t3070-wildmatch/wildtest.txt >actual && + echo 'No wildmatch errors found.' >expected && + test_cmp expected actual + " +} + +test_wildmatch -x1 +test_wildmatch -x1 -e1 +test_wildmatch -x1 -else +test_wildmatch -x2 +test_wildmatch -x2 -ese +test_wildmatch -x3 +test_wildmatch -x3 -e1 +test_wildmatch -x4 +test_wildmatch -x4 -e2e +test_wildmatch -x5 +test_wildmatch -x5 -es + +test_done diff --git a/test-wildmatch.c b/test-wildmatch.c index 88585c2..828188a 100644 --- a/test-wildmatch.c +++ b/test-wildmatch.c @@ -20,7 +20,13 @@ /*#define COMPARE_WITH_FNMATCH*/ #define WILD_TEST_ITERATIONS -#include "lib/wildmatch.c" +#include "wildmatch.c" + +#define MAXPATHLEN 1024 +#ifdef NO_STRLCPY +#include "compat/strlcpy.c" +#define strlcpy gitstrlcpy +#endif #include <popt.h> diff --git a/wildmatch.c b/wildmatch.c index c7f7f9f..625cb0c 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -9,7 +9,13 @@ ** work differently than '*', and to fix the character-class code. */ -#include "rsync.h" +#include <stddef.h> +#include <ctype.h> +#include <string.h> + +#include "wildmatch.h" + +typedef unsigned char uchar; /* What character marks an inverted character class? */ #define NEGATE_CLASS '!' -- 1.7.12.403.gce5cf6f.dirty ^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: [PATCH v2 4/5] Integrate wildmatch to git 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 0 siblings, 1 reply; 34+ messages in thread From: Junio C Hamano @ 2012-09-17 5:31 UTC (permalink / raw) To: Nguyễn Thái Ngọc Duy; +Cc: git Am I missing some includes? test-wildmatch.c:50: error: array type has incomplete element type test-wildmatch.c:52: error: 'POPT_ARG_NONE' undeclared here (not in a function) test-wildmatch.c:53: error: 'POPT_ARG_STRING' undeclared here (not in a function) test-wildmatch.c:54: error: 'POPT_ARG_INT' undeclared here (not in a function) test-wildmatch.c: In function 'main': test-wildmatch.c:122: error: 'poptContext' undeclared (first use in this function) test-wildmatch.c:122: error: (Each undeclared identifier is reported only once test-wildmatch.c:122: error: for each function it appears in.) test-wildmatch.c:122: error: expected ';' before 'pc' cc1: warnings being treated as errors test-wildmatch.c:125: error: implicit declaration of function 'poptGetNextOpt' test-wildmatch.c:125: error: 'pc' undeclared (first use in this function) test-wildmatch.c:128: error: implicit declaration of function 'poptGetOptArg' test-wildmatch.c:139: error: implicit declaration of function 'poptBadOption' test-wildmatch.c:139: error: 'POPT_BADOPTION_NOALIAS' undeclared (first use in this function) test-wildmatch.c:140: error: implicit declaration of function 'poptStrerror' test-wildmatch.c:148: error: implicit declaration of function 'poptGetArgs' ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v2 4/5] Integrate wildmatch to git 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 0 siblings, 2 replies; 34+ messages in thread From: Junio C Hamano @ 2012-09-17 5:54 UTC (permalink / raw) To: Nguyễn Thái Ngọc Duy; +Cc: git Junio C Hamano <gitster@pobox.com> writes: > Am I missing some includes? > > test-wildmatch.c:50: error: array type has incomplete element type > test-wildmatch.c:52: error: 'POPT_ARG_NONE' undeclared here (not in a function) > test-wildmatch.c:53: error: 'POPT_ARG_STRING' undeclared here (not in a function) > test-wildmatch.c:54: error: 'POPT_ARG_INT' undeclared here (not in a function) > test-wildmatch.c: In function 'main': > test-wildmatch.c:122: error: 'poptContext' undeclared (first use in this function) > test-wildmatch.c:122: error: (Each undeclared identifier is reported only once > test-wildmatch.c:122: error: for each function it appears in.) > test-wildmatch.c:122: error: expected ';' before 'pc' > cc1: warnings being treated as errors > test-wildmatch.c:125: error: implicit declaration of function 'poptGetNextOpt' > test-wildmatch.c:125: error: 'pc' undeclared (first use in this function) > test-wildmatch.c:128: error: implicit declaration of function 'poptGetOptArg' > test-wildmatch.c:139: error: implicit declaration of function 'poptBadOption' > test-wildmatch.c:139: error: 'POPT_BADOPTION_NOALIAS' undeclared (first use in this function) > test-wildmatch.c:140: error: implicit declaration of function 'poptStrerror' > test-wildmatch.c:148: error: implicit declaration of function 'poptGetArgs' Yeah, popt.h it is. It is a bit distasteful that we have a build dependency only to build test-* helper on something that we do not even have runtime dependency on. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v2 4/5] Integrate wildmatch to git 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 1 sibling, 0 replies; 34+ messages in thread From: Nguyen Thai Ngoc Duy @ 2012-09-17 5:57 UTC (permalink / raw) To: Junio C Hamano; +Cc: git On Mon, Sep 17, 2012 at 12:54 PM, Junio C Hamano <gitster@pobox.com> wrote: > Yeah, popt.h it is. It is a bit distasteful that we have a build > dependency only to build test-* helper on something that we do not > even have runtime dependency on. Yep. I don't know how popular libpopt is. But if it's undesired, we could rewrite test-wildmatch.c to use parse-options.c instead. We fork from rsync anyway due to license conflict so we don't have to keep it close to upstream any more. -- Duy ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v2 4/5] Integrate wildmatch to git 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 1 sibling, 1 reply; 34+ messages in thread From: Nguyen Thai Ngoc Duy @ 2012-09-17 12:40 UTC (permalink / raw) To: Junio C Hamano; +Cc: git On Sun, Sep 16, 2012 at 10:54:57PM -0700, Junio C Hamano wrote: > Yeah, popt.h it is. It is a bit distasteful that we have a build > dependency only to build test-* helper on something that we do not > even have runtime dependency on. Perhaps this squash-in? It kills libpopt and removes the "#include "wildmatch.c"". Now we really do test what libgit.a carries. -- 8< -- diff --git a/Makefile b/Makefile index 745e88c..093ab9c 100644 --- a/Makefile +++ b/Makefile @@ -2589,9 +2589,6 @@ test-svn-fe$X: vcs-svn/lib.a test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS) -test-wildmatch$X: test-wildmatch.o GIT-LDFLAGS - $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) -lpopt - check-sha1:: test-sha1$X ./test-sha1.sh diff --git a/test-wildmatch.c b/test-wildmatch.c index 828188a..b94921b 100644 --- a/test-wildmatch.c +++ b/test-wildmatch.c @@ -19,17 +19,18 @@ /*#define COMPARE_WITH_FNMATCH*/ -#define WILD_TEST_ITERATIONS -#include "wildmatch.c" +#include "cache.h" +#include "parse-options.h" +#include "wildmatch.h" +#ifndef MAXPATHLEN #define MAXPATHLEN 1024 +#endif #ifdef NO_STRLCPY #include "compat/strlcpy.c" #define strlcpy gitstrlcpy #endif -#include <popt.h> - #ifdef COMPARE_WITH_FNMATCH #include <fnmatch.h> @@ -41,18 +42,16 @@ 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; +char *empties; -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} +static struct option options[] = { + OPT_STRING('e', "empties", &empties, "", ""), + OPT_INTEGER('x', "explode", &explode_mod, ""), + OPT_END(), }; /* match just at the start of string (anchored tests) */ @@ -106,51 +105,33 @@ run_test(int line, bool matches, 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); - } + int line, i, flag[2]; + const char *help[] = { NULL }; + + argc = parse_options(argc, (const char **)argv, "", options, help, 0); + if (argc != 1) + die("redundant options"); + if (empties) { + const char *arg = empties; + empties_mod = atoi(arg); + if (strchr(empties, 's')) + empty_at_start = 1; + if (strchr(arg, 'e')) + empty_at_end = 1; + if (!explode_mod) + explode_mod = 1024; } 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); diff --git a/wildmatch.c b/wildmatch.c index 625cb0c..f153f8a 100644 --- a/wildmatch.c +++ b/wildmatch.c @@ -59,10 +59,6 @@ typedef unsigned char uchar; #define ISUPPER(c) (ISASCII(c) && isupper(c)) #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c)) -#ifdef WILD_TEST_ITERATIONS -int wildmatch_iteration_count; -#endif - /* 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, @@ -70,10 +66,6 @@ static int dowild(const uchar *p, const uchar *text, { 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; @@ -295,9 +287,6 @@ static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count) 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, 0) == TRUE; } @@ -306,9 +295,6 @@ 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 ret = dowild((const uchar*)pattern, (const uchar*)text, nomore, 1) == TRUE; return ret; } @@ -325,10 +311,6 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where) const uchar *text; int matched; -#ifdef WILD_TEST_ITERATIONS - wildmatch_iteration_count = 0; -#endif - if (where > 0) text = trailing_N_elements(&a, where); else -- 8< -- ^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: [PATCH v2 4/5] Integrate wildmatch to git 2012-09-17 12:40 ` Nguyen Thai Ngoc Duy @ 2012-09-17 17:20 ` Junio C Hamano 0 siblings, 0 replies; 34+ messages in thread From: Junio C Hamano @ 2012-09-17 17:20 UTC (permalink / raw) To: Nguyen Thai Ngoc Duy; +Cc: git Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes: > On Sun, Sep 16, 2012 at 10:54:57PM -0700, Junio C Hamano wrote: >> Yeah, popt.h it is. It is a bit distasteful that we have a build >> dependency only to build test-* helper on something that we do not >> even have runtime dependency on. > > Perhaps this squash-in? It kills libpopt and removes the "#include > "wildmatch.c"". Now we really do test what libgit.a carries. Sounds fine to me. Thanks; will take a look. > > -- 8< -- > diff --git a/Makefile b/Makefile > index 745e88c..093ab9c 100644 > --- a/Makefile > +++ b/Makefile > @@ -2589,9 +2589,6 @@ test-svn-fe$X: vcs-svn/lib.a > test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS) > $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS) > > -test-wildmatch$X: test-wildmatch.o GIT-LDFLAGS > - $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) -lpopt > - > check-sha1:: test-sha1$X > ./test-sha1.sh > > diff --git a/test-wildmatch.c b/test-wildmatch.c > index 828188a..b94921b 100644 > --- a/test-wildmatch.c > +++ b/test-wildmatch.c > @@ -19,17 +19,18 @@ > > /*#define COMPARE_WITH_FNMATCH*/ > > -#define WILD_TEST_ITERATIONS > -#include "wildmatch.c" > +#include "cache.h" > +#include "parse-options.h" > +#include "wildmatch.h" > > +#ifndef MAXPATHLEN > #define MAXPATHLEN 1024 > +#endif > #ifdef NO_STRLCPY > #include "compat/strlcpy.c" > #define strlcpy gitstrlcpy > #endif > > -#include <popt.h> > - > #ifdef COMPARE_WITH_FNMATCH > #include <fnmatch.h> > > @@ -41,18 +42,16 @@ 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; > +char *empties; > > -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} > +static struct option options[] = { > + OPT_STRING('e', "empties", &empties, "", ""), > + OPT_INTEGER('x', "explode", &explode_mod, ""), > + OPT_END(), > }; > > /* match just at the start of string (anchored tests) */ > @@ -106,51 +105,33 @@ run_test(int line, bool matches, > 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); > - } > + int line, i, flag[2]; > + const char *help[] = { NULL }; > + > + argc = parse_options(argc, (const char **)argv, "", options, help, 0); > + if (argc != 1) > + die("redundant options"); > + if (empties) { > + const char *arg = empties; > + empties_mod = atoi(arg); > + if (strchr(empties, 's')) > + empty_at_start = 1; > + if (strchr(arg, 'e')) > + empty_at_end = 1; > + if (!explode_mod) > + explode_mod = 1024; > } > > 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); > diff --git a/wildmatch.c b/wildmatch.c > index 625cb0c..f153f8a 100644 > --- a/wildmatch.c > +++ b/wildmatch.c > @@ -59,10 +59,6 @@ typedef unsigned char uchar; > #define ISUPPER(c) (ISASCII(c) && isupper(c)) > #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c)) > > -#ifdef WILD_TEST_ITERATIONS > -int wildmatch_iteration_count; > -#endif > - > /* 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, > @@ -70,10 +66,6 @@ static int dowild(const uchar *p, const uchar *text, > { > 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; > @@ -295,9 +287,6 @@ static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count) > 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, 0) == TRUE; > } > > @@ -306,9 +295,6 @@ 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 > ret = dowild((const uchar*)pattern, (const uchar*)text, nomore, 1) == TRUE; > return ret; > } > @@ -325,10 +311,6 @@ int wildmatch_array(const char *pattern, const char*const *texts, int where) > const uchar *text; > int matched; > > -#ifdef WILD_TEST_ITERATIONS > - wildmatch_iteration_count = 0; > -#endif > - > if (where > 0) > text = trailing_N_elements(&a, where); > else > -- 8< -- ^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v2 5/5] Support "**" in .gitignore and .gitattributes patterns using wildmatch() 2012-09-16 15:27 ` [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy ` (3 preceding siblings ...) 2012-09-16 15:27 ` [PATCH v2 4/5] Integrate wildmatch to git Nguyễn Thái Ngọc Duy @ 2012-09-16 15:27 ` 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 5 siblings, 0 replies; 34+ messages in thread From: Nguyễn Thái Ngọc Duy @ 2012-09-16 15:27 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- Documentation/gitignore.txt | 3 +++ attr.c | 4 +++- dir.c | 5 ++++- t/t0003-attributes.sh | 17 +++++++++++++++++ t/t3001-ls-files-others-exclude.sh | 11 +++++++++++ 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt index c1f692a..eb81d31 100644 --- a/Documentation/gitignore.txt +++ b/Documentation/gitignore.txt @@ -93,6 +93,9 @@ PATTERN FORMAT For example, "Documentation/{asterisk}.html" matches "Documentation/git.html" but not "Documentation/ppc/ppc.html" or "tools/perf/Documentation/perf.html". ++ +Contrary to fnmatch(3), git matches "**" to anything including +slashes, similar to rsync(1). - A leading slash matches the beginning of the pathname. For example, "/{asterisk}.c" matches "cat-file.c" but not diff --git a/attr.c b/attr.c index 3430faf..2cea18c 100644 --- a/attr.c +++ b/attr.c @@ -12,6 +12,7 @@ #include "exec_cmd.h" #include "attr.h" #include "dir.h" +#include "wildmatch.h" const char git_attr__true[] = "(builtin)true"; const char git_attr__false[] = "\0(builtin)false"; @@ -666,7 +667,8 @@ static int path_matches(const char *pathname, int pathlen, return 0; if (baselen != 0) baselen++; - return fnmatch_icase(pattern, pathname + baselen, FNM_PATHNAME) == 0; + return (ignore_case && iwildmatch(pattern, pathname + baselen)) || + (!ignore_case && wildmatch(pattern, pathname + baselen)); } static int macroexpand_one(int attr_nr, int rem); diff --git a/dir.c b/dir.c index 4868339..55ab2b3 100644 --- a/dir.c +++ b/dir.c @@ -8,6 +8,7 @@ #include "cache.h" #include "dir.h" #include "refs.h" +#include "wildmatch.h" struct path_simplify { int len; @@ -575,7 +576,9 @@ int excluded_from_list(const char *pathname, namelen -= prefix; } - if (!namelen || !fnmatch_icase(exclude, name, FNM_PATHNAME)) + if (!namelen || + ((ignore_case && iwildmatch(exclude, name)) || + (!ignore_case && wildmatch(exclude, name)))) return to_exclude; } return -1; /* undecided */ diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh index febc45c..6c3c554 100755 --- a/t/t0003-attributes.sh +++ b/t/t0003-attributes.sh @@ -232,4 +232,21 @@ test_expect_success 'bare repository: test info/attributes' ' attr_check subdir/a/i unspecified ' +test_expect_success '"**" test' ' + cd .. && + echo "**/f foo=bar" >.gitattributes && + cat <<\EOF >expect && +f: foo: unspecified +a/f: foo: bar +a/b/f: foo: bar +a/b/c/f: foo: bar +EOF + git check-attr foo -- "f" >actual 2>err && + git check-attr foo -- "a/f" >>actual 2>>err && + git check-attr foo -- "a/b/f" >>actual 2>>err && + git check-attr foo -- "a/b/c/f" >>actual 2>>err && + test_cmp expect actual && + test_line_count = 0 err +' + test_done diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh index c8fe978..67c8bcf 100755 --- a/t/t3001-ls-files-others-exclude.sh +++ b/t/t3001-ls-files-others-exclude.sh @@ -214,4 +214,15 @@ test_expect_success 'subdirectory ignore (l1)' ' test_cmp expect actual ' + +test_expect_success 'ls-files with "**" patterns' ' + cat <<\EOF >expect && +one/a.1 +one/two/a.1 +three/a.1 +EOF + git ls-files -o -i --exclude "**/a.1" >actual + test_cmp expect actual +' + test_done -- 1.7.12.403.gce5cf6f.dirty ^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 6/5] side-step a make rule that builds t3070-wildmatch 2012-09-16 15:27 ` [PATCH v2 0/5] Support matching "**" in .gitattributes and .gitignore Nguyễn Thái Ngọc Duy ` (4 preceding siblings ...) 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 ` Johannes Sixt 5 siblings, 0 replies; 34+ messages in thread From: Johannes Sixt @ 2012-09-25 7:01 UTC (permalink / raw) To: Nguyễn Thái Ngọc Duy; +Cc: git, Junio C Hamano From: Johannes Sixt <j6t@kdbg.org> Running "make t3070-wildmatch" aborts with this error: $ make t3070-wildmatch *** t3070-wildmatch.sh *** # passed all 11 test(s) 1..11 cat t3070-wildmatch.sh >t3070-wildmatch /bin/sh.exe: t3070-wildmatch: File exists make: *** [t3070-wildmatch] Error 1 It seems that there is a make rule that attempts to transform a *.sh file into a file without the extension. The rule fails because t3070-wildmatch is a directory. Rename it to t3070. Of course, nobody would run exactly "make t3070-wildmatch". But it is common (at least for me) to run "make t[3-9]*" or similar, which would include t3070-wildmatch. With this patch, it includes t3070, but that does not hurt because make just skips it with "Nothing to be done for `t3070'." Signed-off-by: Johannes Sixt <j6t@kdbg.org> --- Feel free to squash this patch if you re-roll the series. t/t3070-wildmatch.sh | 2 +- t/{t3070-wildmatch => t3070}/wildtest.txt | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename t/{t3070-wildmatch => t3070}/wildtest.txt (100%) diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh index 7fb63ff..c4da26c 100755 --- a/t/t3070-wildmatch.sh +++ b/t/t3070-wildmatch.sh @@ -6,7 +6,7 @@ test_description='wildmatch tests' test_wildmatch() { test_expect_success "wildmatch $*" " - test-wildmatch $* ../t3070-wildmatch/wildtest.txt >actual && + test-wildmatch $* ../t3070/wildtest.txt >actual && echo 'No wildmatch errors found.' >expected && test_cmp expected actual " diff --git a/t/t3070-wildmatch/wildtest.txt b/t/t3070/wildtest.txt similarity index 100% rename from t/t3070-wildmatch/wildtest.txt rename to t/t3070/wildtest.txt -- 1.7.12.1.1626.gf25df0e ^ permalink raw reply related [flat|nested] 34+ messages in thread
end of thread, other threads:[~2012-09-25 7:02 UTC | newest] Thread overview: 34+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 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 ` [PATCH v2 1/5] Import wildmatch from rsync Nguyễn Thái Ngọc Duy 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
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).