This is a live mirror of the Perl 5 development currently hosted at
uc(): Handle Greek YPOGEGRAMMENI
authorKarl Williamson <khw@khw-desktop.(none)>
Wed, 2 Jun 2010 21:07:29 +0000 (15:07 -0600)
committerRafael Garcia-Suarez <>
Tue, 15 Jun 2010 10:30:19 +0000 (12:30 +0200)
Unicode contains two context-sensitive case-changing rules.  This patch
enables one of them, dealing with the Greek YPOGEGRAMMENI.  The code had
been #ifdef'd out, so the changes here are more than what the diff
shows.  The reason it was #ifdef'd out was because more research was
needed to verify that it was correct, which I have now done, and think
it is.

The issue is we may just be uppercasing a portion of the context, so
don't have complete knowledge of what should be done.  This patch causes
us to move the ypogegrammeni to as far right as it should go, or to the
end of the context we know about, whichever comes first.  That's the
best we can do.  If it really should be moved further to the right,
there's no way we can do it, because the user has not called uc()
with the full information needed.  So, in all cases, this is better than
just leaving it where it was in the input.  Also, the applicable context
is limited to a logical character, that matched by /\X/, so if the user
is calling uc() on a subset of a logical character, it really is their


diff --git a/pp.c b/pp.c
index 4c98581..57f1ca6 100644 (file)
--- a/pp.c
+++ b/pp.c
@@ -4056,18 +4056,19 @@ PP(pp_uc)
        const U8 *const send = s + len;
        U8 tmpbuf[UTF8_MAXBYTES+1];
-/* This is ifdefd out because it needs more work and thought.  It isn't clear
- * that we should do it.  These are hard-coded rules from the Unicode standard,
- * and may change.  5.2 gives new guidance on the iota subscript, for example,
- * which has not been checked against this; and secondly it may be that we are
- * passed a subset of the context, via a \U...\E, for example, and its not
- * clear what the best approach is to that */
+       /* All occurrences of these are to be moved to follow any other marks.
+        * This is context-dependent.  We may not be passed enough context to
+        * move the iota subscript beyond all of them, but we do the best we can
+        * with what we're given.  The result is always better than if we
+        * hadn't done this.  And, the problem would only arise if we are
+        * passed a character without all its combining marks, which would be
+        * the caller's mistake.  The information this is based on comes from a
+        * comment in Unicode SpecialCasing.txt, (and the Standard's text
+        * itself) and so can't be checked properly to see if it ever gets
+        * revised.  But the likelihood of it changing is remote */
        bool in_iota_subscript = FALSE;
        while (s < send) {
            if (in_iota_subscript && ! is_utf8_mark(s)) {
                /* A non-mark.  Time to output the iota subscript */
@@ -4076,7 +4077,6 @@ PP(pp_uc)
                in_iota_subscript = FALSE;
 /* See comments at the first instance in this file of this ifdef */
@@ -4108,15 +4108,13 @@ PP(pp_uc)
                const STRLEN u = UTF8SKIP(s);
                STRLEN ulen;
-               toUPPER_utf8(s, tmpbuf, &ulen);
                const UV uv = toUPPER_utf8(s, tmpbuf, &ulen);
-               if (uv == GREEK_CAPITAL_LETTER_IOTA && utf8_to_uvchr(s, 0) == COMBINING_GREEK_YPOGEGRAMMENI) {
+               if (uv == GREEK_CAPITAL_LETTER_IOTA
+                   && utf8_to_uvchr(s, 0) == COMBINING_GREEK_YPOGEGRAMMENI)
+               {
                    in_iota_subscript = TRUE;
                else {
                    if (ulen > u && (SvLEN(dest) < (min += ulen - u))) {
                        /* If the eventually required minimum size outgrows
                         * the available space, we need to grow. */
@@ -4125,26 +4123,25 @@ PP(pp_uc)
                        /* If someone uppercases one million U+03B0s we
                         * SvGROW() one million times.  Or we could try
                         * guessing how much to allocate without allocating too
-                        * much.  Such is life.  See corresponding comment in lc code
-                        * for another option */
+                        * much.  Such is life.  See corresponding comment in
+                        * lc code for another option */
                        SvGROW(dest, min);
                        d = (U8*)SvPVX(dest) + o;
                    Copy(tmpbuf, d, ulen, U8);
                    d += ulen;
                s += u;
-       if (in_iota_subscript) CAT_UNI_TO_UTF8_TWO_BYTE(d, GREEK_CAPITAL_LETTER_IOTA);
+       if (in_iota_subscript) {
+       }
        *d = '\0';
        SvCUR_set(dest, d - (U8*)SvPVX_const(dest));
-    } else {   /* Not UTF-8 */
+    }
+    else {     /* Not UTF-8 */
        if (len) {
            const U8 *const send = s + len;
@@ -4345,12 +4342,23 @@ PP(pp_lc)
                const STRLEN u = UTF8SKIP(s);
                STRLEN ulen;
-/* See comments at the first instance in this file of this ifdef */
                toLOWER_utf8(s, tmpbuf, &ulen);
-               /* Here is context dependent casing, not compiled in currently;
-                * needs more thought and work */
+/* This is ifdefd out because it needs more work and thought.  It isn't clear
+ * that we should do it.
+ * A minor objection is that this is based on a hard-coded rule from the
+ *  Unicode standard, and may change, but this is not very likely at all.
+ *  mktables should check and warn if it does.
+ * More importantly, if the sigma occurs at the end of the string, we don't
+ * have enough context to know whether it is part of a larger string or going
+ * to be or not.  It may be that we are passed a subset of the context, via
+ * a \U...\E, for example, and we could conceivably know the larger context if
+ * code were changed to pass that in.  But, if the string passed in is an
+ * intermediate result, and the user concatenates two strings together
+ * after we have made a final sigma, that would be wrong.  If the final sigma
+ * occurs in the middle of the string we are working on, then we know that it
+ * should be a final sigma, but otherwise we can't be sure. */
                const UV uv = toLOWER_utf8(s, tmpbuf, &ulen);
index 4229cfe..b343a1f 100644 (file)
@@ -4,7 +4,9 @@ BEGIN {
     require "";
-casetest( 0,   # extra tests already run
+is(uc("\x{3B1}\x{345}\x{301}"), "\x{391}\x{301}\x{399}", 'Verify moves YPOGEGRAMMENI');
+casetest( 1,   # extra tests already run
        "Upper", \%utf8::ToSpecUpper,
         sub { uc $_[0] },
         sub { my $a = ""; uc ($_[0] . $a) });