[PATCH] Use ReadConsoleW and WriteConsoleW on Windows
(1) By anonymous on 2021-02-27 23:51:38 [source]
Index: src/shell.c.in ================================================================== --- src/shell.c.in +++ src/shell.c.in @@ -437,21 +437,24 @@ static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */ /* ** Render output like fprintf(). Except, if the output is going to the ** console and if this is running on a Windows machine, translate the -** output from UTF-8 into MBCS. +** output from UTF-8 into UTF-16. */ #if defined(_WIN32) || defined(WIN32) void utf8_printf(FILE *out, const char *zFormat, ...){ va_list ap; va_start(ap, zFormat); if( stdout_is_console && (out==stdout || out==stderr) ){ char *z1 = sqlite3_vmprintf(zFormat, ap); - char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0); + wchar_t *z2 = sqlite3_win32_utf8_to_unicode(z1); + int outfd = _fileno(out); + HANDLE outHandle = (HANDLE)_get_osfhandle(outfd); sqlite3_free(z1); - fputs(z2, out); + fflush(out); + WriteConsoleW(outHandle, z2, (DWORD)wcslen(z2), 0, 0); sqlite3_free(z2); }else{ vfprintf(out, zFormat, ap); } va_end(ap); @@ -634,10 +637,56 @@ rc = stat(zFile, &x); return rc || !S_ISREG(x.st_mode); } #endif +#if defined(_WIN32) || defined(WIN32) +static char *win32_console_getline(char *zLine, FILE *in){ + wchar_t* zWLine = (wchar_t*)zLine; + DWORD nLine = zWLine==0 ? 0 : 50; + DWORD n = 0; + DWORD nRead; + int infd = _fileno(in); + HANDLE inHandle = (HANDLE)_get_osfhandle(infd); + char *zTrans; + while( 1 ){ + if( n+50>nLine ){ + nLine = nLine*2 + 50; + zLine = realloc(zLine, nLine*2); + if( zLine==0 ) shell_out_of_memory(); + zWLine = (wchar_t*)zLine; + } + if( ReadConsoleW(inHandle, &zWLine[n], nLine - n, &nRead, 0)==0 ){ + if( n==0 ){ + free(zLine); + return 0; + } + zWLine[n] = 0; + break; + } + n += nRead; + if( n>0 && zWLine[n-1]==L'\n' ){ + n--; + if( n>0 && zWLine[n-1]==L'\r' ) n--; + zWLine[n] = 0; + break; + } + } + zTrans = sqlite3_win32_unicode_to_utf8(zWLine); + if( zTrans ){ + DWORD nTrans = (DWORD)strlen30(zTrans)+1; + if( nTrans>nLine*2 ){ + zLine = realloc(zLine, nTrans); + if( zLine==0 ) shell_out_of_memory(); + } + memcpy(zLine, zTrans, nTrans); + sqlite3_free(zTrans); + } + return zLine; +} +#endif /* defined(_WIN32) || defined(WIN32) */ + /* ** This routine reads a line of text from FILE in, stores ** the text in memory obtained from malloc() and returns a pointer ** to the text. NULL is returned at end of file, or if malloc() ** fails. @@ -646,11 +695,15 @@ ** a previous call to this routine that may be reused. */ static char *local_getline(char *zLine, FILE *in){ int nLine = zLine==0 ? 0 : 100; int n = 0; - +#if defined(_WIN32) || defined(WIN32) + if( stdin_is_interactive && in==stdin ){ + return win32_console_getline(zLine, in); + } +#endif /* defined(_WIN32) || defined(WIN32) */ while( 1 ){ if( n+100>nLine ){ nLine = nLine*2 + 100; zLine = realloc(zLine, nLine); if( zLine==0 ) shell_out_of_memory(); @@ -669,26 +722,10 @@ if( n>0 && zLine[n-1]=='\r' ) n--; zLine[n] = 0; break; } } -#if defined(_WIN32) || defined(WIN32) - /* For interactive input on Windows systems, translate the - ** multi-byte characterset characters into UTF-8. */ - if( stdin_is_interactive && in==stdin ){ - char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0); - if( zTrans ){ - int nTrans = strlen30(zTrans)+1; - if( nTrans>nLine ){ - zLine = realloc(zLine, nTrans); - if( zLine==0 ) shell_out_of_memory(); - } - memcpy(zLine, zTrans, nTrans); - sqlite3_free(zTrans); - } - } -#endif /* defined(_WIN32) || defined(WIN32) */ return zLine; } /* ** Retrieve a single line of input text. @@ -710,11 +747,11 @@ if( in!=0 ){ zResult = local_getline(zPrior, in); }else{ zPrompt = isContinuation ? continuePrompt : mainPrompt; #if SHELL_USE_LOCAL_GETLINE - printf("%s", zPrompt); + utf8_printf(stdout, "%s", zPrompt); fflush(stdout); zResult = local_getline(zPrior, stdin); #else free(zPrior); zResult = shell_readline(zPrompt);
(2) By anonymous on 2021-02-28 12:46:35 in reply to 1 [link] [source]
It may be good practice to call SetConsoleMode()
before ReadConsoleW()
to make sure at least ENABLE_LINE_INPUT
(and maybe other desired modes, such as ENABLE_ECHO_INPUT
) are enabled, and restore the previous modes when done.
Also, not sure if there can be "short writes" with WriteConsoleW()
, i.e. maybe it's okay to omit checking lpNumberOfCharsWritten
, instead of repeating in a loop until all of nNumberOfCharsToWrite
have been written. But at least on some versions of Windows, the amount of data that WriteConsoleW()
can process in a chunk is limited, see for example: