I had earlier wine outputting utf-8 if output codepage was set to utf-8. Something changed making output garbage. I don't know why it stopped working but only varriable which I currently suspect is a wine upgrade from 5.7 to 5.10. (version inbetween had dll loading issues)
First issue which I noticed was that stdout was unbuffered which made printf and puts output each character with separated WriteFile call. As output included multibyte characters they were converted incorrectly. I decided to try to set stdout to buffered mode because that seemed like easiest way to make wine write complete characters using WriteFile.
I just added a call to setvbuf into wmain. wmain is just wrapper to main which expects utf-8 encoded arguments. wmain also check console output page and disables utf-8 output if not CP_UTF8.
When I tested the buffered output I had mixed results. Characters rendedred correctly in wineconsole cmd. Too bad output was still grabage because same output was written multiple times. After reading wine debug log I figured that issue was around WriteFile call from MSVCRT__write. stdout has WX_TEXT set leading to path which assumes WriteFile sets BytesWritten to actual number of bytes. But kernel32.WriteFile does mb to wc conversion (correctly) but it then writes number of wide characters written.
While I was writing following tests I noticed that kernelbase.WriteFile would return correct number of bytes. kernelbase.WriteFile appears to produce correct results to gnome-terminal. But I don't know if output would look correct in Windows.
Did I find a wine bug? Is it a Windows feature?
I can't test in real windows as I don't have reliable access to a Windows machine.
Tests are demostrations where I think the potential bug might be. Calling setvbuf on stdout later in aplication results to unspecified behavior. This makes dlls/msvcrt/tests/file.c unreliable test. --- dlls/kernel32/tests/file.c | 13 +++++++++++++ dlls/msvcrt/tests/file.c | 15 +++++++++++++++ 2 files changed, 28 insertions(+)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 7001baa173..b4836179c2 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -34,6 +34,7 @@ #include "winternl.h" #include "winnls.h" #include "fileapi.h" +#include "wincon.h"
#undef DeleteFile /* needed for FILE_DISPOSITION_INFO */
@@ -5732,6 +5733,17 @@ static void test_move_file(void) SetCurrentDirectoryA( cwd ); }
+static void test_multibyte_write(void) +{ + const char test[4] = {0xe2,0x99,0xa0,0}; + DWORD written = -1; + HANDLE sout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleOutputCP(CP_UTF8); + ok(WriteFile(sout, test, sizeof(test) - 1, &written, NULL) == TRUE, + "Expected successful write\n"); + ok(written == sizeof(test) - 1, "written = %d expected %d", written, sizeof(test) - 1); +} + START_TEST(file) { char temp_path[MAX_PATH]; @@ -5808,4 +5820,5 @@ START_TEST(file) test_ReOpenFile(); test_hard_link(); test_move_file(); + test_multibyte_write(); } diff --git a/dlls/msvcrt/tests/file.c b/dlls/msvcrt/tests/file.c index 61f6ffe373..3d751298f9 100644 --- a/dlls/msvcrt/tests/file.c +++ b/dlls/msvcrt/tests/file.c @@ -36,6 +36,8 @@ #include <errno.h> #include <locale.h>
+#include <wincon.h> + #define MSVCRT_FD_BLOCK_SIZE 32 typedef struct { HANDLE handle; @@ -2608,6 +2610,18 @@ static void test_lseek(void) DeleteFileA("_creat.tst"); }
+static void test_multibyte_output(void) +{ + const char test[4] = {0xe2,0x99,0xa0,0}; + SetConsoleOutputCP(CP_UTF8); + ok(setvbuf(stdout, NULL, _IOLBF, BUFSIZ) == 0, "expected buffer set\n"); + errno = 0xdeadbeef; + ok(puts(test) >= 0, "Expected successful write\n"); + ok(errno == 0xdeadbeef, "errno = %d\n", errno); + ok(fflush(stdout) == 0, "Expected successful flush\n"); + ok(errno == 0xdeadbeef, "flush -> errno = %d\n", errno); +} + START_TEST(file) { int arg_c; @@ -2680,6 +2694,7 @@ START_TEST(file) test_close(); test__creat(); test_lseek(); + test_multibyte_output();
/* Wait for the (_P_NOWAIT) spawned processes to finish to make sure the report * file contains lines in the correct order