bidi: do not pass non-ASCII codepoints to ispunct()

rockbox-gerrit-noreply--- via rockbox-cvs <[email protected]> Wed, 1 Jul 2026 21:03:39 -0400
Newsgroups gmane.comp.systems.archos.rockbox.cvs
Message-ID <[email protected]>
commit 5c02cf3b3c1b75e5d0f92097afefcb2beda1186a
Author: elishay <[email protected]>
Date:   Wed Jul 1 17:55:30 2026 +0000

    bidi: do not pass non-ASCII codepoints to ispunct()
    
    bidi_l2v() classified each character with ispunct((int)c), but c is
    a decoded Unicode codepoint. ispunct() is only defined for values
    that fit in an unsigned char (or EOF); a larger value is undefined
    behaviour, and glibc then reads past the ctype table and reports
    some codepoints as punctuation.
    
    Hebrew final kaf (U+05DA) and gimel (U+05D2) land on such entries,
    so a string ending in one had that letter trimmed off its RTL block
    as if it were trailing punctuation and moved to the wrong end of
    the line. Limit the punctuation test to ASCII so the result is
    well-defined.
    
    Change-Id: Ie6c3d8413f35ec3652e9228e3d5af05ef5bb811a

diff --git a/firmware/bidi.c b/firmware/bidi.c
index 558ca8831f..1fbc8f1d70 100644
--- a/firmware/bidi.c
+++ b/firmware/bidi.c
@@ -39,6 +39,12 @@
 /* ischar() now lives in bidi.h as the shared is_rtl_char() macro */
 #define _isblank(c) ((c==' ' || c=='\t') ? 1 : 0)
 #define _isnewline(c) ((c=='\n' || c=='\r') ? 1 : 0)
+/* ctype ispunct() is only defined for unsigned char values; feeding it a
+ * decoded Unicode codepoint (e.g. Hebrew final kaf U+05DA, gimel U+05D2) is
+ * undefined and some libcs then mis-report it as punctuation, which trims the
+ * final letter off its RTL block and throws it to the wrong end of the line.
+ * Restrict the punctuation test to ASCII. */
+#define _ispunct(c) ((c) < 0x80 && ispunct((int)(c)))
 #define XOR(a,b) ((a||b) && !(a&&b))
 
 #ifndef BOOTLOADER
@@ -203,7 +209,7 @@ ucschar_t *bidi_l2v(const unsigned char *str, int orientation)
 
     do {
         while((XOR(is_rtl_char(*(tmp+1)),block_type)
-               || _isblank(*(tmp+1)) || ispunct((int)*(tmp+1))
+               || _isblank(*(tmp+1)) || _ispunct(*(tmp+1))
                || *(tmp+1)=='\n')
               && block_end < length-1) {
                 tmp++;
@@ -212,7 +218,7 @@ ucschar_t *bidi_l2v(const unsigned char *str, int orientation)
         }
 
         if (block_type != orientation) {
-            while ((_isblank(*tmp) || ispunct((int)*tmp))
+            while ((_isblank(*tmp) || _ispunct(*tmp))
                    && *tmp!='/' && *tmp!='-' && block_end>block_start) {
                 tmp--;
                 block_end--;
-- 
rockbox-cvs mailing list
[email protected]
https://lists.haxx.se/mailman/listinfo/rockbox-cvs