From f165c8a737cc968554c9d78932c69869456108ff Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 5 Mar 2021 10:13:21 +0100 Subject: [PATCH] common,w32: Allow Unicode input and output with the console. * common/ttyio.c (do_get) [W32]: Use ReadConsoleW. (w32_write_console): New. (tty_printf, tty_fprintf) [W32]: Use new function. -- Note that due this change fixed stings (i.e. gettext translations) printed to the console will not be rendered correctly unless "chcp 65001" has been used. This needs to be fixed by followup patch. GnuPG-bug-id: 4365 --- common/ttyio.c | 104 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 31 deletions(-) diff --git a/common/ttyio.c b/common/ttyio.c index b19c3988c..eb6583436 100644 --- a/common/ttyio.c +++ b/common/ttyio.c @@ -221,6 +221,30 @@ tty_no_terminal(int onoff) return old; } + +#ifdef HAVE_W32_SYSTEM +/* Write the UTF-8 encoded STRING to the console. */ +static void +w32_write_console (const char *string) +{ + wchar_t *wstring; + DWORD n, nwritten; + + wstring = utf8_to_wchar (string); + if (!wstring) + log_fatal ("w32_write_console failed: %s", strerror (errno)); + n = wcslen (wstring); + + if (!WriteConsoleW (con.out, wstring, n, &nwritten, NULL)) + log_fatal ("WriteConsole failed: %s", w32_strerror (-1)); + if (n != nwritten) + log_fatal ("WriteConsole failed: %lu != %lu\n", + (unsigned long)n, (unsigned long)nwritten); + last_prompt_len += n; +} +#endif /*HAVE_W32_SYSTEM*/ + + void tty_printf (const char *fmt, ... ) { @@ -237,18 +261,11 @@ tty_printf (const char *fmt, ... ) #ifdef HAVE_W32_SYSTEM { char *buf = NULL; - int n; - DWORD nwritten; - n = vasprintf(&buf, fmt, arg_ptr); + vasprintf(&buf, fmt, arg_ptr); if (!buf) log_bug ("vasprintf() failed\n"); - - if (!WriteConsoleA (con.out, buf, n, &nwritten, NULL)) - log_fatal ("WriteConsole failed: rc=%d", (int)GetLastError()); - if (n != nwritten) - log_fatal ("WriteConsole failed: %d != %d\n", n, (int)nwritten); - last_prompt_len += n; + w32_write_console (buf); xfree (buf); } #else /* Unix */ @@ -285,18 +302,11 @@ tty_fprintf (estream_t fp, const char *fmt, ... ) #ifdef HAVE_W32_SYSTEM { char *buf = NULL; - int n; - DWORD nwritten; - n = vasprintf (&buf, fmt, arg_ptr); + vasprintf (&buf, fmt, arg_ptr); if (!buf) log_bug ("vasprintf() failed\n"); - - if (!WriteConsoleA (con.out, buf, n, &nwritten, NULL)) - log_fatal ("WriteConsole failed: rc=%d", (int)GetLastError()); - if (n != nwritten) - log_fatal ("WriteConsole failed: %d != %d\n", n, (int)nwritten); - last_prompt_len += n; + w32_write_console (buf); xfree (buf); } #else /* Unix */ @@ -410,7 +420,12 @@ do_get (const char *prompt, int hidden) int n; /* Allocated size of BUF. */ int i; /* Number of bytes in BUF. */ int c; +#ifdef HAVE_W32_SYSTEM + char *utf8buf; + int errcount = 0; +#else byte cbuf[1]; +#endif if (batchmode) { @@ -436,31 +451,58 @@ do_get (const char *prompt, int hidden) if (hidden) SetConsoleMode(con.in, HID_INPMODE ); + utf8buf = NULL; for (;;) { DWORD nread; + wchar_t wbuf[2]; + const unsigned char *s; - if (!ReadConsoleA( con.in, cbuf, 1, &nread, NULL)) - log_fatal ("ReadConsole failed: rc=%d", (int)GetLastError ()); + if (!ReadConsoleW (con.in, wbuf, 1, &nread, NULL)) + log_fatal ("ReadConsole failed: %s", w32_strerror (-1)); if (!nread) continue; - if (*cbuf == '\n') - break; + wbuf[1] = 0; + xfree (utf8buf); + utf8buf = wchar_to_utf8 (wbuf); + if (!utf8buf) + { + log_info ("wchar_to_utf8 failed: %s\n", strerror (errno)); + if (++errcount > 10) + log_fatal (_("too many errors; giving up\n")); + continue; + } + if (*utf8buf == '\n') + { + if (utf8buf[1]) + { + log_info ("ReadConsole returned more than requested" + " (0x0a,0x%02x)\n", utf8buf[1]); + if (++errcount > 10) + log_fatal (_("too many errors; giving up\n")); + } + break; + } if (!hidden) last_prompt_len++; - c = *cbuf; - if (c == '\t') - c = ' '; - else if ( (c >= 0 && c <= 0x1f) || c == 0x7f) - continue; - if (!(i < n-1)) + + for (s=utf8buf; *s; s++) { - n += 50; - buf = xrealloc (buf, n); + c = *s; + if (c == '\t') + c = ' '; /* Map tab to a space. */ + else if ((c >= 0 && c <= 0x1f) || c == 0x7f) + continue; /* Remove control characters. */ + if (!(i < n-1)) + { + n += 50; + buf = xrealloc (buf, n); + } + buf[i++] = c; } - buf[i++] = c; } + xfree (utf8buf); if (hidden) SetConsoleMode(con.in, DEF_INPMODE );