From 1b3504119da8a4a11cd614cef1e7358acf2ed31e Mon Sep 17 00:00:00 2001 From: Pavel Punegov Date: Fri, 22 May 2020 21:17:03 +0300 Subject: [PATCH] Better unicode support: use UTF-16 for console input, while piped input still uses UTF-8. --- runtime/src/main/cpp/Porting.cpp | 68 +++++++++++++++++++------------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/runtime/src/main/cpp/Porting.cpp b/runtime/src/main/cpp/Porting.cpp index 311271e1784..12633d4dbcc 100644 --- a/runtime/src/main/cpp/Porting.cpp +++ b/runtime/src/main/cpp/Porting.cpp @@ -34,6 +34,7 @@ #include "Common.h" #include "Porting.h" +#include "KAssert.h" #if KONAN_WASM || KONAN_ZEPHYR extern "C" RUNTIME_NORETURN void Konan_abort(const char*); @@ -53,6 +54,8 @@ void consoleInit() { // how we perform console IO, if it turns out, that UTF-16 is better output format. ::SetConsoleCP(CP_UTF8); ::SetConsoleOutputCP(CP_UTF8); + // FIXME: should set original CP back during the deinit of the program. + // Otherwise, this codepage remains in the console. #endif } @@ -60,11 +63,6 @@ void consoleWriteUtf8(const void* utf8, uint32_t sizeBytes) { #ifdef KONAN_ANDROID // TODO: use sizeBytes! __android_log_print(ANDROID_LOG_INFO, "Konan_main", "%s", utf8); -//#elif KONAN_WINDOWS -// // UTF-16 write -// void *con = GetStdHandle(STD_OUTPUT_HANDLE); -// unsigned long num; -// ::WriteConsole(con, utf8, sizeBytes, &num, NULL); #else ::write(STDOUT_FILENO, utf8, sizeBytes); #endif @@ -74,40 +72,54 @@ void consoleErrorUtf8(const void* utf8, uint32_t sizeBytes) { #ifdef KONAN_ANDROID // TODO: use sizeBytes! __android_log_print(ANDROID_LOG_ERROR, "Konan_main", "%s", utf8); -//#elif KONAN_WINDOWS -// // UTF-16 write -// void *con = GetStdHandle(STD_ERROR_HANDLE); -// unsigned long num; -// ::WriteConsole(con, utf8, sizeBytes, &num, NULL); #else ::write(STDERR_FILENO, utf8, sizeBytes); #endif } +#if KONAN_WINDOWS +int getLastErrorMessage(char* message, uint32_t size) { + auto errCode = ::GetLastError(); + if (errCode) { + auto flags = FORMAT_MESSAGE_FROM_SYSTEM; + auto errMsgBufSize = size / 4; + wchar_t errMsgBuffer[errMsgBufSize]; + ::FormatMessageW(flags, NULL, errCode, 0, errMsgBuffer, errMsgBufSize, NULL); + auto errMsgLength = ::WideCharToMultiByte(CP_UTF8, 0, errMsgBuffer, -1, message, size, NULL, NULL); + } + return errCode; +} +#endif + int32_t consoleReadUtf8(void* utf8, uint32_t maxSizeBytes) { #ifdef KONAN_ZEPHYR return 0; #elif KONAN_WINDOWS - void *con = ::GetStdHandle(STD_INPUT_HANDLE); - unsigned long bufferRead; - auto bufferLength = maxSizeBytes / 4; - wchar_t buffer[bufferLength]; - ::ReadConsoleW(con, buffer, bufferLength, &bufferRead, NULL); - auto length = ::WideCharToMultiByte(CP_UTF8, 0, buffer, bufferRead, (char*) utf8, maxSizeBytes, NULL, NULL); - if (!length) { - auto errCode = ::GetLastError(); - if (errCode) { - auto flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER; - wchar_t *errMsgBuffer; - auto errMsgLength = ::FormatMessageW(flags, NULL, errCode, 0, errMsgBuffer, 1024, NULL); - char errMsgUtf8[4096]; - ::WideCharToMultiByte(CP_UTF8, 0, errMsgBuffer, errMsgLength, errMsgUtf8, - sizeof(errMsgUtf8), NULL, NULL); - consoleErrorf("UTF-16 to UTF-8 convertion error %d: %s\n", errCode, errMsgUtf8); - ::LocalFree(errMsgBuffer); + auto length = 0; + void *stdInHandle = ::GetStdHandle(STD_INPUT_HANDLE); + if (::GetFileType(stdInHandle) == FILE_TYPE_CHAR) { + unsigned long bufferRead; + // In UTF-16 there are surrogate pairs that a 2 * 16-bit long (4 bytes). + auto bufferLength = maxSizeBytes / 4 - 1; + wchar_t buffer[bufferLength]; + if (::ReadConsoleW(stdInHandle, buffer, bufferLength, &bufferRead, NULL)) { + length = ::WideCharToMultiByte(CP_UTF8, 0, buffer, bufferRead, (char*) utf8, + maxSizeBytes - 1, NULL, NULL); + if (!length && KonanNeedDebugInfo) { + char msg[512]; + auto errCode = getLastErrorMessage(msg, sizeof(msg)); + char buffer[1024]; + consoleErrorf("UTF-16 to UTF-8 conversion error %d: %s", errCode, msg); + } + ((char*) utf8)[length] = 0; + } else if (KonanNeedDebugInfo) { + char msg[512]; + auto errCode = getLastErrorMessage(msg, sizeof(msg)); + consoleErrorf("Console read failure: %d %s", errCode, msg); } + } else { + length = ::read(STDIN_FILENO, utf8, maxSizeBytes - 1); } - ((char*) utf8)[length] = 0; #else auto length = ::read(STDIN_FILENO, utf8, maxSizeBytes - 1); #endif