This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
make qr/(?{})/ behave with closures
[perl5.git] / utf8.c
diff --git a/utf8.c b/utf8.c
index 29b30cf..0a6f9ed 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -998,7 +998,7 @@ Perl_utf8_to_uvuni_buf(pTHX_ const U8 *s, const U8 *send, STRLEN *retlen)
 }
 
 /* Like L</utf8_to_uvuni_buf>(), but should only be called when it is known that
- * there are no malformations in the input UTF-8 string C<s>.  surrogates,
+ * there are no malformations in the input UTF-8 string C<s>.  Surrogates,
  * non-character code points, and non-Unicode code points are allowed */
 
 UV
@@ -1761,20 +1761,44 @@ Perl__to_fold_latin1(pTHX_ const U8 c, U8* p, STRLEN *lenp, const bool flags)
 }
 
 UV
-Perl__to_uni_fold_flags(pTHX_ UV c, U8* p, STRLEN *lenp, const bool flags)
+Perl__to_uni_fold_flags(pTHX_ UV c, U8* p, STRLEN *lenp, const U8 flags)
 {
 
-    /* Not currently externally documented, and subject to change, <flags> is
-     * TRUE iff full folding is to be used */
+    /* Not currently externally documented, and subject to change
+     *  <flags> bits meanings:
+     *     FOLD_FLAGS_FULL  iff full folding is to be used;
+     *     FOLD_FLAGS_LOCALE iff in locale
+     *     FOLD_FLAGS_NOMIX_ASCII iff non-ASCII to ASCII folds are prohibited
+     */
 
     PERL_ARGS_ASSERT__TO_UNI_FOLD_FLAGS;
 
     if (c < 256) {
-       return _to_fold_latin1((U8) c, p, lenp, flags);
+       UV result = _to_fold_latin1((U8) c, p, lenp,
+                              cBOOL(((flags & FOLD_FLAGS_FULL)
+                                  /* If ASCII-safe, don't allow full folding,
+                                   * as that could include SHARP S => ss;
+                                   * otherwise there is no crossing of
+                                   * ascii/non-ascii in the latin1 range */
+                                  && ! (flags & FOLD_FLAGS_NOMIX_ASCII))));
+       /* It is illegal for the fold to cross the 255/256 boundary under
+        * locale; in this case return the original */
+       return (result > 256 && flags & FOLD_FLAGS_LOCALE)
+              ? c
+              : result;
+    }
+
+    /* If no special needs, just use the macro */
+    if ( ! (flags & (FOLD_FLAGS_LOCALE|FOLD_FLAGS_NOMIX_ASCII))) {
+       uvchr_to_utf8(p, c);
+       return CALL_FOLD_CASE(p, p, lenp, flags & FOLD_FLAGS_FULL);
+    }
+    else {  /* Otherwise, _to_utf8_fold_flags has the intelligence to deal with
+              the special flags. */
+       U8 utf8_c[UTF8_MAXBYTES + 1];
+       uvchr_to_utf8(utf8_c, c);
+       return _to_utf8_fold_flags(utf8_c, p, lenp, flags, NULL);
     }
-
-    uvchr_to_utf8(p, c);
-    return CALL_FOLD_CASE(p, p, lenp, flags);
 }
 
 /* for now these all assume no locale info available for Unicode > 255; and
@@ -2182,7 +2206,7 @@ Perl_is_utf8_X_prepend(pTHX_ const U8 *p)
 
     PERL_ARGS_ASSERT_IS_UTF8_X_PREPEND;
 
-    return is_utf8_common(p, &PL_utf8_X_prepend, "GCB=Prepend");
+    return is_utf8_common(p, &PL_utf8_X_prepend, "_X_GCB_Prepend");
 }
 
 bool
@@ -2192,7 +2216,7 @@ Perl_is_utf8_X_non_hangul(pTHX_ const U8 *p)
 
     PERL_ARGS_ASSERT_IS_UTF8_X_NON_HANGUL;
 
-    return is_utf8_common(p, &PL_utf8_X_non_hangul, "HST=Not_Applicable");
+    return is_utf8_common(p, &PL_utf8_X_non_hangul, "_X_HST_Not_Applicable");
 }
 
 bool
@@ -2202,7 +2226,7 @@ Perl_is_utf8_X_L(pTHX_ const U8 *p)
 
     PERL_ARGS_ASSERT_IS_UTF8_X_L;
 
-    return is_utf8_common(p, &PL_utf8_X_L, "GCB=L");
+    return is_utf8_common(p, &PL_utf8_X_L, "_X_GCB_L");
 }
 
 bool
@@ -2212,7 +2236,7 @@ Perl_is_utf8_X_LV(pTHX_ const U8 *p)
 
     PERL_ARGS_ASSERT_IS_UTF8_X_LV;
 
-    return is_utf8_common(p, &PL_utf8_X_LV, "GCB=LV");
+    return is_utf8_common(p, &PL_utf8_X_LV, "_X_GCB_LV");
 }
 
 bool
@@ -2222,7 +2246,7 @@ Perl_is_utf8_X_LVT(pTHX_ const U8 *p)
 
     PERL_ARGS_ASSERT_IS_UTF8_X_LVT;
 
-    return is_utf8_common(p, &PL_utf8_X_LVT, "GCB=LVT");
+    return is_utf8_common(p, &PL_utf8_X_LVT, "_X_GCB_LVT");
 }
 
 bool
@@ -2232,7 +2256,7 @@ Perl_is_utf8_X_T(pTHX_ const U8 *p)
 
     PERL_ARGS_ASSERT_IS_UTF8_X_T;
 
-    return is_utf8_common(p, &PL_utf8_X_T, "GCB=T");
+    return is_utf8_common(p, &PL_utf8_X_T, "_X_GCB_T");
 }
 
 bool
@@ -2242,7 +2266,7 @@ Perl_is_utf8_X_V(pTHX_ const U8 *p)
 
     PERL_ARGS_ASSERT_IS_UTF8_X_V;
 
-    return is_utf8_common(p, &PL_utf8_X_V, "GCB=V");
+    return is_utf8_common(p, &PL_utf8_X_V, "_X_GCB_V");
 }
 
 bool
@@ -2386,7 +2410,7 @@ Perl_to_utf8_case(pTHX_ const U8 *p, U8* ustrp, STRLEN *lenp,
     }
 
     if (!len && *swashp) {
-       const UV uv2 = swash_fetch(*swashp, tmpbuf, TRUE);
+       const UV uv2 = swash_fetch(*swashp, tmpbuf, TRUE /* => is utf8 */);
 
         if (uv2) {
              /* It was "normal" (a single character mapping). */
@@ -2395,14 +2419,23 @@ Perl_to_utf8_case(pTHX_ const U8 *p, U8* ustrp, STRLEN *lenp,
         }
     }
 
-    if (!len) /* Neither: just copy.  In other words, there was no mapping
-                defined, which means that the code point maps to itself */
-        len = uvchr_to_utf8(ustrp, uv0) - ustrp;
+    if (len) {
+        if (lenp) {
+            *lenp = len;
+        }
+        return valid_utf8_to_uvchr(ustrp, 0);
+    }
+
+    /* Here, there was no mapping defined, which means that the code point maps
+     * to itself.  Return the inputs */
+    len = UTF8SKIP(p);
+    Copy(p, ustrp, len, U8);
 
     if (lenp)
         *lenp = len;
 
-    return len ? valid_utf8_to_uvchr(ustrp, 0) : 0;
+    return uv0;
+
 }
 
 STATIC UV
@@ -2695,6 +2728,8 @@ The character at C<p> is assumed by this routine to be well-formed.
  *                           POSIX, lowercase is used instead
  *      bit FOLD_FLAGS_FULL   is set iff full case folds are to be used;
  *                           otherwise simple folds
+ *      bit FOLD_FLAGS_NOMIX_ASCII is set iff folds of non-ASCII to ASCII are
+ *                           prohibited
  * <tainted_ptr> if non-null, *tainted_ptr will be set TRUE iff locale rules
  *              were used in the calculation; otherwise unchanged. */
 
@@ -2707,6 +2742,9 @@ Perl__to_utf8_fold_flags(pTHX_ const U8 *p, U8* ustrp, STRLEN *lenp, U8 flags, b
 
     PERL_ARGS_ASSERT__TO_UTF8_FOLD_FLAGS;
 
+    /* These are mutually exclusive */
+    assert (! ((flags & FOLD_FLAGS_LOCALE) && (flags & FOLD_FLAGS_NOMIX_ASCII)));
+
     assert(p != ustrp); /* Otherwise overwrites */
 
     if (UTF8_IS_INVARIANT(*p)) {
@@ -2724,17 +2762,49 @@ Perl__to_utf8_fold_flags(pTHX_ const U8 *p, U8* ustrp, STRLEN *lenp, U8 flags, b
        }
        else {
            return _to_fold_latin1(TWO_BYTE_UTF8_TO_UNI(*p, *(p+1)),
-                                  ustrp, lenp, cBOOL(flags & FOLD_FLAGS_FULL));
+                                  ustrp, lenp,
+                                  cBOOL((flags & FOLD_FLAGS_FULL
+                                      /* If ASCII safe, don't allow full
+                                       * folding, as that could include SHARP
+                                       * S => ss; otherwise there is no
+                                       * crossing of ascii/non-ascii in the
+                                       * latin1 range */
+                                      && ! (flags & FOLD_FLAGS_NOMIX_ASCII))));
        }
     }
     else {  /* utf8, ord above 255 */
-       result = CALL_FOLD_CASE(p, ustrp, lenp, flags);
+       result = CALL_FOLD_CASE(p, ustrp, lenp, flags & FOLD_FLAGS_FULL);
 
        if ((flags & FOLD_FLAGS_LOCALE)) {
-           result = check_locale_boundary_crossing(p, result, ustrp, lenp);
+           return check_locale_boundary_crossing(p, result, ustrp, lenp);
        }
+       else if (! (flags & FOLD_FLAGS_NOMIX_ASCII)) {
+           return result;
+       }
+       else {
+           /* This is called when changing the case of a utf8-encoded
+            * character above the Latin1 range, and the result should not
+            * contain an ASCII character. */
+
+           UV original;    /* To store the first code point of <p> */
+
+           /* Look at every character in the result; if any cross the
+           * boundary, the whole thing is disallowed */
+           U8* s = ustrp;
+           U8* e = ustrp + *lenp;
+           while (s < e) {
+               if (isASCII(*s)) {
+                   /* Crossed, have to return the original */
+                   original = valid_utf8_to_uvchr(p, lenp);
+                   Copy(p, ustrp, *lenp, char);
+                   return original;
+               }
+               s += UTF8SKIP(s);
+           }
 
-       return result;
+           /* Here, no characters crossed, result is ok as-is */
+           return result;
+       }
     }
 
     /* Here, used locale rules.  Convert back to utf8 */
@@ -4496,8 +4566,8 @@ Perl_foldEQ_utf8_flags(pTHX_ const char *s1, char **pe1, register UV l1, bool u1
  * Local variables:
  * c-indentation-style: bsd
  * c-basic-offset: 4
- * indent-tabs-mode: t
+ * indent-tabs-mode: nil
  * End:
  *
- * ex: set ts=8 sts=4 sw=4 noet:
+ * ex: set ts=8 sts=4 sw=4 et:
  */