diff options
author | Vladislav Vaintroub <wlad@mariadb.com> | 2021-11-10 10:27:17 +0100 |
---|---|---|
committer | Vladislav Vaintroub <wlad@mariadb.com> | 2021-11-18 17:25:40 +0100 |
commit | 012d3cecb861268937fa05d5dfa2940f80fc235b (patch) | |
tree | 444ab9cf63e87729ca1beaddab9b5704e21a5b82 | |
parent | e36a257323805029e4ad78111e4c40d94f04dc99 (diff) | |
download | mariadb-git-012d3cecb861268937fa05d5dfa2940f80fc235b.tar.gz |
MDEV-26713 Windows - improve utf8 support for command line tools
-rw-r--r-- | client/mysql.cc | 126 | ||||
-rw-r--r-- | cmake/win_compatibility.manifest | 5 | ||||
-rw-r--r-- | mysql-test/main/charset_client_win.test | 2 | ||||
-rw-r--r-- | mysql-test/main/charset_client_win_utf8mb4.result | 2 | ||||
-rw-r--r-- | mysql-test/main/charset_client_win_utf8mb4.test | 2 | ||||
-rw-r--r-- | mysql-test/main/grant_utf8_cli.result (renamed from mysql-test/main/grant_not_windows.result) | 0 | ||||
-rw-r--r-- | mysql-test/main/grant_utf8_cli.test (renamed from mysql-test/main/grant_not_windows.test) | 2 | ||||
-rw-r--r-- | mysql-test/suite.pm | 17 | ||||
-rw-r--r-- | mysys/charset.c | 2 | ||||
-rw-r--r-- | mysys/get_password.c | 36 | ||||
-rw-r--r-- | mysys/my_init.c | 33 | ||||
-rw-r--r-- | win/packaging/extra.wxs.in | 2 |
12 files changed, 184 insertions, 45 deletions
diff --git a/client/mysql.cc b/client/mysql.cc index ee963b9220d..c41232d2185 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -88,9 +88,7 @@ extern "C" { #endif /* defined(HAVE_CURSES_H) && defined(HAVE_TERM_H) */ #undef bcmp // Fix problem with new readline -#if defined(_WIN32) -#include <conio.h> -#else +#if !defined(_WIN32) # ifdef __APPLE__ # include <editline/readline.h> # else @@ -104,6 +102,101 @@ extern "C" { #endif } + +#if defined(_WIN32) +/* + Set console mode for the whole duration of the client session. + + We need for input + - line input (i.e read lines from console) + - echo typed characters + - "cooked" mode, i.e we do not want to handle all keystrokes, + like DEL etc ourselves, yet. We might want handle keystrokes + in the future, to implement tab completion, and better + (multiline) history. + + Disable VT escapes for the output.We do not know what kind of escapes SELECT would return. +*/ +struct Console_mode +{ + HANDLE in= GetStdHandle(STD_INPUT_HANDLE); + HANDLE out= GetStdHandle(STD_OUTPUT_HANDLE); + DWORD mode_in=0; + DWORD mode_out=0; + + enum {STDIN_CHANGED = 1, STDOUT_CHANGED = 2}; + int changes=0; + + Console_mode() + { + if (in && in != INVALID_HANDLE_VALUE && GetConsoleMode(in, &mode_in)) + { + SetConsoleMode(in, ENABLE_ECHO_INPUT|ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT); + changes |= STDIN_CHANGED; + } + + if (out && out != INVALID_HANDLE_VALUE && GetConsoleMode(out, &mode_out)) + { +#ifdef ENABLE_VIRTUAL_TERMINAL_INPUT + SetConsoleMode(out, mode_out & ~ENABLE_VIRTUAL_TERMINAL_INPUT); + changes |= STDOUT_CHANGED; +#endif + } + } + + ~Console_mode() + { + if (changes & STDIN_CHANGED) + SetConsoleMode(in, mode_in); + + if(changes & STDOUT_CHANGED) + SetConsoleMode(out, mode_out); + } +}; + +static Console_mode my_conmode; + +#define MAX_CGETS_LINE_LEN 65535 +/** Read line from console in ANSI codepage, chomp EOL*/ +static char *win_readline() +{ + static wchar_t wstrbuf[MAX_CGETS_LINE_LEN]; + static char strbuf[MAX_CGETS_LINE_LEN*2]; + + DWORD nchars= 0; + SetLastError(0); + if (!ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), wstrbuf, MAX_CGETS_LINE_LEN-1, + &nchars, NULL)) + goto err; + if (nchars == 0 && GetLastError() == ERROR_OPERATION_ABORTED) + goto err; + + while (nchars > 0) + { + if (wstrbuf[nchars - 1] != '\n' && wstrbuf[nchars - 1] != '\r') + break; + wstrbuf[--nchars]= 0; + } + + wstrbuf[nchars]= 0; + if (nchars > 0) + { + int len = WideCharToMultiByte(GetConsoleCP(), 0, wstrbuf, nchars + 1, + strbuf, sizeof(strbuf),NULL, NULL); + if (len < 1) + strbuf[0]= 0; + } + else + { + strbuf[0]= 0; + } + return strbuf; +err: + return NULL; +} +#endif + + #ifdef HAVE_VIDATTR static int have_curses= 0; static void my_vidattr(chtype attrs) @@ -1719,8 +1812,10 @@ static void usage(int version) my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE, readline, rl_library_version); #else - printf("%s Ver %s Distrib %s, for %s (%s), source revision %s\n", my_progname, VER, - MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE,SOURCE_REVISION); + printf("%s Ver %s Distrib %s, for %s (%s), source revision %s" + IF_WIN(", codepage %u",) "\n", my_progname, VER, + MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE,SOURCE_REVISION, + IF_WIN(GetConsoleCP(),)); #endif if (version) @@ -2115,26 +2210,7 @@ static int read_and_execute(bool interactive) #if defined(_WIN32) tee_fputs(prompt, stdout); - if (!tmpbuf.is_alloced()) - tmpbuf.alloc(65535); - tmpbuf.length(0); - buffer.length(0); - size_t clen; - do - { - line= my_cgets((char*)tmpbuf.ptr(), tmpbuf.alloced_length()-1, &clen); - buffer.append(line, clen); - /* - if we got buffer fully filled than there is a chance that - something else is still in console input buffer - */ - } while (tmpbuf.alloced_length() <= clen); - /* - An empty line is returned from my_cgets when there's error reading : - Ctrl-c for example - */ - if (line) - line= buffer.c_ptr(); + line= win_readline(); #else if (opt_outfile) fputs(prompt, OUTFILE); diff --git a/cmake/win_compatibility.manifest b/cmake/win_compatibility.manifest index 2e4b27a6dc4..0e7ce667d68 100644 --- a/cmake/win_compatibility.manifest +++ b/cmake/win_compatibility.manifest @@ -19,4 +19,9 @@ </application> </compatibility> + <application> + <windowsSettings> + <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage> + </windowsSettings> + </application> </asmv1:assembly> diff --git a/mysql-test/main/charset_client_win.test b/mysql-test/main/charset_client_win.test index b4a21d4f0a4..dd0162ca23a 100644 --- a/mysql-test/main/charset_client_win.test +++ b/mysql-test/main/charset_client_win.test @@ -1,2 +1,2 @@ --source include/windows.inc ---exec chcp 1257 > NUL && $MYSQL --default-character-set=auto -e "select @@character_set_client" +--exec set MARIADB_DISABLE_UTF8_CONSOLE=1 && chcp 1257 > NUL && $MYSQL --default-character-set=auto -e "select @@character_set_client" diff --git a/mysql-test/main/charset_client_win_utf8mb4.result b/mysql-test/main/charset_client_win_utf8mb4.result new file mode 100644 index 00000000000..f7b5d376f9a --- /dev/null +++ b/mysql-test/main/charset_client_win_utf8mb4.result @@ -0,0 +1,2 @@ +@@character_set_client +utf8mb4 diff --git a/mysql-test/main/charset_client_win_utf8mb4.test b/mysql-test/main/charset_client_win_utf8mb4.test new file mode 100644 index 00000000000..3a4b486632d --- /dev/null +++ b/mysql-test/main/charset_client_win_utf8mb4.test @@ -0,0 +1,2 @@ +--source include/windows.inc +--exec $MYSQL --default-character-set=auto -e "select @@character_set_client" diff --git a/mysql-test/main/grant_not_windows.result b/mysql-test/main/grant_utf8_cli.result index fedfaf984b2..fedfaf984b2 100644 --- a/mysql-test/main/grant_not_windows.result +++ b/mysql-test/main/grant_utf8_cli.result diff --git a/mysql-test/main/grant_not_windows.test b/mysql-test/main/grant_utf8_cli.test index 55b09232edc..97d9fdbff40 100644 --- a/mysql-test/main/grant_not_windows.test +++ b/mysql-test/main/grant_utf8_cli.test @@ -1,5 +1,3 @@ - # UTF8 parameters to mysql client do not work on Windows ---source include/not_windows.inc --source include/not_embedded.inc # diff --git a/mysql-test/suite.pm b/mysql-test/suite.pm index 4cc6b410fa1..8dc76283fc6 100644 --- a/mysql-test/suite.pm +++ b/mysql-test/suite.pm @@ -87,6 +87,23 @@ sub skip_combinations { $skip{'main/ssl_verify_ip.test'} = 'x509v3 support required' unless $openssl_ver ge "1.0.2"; + sub utf8_command_line_ok() { + if (IS_WINDOWS) { + # Can use UTF8 on command line since Windows 10 1903 (10.0.18362) + my($os_name, $os_major, $os_minor, $os_build, $os_id) = Win32::GetOSVersion(); + if($os_major lt 10){ + return 0; + } elsif($os_major gt 10 or $os_minor gt 0 or $os_build ge 18362) { + return 1; + } + return 0; + } + return 1; + } + + $skip{'main/charset_client_win_utf8mb4.test'} = + $skip{'main/grant_utf8_cli.test'} = 'No utf8 command line support' + unless utf8_command_line_ok(); %skip; } diff --git a/mysys/charset.c b/mysys/charset.c index 19cad76fdf4..5d4bc0e10c4 100644 --- a/mysys/charset.c +++ b/mysys/charset.c @@ -1393,7 +1393,7 @@ static const MY_CSET_OS_NAME charsets[] = #ifdef UNCOMMENT_THIS_WHEN_WL_WL_4024_IS_DONE {"cp54936", "gb18030", my_cs_exact}, #endif - {"cp65001", "utf8", my_cs_exact}, + {"cp65001", "utf8mb4", my_cs_exact}, #else /* not Windows */ diff --git a/mysys/get_password.c b/mysys/get_password.c index 24befa6b5df..a30a65c8590 100644 --- a/mysys/get_password.c +++ b/mysys/get_password.c @@ -62,34 +62,40 @@ char *get_tty_password(const char *opt_message) { - char to[80]; - char *pos=to,*end=to+sizeof(to)-1; + wchar_t wbuf[80]; + char to[80*3]; /* there is at most 3 bytes per wchar, in any codepage */ + wchar_t *pos=wbuf,*end=wbuf + array_elements(wbuf)-1; DBUG_ENTER("get_tty_password"); _cputs(opt_message ? opt_message : "Enter password: "); for (;;) { - char tmp; - tmp=_getch(); - if (tmp == '\b' || (int) tmp == 127) + int wc; + wc=_getwch(); + if (wc == '\b' || wc == 127) { - if (pos != to) + if (pos != wbuf) { - _cputs("\b \b"); - pos--; - continue; + _cputs("\b \b"); + pos--; + continue; } } - if (tmp == '\n' || tmp == '\r' || tmp == 3) + if (wc == '\n' || wc == '\r' || wc == 3 || pos == end) break; - if (iscntrl(tmp) || pos == end) + if (iswcntrl(wc)) continue; - _cputs("*"); - *(pos++) = tmp; + + /* Do not print '*' for half-unicode char(high surrogate)*/ + if (wc < 0xD800 || wc > 0xDBFF) + { + _cputs("*"); + } + *(pos++)= (wchar_t)wc; } - while (pos != to && isspace(pos[-1]) == ' ') - pos--; /* Allow dummy space at end */ *pos=0; _cputs("\n"); + if (!WideCharToMultiByte(GetConsoleCP(), 0 , wbuf , -1 , to, sizeof(to), NULL, NULL)) + to[0]=0; DBUG_RETURN(my_strdup(PSI_INSTRUMENT_ME, to,MYF(MY_FAE))); } diff --git a/mysys/my_init.c b/mysys/my_init.c index d201d45a4ee..b52f593af1e 100644 --- a/mysys/my_init.c +++ b/mysys/my_init.c @@ -67,6 +67,38 @@ static ulong atoi_octal(const char *str) MYSQL_FILE *mysql_stdin= NULL; static MYSQL_FILE instrumented_stdin; +#ifdef _WIN32 +UINT orig_console_cp, orig_console_output_cp; + +static void reset_console_cp(void) +{ + SetConsoleCP(orig_console_cp); + SetConsoleOutputCP(orig_console_output_cp); +} + +/* + The below fixes discrepancies in console output and + command line parameter encoding. command line is in + ANSI codepage, output to console by default is in OEM, but + we like them to be in the same encoding. + + We do this only if current codepage is UTF8, i.e when we + know we're on Windows that can handle UTF8 well. + */ +static void set_console_cp() +{ + UINT acp= GetACP(); + if (acp == CP_UTF8 && !getenv("MARIADB_DISABLE_UTF8_CONSOLE")) + { + orig_console_cp= GetConsoleCP(); + SetConsoleCP(acp); + orig_console_output_cp= GetConsoleOutputCP(); + SetConsoleOutputCP(acp); + atexit(reset_console_cp); + } +} +#endif + /** Initialize my_sys functions, resources and variables @@ -131,6 +163,7 @@ my_bool my_init(void) #ifdef _WIN32 if (win32_init_tcp_ip()) DBUG_RETURN(1); + set_console_cp(); #endif #ifdef CHECK_UNLIKELY init_my_likely(); diff --git a/win/packaging/extra.wxs.in b/win/packaging/extra.wxs.in index 11fe60719ec..dd76cc816f5 100644 --- a/win/packaging/extra.wxs.in +++ b/win/packaging/extra.wxs.in @@ -461,7 +461,7 @@ Section="mysqld" Name="my.ini" Key="character-set-server" - Value="utf8" /> + Value="utf8mb4" /> </Component> <!-- Shortcuts in program menu (mysql client etc) --> |