8000 refdb: vendor and use wildmatch over fnmatch · libgit2/pygit2@9f1ed86 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9f1ed86

Browse files
committed
refdb: vendor and use wildmatch over fnmatch
This is imported from libgit2
1 parent 17c5bf1 commit 9f1ed86

File tree

3 files changed

+345
-2
lines changed

3 files changed

+345
-2
lines changed

src/refdb_backend.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@
2727

2828
#define PY_SSIZE_T_CLEAN
2929
#include <Python.h>
30-
#include <fnmatch.h>
3130
#include "error.h"
3231
#include "types.h"
3332
#include "oid.h"
3433
#include "reference.h"
3534
#include "signature.h"
3635
#include "utils.h"
36+
#include "wildmatch.h"
3737
#include <git2/refdb.h>
3838
#include <git2/sys/refdb_backend.h>
3939

@@ -77,7 +77,7 @@ iterator_get_next(struct pygit2_refdb_iterator *iter)
7777
return ref;
7878
}
7979
const char *name = git_reference_name(ref->reference);
80-
if (fnmatch(iter->glob, name, 0) != FNM_NOMATCH) {
80+
if (wildmatch(iter->glob, name, 0) != WM_NOMATCH) {
8181
return ref;
8282
}
8383
}

src/wildmatch.c

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
/*
2+
* Copyright (C) the libgit2 contributors. All rights reserved.
3+
*
4+
* This file is part of libgit2, distributed under the GNU GPL v2 with
5+
* a Linking Exception. For full terms see the included COPYING file.
6+
*
7+
* Do shell-style pattern matching for ?, \, [], and * characters.
8+
* It is 8bit clean.
9+
*
10+
* Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
11+
* Rich $alz is now <rsalz@bbn.com>.
12+
*
13+
* Modified by Wayne Davison to special-case '/' matching, to make '**'
14+
* work differently than '*', and to fix the character-class code.
15+
*
16+
* Imported from git.git.
17+
*/
18+
19+
#include <ctype.h>
20+
#include <string.h>
21+
#include "wildmatch.h"
22+
23+
#define GIT_SPACE 0x01
24+
#define GIT_DIGIT 0x02
25+
#define GIT_ALPHA 0x04
26+
#define GIT_GLOB_SPECIAL 0x08
27+
#define GIT_REGEX_SPECIAL 0x10
28+
#define GIT_PATHSPEC_MAGIC 0x20
29+
#define GIT_CNTRL 0x40
30+
#define GIT_PUNCT 0x80
31+
32+
enum {
33+
S = GIT_SPACE,
34+
A = GIT_ALPHA,
35+
D = GIT_DIGIT,
36+
G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */
37+
R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | */
38+
P = GIT_PATHSPEC_MAGIC, /* other non-alnum, except for ] and } */
39+
X = GIT_CNTRL,
40+
U = GIT_PUNCT,
41+
Z = GIT_CNTRL | GIT_SPACE
42+
};
43+
44+
static const unsigned char sane_ctype[256] = {
45+
X, X, X, X, X, X, X, X, X, Z, Z, X, X, Z, X, X, /* 0.. 15 */
46+
X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 16.. 31 */
47+
S, P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */
48+
D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G, /* 48.. 63 */
49+
P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */
50+
A, A, A, A, A, A, A, A, A, A, A, G, G, U, R, P, /* 80.. 95 */
51+
P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */
52+
A, A, A, A, A, A, A, A, A, A, A, R, R, U, P, X, /* 112..127 */
53+
/* Nothing in the 128.. range */
54+
};
55+
56+
#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
57+
#define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
58+
59+
typedef unsigned char uchar;
60+
61+
/* What character marks an inverted character class? */
62+
#define NEGATE_CLASS '!'
63+
#define NEGATE_CLASS2 '^'
64+
65+
#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \
66+
&& *(class) == *(litmatch) \
67+
&& strncmp((char*)class, litmatch, len) == 0)
68+
69+
#if defined STDC_HEADERS || !defined isascii
70+
# define ISASCII(c) 1
71+
#else
72+
# define ISASCII(c) isascii(c)
73+
#endif
74+
75+
#ifdef isblank
76+
# define ISBLANK(c) (ISASCII(c) && isblank(c))
77+
#else
78+
# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
79+
#endif
80+
81+
#ifdef isgraph
82+
# define ISGRAPH(c) (ISASCII(c) && isgraph(c))
83+
#else
84+
# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))
85+
#endif
86+
87+
#define ISPRINT(c) (ISASCII(c) && isprint(c))
88+
#define ISDIGIT(c) (ISASCII(c) && isdigit(c))
89+
#define ISALNUM(c) (ISASCII(c) && isalnum(c))
90+
#define ISALPHA(c) (ISASCII(c) && isalpha(c))
91+
#define ISCNTRL(c) (ISASCII(c) && iscntrl(c))
92+
#define ISLOWER(c) (ISASCII(c) && islower(c))
93+
#define ISPUNCT(c) (ISASCII(c) && ispunct(c))
94+
#define ISSPACE(c) (ISASCII(c) && isspace(c))
95+
#define ISUPPER(c) (ISASCII(c) && isupper(c))
96+
#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
97+
98+
/* Match pattern "p" against "text" */
99+
static int dowild(const uchar *p, const uchar *text, unsigned int flags)
100+
{
101+
uchar p_ch;
102+
const uchar *pattern = p;
103+
104+
for ( ; (p_ch = *p) != '\0'; text++, p++) {
105+
int matched, match_slash, negated;
106+
uchar t_ch, prev_ch;
107+
if ((t_ch = *text) == '\0' && p_ch != '*')
108+
return WM_ABORT_ALL;
109+
if ((flags & WM_CASEFOLD) && ISUPPER(t_ch))
110+
t_ch = tolower(t_ch);
111+
if ((flags & WM_CASEFOLD) && ISUPPER(p_ch))
112+
p_ch = tolower(p_ch);
113+
switch (p_ch) {
114+
case '\\':
115+
/* Literal match with following character. Note that the test
116+
* in "default" handles the p[1] == '\0' failure case. */
117+
p_ch = *++p;
118+
/* FALLTHROUGH */
119+
default:
120+
if (t_ch != p_ch)
121+
return WM_NOMATCH;
122+
continue;
123+
case '?':
124+
/* Match anything but '/'. */
125+
if ((flags & WM_PATHNAME) && t_ch == '/')
126+
return WM_NOMATCH;
127+
continue;
128+
case '*':
129+
if (*++p == '*') {
130+
const uchar *prev_p = p - 2;
131+
while (*++p == '*') {}
132+
if (!(flags & WM_PATHNAME))
133+
/* without WM_PATHNAME, '*' == '**' */
134+
match_slash = 1;
135+
else if ((prev_p < pattern || *prev_p == '/') &&
136+
(*p == '\0' || *p == '/' ||
137+
(p[0] == '\\' && p[1] == '/'))) {
138+
/*
139+
* Assuming we already match 'foo/' and are at
140+
* <star star slash>, just assume it matches
141+
* nothing and go ahead match the rest of the
142+
* pattern with the remaining string. This
143+
* helps make foo/<*><*>/bar (<> because
144+
* otherwise it breaks C comment syntax) match
145+
* both foo/bar and foo/a/bar.
146+
*/
147+
if (p[0] == '/' &&
148+
dowild(p + 1, text, flags) == WM_MATCH)
149+
return WM_MATCH;
150+
match_slash = 1;
151+
} else /* WM_PATHNAME is set */
152+
match_slash = 0;
153+
} else
154+
/* without WM_PATHNAME, '*' == '**' */
155+
match_slash = flags & WM_PATHNAME ? 0 : 1;
156+
if (*p == '\0') {
157+
/* Trailing "**" matches everything. Trailing "*" matches
158+
* only if there are no more slash characters. */
159+
if (!match_slash) {
160+
if (strchr((char*)text, '/') != NULL)
161+
return WM_NOMATCH;
162+
}
163+
return WM_MATCH;
164+
} else if (!match_slash && *p == '/') {
165+
/*
166+
* _one_ asterisk followed by a slash
167+
* with WM_PATHNAME matches the next
168+
* directory
169+
*/
170+
const char *slash = strchr((char*)text, '/');
171+
if (!slash)
172+
return WM_NOMATCH;
173+
text = (const uchar*)slash;
174+
/* the slash is consumed by the top-level for loop */
175+
break;
176+
}
177+
while (1) {
178+
if (t_ch == '\0')
179+
break;
180+
/*
181+
* Try to advance faster when an asterisk is
182+
* followed by a literal. We know in this case
183+
* that the string before the literal
184+
* must belong to "*".
185+
* If match_slash is false, do not look past
186+
* the first slash as it cannot belong to '*'.
187+
*/
188+
if (!is_glob_special(*p)) {
189+
p_ch = *p;
190+
if ((flags & WM_CASEFOLD) && ISUPPER(p_ch))
191+
p_ch = tolower(p_ch);
192+
while ((t_ch = *text) != '\0' &&
193+
(match_slash || t_ch != '/')) {
194+
if ((flags & WM_CASEFOLD) && ISUPPER(t_ch))
195+
t_ch = tolower(t_ch);
196+
if (t_ch == p_ch)
197+
break;
198+
text++;
199+
}
200+
if (t_ch != p_ch)
201+
return WM_NOMATCH;
202+
}
203+
if ((matched = dowild(p, text, flags)) != WM_NOMATCH) {
204+
if (!match_slash || matched != WM_ABORT_TO_STARSTAR)
205+
return matched;
206+
} else if (!match_slash && t_ch == '/')
207+
return WM_ABORT_TO_STARSTAR;
208+
t_ch = *++text;
209+
}
210+
return WM_ABORT_ALL;
211+
case '[':
212+
p_ch = *++p;
213+
#ifdef NEGATE_CLASS2
214+
if (p_ch == NEGATE_CLASS2)
215+
p_ch = NEGATE_CLASS;
216+
#endif
217+
/* Assign literal 1/0 because of "matched" comparison. */
218+
negated = p_ch == NEGATE_CLASS ? 1 : 0;
219+
if (negated) {
220+
/* Inverted character class. */
221+
p_ch = *++p;
222+
}
223+
prev_ch = 0;
224+
matched = 0;
225+
do {
226+
if (!p_ch)
227+
return WM_ABORT_ALL;
228+
if (p_ch == '\\') {
229+
p_ch = *++p;
230+
if (!p_ch)
231+
return WM_ABORT_ALL;
232+
if (t_ch == p_ch)
233+
matched = 1;
234+
} else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {
235+
p_ch = *++p;
236+
if (p_ch == '\\') {
237+
p_ch = *++p;
238+
if (!p_ch)
239+
return WM_ABORT_ALL;
240+
}
241+
if (t_ch <= p_ch && t_ch >= prev_ch)
242+
matched = 1;
243+
else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch)) {
244+
uchar t_ch_upper = toupper(t_ch);
245+
if (t_ch_upper <= p_ch && t_ch_upper >= prev_ch)
246+
matched = 1;
247+
}
248+
p_ch = 0; /* This makes "prev_ch" get set to 0. */
249+
} else if (p_ch == '[' && p[1] == ':') {
250+
const uchar *s;
251+
int i;
252+
for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/
253+
if (!p_ch)
254+
return WM_ABORT_ALL;
255+
i = (int)(p - s - 1);
256+
if (i < 0 || p[-1] != ':') {
257+
/* Didn't find ":]", so treat like a normal set. */
258+
p = s - 2;
259+
p_ch = '[';
260+
if (t_ch == p_ch)
261+
matched = 1;
262+
continue;
263+
}
264+
if (CC_EQ(s,i, "alnum")) {
265+
if (ISALNUM(t_ch))
266+
matched = 1;
267+
} else if (CC_EQ(s,i, "alpha")) {
268+
if (ISALPHA(t_ch))
269+
matched = 1;
270+
} else if (CC_EQ(s,i, "blank")) {
271+
if (ISBLANK(t_ch))
272+
matched = 1;
273+
} else if (CC_EQ(s,i, "cntrl")) {
274+
if (ISCNTRL(t_ch))
275+
matched = 1;
276+
} else if (CC_EQ(s,i, "digit")) {
277+
if (ISDIGIT(t_ch))
278+
matched = 1;
279+
} else if (CC_EQ(s,i, "graph")) {
280+
if (ISGRAPH(t_ch))
281+
matched = 1;
282+
} else if (CC_EQ(s,i, "lower")) {
283+
if (ISLOWER(t_ch))
284+
matched = 1;
285+
} else if (CC_EQ(s,i, "print")) {
286+
if (ISPRINT(t_ch))
287+
matched = 1;
288+
} else if (CC_EQ(s,i, "punct")) {
289+
if (ISPUNCT(t_ch))
290+
matched = 1;
291+
} else if (CC_EQ(s,i, "space")) {
292+
if (ISSPACE(t_ch))
293+
matched = 1;
294+
} else if (CC_EQ(s,i, "upper")) {
295+
if (ISUPPER(t_ch))
296+
matched = 1;
297+
else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch))
298+
matched = 1;
299+
} else if (CC_EQ(s,i, "xdigit")) {
300+
if (ISXDIGIT(t_ch))
301+
matched = 1;
302+
} else /* malformed [:class:] string */
303+
return WM_ABORT_ALL;
304+
p_ch = 0; /* This makes "prev_ch" get set to 0. */
305+
} else if (t_ch == p_ch)
306+
matched = 1;
307+
} while (prev_ch = p_ch, (p_ch = *++p) != ']');
308+
if (matched == negated ||
309+
((flags & WM_PATHNAME) && t_ch == '/'))
310+
return WM_NOMATCH;
311+
continue;
312+
}
313+
}
314+
315+
return *text ? WM_NOMATCH : WM_MATCH;
316+
}
317+
318+
/* Match the "pattern" against the "text" string. */
319+
int wildmatch(const char *pattern, const char *text, unsigned int flags)
320+
{
321+
return dowild((const uchar*)pattern, (const uchar*)text, flags);
322+
}

src/wildmatch.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright (C) the libgit2 contributors. All rights reserved.
3+
*
4+
* This file is part of libgit2, distributed under the GNU GPL v2 with
5+
* a Linking Exception. For full terms see the included COPYING file.
6+
*/
7+
8+
#ifndef INCLUDE_wildmatch_h__
9+
#define INCLUDE_wildmatch_h__
10+
11+
#define WM_CASEFOLD 1
12+
#define WM_PATHNAME 2
13+
14+
#define WM_NOMATCH 1
15+
#define WM_MATCH 0
16+
#define WM_ABORT_ALL -1
17+
#define WM_ABORT_TO_STARSTAR -2
18+
19+
int wildmatch(const char *pattern, const char *text, unsigned int flags);
20+
21+
#endif

0 commit comments

Comments
 (0)
0