ui/curses: do not assume wchar_t contains unicode

E.g. BSD and Solaris even use locale-specific encoding there.

We thus have to go through the native multibyte representation and use
mbrtowc/wcrtomb to make a proper conversion.

Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
Tested-by: Kamil Rytarowski <n54@gmx.com>
Message-Id: <20190427183307.12796-2-samuel.thibault@ens-lyon.org>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
Samuel Thibault 2019-04-27 20:33:06 +02:00 committed by Gerd Hoffmann
parent b0f9690e78
commit b7b664a4fe

View file

@ -400,65 +400,108 @@ static void curses_atexit(void)
endwin(); endwin();
} }
/*
* In the following:
* - fch is the font glyph number
* - uch is the unicode value
* - wch is the wchar_t value (may not be unicode, e.g. on BSD/solaris)
* - mbch is the native local-dependent multibyte representation
*/
/* Setup wchar glyph for one UCS-2 char */ /* Setup wchar glyph for one UCS-2 char */
static void convert_ucs(int glyph, uint16_t ch, iconv_t conv) static void convert_ucs(unsigned char fch, uint16_t uch, iconv_t conv)
{ {
char mbch[MB_LEN_MAX];
wchar_t wch; wchar_t wch;
char *pch, *pwch; char *puch, *pmbch;
size_t sch, swch; size_t such, smbch;
mbstate_t ps;
pch = (char *) &ch; puch = (char *) &uch;
pwch = (char *) &wch; pmbch = (char *) mbch;
sch = sizeof(ch); such = sizeof(uch);
swch = sizeof(wch); smbch = sizeof(mbch);
if (iconv(conv, &pch, &sch, &pwch, &swch) == (size_t) -1) { if (iconv(conv, &puch, &such, &pmbch, &smbch) == (size_t) -1) {
fprintf(stderr, "Could not convert 0x%04x from UCS-2 to WCHAR_T: %s\n", fprintf(stderr, "Could not convert 0x%04x "
ch, strerror(errno)); "from UCS-2 to a multibyte character: %s\n",
} else { uch, strerror(errno));
vga_to_curses[glyph].chars[0] = wch; return;
} }
memset(&ps, 0, sizeof(ps));
if (mbrtowc(&wch, mbch, sizeof(mbch) - smbch, &ps) == -1) {
fprintf(stderr, "Could not convert 0x%04x "
"from a multibyte character to wchar_t: %s\n",
uch, strerror(errno));
return;
}
vga_to_curses[fch].chars[0] = wch;
} }
/* Setup wchar glyph for one font character */ /* Setup wchar glyph for one font character */
static void convert_font(unsigned char ch, iconv_t conv) static void convert_font(unsigned char fch, iconv_t conv)
{ {
char mbch[MB_LEN_MAX];
wchar_t wch; wchar_t wch;
char *pch, *pwch; char *pfch, *pmbch;
size_t sch, swch; size_t sfch, smbch;
mbstate_t ps;
pch = (char *) &ch; pfch = (char *) &fch;
pwch = (char *) &wch; pmbch = (char *) &mbch;
sch = sizeof(ch); sfch = sizeof(fch);
swch = sizeof(wch); smbch = sizeof(mbch);
if (iconv(conv, &pch, &sch, &pwch, &swch) == (size_t) -1) { if (iconv(conv, &pfch, &sfch, &pmbch, &smbch) == (size_t) -1) {
fprintf(stderr, "Could not convert 0x%02x from %s to WCHAR_T: %s\n", fprintf(stderr, "Could not convert font glyph 0x%02x "
ch, font_charset, strerror(errno)); "from %s to a multibyte character: %s\n",
} else { fch, font_charset, strerror(errno));
vga_to_curses[ch].chars[0] = wch; return;
} }
memset(&ps, 0, sizeof(ps));
if (mbrtowc(&wch, mbch, sizeof(mbch) - smbch, &ps) == -1) {
fprintf(stderr, "Could not convert font glyph 0x%02x "
"from a multibyte character to wchar_t: %s\n",
fch, strerror(errno));
return;
}
vga_to_curses[fch].chars[0] = wch;
} }
/* Convert one wchar to UCS-2 */ /* Convert one wchar to UCS-2 */
static uint16_t get_ucs(wchar_t wch, iconv_t conv) static uint16_t get_ucs(wchar_t wch, iconv_t conv)
{ {
uint16_t ch; char mbch[MB_LEN_MAX];
char *pch, *pwch; uint16_t uch;
size_t sch, swch; char *pmbch, *puch;
size_t smbch, such;
mbstate_t ps;
int ret;
pch = (char *) &ch; memset(&ps, 0, sizeof(ps));
pwch = (char *) &wch; ret = wcrtomb(mbch, wch, &ps);
sch = sizeof(ch); if (ret == -1) {
swch = sizeof(wch); fprintf(stderr, "Could not convert 0x%04x "
"from wchar_t to a multibyte character: %s\n",
if (iconv(conv, &pwch, &swch, &pch, &sch) == (size_t) -1) { wch, strerror(errno));
fprintf(stderr, "Could not convert 0x%02lx from WCHAR_T to UCS-2: %s\n",
(unsigned long)wch, strerror(errno));
return 0xFFFD; return 0xFFFD;
} }
return ch; pmbch = (char *) mbch;
puch = (char *) &uch;
smbch = ret;
such = sizeof(uch);
if (iconv(conv, &pmbch, &smbch, &puch, &such) == (size_t) -1) {
fprintf(stderr, "Could not convert 0x%04x "
"from a multibyte character to UCS-2 : %s\n",
wch, strerror(errno));
return 0xFFFD;
}
return uch;
} }
/* /*
@ -466,6 +509,11 @@ static uint16_t get_ucs(wchar_t wch, iconv_t conv)
*/ */
static void font_setup(void) static void font_setup(void)
{ {
iconv_t ucs2_to_nativecharset;
iconv_t nativecharset_to_ucs2;
iconv_t font_conv;
int i;
/* /*
* Control characters are normally non-printable, but VGA does have * Control characters are normally non-printable, but VGA does have
* well-known glyphs for them. * well-known glyphs for them.
@ -505,30 +553,25 @@ static void font_setup(void)
0x25bc 0x25bc
}; };
iconv_t ucs_to_wchar_conv; ucs2_to_nativecharset = iconv_open(nl_langinfo(CODESET), "UCS-2");
iconv_t wchar_to_ucs_conv; if (ucs2_to_nativecharset == (iconv_t) -1) {
iconv_t font_conv;
int i;
ucs_to_wchar_conv = iconv_open("WCHAR_T", "UCS-2");
if (ucs_to_wchar_conv == (iconv_t) -1) {
fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n", fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n",
strerror(errno)); strerror(errno));
exit(1); exit(1);
} }
wchar_to_ucs_conv = iconv_open("UCS-2", "WCHAR_T"); nativecharset_to_ucs2 = iconv_open("UCS-2", nl_langinfo(CODESET));
if (wchar_to_ucs_conv == (iconv_t) -1) { if (nativecharset_to_ucs2 == (iconv_t) -1) {
iconv_close(ucs_to_wchar_conv); iconv_close(ucs2_to_nativecharset);
fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n", fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n",
strerror(errno)); strerror(errno));
exit(1); exit(1);
} }
font_conv = iconv_open("WCHAR_T", font_charset); font_conv = iconv_open(nl_langinfo(CODESET), font_charset);
if (font_conv == (iconv_t) -1) { if (font_conv == (iconv_t) -1) {
iconv_close(ucs_to_wchar_conv); iconv_close(ucs2_to_nativecharset);
iconv_close(wchar_to_ucs_conv); iconv_close(nativecharset_to_ucs2);
fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n", fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n",
font_charset, strerror(errno)); font_charset, strerror(errno));
exit(1); exit(1);
@ -536,7 +579,7 @@ static void font_setup(void)
/* Control characters */ /* Control characters */
for (i = 0; i <= 0x1F; i++) { for (i = 0; i <= 0x1F; i++) {
convert_ucs(i, control_characters[i], ucs_to_wchar_conv); convert_ucs(i, control_characters[i], ucs2_to_nativecharset);
} }
for (i = 0x20; i <= 0xFF; i++) { for (i = 0x20; i <= 0xFF; i++) {
@ -544,12 +587,12 @@ static void font_setup(void)
} }
/* DEL */ /* DEL */
convert_ucs(0x7F, 0x2302, ucs_to_wchar_conv); convert_ucs(0x7F, 0x2302, ucs2_to_nativecharset);
if (strcmp(nl_langinfo(CODESET), "UTF-8")) { if (strcmp(nl_langinfo(CODESET), "UTF-8")) {
/* Non-Unicode capable, use termcap equivalents for those available */ /* Non-Unicode capable, use termcap equivalents for those available */
for (i = 0; i <= 0xFF; i++) { for (i = 0; i <= 0xFF; i++) {
switch (get_ucs(vga_to_curses[i].chars[0], wchar_to_ucs_conv)) { switch (get_ucs(vga_to_curses[i].chars[0], nativecharset_to_ucs2)) {
case 0x00a3: case 0x00a3:
vga_to_curses[i] = *WACS_STERLING; vga_to_curses[i] = *WACS_STERLING;
break; break;
@ -649,8 +692,8 @@ static void font_setup(void)
} }
} }
} }
iconv_close(ucs_to_wchar_conv); iconv_close(ucs2_to_nativecharset);
iconv_close(wchar_to_ucs_conv); iconv_close(nativecharset_to_ucs2);
iconv_close(font_conv); iconv_close(font_conv);
} }