Re: curses inch()/mvinch() mangle non-ASCII characters of an 8-bit locale on a wide build

Serhiy Storchaka <[email protected]> Tue, 4 Aug 2026 18:27:48 +0300
Newsgroups gmane.comp.lib.ncurses.bugs
Message-ID <[email protected]>
03.08.26 22:17, Thomas Dickey:
> I could either do
> 	    result = ((chtype) UChar(CharOf(*cell)) | AttrOf(*cell));
>
> or check if CharOf(*cell) is > 255, and return ERR (i.e., the better solution).


Python had to answer the same question, and took neither.  inch() now
reads the cell with win_wch() and builds the chtype itself:

     int byte = 0;
     if (wstr[0] != L'\0' && wstr[1] == L'\0') {
         byte = wctob(wstr[0]);
         if (byte == EOF) {
             byte = 0;
         }
     }
     rtn = (chtype)byte | (attrs & ~(attr_t)A_COLOR) | COLOR_PAIR(pair);

wctob() is X/Open's own condition.  "Fits into a single byte" is about the
representation in the locale encoding, not the code point, and that is
what wctob() tests.  CharOf(*cell) > 255 tests the code point instead, and
is wrong both ways: in ISO-8859-15 U+20AC is one byte, 0xa4, and in UTF-8
U+00E4 is two.  Masking is wrong the same way -- it gives 0xac for U+20AC,
a Latin-1 character that was never on the screen, where wctob() gives 0xa4.

Not failing keeps the rendition reachable.  inch() is the only call in the
chtype interface that returns the attributes and colour pair of a single
cell, and those are representable whatever the character is.

Zero says what happened.  A painted cell never holds character 0, so a
character field of 0 with the attributes intact means "no single-byte form
here" without discarding what does fit, and a caller that needs the
character knows to use win_wch().

This is not a request to change ncurses -- ERR is defensible -- just the
reasoning, in case it is useful.