I am sorry for replying so late.

On Tue, 9 Aug 2022 16:12:59 +0300 (EEST)
Martin Storsjö wrote:
> On Sun, 7 Aug 2022, Takashi Yano wrote:
> 
> > On Thu, 4 Aug 2022 16:16:28 +0300 (EEST)
> > Martin Storsjö wrote:
> >> Wouldn't it be better to just tweak the code in stdio/mingw_pformat.c,
> >> that whenever we have converted a sequence of wide chars to a multibyte
> >> sequence, try to prepare things differently and output it all with one
> >> call to fwrite() or similar, instead of calling putchar repeatedly?
> >
> > Do you mean the patch like:
> > 0001-crt-Add-workaround-for-printf-on-multibyte-string-ou.patch
> > attached?
> 
> Yes, something like that looks great to me!

Thanks for having a look.

> > Even with this patch, Case 1,2,3,4 in the following test
> > case fail. The problem is not only in printf, but also many
> > of stdio functions. Therefore, patching stdio/mingw_pformat.c
> > is not sufficient. I wonder why the Case 1 and 3 fail even
> > though they write multibyte chars at once...
> >
> > #include <windows.h>
> > #include <stdio.h>
> > #include <locale.h>
> >
> > int main()
> > {
> >     wchar_t *wstr = L"αβγδ\n";
> >     char str[10];
> >     UINT orig_cp = GetConsoleOutputCP();
> >
> >     SetConsoleOutputCP(932);
> >     setlocale(LC_CTYPE, "Japanese");
> >
> >     sprintf(str, "%ls", wstr);
> >
> >     printf("Case 1\n");
> >     fputws(wstr, stdout);
> >
> >     printf("Case 2\n");
> >     for (int i=0; i<strlen(str); i++) fputc(str[i], stdout);
> 
> Well honestly, I think this particular case (intentionally writing 
> multibyte sequences one by one) isn't one we really need to bend over 
> backwards to fix.

I agree that Case 2 is not a practical case, except for the current
mingw_pformat.c code.

> >     printf("Case 3\n");
> >     fwrite(str, strlen(str), 1, stdout);
> 
> Is there any difference if you do
>      fwrite(str, 1, strlen(str), stdout)?

The results are the same. No output.

> That's quite puzzling if things work with fputs but not with fwrite. And 
> it would be easy to reimplement fputs() with fwrite(), but not the other 
> way around...

I tried to do that in v6 patch attached. Please see mingw_fwrite.c.

> >     printf("Case 4\n");
> >     setvbuf(stdout, NULL, _IOFBF, BUFSIZ);
> >     for (int i=0; i<BUFSIZ-1; i++) fputc('A', stdout);
> >     printf("%ls", wstr);
> 
> So this shows that one single fputs() for the whole multibyte sequence 
> isn't enough, if the underlying FILE* buffer happens to get filled halfway 
> through? I guess that's understandable... I guess the mingw_printf.c 
> implementation could flush the buffer before writing multibyte sequences. 
> That's probably not great performance wise though.
> 
> (One might be tempted to try to inspect the buffer size/status before 
> deciding whether to flush or not, but that's hard to do from 
> mingw_printf.c, which is in libmingwex and should be CRT agnostic - 
> because the contents of the FILE* struct differs between msvcrt and UCRT).

The problem of Case 4 occurs only when setvbuf() is used. So,
this is also the rare case and not practical one. Without
setvbuf(), console output is not buffered, so this is not
the case.

> I'm still of the opinion, that while it's a shame that this doesn't work 
> perfectly with msvcrt.dll, it's good if we can make it work better in the 
> most practical cases (e.g. printf) with a reasonable patch, instead of 
> going down the path of the very intrusive patch.
> 
> 
> From the cases listed here, case 1, fputws is indeed a shame if that 
> doesn't work. But is it used commonly in practice? If it is, maybe we 
> could do a thin selfcontained wrapper only in the libmsvcrt-os.a import 
> library, not in libmingwex.a, which converts the string and calls fputs?
> 
> Case 2, repeated fputc, doesn't seem like a realistic usecase that we need 
> to bend over to fix. Case 3, fwrite, also probably isn't used in real 
> world code for this, even if it's a very useful thing to inspect for 
> knowing how to work around the issue.

fwrite() is used by ostream in libstdc++-6.dll. Therefore, ostream
(e.g. std::cout, std::wcout) cannot print out multibyte chars after
setlocale(). So I think fwrite() also needs to be fixed.

> Case 4 is a bit tricky though...

I have built the partially fixed version (v6) attached, where only
printf, *putw* and fwrite are fixed, as well as the fully fixed
version (v7) attached. v7 patch is the modification of v5 where
the workaround code is moved into libmsvcr*.a from libmingwex.a.

With v6 patch, printf and Case 1, 3 works correctly as well as
c++ ostream, while v7 patch works fine in every test case I can
think of.

Could you please take a look?

-- 
Takashi Yano <[email protected]>
From 70af36273bb72196080b6ca966d1be0a2bf2e934 Mon Sep 17 00:00:00 2001
From: Takashi Yano <[email protected]>
Date: Sat, 6 Aug 2022 10:59:26 +0900
Subject: [PATCH v6] crt,headers: Add workaround for multibyte string output
 issue.

This patch adds workaround for multibyte char output issue in
msvcrt.dll. If printf("%ls\n", L"<multibyte string>") is called
after setlocale(LC_CTYPE, ""), the multibyte string is not printed
out correctly. Similary, fputws() and fwrite() to stdout fails to
print multibyte string. This only occurs when the stdout is a
console and setlocale() is called. Redirecting the output to pipe
or file is not affected.

This seems due to the problem of msvcrt.dll. To avoid this problem,
multibyte char is outputted using fputs() in one action at once.
Note that this patch does not fix all cases of the problem on
outputting multibyte chars, but only the cases mentioned above.
---
 mingw-w64-crt/Makefile.am                     | 13 ++-
 .../def-include/msvcrt-common.def.in          |  2 +
 mingw-w64-crt/def-include/msvcrt-mbfix.def.in | 18 ++++
 ...def => api-ms-win-crt-stdio-l1-1-0.def.in} |  2 +
 mingw-w64-crt/lib-common/msvcr120_app.def.in  |  1 +
 mingw-w64-crt/lib-common/msvcrt.def.in        |  2 +
 mingw-w64-crt/lib32/msvcr100.def.in           |  1 +
 mingw-w64-crt/lib32/msvcr110.def.in           |  1 +
 mingw-w64-crt/lib32/msvcr120.def.in           |  1 +
 mingw-w64-crt/lib32/msvcr120d.def.in          |  1 +
 .../lib32/{msvcr70.def => msvcr70.def.in}     |  4 +
 .../lib32/{msvcr71.def => msvcr71.def.in}     |  4 +
 mingw-w64-crt/lib32/msvcr80.def.in            |  1 +
 mingw-w64-crt/lib32/msvcr90.def.in            |  1 +
 mingw-w64-crt/lib32/msvcr90d.def.in           |  1 +
 .../lib32/{msvcrt10.def => msvcrt10.def.in}   |  5 +
 .../lib32/{msvcrt20.def => msvcrt20.def.in}   |  5 +
 .../lib32/{msvcrt40.def => msvcrt40.def.in}   |  5 +
 mingw-w64-crt/lib64/msvcr100.def.in           |  1 +
 mingw-w64-crt/lib64/msvcr110.def.in           |  1 +
 mingw-w64-crt/lib64/msvcr120.def.in           |  1 +
 mingw-w64-crt/lib64/msvcr120d.def.in          |  1 +
 mingw-w64-crt/lib64/msvcr80.def.in            |  1 +
 mingw-w64-crt/lib64/msvcr90.def.in            |  1 +
 mingw-w64-crt/lib64/msvcr90d.def.in           |  1 +
 mingw-w64-crt/stdio/mingw_fwrite.c            | 77 +++++++++++++++
 mingw-w64-crt/stdio/mingw_pformat.c           | 48 +++++++---
 mingw-w64-crt/stdio/mingw_pformat.h           |  3 +-
 mingw-w64-crt/stdio/msvcrt_mbfix.c            | 96 +++++++++++++++++++
 mingw-w64-headers/crt/stdio.h                 | 39 ++++++++
 mingw-w64-headers/crt/wchar.h                 | 32 +++++++
 31 files changed, 353 insertions(+), 17 deletions(-)
 create mode 100644 mingw-w64-crt/def-include/msvcrt-mbfix.def.in
 rename mingw-w64-crt/lib-common/{api-ms-win-crt-stdio-l1-1-0.def => 
api-ms-win-crt-stdio-l1-1-0.def.in} (98%)
 rename mingw-w64-crt/lib32/{msvcr70.def => msvcr70.def.in} (99%)
 rename mingw-w64-crt/lib32/{msvcr71.def => msvcr71.def.in} (99%)
 rename mingw-w64-crt/lib32/{msvcrt10.def => msvcrt10.def.in} (99%)
 rename mingw-w64-crt/lib32/{msvcrt20.def => msvcrt20.def.in} (99%)
 rename mingw-w64-crt/lib32/{msvcrt40.def => msvcrt40.def.in} (99%)
 create mode 100644 mingw-w64-crt/stdio/mingw_fwrite.c
 create mode 100644 mingw-w64-crt/stdio/msvcrt_mbfix.c

diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index 802657117..261f24aca 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -183,6 +183,7 @@ src_msvcrt_common=\
   stdio/acrt_iob_func.c \
   stdio/snprintf_alias.c \
   stdio/vsnprintf_alias.c \
+  stdio/mingw_fwrite.c \
   math/frexp.c
 
 src_msvcrt=\
@@ -234,7 +235,8 @@ src_msvcrt=\
   secapi/wmemcpy_s.c \
   secapi/wmemmove_s.c \
   stdio/fseeki64.c \
-  stdio/mingw_lock.c
+  stdio/mingw_lock.c \
+  stdio/msvcrt_mbfix.c
 
 src_ucrtbase=\
   crt/ucrtbase_compat.c \
@@ -451,7 +453,8 @@ src_msvcrt10=\
   stdio/_scprintf.c \
   stdio/_vscprintf.c \
   stdio/mingw_dummy__lock.c \
-  stdio/mingw_lock.c
+  stdio/mingw_lock.c \
+  stdio/msvcrt_mbfix.c
 
 src_msvcrt20=\
   misc/___mb_cur_max_func.c \
@@ -463,7 +466,8 @@ src_msvcrt20=\
   stdio/_scprintf.c \
   stdio/_vscprintf.c \
   stdio/mingw_dummy__lock.c \
-  stdio/mingw_lock.c
+  stdio/mingw_lock.c \
+  stdio/msvcrt_mbfix.c
 
 src_msvcrt40=\
   misc/___mb_cur_max_func.c \
@@ -473,7 +477,8 @@ src_msvcrt40=\
   stdio/_scprintf.c \
   stdio/_vscprintf.c \
   stdio/mingw_dummy__lock.c \
-  stdio/mingw_lock.c
+  stdio/mingw_lock.c \
+  stdio/msvcrt_mbfix.c
 
 src_msvcr70=\
   misc/invalid_parameter_handler.c \
diff --git a/mingw-w64-crt/def-include/msvcrt-common.def.in 
b/mingw-w64-crt/def-include/msvcrt-common.def.in
index c31c6b631..8ddf6d38f 100644
--- a/mingw-w64-crt/def-include/msvcrt-common.def.in
+++ b/mingw-w64-crt/def-include/msvcrt-common.def.in
@@ -155,3 +155,5 @@ ADD_UNDERSCORE(tzname)
 
 ADD_UNDERSCORE(vsnprintf_s)
 #endif
+
+#include "msvcrt-mbfix.def.in"
diff --git a/mingw-w64-crt/def-include/msvcrt-mbfix.def.in 
b/mingw-w64-crt/def-include/msvcrt-mbfix.def.in
new file mode 100644
index 000000000..b9b5e2cb2
--- /dev/null
+++ b/mingw-w64-crt/def-include/msvcrt-mbfix.def.in
@@ -0,0 +1,18 @@
+#ifdef HAS_BROKEN_FPUTWC
+__ms_fputwchar == _fputwchar
+__ms_putws == _putws
+__ms_fputwc == fputwc
+__ms_fputws == fputws
+__ms_putwc == putwc
+#else
+__mingw_fputwchar == _fputwchar
+__mingw_putws == _putws
+__mingw_fputwc == fputwc
+__mingw_fputws == fputws
+__mingw_putwc == putwc
+#endif
+#ifdef HAS_BROKEN_FWRITE
+__ms_fwrite == fwrite
+#else
+__mingw_fwrite == fwrite
+#endif
diff --git a/mingw-w64-crt/lib-common/api-ms-win-crt-stdio-l1-1-0.def 
b/mingw-w64-crt/lib-common/api-ms-win-crt-stdio-l1-1-0.def.in
similarity index 98%
rename from mingw-w64-crt/lib-common/api-ms-win-crt-stdio-l1-1-0.def
rename to mingw-w64-crt/lib-common/api-ms-win-crt-stdio-l1-1-0.def.in
index d59859ced..99a972298 100644
--- a/mingw-w64-crt/lib-common/api-ms-win-crt-stdio-l1-1-0.def
+++ b/mingw-w64-crt/lib-common/api-ms-win-crt-stdio-l1-1-0.def.in
@@ -2,6 +2,8 @@ LIBRARY api-ms-win-crt-stdio-l1-1-0
 
 EXPORTS
 
+#include "msvcrt-mbfix.def.in"
+
 __acrt_iob_func
 __p__commode
 __p__fmode
diff --git a/mingw-w64-crt/lib-common/msvcr120_app.def.in 
b/mingw-w64-crt/lib-common/msvcr120_app.def.in
index 10cf84d07..6e8cd7359 100644
--- a/mingw-w64-crt/lib-common/msvcr120_app.def.in
+++ b/mingw-w64-crt/lib-common/msvcr120_app.def.in
@@ -8,6 +8,7 @@ EXPORTS
 
 #include "func.def.in"
 #define NO_GETPID_ALIAS
+#define HAS_BROKEN_FWRITE
 #include "msvcrt-common.def.in"
 
 #ifdef DEF_X64
diff --git a/mingw-w64-crt/lib-common/msvcrt.def.in 
b/mingw-w64-crt/lib-common/msvcrt.def.in
index 942c4c4eb..49ea44d20 100644
--- a/mingw-w64-crt/lib-common/msvcrt.def.in
+++ b/mingw-w64-crt/lib-common/msvcrt.def.in
@@ -2,6 +2,8 @@ LIBRARY "msvcrt.dll"
 EXPORTS
 
 #include "func.def.in"
+#define HAS_BROKEN_FPUTWC
+#define HAS_BROKEN_FWRITE
 #include "msvcrt-common.def.in"
 
 #ifdef DEF_I386
diff --git a/mingw-w64-crt/lib32/msvcr100.def.in 
b/mingw-w64-crt/lib32/msvcr100.def.in
index 8e8dfb460..c71bf9439 100644
--- a/mingw-w64-crt/lib32/msvcr100.def.in
+++ b/mingw-w64-crt/lib32/msvcr100.def.in
@@ -6,6 +6,7 @@
 LIBRARY "msvcr100.dll"
 EXPORTS
 
+#define HAS_BROKEN_FWRITE
 #include "msvcrt-common.def.in"
 
 ; public: __thiscall Concurrency::details::<0x1ULL>::<0x1ULL>(void(__cdecl 
*)(void))
diff --git a/mingw-w64-crt/lib32/msvcr110.def.in 
b/mingw-w64-crt/lib32/msvcr110.def.in
index b864009b6..0763c525d 100644
--- a/mingw-w64-crt/lib32/msvcr110.def.in
+++ b/mingw-w64-crt/lib32/msvcr110.def.in
@@ -6,6 +6,7 @@
 LIBRARY "MSVCR110.dll"
 EXPORTS
 
+#define HAS_BROKEN_FWRITE
 #include "msvcrt-common.def.in"
 
 ; public: __thiscall Concurrency::details::<0x1ULL>::<0x1ULL>(void(__cdecl 
*)(void))
diff --git a/mingw-w64-crt/lib32/msvcr120.def.in 
b/mingw-w64-crt/lib32/msvcr120.def.in
index 98567a956..81c5130e6 100644
--- a/mingw-w64-crt/lib32/msvcr120.def.in
+++ b/mingw-w64-crt/lib32/msvcr120.def.in
@@ -6,6 +6,7 @@
 LIBRARY "MSVCR120.dll"
 EXPORTS
 
+#define HAS_BROKEN_FWRITE
 #include "msvcrt-common.def.in"
 
 ; public: __thiscall Concurrency::details::<0x1ULL>::<0x1ULL>(void(__cdecl 
*)(void))
diff --git a/mingw-w64-crt/lib32/msvcr120d.def.in 
b/mingw-w64-crt/lib32/msvcr120d.def.in
index 658e5a314..1bf69f101 100644
--- a/mingw-w64-crt/lib32/msvcr120d.def.in
+++ b/mingw-w64-crt/lib32/msvcr120d.def.in
@@ -6,6 +6,7 @@
 LIBRARY "MSVCR120D.dll"
 EXPORTS
 
+#define HAS_BROKEN_FWRITE
 #include "msvcrt-common.def.in"
 
 ; public: __thiscall Concurrency::details::<0x1ULL>::<0x1ULL>(void(__cdecl 
*)(void))
diff --git a/mingw-w64-crt/lib32/msvcr70.def 
b/mingw-w64-crt/lib32/msvcr70.def.in
similarity index 99%
rename from mingw-w64-crt/lib32/msvcr70.def
rename to mingw-w64-crt/lib32/msvcr70.def.in
index 5ec73e781..a26d6a23f 100644
--- a/mingw-w64-crt/lib32/msvcr70.def
+++ b/mingw-w64-crt/lib32/msvcr70.def.in
@@ -5,6 +5,10 @@
 ;
 LIBRARY "MSVCR70.dll"
 EXPORTS
+
+#define HAS_BROKEN_FWRITE
+#include "msvcrt-mbfix.def.in"
+
 ; public: __thiscall __non_rtti_object::__non_rtti_object(class 
__non_rtti_object const &)
 ??0__non_rtti_object@@QAE@ABV0@@Z ; has WINAPI (@4)
 ; public: __thiscall __non_rtti_object::__non_rtti_object(char const *)
diff --git a/mingw-w64-crt/lib32/msvcr71.def 
b/mingw-w64-crt/lib32/msvcr71.def.in
similarity index 99%
rename from mingw-w64-crt/lib32/msvcr71.def
rename to mingw-w64-crt/lib32/msvcr71.def.in
index 1db6d0b58..2fdc006a3 100644
--- a/mingw-w64-crt/lib32/msvcr71.def
+++ b/mingw-w64-crt/lib32/msvcr71.def.in
@@ -5,6 +5,10 @@
 ;
 LIBRARY "MSVCR71.dll"
 EXPORTS
+
+#define HAS_BROKEN_FWRITE
+#include "msvcrt-mbfix.def.in"
+
 ; public: __thiscall __non_rtti_object::__non_rtti_object(class 
__non_rtti_object const &)
 ??0__non_rtti_object@@QAE@ABV0@@Z ; has WINAPI (@4)
 ; public: __thiscall __non_rtti_object::__non_rtti_object(char const *)
diff --git a/mingw-w64-crt/lib32/msvcr80.def.in 
b/mingw-w64-crt/lib32/msvcr80.def.in
index bb1ec8444..6eac8926e 100644
--- a/mingw-w64-crt/lib32/msvcr80.def.in
+++ b/mingw-w64-crt/lib32/msvcr80.def.in
@@ -1,6 +1,7 @@
 LIBRARY "msvcr80.dll"
 EXPORTS
 
+#define HAS_BROKEN_FWRITE
 #include "msvcrt-common.def.in"
 
 _CIacos
diff --git a/mingw-w64-crt/lib32/msvcr90.def.in 
b/mingw-w64-crt/lib32/msvcr90.def.in
index 62789337b..036282a57 100644
--- a/mingw-w64-crt/lib32/msvcr90.def.in
+++ b/mingw-w64-crt/lib32/msvcr90.def.in
@@ -6,6 +6,7 @@
 LIBRARY "msvcr90.dll"
 EXPORTS
 
+#define HAS_BROKEN_FWRITE
 #include "msvcrt-common.def.in"
 
 ; public: __thiscall std::__non_rtti_object::__non_rtti_object(class 
std::__non_rtti_object const &)
diff --git a/mingw-w64-crt/lib32/msvcr90d.def.in 
b/mingw-w64-crt/lib32/msvcr90d.def.in
index 1175bfec1..e68fd5f0f 100644
--- a/mingw-w64-crt/lib32/msvcr90d.def.in
+++ b/mingw-w64-crt/lib32/msvcr90d.def.in
@@ -6,6 +6,7 @@
 LIBRARY "MSVCR90D.dll"
 EXPORTS
 
+#define HAS_BROKEN_FWRITE
 #include "msvcrt-common.def.in"
 
 ; public: __thiscall std::__non_rtti_object::__non_rtti_object(class 
std::__non_rtti_object const &)
diff --git a/mingw-w64-crt/lib32/msvcrt10.def 
b/mingw-w64-crt/lib32/msvcrt10.def.in
similarity index 99%
rename from mingw-w64-crt/lib32/msvcrt10.def
rename to mingw-w64-crt/lib32/msvcrt10.def.in
index 5a3edf6e6..70db0eec6 100644
--- a/mingw-w64-crt/lib32/msvcrt10.def
+++ b/mingw-w64-crt/lib32/msvcrt10.def.in
@@ -5,6 +5,11 @@
 ;
 LIBRARY "MSVCRT10.dll"
 EXPORTS
+
+#define HAS_BROKEN_FPUTWC
+#define HAS_BROKEN_FWRITE
+#include "msvcrt-mbfix.def.in"
+
 ; public: __thiscall Iostream_init::Iostream_init(class ios &,int)
 ??0Iostream_init@@QAE@AAVios@@H@Z ; has WINAPI (@8)
 ; public: __thiscall Iostream_init::Iostream_init(void)
diff --git a/mingw-w64-crt/lib32/msvcrt20.def 
b/mingw-w64-crt/lib32/msvcrt20.def.in
similarity index 99%
rename from mingw-w64-crt/lib32/msvcrt20.def
rename to mingw-w64-crt/lib32/msvcrt20.def.in
index 48d0487b9..a6259872b 100644
--- a/mingw-w64-crt/lib32/msvcrt20.def
+++ b/mingw-w64-crt/lib32/msvcrt20.def.in
@@ -5,6 +5,11 @@
 ;
 LIBRARY "MSVCRT20.dll"
 EXPORTS
+
+#define HAS_BROKEN_FPUTWC
+#define HAS_BROKEN_FWRITE
+#include "msvcrt-mbfix.def.in"
+
 ; public: __thiscall Iostream_init::Iostream_init(class ios &,int)
 ??0Iostream_init@@QAE@AAVios@@H@Z ; has WINAPI (@8)
 ; public: __thiscall Iostream_init::Iostream_init(void)
diff --git a/mingw-w64-crt/lib32/msvcrt40.def 
b/mingw-w64-crt/lib32/msvcrt40.def.in
similarity index 99%
rename from mingw-w64-crt/lib32/msvcrt40.def
rename to mingw-w64-crt/lib32/msvcrt40.def.in
index 648f792df..36ce49f1f 100644
--- a/mingw-w64-crt/lib32/msvcrt40.def
+++ b/mingw-w64-crt/lib32/msvcrt40.def.in
@@ -5,6 +5,11 @@
 ;
 LIBRARY "MSVCRT40.dll"
 EXPORTS
+
+#define HAS_BROKEN_FPUTWC
+#define HAS_BROKEN_FWRITE
+#include "msvcrt-mbfix.def.in"
+
 ; public: __thiscall Iostream_init::Iostream_init(class ios &,int)
 ??0Iostream_init@@QAE@AAVios@@H@Z ; has WINAPI (@8)
 ; public: __thiscall Iostream_init::Iostream_init(void)
diff --git a/mingw-w64-crt/lib64/msvcr100.def.in 
b/mingw-w64-crt/lib64/msvcr100.def.in
index aab982691..4fc115f0a 100644
--- a/mingw-w64-crt/lib64/msvcr100.def.in
+++ b/mingw-w64-crt/lib64/msvcr100.def.in
@@ -6,6 +6,7 @@
 LIBRARY "msvcr100.dll"
 EXPORTS
 
+#define HAS_BROKEN_FWRITE
 #include "msvcrt-common.def.in"
 
 $I10_OUTPUT
diff --git a/mingw-w64-crt/lib64/msvcr110.def.in 
b/mingw-w64-crt/lib64/msvcr110.def.in
index 60304f9fd..378a5ac60 100644
--- a/mingw-w64-crt/lib64/msvcr110.def.in
+++ b/mingw-w64-crt/lib64/msvcr110.def.in
@@ -6,6 +6,7 @@
 LIBRARY "MSVCR110.dll"
 EXPORTS
 
+#define HAS_BROKEN_FWRITE
 #include "msvcrt-common.def.in"
 
 $I10_OUTPUT
diff --git a/mingw-w64-crt/lib64/msvcr120.def.in 
b/mingw-w64-crt/lib64/msvcr120.def.in
index 0710c6bfb..ac72ebd52 100644
--- a/mingw-w64-crt/lib64/msvcr120.def.in
+++ b/mingw-w64-crt/lib64/msvcr120.def.in
@@ -6,6 +6,7 @@
 LIBRARY "MSVCR120.dll"
 EXPORTS
 
+#define HAS_BROKEN_FWRITE
 #include "msvcrt-common.def.in"
 
 $I10_OUTPUT
diff --git a/mingw-w64-crt/lib64/msvcr120d.def.in 
b/mingw-w64-crt/lib64/msvcr120d.def.in
index 4f2d65ee1..d5aab9796 100644
--- a/mingw-w64-crt/lib64/msvcr120d.def.in
+++ b/mingw-w64-crt/lib64/msvcr120d.def.in
@@ -6,6 +6,7 @@
 LIBRARY "MSVCR120D.dll"
 EXPORTS
 
+#define HAS_BROKEN_FWRITE
 #include "msvcrt-common.def.in"
 
 $I10_OUTPUT
diff --git a/mingw-w64-crt/lib64/msvcr80.def.in 
b/mingw-w64-crt/lib64/msvcr80.def.in
index 045052ec8..fd9bd7d82 100644
--- a/mingw-w64-crt/lib64/msvcr80.def.in
+++ b/mingw-w64-crt/lib64/msvcr80.def.in
@@ -7,6 +7,7 @@
 LIBRARY "msvcr80.dll"
 EXPORTS
 
+#define HAS_BROKEN_FWRITE
 #include "msvcrt-common.def.in"
 
 $I10_OUTPUT
diff --git a/mingw-w64-crt/lib64/msvcr90.def.in 
b/mingw-w64-crt/lib64/msvcr90.def.in
index c8cf9b220..a98d3daeb 100644
--- a/mingw-w64-crt/lib64/msvcr90.def.in
+++ b/mingw-w64-crt/lib64/msvcr90.def.in
@@ -6,6 +6,7 @@
 LIBRARY "msvcr90.dll"
 EXPORTS
 
+#define HAS_BROKEN_FWRITE
 #include "msvcrt-common.def.in"
 
 $I10_OUTPUT
diff --git a/mingw-w64-crt/lib64/msvcr90d.def.in 
b/mingw-w64-crt/lib64/msvcr90d.def.in
index 2acdc3fcf..479bb2ead 100644
--- a/mingw-w64-crt/lib64/msvcr90d.def.in
+++ b/mingw-w64-crt/lib64/msvcr90d.def.in
@@ -6,6 +6,7 @@
 LIBRARY "MSVCR90D.dll"
 EXPORTS
 
+#define HAS_BROKEN_FWRITE
 #include "msvcrt-common.def.in"
 
 $I10_OUTPUT
diff --git a/mingw-w64-crt/stdio/mingw_fwrite.c 
b/mingw-w64-crt/stdio/mingw_fwrite.c
new file mode 100644
index 000000000..8b36a6239
--- /dev/null
+++ b/mingw-w64-crt/stdio/mingw_fwrite.c
@@ -0,0 +1,77 @@
+#include <stdio.h>
+#include <io.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+#endif
+
+static __inline int __getmode(int _Fd)
+{
+  int orig_mode = _setmode(_Fd, _O_TEXT);
+  _setmode(_Fd, orig_mode);
+  return orig_mode;
+}
+
+static __inline int __is_textmode(int _Fd)
+{
+  return __getmode(_Fd) == _O_TEXT;
+}
+
+size_t __cdecl __ms_fwrite(const void *, size_t, size_t, FILE *);
+int __cdecl __mingw_fputws(const wchar_t *, FILE *);
+wint_t __cdecl __mingw_fputwc(wchar_t, FILE *);
+size_t __cdecl __mingw_fwrite(const void *_Buf, size_t _Size, size_t _Cnt, 
FILE * _File)
+{
+  if (_isatty(_fileno(_File))) {
+    int mode;
+    if (MB_CUR_MAX > 1 && __is_textmode(_fileno(_File))) {
+      char *buf, *p;
+      buf = (char *) malloc(_Size * _Cnt + 1);
+      memcpy(buf, _Buf, _Size * _Cnt);
+      buf[_Size * _Cnt] = '\0';
+      p = buf;
+      for (p = buf; p < buf + _Size * _Cnt; ) {
+       if (fputs(p, _File) == EOF)
+         break;
+       p += strlen(p);
+       if (p < buf + _Size * _Cnt) {
+         if (fputc('\0', _File) == EOF)
+          break;
+         p++;
+       }
+      }
+      free(buf);
+      return (p - buf) / _Size;
+    }
+    mode = __getmode(_fileno(_File));
+    if (mode == _O_WTEXT || mode == _O_U8TEXT || mode == _O_U16TEXT) {
+      wchar_t *buf, *p;
+      if ((_Size * _Cnt) % sizeof(wchar_t)) {
+       errno = EINVAL;
+       return 0;
+      }
+      buf = (wchar_t *) malloc(_Size * _Cnt + sizeof(wchar_t));
+      memcpy(buf, _Buf, _Size * _Cnt);
+      buf[(_Size * _Cnt) / sizeof(wchar_t)] = L'\0';
+      p = buf;
+      for (p = buf; p < buf + (_Size * _Cnt) / sizeof(wchar_t); ) {
+       if (__mingw_fputws(p, _File) == EOF)
+         break;
+       p += wcslen(p);
+       if (p < buf + (_Size * _Cnt) / sizeof(wchar_t)) {
+         if (__mingw_fputwc(L'\0', _File) == WEOF)
+          break;
+         p++;
+       }
+      }
+      free(buf);
+      return (p - buf) * sizeof(wchar_t) / _Size;
+    }
+    /* _O_BINARY || _O_TEXT with no multibyte locale */
+    return __ms_fwrite(_Buf, _Size, _Cnt, _File);
+  }
+  return __ms_fwrite(_Buf, _Size, _Cnt, _File);
+}
diff --git a/mingw-w64-crt/stdio/mingw_pformat.c 
b/mingw-w64-crt/stdio/mingw_pformat.c
index d9f5de7ce..488365631 100644
--- a/mingw-w64-crt/stdio/mingw_pformat.c
+++ b/mingw-w64-crt/stdio/mingw_pformat.c
@@ -456,11 +456,19 @@ void __pformat_putchars( const char *s, int count, 
__pformat_t *stream )
 
   /* Emit the data...
    */
-  while( count-- )
-    /*
-     * copying the requisite number of characters from the input.
-     */
-    __pformat_putc( *s++, stream );
+  if( stream->flags & PFORMAT_TO_FILE )
+  {
+    char buf[count + 1];
+    memcpy( buf, s, count );
+    buf[count] = '\0';
+    fputs( buf, (FILE *)(stream->dest) );
+  }
+  else
+    while( count-- )
+      /*
+       * copying the requisite number of characters from the input.
+       */
+      __pformat_putc( *s++, stream );
 
   /* If we still haven't consumed the entire specified field width,
    * we must be doing flush left justification; any residual width
@@ -608,9 +616,17 @@ void __pformat_wputchars( const wchar_t *s, int count, 
__pformat_t *stream )
    */
   while( (count-- > 0) && ((len = wcrtomb( buf, *s++, &state )) > 0) )
   {
-    char *p = buf;
-    while( len-- > 0 )
-      __pformat_putc( *p++, stream );
+    if( stream->flags & PFORMAT_TO_FILE )
+    {
+      buf[len] = '\0';
+      fputs( buf, (FILE *)(stream->dest) );
+    }
+    else
+    {
+      char *p = buf;
+      while( len-- > 0 )
+       __pformat_putc( *p++, stream );
+    }
   }
 
   /* If we still haven't consumed the entire specified field width,
@@ -1262,7 +1278,7 @@ void __pformat_emit_radix_point( __pformat_t *stream )
 #ifdef __BUILD_WIDEAPI
    __pformat_putc (stream->rpchr, stream);
 #else
-    int len; char buf[len = stream->rplen]; mbstate_t state;
+    int len; char buf[len = stream->rplen + 1]; mbstate_t state;
 
     /* Initialise the conversion state...
      */
@@ -1274,9 +1290,17 @@ void __pformat_emit_radix_point( __pformat_t *stream )
     {
       /* and copy to the output destination, when valid...
        */
-      char *p = buf;
-      while( len-- > 0 )
-        __pformat_putc( *p++, stream );
+      if( stream->flags & PFORMAT_TO_FILE )
+      {
+       buf[len] = '\0';
+       fputs( buf, (FILE *)(stream->dest) );
+      }
+      else
+      {
+       char *p = buf;
+       while( len-- > 0 )
+       __pformat_putc( *p++, stream );
+      }
     }
 
     else
diff --git a/mingw-w64-crt/stdio/mingw_pformat.h 
b/mingw-w64-crt/stdio/mingw_pformat.h
index 477780432..f81818078 100644
--- a/mingw-w64-crt/stdio/mingw_pformat.h
+++ b/mingw-w64-crt/stdio/mingw_pformat.h
@@ -68,7 +68,8 @@
   */
 #ifdef __BUILD_WIDEAPI
 # define __pformat        __mingw_wpformat
-#define __fputc(X,STR) fputwc((wchar_t) (X), (STR))
+wint_t __cdecl __mingw_fputwc(wchar_t, FILE *);
+#define __fputc(X,STR) __mingw_fputwc((wchar_t) (X), (STR))
 
 # define __printf         __mingw_wprintf
 # define __fprintf        __mingw_fwprintf
diff --git a/mingw-w64-crt/stdio/msvcrt_mbfix.c 
b/mingw-w64-crt/stdio/msvcrt_mbfix.c
new file mode 100644
index 000000000..55e203499
--- /dev/null
+++ b/mingw-w64-crt/stdio/msvcrt_mbfix.c
@@ -0,0 +1,96 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <io.h>
+#include <fcntl.h>
+
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+#endif
+
+wint_t __cdecl __ms_fputwc(wchar_t, FILE *);
+wint_t __cdecl __ms_fputwchar(wchar_t);
+int __cdecl __ms_fputws(const wchar_t *, FILE *);
+wint_t __cdecl __ms_putwc(wchar_t, FILE *);
+int __cdecl __ms_putws(const wchar_t *);
+
+static __inline wint_t __fputwc(wchar_t _Ch, FILE *_File)
+{
+  char buf[MB_LEN_MAX+1];
+  int l;
+  if (_Ch == L'\0')
+    return __ms_fputwc(_Ch, _File);
+  l = wctomb(buf, _Ch);
+  if (l == -1)
+    return WEOF;
+  buf[l] = '\0';
+  if (fputs(buf, _File) == EOF)
+    return WEOF;
+  return _Ch;
+}
+
+static __inline int __fputws(const wchar_t *_Str, FILE *_File)
+{
+  int ret = 0;
+  const wchar_t *p;
+  for (p = _Str; *p; p++)
+    if (__fputwc(*p, _File) == WEOF)
+      return EOF;
+  return ret;
+}
+
+static __inline int __getmode(int _Fd)
+{
+  int orig_mode = _setmode(_Fd, _O_TEXT);
+  _setmode(_Fd, orig_mode);
+  return orig_mode;
+}
+
+static __inline int __is_textmode(int _Fd)
+{
+  return __getmode(_Fd) == _O_TEXT;
+}
+
+static __inline int __mb_to_be_fixed(int _Fd)
+{
+  return (MB_CUR_MAX > 1 && _isatty(_Fd) && __is_textmode(_Fd));
+}
+
+wint_t __cdecl __mingw_fputwchar(wchar_t _Ch)
+{
+  if (__mb_to_be_fixed(_fileno(stdout)))
+    return __fputwc (_Ch, stdout);
+  return __ms_fputwchar(_Ch);
+}
+
+wint_t __cdecl __mingw_fputwc(wchar_t _Ch, FILE *_File)
+{
+  if (__mb_to_be_fixed(_fileno(_File)))
+    return __fputwc(_Ch, _File);
+  return __ms_fputwc(_Ch, _File);
+}
+
+int __cdecl __mingw_fputws(const wchar_t *_Str, FILE *_File)
+{
+  if (__mb_to_be_fixed(_fileno(_File)))
+    return __fputws(_Str, _File);
+  return __ms_fputws(_Str, _File);
+}
+
+wint_t __cdecl __mingw_putwc(wchar_t _Ch, FILE *_File)
+{
+  if (__mb_to_be_fixed(_fileno(_File)))
+    return __fputwc(_Ch, _File);
+  return __ms_putwc(_Ch, _File);
+}
+
+int __cdecl __mingw_putws(const wchar_t *_Str)
+{
+  if (__mb_to_be_fixed(_fileno(stdout))) {
+    if (__fputws(_Str, stdout) == EOF)
+      return WEOF;
+    if (fputc('\n', stdout) == EOF)
+      return WEOF;
+    return 0;
+  }
+  return __ms_putws(_Str);
+}
diff --git a/mingw-w64-headers/crt/stdio.h b/mingw-w64-headers/crt/stdio.h
index 76fffa177..547475494 100644
--- a/mingw-w64-headers/crt/stdio.h
+++ b/mingw-w64-headers/crt/stdio.h
@@ -680,7 +680,14 @@ int vsnprintf (char *__stream, size_t __n, const char 
*__format, __builtin_va_li
 #endif /* (defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)) */
 #endif /* _FILE_OFFSET_BITS_SET_FTELLO */
 
+#ifndef _UCRT
+  __mingw_ovr size_t fwrite(const void * __restrict__ _Str,size_t _Size,size_t 
_Count,FILE * __restrict__ _File) {
+    size_t __cdecl __mingw_fwrite(const void *, size_t, size_t, FILE *);
+    return __mingw_fwrite(_Str, _Size, _Count, _File);
+  }
+#else
   size_t __cdecl fwrite(const void * __restrict__ _Str,size_t _Size,size_t 
_Count,FILE * __restrict__ _File);
+#endif
   int __cdecl getc(FILE *_File);
   int __cdecl getchar(void);
   _CRTIMP int __cdecl _getmaxstdio(void);
@@ -1271,17 +1278,49 @@ int vsnwprintf (wchar_t *__stream, size_t __n, const 
wchar_t *__format, __builti
 
   wint_t __cdecl fgetwc(FILE *_File);
   _CRTIMP wint_t __cdecl _fgetwchar(void);
+#ifndef _UCRT
+  __mingw_ovr wint_t fputwc(wchar_t _Ch,FILE *_File) {
+    wint_t __cdecl __mingw_fputwc(wchar_t, FILE *);
+    return __mingw_fputwc(_Ch, _File);
+  }
+  __mingw_ovr wint_t _fputwchar(wchar_t _Ch) {
+    wint_t __cdecl __mingw_fputwchar(wchar_t);
+    return __mingw_fputwchar(_Ch);
+  }
+#else
   wint_t __cdecl fputwc(wchar_t _Ch,FILE *_File);
   _CRTIMP wint_t __cdecl _fputwchar(wchar_t _Ch);
+#endif
   wint_t __cdecl getwc(FILE *_File);
   wint_t __cdecl getwchar(void);
+#ifndef _UCRT
+  __mingw_ovr wint_t putwc(wchar_t _Ch,FILE *_File) {
+    wint_t __cdecl __mingw_putwc(wchar_t, FILE *);
+    return __mingw_putwc(_Ch, _File);
+  }
+#else
   wint_t __cdecl putwc(wchar_t _Ch,FILE *_File);
+#endif
   wint_t __cdecl putwchar(wchar_t _Ch);
   wint_t __cdecl ungetwc(wint_t _Ch,FILE *_File);
   wchar_t *__cdecl fgetws(wchar_t * __restrict__ _Dst,int _SizeInWords,FILE * 
__restrict__ _File);
+#ifndef _UCRT
+  __mingw_ovr int fputws(const wchar_t * __restrict__ _Str,FILE * __restrict__ 
_File) {
+    int __cdecl __mingw_fputws(const wchar_t *,FILE *);
+    return __mingw_fputws(_Str, _File);
+  }
+#else
   int __cdecl fputws(const wchar_t * __restrict__ _Str,FILE * __restrict__ 
_File);
+#endif
   _CRTIMP wchar_t *__cdecl _getws(wchar_t *_String) 
__MINGW_ATTRIB_DEPRECATED_SEC_WARN;
+#ifndef _UCRT
+  __mingw_ovr int _putws(const wchar_t *_Str) {
+    int __cdecl __mingw_putws(const wchar_t *);
+    return __mingw_putws(_Str);
+  }
+#else
   _CRTIMP int __cdecl _putws(const wchar_t *_Str);
+#endif
 
 #ifdef _UCRT
   __mingw_ovr
diff --git a/mingw-w64-headers/crt/wchar.h b/mingw-w64-headers/crt/wchar.h
index ab7187721..e489cdac8 100644
--- a/mingw-w64-headers/crt/wchar.h
+++ b/mingw-w64-headers/crt/wchar.h
@@ -786,17 +786,49 @@ int vsnwprintf (wchar_t *__stream, size_t __n, const 
wchar_t *__format, __builti
 
   wint_t __cdecl fgetwc(FILE *_File);
   _CRTIMP wint_t __cdecl _fgetwchar(void);
+#ifndef _UCRT
+  __mingw_ovr wint_t fputwc(wchar_t _Ch,FILE *_File) {
+    wint_t __cdecl __mingw_fputwc(wchar_t, FILE *);
+    return __mingw_fputwc(_Ch, _File);
+  }
+  __mingw_ovr wint_t _fputwchar(wchar_t _Ch) {
+    wint_t __cdecl __mingw_fputwchar(wchar_t);
+    return __mingw_fputwchar(_Ch);
+  }
+#else
   wint_t __cdecl fputwc(wchar_t _Ch,FILE *_File);
   _CRTIMP wint_t __cdecl _fputwchar(wchar_t _Ch);
+#endif
   wint_t __cdecl getwc(FILE *_File);
   wint_t __cdecl getwchar(void);
+#ifndef _UCRT
+  __mingw_ovr wint_t putwc(wchar_t _Ch,FILE *_File) {
+    wint_t __cdecl __mingw_putwc(wchar_t, FILE *);
+    return __mingw_putwc(_Ch, _File);
+  }
+#else
   wint_t __cdecl putwc(wchar_t _Ch,FILE *_File);
+#endif
   wint_t __cdecl putwchar(wchar_t _Ch);
   wint_t __cdecl ungetwc(wint_t _Ch,FILE *_File);
   wchar_t *__cdecl fgetws(wchar_t * __restrict__ _Dst,int _SizeInWords,FILE * 
__restrict__ _File);
+#ifndef _UCRT
+  __mingw_ovr int fputws(const wchar_t * __restrict__ _Str,FILE * __restrict__ 
_File) {
+    int __cdecl __mingw_fputws(const wchar_t *,FILE *);
+    return __mingw_fputws(_Str, _File);
+  }
+#else
   int __cdecl fputws(const wchar_t * __restrict__ _Str,FILE * __restrict__ 
_File);
+#endif
   _CRTIMP wchar_t *__cdecl _getws(wchar_t *_String) 
__MINGW_ATTRIB_DEPRECATED_SEC_WARN;
+#ifndef _UCRT
+  __mingw_ovr int _putws(const wchar_t *_Str) {
+    int __cdecl __mingw_putws(const wchar_t *);
+    return __mingw_putws(_Str);
+  }
+#else
   _CRTIMP int __cdecl _putws(const wchar_t *_Str);
+#endif
 
 #ifdef _UCRT
   __mingw_ovr
-- 
2.37.1

_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to