Skip to content

Commit 9ecd9f5

Browse files
committed
Merge branch 'nd/retire-fnmatch'
Replace our use of fnmatch(3) with a more feature-rich wildmatch. A handful patches at the bottom have been moved to nd/wildmatch to graduate as part of that branch, before this series solidifies. We may want to mark USE_WILDMATCH as an experimental curiosity a bit more clearly (i.e. should not be enabled in production environment, because it will make the behaviour between builds unpredictable). * nd/retire-fnmatch: Makefile: add USE_WILDMATCH to use wildmatch as fnmatch wildmatch: advance faster in <asterisk> + <literal> patterns wildmatch: make a special case for "*/" with FNM_PATHNAME test-wildmatch: add "perf" command to compare wildmatch and fnmatch wildmatch: support "no FNM_PATHNAME" mode wildmatch: make dowild() take arbitrary flags wildmatch: rename constants and update prototype
2 parents bb9aa10 + cebcab1 commit 9ecd9f5

File tree

7 files changed

+249
-62
lines changed

7 files changed

+249
-62
lines changed

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ all::
105105
# Define NO_FNMATCH_CASEFOLD if your fnmatch function doesn't have the
106106
# FNM_CASEFOLD GNU extension.
107107
#
108+
# Define USE_WILDMATCH if you want to use Git's wildmatch
109+
# implementation as fnmatch
110+
#
108111
# Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd
109112
# in the C library.
110113
#
@@ -1225,6 +1228,9 @@ ifdef NO_FNMATCH_CASEFOLD
12251228
COMPAT_OBJS += compat/fnmatch/fnmatch.o
12261229
endif
12271230
endif
1231+
ifdef USE_WILDMATCH
1232+
COMPAT_CFLAGS += -DUSE_WILDMATCH
1233+
endif
12281234
ifdef NO_SETENV
12291235
COMPAT_CFLAGS += -DNO_SETENV
12301236
COMPAT_OBJS += compat/setenv.o

dir.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,8 @@ int match_pathname(const char *pathname, int pathlen,
685685
}
686686

687687
return wildmatch(pattern, name,
688-
ignore_case ? FNM_CASEFOLD : 0) == 0;
688+
WM_PATHNAME | (ignore_case ? WM_CASEFOLD : 0),
689+
NULL) == 0;
689690
}
690691

691692
/*

git-compat-util.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@
112112
#include <sys/time.h>
113113
#include <time.h>
114114
#include <signal.h>
115+
#ifndef USE_WILDMATCH
115116
#include <fnmatch.h>
117+
#endif
116118
#include <assert.h>
117119
#include <regex.h>
118120
#include <utime.h>
@@ -280,6 +282,17 @@ extern char *gitbasename(char *);
280282

281283
#include "compat/bswap.h"
282284

285+
#ifdef USE_WILDMATCH
286+
#include "wildmatch.h"
287+
#define FNM_PATHNAME WM_PATHNAME
288+
#define FNM_CASEFOLD WM_CASEFOLD
289+
#define FNM_NOMATCH WM_NOMATCH
290+
static inline int fnmatch(const char *pattern, const char *string, int flags)
291+
{
292+
return wildmatch(pattern, string, flags, NULL);
293+
}
294+
#endif
295+
283296
/* General helper functions */
284297
extern void vreportf(const char *prefix, const char *err, va_list params);
285298
extern void vwritef(int fd, const char *prefix, const char *err, va_list params);

t/t3070-wildmatch.sh

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@ match() {
2929
fi
3030
}
3131

32+
pathmatch() {
33+
if [ $1 = 1 ]; then
34+
test_expect_success "pathmatch: match '$2' '$3'" "
35+
test-wildmatch pathmatch '$2' '$3'
36+
"
37+
else
38+
test_expect_success "pathmatch: no match '$2' '$3'" "
39+
! test-wildmatch pathmatch '$2' '$3'
40+
"
41+
fi
42+
}
43+
3244
# Basic wildmat features
3345
match 1 1 foo foo
3446
match 0 0 foo bar
@@ -191,5 +203,36 @@ match 1 1 'XXX/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' 'XXX/*/
191203
match 0 0 'XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*'
192204
match 1 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt' '**/*a*b*g*n*t'
193205
match 0 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz' '**/*a*b*g*n*t'
206+
match 0 x foo '*/*/*'
207+
match 0 x foo/bar '*/*/*'
208+
match 1 x foo/bba/arr '*/*/*'
209+
match 0 x foo/bb/aa/rr '*/*/*'
210+
match 1 x foo/bb/aa/rr '**/**/**'
211+
match 1 x abcXdefXghi '*X*i'
212+
match 0 x ab/cXd/efXg/hi '*X*i'
213+
match 1 x ab/cXd/efXg/hi '*/*X*/*/*i'
214+
match 1 x ab/cXd/efXg/hi '**/*X*/**/*i'
215+
216+
pathmatch 1 foo foo
217+
pathmatch 0 foo fo
218+
pathmatch 1 foo/bar foo/bar
219+
pathmatch 1 foo/bar 'foo/*'
220+
pathmatch 1 foo/bba/arr 'foo/*'
221+
pathmatch 1 foo/bba/arr 'foo/**'
222+
pathmatch 1 foo/bba/arr 'foo*'
223+
pathmatch 1 foo/bba/arr 'foo**'
224+
pathmatch 1 foo/bba/arr 'foo/*arr'
225+
pathmatch 1 foo/bba/arr 'foo/**arr'
226+
pathmatch 0 foo/bba/arr 'foo/*z'
227+
pathmatch 0 foo/bba/arr 'foo/**z'
228+
pathmatch 1 foo/bar 'foo?bar'
229+
pathmatch 1 foo/bar 'foo[/]bar'
230+
pathmatch 0 foo '*/*/*'
231+
pathmatch 0 foo/bar '*/*/*'
232+
pathmatch 1 foo/bba/arr '*/*/*'
233+
pathmatch 1 foo/bb/aa/rr '*/*/*'
234+
pathmatch 1 abcXdefXghi '*X*i'
235+
pathmatch 1 ab/cXd/efXg/hi '*/*X*/*/*i'
236+
pathmatch 1 ab/cXd/efXg/hi '*Xg*i'
194237

195238
test_done

test-wildmatch.c

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,85 @@
1+
#ifdef USE_WILDMATCH
2+
#undef USE_WILDMATCH /* We need real fnmatch implementation here */
3+
#endif
14
#include "cache.h"
25
#include "wildmatch.h"
36

7+
static int perf(int ac, char **av)
8+
{
9+
struct timeval tv1, tv2;
10+
struct stat st;
11+
int fd, i, n, flags1 = 0, flags2 = 0;
12+
char *buffer, *p;
13+
uint32_t usec1, usec2;
14+
const char *lang;
15+
const char *file = av[0];
16+
const char *pattern = av[1];
17+
18+
lang = getenv("LANG");
19+
if (lang && strcmp(lang, "C"))
20+
die("Please test it on C locale.");
21+
22+
if ((fd = open(file, O_RDONLY)) == -1 || fstat(fd, &st))
23+
die_errno("file open");
24+
25+
buffer = xmalloc(st.st_size + 2);
26+
if (read(fd, buffer, st.st_size) != st.st_size)
27+
die_errno("read");
28+
29+
buffer[st.st_size] = '\0';
30+
buffer[st.st_size + 1] = '\0';
31+
for (i = 0; i < st.st_size; i++)
32+
if (buffer[i] == '\n')
33+
buffer[i] = '\0';
34+
35+
n = atoi(av[2]);
36+
if (av[3] && !strcmp(av[3], "pathname")) {
37+
flags1 = WM_PATHNAME;
38+
flags2 = FNM_PATHNAME;
39+
}
40+
41+
gettimeofday(&tv1, NULL);
42+
for (i = 0; i < n; i++) {
43+
for (p = buffer; *p; p += strlen(p) + 1)
44+
wildmatch(pattern, p, flags1, NULL);
45+
}
46+
gettimeofday(&tv2, NULL);
47+
48+
usec1 = (uint32_t)tv2.tv_sec * 1000000 + tv2.tv_usec;
49+
usec1 -= (uint32_t)tv1.tv_sec * 1000000 + tv1.tv_usec;
50+
printf("wildmatch %ds %dus\n",
51+
(int)(usec1 / 1000000),
52+
(int)(usec1 % 1000000));
53+
54+
gettimeofday(&tv1, NULL);
55+
for (i = 0; i < n; i++) {
56+
for (p = buffer; *p; p += strlen(p) + 1)
57+
fnmatch(pattern, p, flags2);
58+
}
59+
gettimeofday(&tv2, NULL);
60+
61+
usec2 = (uint32_t)tv2.tv_sec * 1000000 + tv2.tv_usec;
62+
usec2 -= (uint32_t)tv1.tv_sec * 1000000 + tv1.tv_usec;
63+
if (usec2 > usec1)
64+
printf("fnmatch %ds %dus or %.2f%% slower\n",
65+
(int)((usec2 - usec1) / 1000000),
66+
(int)((usec2 - usec1) % 1000000),
67+
(float)(usec2 - usec1) / usec1 * 100);
68+
else
69+
printf("fnmatch %ds %dus or %.2f%% faster\n",
70+
(int)((usec1 - usec2) / 1000000),
71+
(int)((usec1 - usec2) % 1000000),
72+
(float)(usec1 - usec2) / usec1 * 100);
73+
return 0;
74+
}
75+
476
int main(int argc, char **argv)
577
{
678
int i;
79+
80+
if (!strcmp(argv[1], "perf"))
81+
return perf(argc - 2, argv + 2);
82+
783
for (i = 2; i < argc; i++) {
884
if (argv[i][0] == '/')
985
die("Forward slash is not allowed at the beginning of the\n"
@@ -12,9 +88,11 @@ int main(int argc, char **argv)
1288
argv[i] += 3;
1389
}
1490
if (!strcmp(argv[1], "wildmatch"))
15-
return !!wildmatch(argv[3], argv[2], 0);
91+
return !!wildmatch(argv[3], argv[2], WM_PATHNAME, NULL);
1692
else if (!strcmp(argv[1], "iwildmatch"))
17-
return !!wildmatch(argv[3], argv[2], FNM_CASEFOLD);
93+
return !!wildmatch(argv[3], argv[2], WM_PATHNAME | WM_CASEFOLD, NULL);
94+
else if (!strcmp(argv[1], "pathmatch"))
95+
return !!wildmatch(argv[3], argv[2], 0, NULL);
1896
else if (!strcmp(argv[1], "fnmatch"))
1997
return !!fnmatch(argv[3], argv[2], FNM_PATHNAME);
2098
else

0 commit comments

Comments
 (0)