wildmatch: properly fold case everywhere

Case folding is not done correctly when matching against the [:upper:] character class and uppercased character ranges (e.g. A-Z). Specifically, an uppercase letter fails to match against any of them when case folding is requested because plain characters in the pattern and the whole string are preemptively lowercased to handle the base case fast. That optimization is kept and ISLOWER() is used in the [:upper:] case when case folding is requested, while matching against a character range is retried with toupper() if the character was lowercase, as the bounds of the range itself cannot be modified (in a case-insensitive context, [A-_] is not equivalent to [a-_]). Signed-off-by: Anthony Ramine <n.oxyde@gmail.com> Reviewed-by: Duy Nguyen <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Anthony Ramine committed May 30, 2013 at 12:19 UTC b79c0c3755f3694a3c7ecd3cad18bda011283db7
2 files changed +56 -6
t/t3070-wildmatch.sh
+49 -6
@@ -6,20 +6,20 @@ test_description='wildmatch tests'
6
7 match() {
8 if [ $1 = 1 ]; then
9 - test_expect_success "wildmatch: match '$3' '$4'" "
9 + test_expect_success "wildmatch: match '$3' '$4'" "
10 test-wildmatch wildmatch '$3' '$4'
11 "
12 else
13 - test_expect_success "wildmatch: no match '$3' '$4'" "
13 + test_expect_success "wildmatch: no match '$3' '$4'" "
14 ! test-wildmatch wildmatch '$3' '$4'
15 "
16 fi
17 if [ $2 = 1 ]; then
18 - test_expect_success "fnmatch: match '$3' '$4'" "
18 + test_expect_success "fnmatch: match '$3' '$4'" "
19 test-wildmatch fnmatch '$3' '$4'
20 "
21 elif [ $2 = 0 ]; then
22 - test_expect_success "fnmatch: no match '$3' '$4'" "
22 + test_expect_success "fnmatch: no match '$3' '$4'" "
23 ! test-wildmatch fnmatch '$3' '$4'
24 "
25 # else
@@ -29,13 +29,25 @@ match() {
29 fi
30 }
31
32 +imatch() {
33 + if [ $1 = 1 ]; then
34 + test_expect_success "iwildmatch: match '$2' '$3'" "
35 + test-wildmatch iwildmatch '$2' '$3'
36 + "
37 + else
38 + test_expect_success "iwildmatch: no match '$2' '$3'" "
39 + ! test-wildmatch iwildmatch '$2' '$3'
40 + "
41 + fi
42 +}
43 +
44 pathmatch() {
45 if [ $1 = 1 ]; then
34 - test_expect_success "pathmatch: match '$2' '$3'" "
46 + test_expect_success "pathmatch: match '$2' '$3'" "
47 test-wildmatch pathmatch '$2' '$3'
48 "
49 else
38 - test_expect_success "pathmatch: no match '$2' '$3'" "
50 + test_expect_success "pathmatch: no match '$2' '$3'" "
51 ! test-wildmatch pathmatch '$2' '$3'
52 "
53 fi
@@ -235,4 +247,35 @@ pathmatch 1 abcXdefXghi '*X*i'
247 pathmatch 1 ab/cXd/efXg/hi '*/*X*/*/*i'
248 pathmatch 1 ab/cXd/efXg/hi '*Xg*i'
249
250 +# Case-sensitivy features
251 +match 0 x 'a' '[A-Z]'
252 +match 1 x 'A' '[A-Z]'
253 +match 0 x 'A' '[a-z]'
254 +match 1 x 'a' '[a-z]'
255 +match 0 x 'a' '[[:upper:]]'
256 +match 1 x 'A' '[[:upper:]]'
257 +match 0 x 'A' '[[:lower:]]'
258 +match 1 x 'a' '[[:lower:]]'
259 +match 0 x 'A' '[B-Za]'
260 +match 1 x 'a' '[B-Za]'
261 +match 0 x 'A' '[B-a]'
262 +match 1 x 'a' '[B-a]'
263 +match 0 x 'z' '[Z-y]'
264 +match 1 x 'Z' '[Z-y]'
265 +
266 +imatch 1 'a' '[A-Z]'
267 +imatch 1 'A' '[A-Z]'
268 +imatch 1 'A' '[a-z]'
269 +imatch 1 'a' '[a-z]'
270 +imatch 1 'a' '[[:upper:]]'
271 +imatch 1 'A' '[[:upper:]]'
272 +imatch 1 'A' '[[:lower:]]'
273 +imatch 1 'a' '[[:lower:]]'
274 +imatch 1 'A' '[B-Za]'
275 +imatch 1 'a' '[B-Za]'
276 +imatch 1 'A' '[B-a]'
277 +imatch 1 'a' '[B-a]'
278 +imatch 1 'z' '[Z-y]'
279 +imatch 1 'Z' '[Z-y]'
280 +
281 test_done
wildmatch.c
+7
@@ -196,6 +196,11 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
196 }
197 if (t_ch <= p_ch && t_ch >= prev_ch)
198 matched = 1;
199 + else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch)) {
200 + uchar t_ch_upper = toupper(t_ch);
201 + if (t_ch_upper <= p_ch && t_ch_upper >= prev_ch)
202 + matched = 1;
203 + }
204 p_ch = 0; /* This makes "prev_ch" get set to 0. */
205 } else if (p_ch == '[' && p[1] == ':') {
206 const uchar *s;
@@ -245,6 +250,8 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
250 } else if (CC_EQ(s,i, "upper")) {
251 if (ISUPPER(t_ch))
252 matched = 1;
253 + else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch))
254 + matched = 1;
255 } else if (CC_EQ(s,i, "xdigit")) {
256 if (ISXDIGIT(t_ch))
257 matched = 1;