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
This commit is contained in:
Werner Koch 2021-03-05 10:13:21 +01:00
parent 8622f53994
commit f165c8a737
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
1 changed files with 73 additions and 31 deletions

View File

@ -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 );