PATCH: [perl #89750]: Unicode regex negated case-insensitivity
authorKarl Williamson <public@khwilliamson.com>
Tue, 3 May 2011 17:44:28 +0000 (11:44 -0600)
committerJesse Vincent <jesse@bestpractical.com>
Tue, 3 May 2011 21:14:06 +0000 (17:14 -0400)
This patch causes inverted [bracketed] character classes to not handle
multi-character folds.  The reason is that these can lead to very
counter-intuitive results (see bug discussion).

In an inverted character class, only single-char folds are now
generated.  However the fold for \xDF=>ss is hard-coded in,
and it was too much trouble sending flags to the sub-sub routine that
does this, so another check is done at the point of storing the list of
multi-char folds.  Since \xDF doesn't have a single char fold, this
works.

regcomp.c
t/re/fold_grind.t
t/re/re_tests

index 0858841..59397a2 100644 (file)
--- a/regcomp.c
+++ b/regcomp.c
@@ -9552,6 +9552,7 @@ S_regclass(pTHX_ RExC_state_t *pRExC_state, U32 depth)
     IV namedclass;
     char *rangebegin = NULL;
     bool need_class = 0;
+    bool allow_full_fold = TRUE;   /* Assume wants multi-char folding */
     SV *listsv = NULL;
     STRLEN initial_listsv_len = 0; /* Kind of a kludge to see if it is more
                                      than just initialized.  */
@@ -9608,6 +9609,16 @@ S_regclass(pTHX_ RExC_state_t *pRExC_state, U32 depth)
        RExC_parse++;
        if (!SIZE_ONLY)
            ANYOF_FLAGS(ret) |= ANYOF_INVERT;
+
+        /* We have decided to not allow multi-char folds in inverted character
+         * classes, due to the confusion that can happen, even with classes
+         * that are designed for a non-Unicode world:  You have the peculiar
+         * case that:
+            "s s" =~ /^[^\xDF]+$/i => Y
+            "ss"  =~ /^[^\xDF]+$/i => N
+         *
+         * See [perl #89750] */
+        allow_full_fold = FALSE;
     }
 
     if (SIZE_ONLY) {
@@ -10136,7 +10147,8 @@ parseit:
                /* Get its fold */
                U8 foldbuf[UTF8_MAXBYTES_CASE+1];
                STRLEN foldlen;
-               const UV f = to_uni_fold(j, foldbuf, &foldlen);
+               const UV f =
+                    _to_uni_fold_flags(j, foldbuf, &foldlen, allow_full_fold);
 
                if (foldlen > (STRLEN)UNISKIP(f)) {
 
@@ -10437,10 +10449,18 @@ parseit:
         * used later (regexec.c:S_reginclass()). */
        av_store(av, 0, listsv);
        av_store(av, 1, NULL);
+
+        /* Store any computed multi-char folds only if we are allowing
+         * them */
+        if (allow_full_fold) {
        av_store(av, 2, MUTABLE_SV(unicode_alternate));
        if (unicode_alternate) { /* This node is variable length */
            OP(ret) = ANYOFV;
        }
+        }
+        else {
+            av_store(av, 2, NULL);
+        }
        rv = newRV_noinc(MUTABLE_SV(av));
        n = add_data(pRExC_state, 1, "s");
        RExC_rxi->data->data[n] = (void*)rv;
index 82ca6ad..460d296 100644 (file)
@@ -452,6 +452,8 @@ foreach my $test (sort { numerically } keys %tests) {
           foreach my $bracketed (0, 1) {   # Put rhs in [...], or not
             foreach my $inverted (0,1) {
                 next if $inverted && ! $bracketed;  # inversion only valid in [^...]
+                next if $inverted && @target != 1;  # [perl #89750] multi-char
+                                                    # not valid in [^...]
 
               # In some cases, add an extra character that doesn't fold, and
               # looks ok in the output.
index 9d5341b..35a7220 100644 (file)
@@ -1517,4 +1517,9 @@ abc\N{def -       c       -       \\N{NAME} must be resolved by the lexer
 /s/aia S       y       $&      S
 /(?aia:s)/     \x{17F} n       -       -
 /(?aia:s)/     S       y       $&      S
+
+# Normally 1E9E generates a multi-char fold, but not in inverted class;
+# See [perl #89750].  This makes sure that the simple fold gets generated
+# in that case, to DF.
+/[^\x{1E9E}]/i \x{DF}  n       -       -
 # vim: softtabstop=0 noexpandtab