wildmatch: properly fold case everywhere
authorAnthony Ramine <n.oxyde@gmail.com>
Thu, 30 May 2013 10:19:10 +0000 (12:19 +0200)
committerJunio C Hamano <gitster@pobox.com>
Sun, 2 Jun 2013 21:13:05 +0000 (14:13 -0700)
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>
t/t3070-wildmatch.sh
wildmatch.c
index 4c37057ddf4a6796c88dba45f0123da1a44fb4af..38446a0e872e90fb008d6d42784a1892bab214e9 100755 (executable)
@@ -6,20 +6,20 @@ test_description='wildmatch tests'
 
 match() {
     if [ $1 = 1 ]; then
-       test_expect_success "wildmatch:    match '$3' '$4'" "
+       test_expect_success "wildmatch:     match '$3' '$4'" "
            test-wildmatch wildmatch '$3' '$4'
        "
     else
-       test_expect_success "wildmatch: no match '$3' '$4'" "
+       test_expect_success "wildmatch:  no match '$3' '$4'" "
            ! test-wildmatch wildmatch '$3' '$4'
        "
     fi
     if [ $2 = 1 ]; then
-       test_expect_success "fnmatch:      match '$3' '$4'" "
+       test_expect_success "fnmatch:       match '$3' '$4'" "
            test-wildmatch fnmatch '$3' '$4'
        "
     elif [ $2 = 0 ]; then
-       test_expect_success "fnmatch:   no match '$3' '$4'" "
+       test_expect_success "fnmatch:    no match '$3' '$4'" "
            ! test-wildmatch fnmatch '$3' '$4'
        "
 #    else
@@ -29,13 +29,25 @@ match() {
     fi
 }
 
+imatch() {
+    if [ $1 = 1 ]; then
+       test_expect_success "iwildmatch:    match '$2' '$3'" "
+           test-wildmatch iwildmatch '$2' '$3'
+       "
+    else
+       test_expect_success "iwildmatch: no match '$2' '$3'" "
+           ! test-wildmatch iwildmatch '$2' '$3'
+       "
+    fi
+}
+
 pathmatch() {
     if [ $1 = 1 ]; then
-       test_expect_success "pathmatch:    match '$2' '$3'" "
+       test_expect_success "pathmatch:     match '$2' '$3'" "
            test-wildmatch pathmatch '$2' '$3'
        "
     else
-       test_expect_success "pathmatch: no match '$2' '$3'" "
+       test_expect_success "pathmatch:  no match '$2' '$3'" "
            ! test-wildmatch pathmatch '$2' '$3'
        "
     fi
@@ -235,4 +247,35 @@ pathmatch 1 abcXdefXghi '*X*i'
 pathmatch 1 ab/cXd/efXg/hi '*/*X*/*/*i'
 pathmatch 1 ab/cXd/efXg/hi '*Xg*i'
 
+# Case-sensitivy features
+match 0 x 'a' '[A-Z]'
+match 1 x 'A' '[A-Z]'
+match 0 x 'A' '[a-z]'
+match 1 x 'a' '[a-z]'
+match 0 x 'a' '[[:upper:]]'
+match 1 x 'A' '[[:upper:]]'
+match 0 x 'A' '[[:lower:]]'
+match 1 x 'a' '[[:lower:]]'
+match 0 x 'A' '[B-Za]'
+match 1 x 'a' '[B-Za]'
+match 0 x 'A' '[B-a]'
+match 1 x 'a' '[B-a]'
+match 0 x 'z' '[Z-y]'
+match 1 x 'Z' '[Z-y]'
+
+imatch 1 'a' '[A-Z]'
+imatch 1 'A' '[A-Z]'
+imatch 1 'A' '[a-z]'
+imatch 1 'a' '[a-z]'
+imatch 1 'a' '[[:upper:]]'
+imatch 1 'A' '[[:upper:]]'
+imatch 1 'A' '[[:lower:]]'
+imatch 1 'a' '[[:lower:]]'
+imatch 1 'A' '[B-Za]'
+imatch 1 'a' '[B-Za]'
+imatch 1 'A' '[B-a]'
+imatch 1 'a' '[B-a]'
+imatch 1 'z' '[Z-y]'
+imatch 1 'Z' '[Z-y]'
+
 test_done
index 7192bdc1b880728a81b33a38091cca8de6a30445..f91ba99f32c047e5f3238668ae83de647ab92df2 100644 (file)
@@ -196,6 +196,11 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
                                        }
                                        if (t_ch <= p_ch && t_ch >= prev_ch)
                                                matched = 1;
+                                       else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch)) {
+                                               uchar t_ch_upper = toupper(t_ch);
+                                               if (t_ch_upper <= p_ch && t_ch_upper >= prev_ch)
+                                                       matched = 1;
+                                       }
                                        p_ch = 0; /* This makes "prev_ch" get set to 0. */
                                } else if (p_ch == '[' && p[1] == ':') {
                                        const uchar *s;
@@ -245,6 +250,8 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
                                        } else if (CC_EQ(s,i, "upper")) {
                                                if (ISUPPER(t_ch))
                                                        matched = 1;
+                                               else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch))
+                                                       matched = 1;
                                        } else if (CC_EQ(s,i, "xdigit")) {
                                                if (ISXDIGIT(t_ch))
                                                        matched = 1;