Yesterday I wrote:
> Bug 1:
> 
> When the application outputs double-byte characters one byte at
> a time, using the functions fputc() or putc(), the console shows JISX0201
> (ASCII and Katakana) characters instead of CP932 (ASCII, Katakana,
> Hiragana, Hanzi) characters.
> ...
> * Normal applications don't write strings one byte at a time, for
>   speed.

But mingw's [v][f]printf functions do this, and they are enabled
by default when __USE_MINGW_ANSI_STDIO is not explicitly defined to 0.
For instance, the __mingw_vfprintf function invokes __mingw_pformat,
and this function calls fputc once for each byte.

How to reproduce:
1. Use Windows 10 or 11. Switch it to Japanese as main language.
2. Use the attached program. In the dev environment:
  $ gcc -Wall foo.c
3. In a cmd.exe console:
  $ chcp 932
  $ .\a
Look at the output of the parts G and H.

This patch adds a workaround. With this, output to console in
double-byte CP932 encoding works as expected.


2025-09-17  Bruno Haible  <[email protected]>

        stdio-h: Work around [v][f]printf bugs in mingw with msvcrt.
        Reported by 松延 英樹 <[email protected]> in
        <https://github.com/mlocati/gettext-iconv-windows/issues/52>.
        * lib/stdio.in.h (gl_consolesafe_fprintf, gl_consolesafe_printf,
        gl_consolesafe_vfprintf, gl_consolesafe_vprintf): New declarations.
        (fprintf): When msvcrt is in use, use gl_consolesafe_fprintf.
        (printf): When msvcrt is in use, use gl_consolesafe_printf.
        (vfprintf): When msvcrt is in use, use gl_consolesafe_vfprintf.
        (vprintf): When msvcrt is in use, use gl_consolesafe_vprintf.
        * lib/stdio-consolesafe.c: Include fseterr.h.
        (gl_consolesafe_fprintf, gl_consolesafe_printf, gl_consolesafe_vfprintf,
        gl_consolesafe_vprintf): New functions.
        * lib/stdio-write.c (vfprintf): When msvcrt is in use, use
        gl_consolesafe_vfprintf.
        * modules/stdio-h (Depends-on): Add fseterr.
        * doc/posix-functions/fprintf.texi: Document the mingw bug.
        * doc/posix-functions/printf.texi: Likewise.
        * doc/posix-functions/vfprintf.texi: Likewise.
        * doc/posix-functions/vprintf.texi: Likewise.

From 84d76cdd169aba11855ae58748f3f4eee3e152c4 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Wed, 17 Sep 2025 09:31:31 +0200
Subject: [PATCH] stdio-h: Work around [v][f]printf bugs in mingw with msvcrt.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Reported by ?????? ?????? <[email protected]> in
<https://github.com/mlocati/gettext-iconv-windows/issues/52>.

* lib/stdio.in.h (gl_consolesafe_fprintf, gl_consolesafe_printf,
gl_consolesafe_vfprintf, gl_consolesafe_vprintf): New declarations.
(fprintf): When msvcrt is in use, use gl_consolesafe_fprintf.
(printf): When msvcrt is in use, use gl_consolesafe_printf.
(vfprintf): When msvcrt is in use, use gl_consolesafe_vfprintf.
(vprintf): When msvcrt is in use, use gl_consolesafe_vprintf.
* lib/stdio-consolesafe.c: Include fseterr.h.
(gl_consolesafe_fprintf, gl_consolesafe_printf, gl_consolesafe_vfprintf,
gl_consolesafe_vprintf): New functions.
* lib/stdio-write.c (vfprintf): When msvcrt is in use, use
gl_consolesafe_vfprintf.
* modules/stdio-h (Depends-on): Add fseterr.
* doc/posix-functions/fprintf.texi: Document the mingw bug.
* doc/posix-functions/printf.texi: Likewise.
* doc/posix-functions/vfprintf.texi: Likewise.
* doc/posix-functions/vprintf.texi: Likewise.
---
 ChangeLog                         | 22 +++++++++
 doc/posix-functions/fprintf.texi  | 10 ++++
 doc/posix-functions/printf.texi   | 10 ++++
 doc/posix-functions/vfprintf.texi | 10 ++++
 doc/posix-functions/vprintf.texi  | 10 ++++
 lib/stdio-consolesafe.c           | 77 +++++++++++++++++++++++++++++++
 lib/stdio-write.c                 |  3 ++
 lib/stdio.in.h                    | 39 ++++++++++++++++
 modules/stdio-h                   |  1 +
 9 files changed, 182 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index 03b2f5bc3b..ab4794a80e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2025-09-17  Bruno Haible  <[email protected]>
+
+	stdio-h: Work around [v][f]printf bugs in mingw with msvcrt.
+	Reported by ?????? ?????? <[email protected]> in
+	<https://github.com/mlocati/gettext-iconv-windows/issues/52>.
+	* lib/stdio.in.h (gl_consolesafe_fprintf, gl_consolesafe_printf,
+	gl_consolesafe_vfprintf, gl_consolesafe_vprintf): New declarations.
+	(fprintf): When msvcrt is in use, use gl_consolesafe_fprintf.
+	(printf): When msvcrt is in use, use gl_consolesafe_printf.
+	(vfprintf): When msvcrt is in use, use gl_consolesafe_vfprintf.
+	(vprintf): When msvcrt is in use, use gl_consolesafe_vprintf.
+	* lib/stdio-consolesafe.c: Include fseterr.h.
+	(gl_consolesafe_fprintf, gl_consolesafe_printf, gl_consolesafe_vfprintf,
+	gl_consolesafe_vprintf): New functions.
+	* lib/stdio-write.c (vfprintf): When msvcrt is in use, use
+	gl_consolesafe_vfprintf.
+	* modules/stdio-h (Depends-on): Add fseterr.
+	* doc/posix-functions/fprintf.texi: Document the mingw bug.
+	* doc/posix-functions/printf.texi: Likewise.
+	* doc/posix-functions/vfprintf.texi: Likewise.
+	* doc/posix-functions/vprintf.texi: Likewise.
+
 2025-09-17  Bruno Haible  <[email protected]>
 
 	stdio-h: Work around fwrite bug in msvcrt.
diff --git a/doc/posix-functions/fprintf.texi b/doc/posix-functions/fprintf.texi
index 4eebaafd8f..dbace97e18 100644
--- a/doc/posix-functions/fprintf.texi
+++ b/doc/posix-functions/fprintf.texi
@@ -11,6 +11,16 @@
 @mindex nonblocking
 @mindex sigpipe
 
+Portability problems fixed by either Gnulib module @code{stdio-h} or @code{fprintf-posix} or @code{fprintf-gnu}:
+@itemize
+@item
+This function does not support non-ASCII characters in double-byte encoding,
+corresponding to the locale, on some platforms:
+@c https://github.com/mlocati/gettext-iconv-windows/issues/52
+mingw with @code{__USE_MINGW_ANSI_STDIO} in combination with msvcrt,
+when the output goes to a Windows console.
+@end itemize
+
 Portability problems fixed by either Gnulib module @code{fprintf-posix} or @code{fprintf-gnu}:
 @itemize
 @item
diff --git a/doc/posix-functions/printf.texi b/doc/posix-functions/printf.texi
index 4cf38b5ba6..8b33702a83 100644
--- a/doc/posix-functions/printf.texi
+++ b/doc/posix-functions/printf.texi
@@ -11,6 +11,16 @@
 @mindex nonblocking
 @mindex sigpipe
 
+Portability problems fixed by either Gnulib module @code{stdio-h} or @code{fprintf-posix} or @code{fprintf-gnu}:
+@itemize
+@item
+This function does not support non-ASCII characters in double-byte encoding,
+corresponding to the locale, on some platforms:
+@c https://github.com/mlocati/gettext-iconv-windows/issues/52
+mingw with @code{__USE_MINGW_ANSI_STDIO} in combination with msvcrt,
+when the output goes to a Windows console.
+@end itemize
+
 Portability problems fixed by either Gnulib module @code{printf-posix} or @code{printf-gnu}:
 @itemize
 @item
diff --git a/doc/posix-functions/vfprintf.texi b/doc/posix-functions/vfprintf.texi
index 53516d374c..8cee58b3d8 100644
--- a/doc/posix-functions/vfprintf.texi
+++ b/doc/posix-functions/vfprintf.texi
@@ -11,6 +11,16 @@
 @mindex nonblocking
 @mindex sigpipe
 
+Portability problems fixed by either Gnulib module @code{stdio-h} or @code{fprintf-posix} or @code{fprintf-gnu}:
+@itemize
+@item
+This function does not support non-ASCII characters in double-byte encoding,
+corresponding to the locale, on some platforms:
+@c https://github.com/mlocati/gettext-iconv-windows/issues/52
+mingw with @code{__USE_MINGW_ANSI_STDIO} in combination with msvcrt,
+when the output goes to a Windows console.
+@end itemize
+
 Portability problems fixed by either Gnulib module @code{vfprintf-posix} or @code{vfprintf-gnu}:
 @itemize
 @item
diff --git a/doc/posix-functions/vprintf.texi b/doc/posix-functions/vprintf.texi
index 8b84f2f91e..7a867172e7 100644
--- a/doc/posix-functions/vprintf.texi
+++ b/doc/posix-functions/vprintf.texi
@@ -11,6 +11,16 @@
 @mindex nonblocking
 @mindex sigpipe
 
+Portability problems fixed by either Gnulib module @code{stdio-h} or @code{fprintf-posix} or @code{fprintf-gnu}:
+@itemize
+@item
+This function does not support non-ASCII characters in double-byte encoding,
+corresponding to the locale, on some platforms:
+@c https://github.com/mlocati/gettext-iconv-windows/issues/52
+mingw with @code{__USE_MINGW_ANSI_STDIO} in combination with msvcrt,
+when the output goes to a Windows console.
+@end itemize
+
 Portability problems fixed by either Gnulib module @code{vprintf-posix} or @code{vprintf-gnu}:
 @itemize
 @item
diff --git a/lib/stdio-consolesafe.c b/lib/stdio-consolesafe.c
index 5cd0b12297..fbea20be22 100644
--- a/lib/stdio-consolesafe.c
+++ b/lib/stdio-consolesafe.c
@@ -70,3 +70,80 @@ gl_consolesafe_fwrite (const void *ptr, size_t size, size_t nmemb, FILE *fp)
   size_t written = workaround_fwrite0 (tmp, nbytes, fp);
   return written / size;
 }
+
+#if defined __MINGW32__ && __USE_MINGW_ANSI_STDIO
+
+# include "fseterr.h"
+
+/* Bypass the functions __mingw_[v][f]printf, that trigger a bug in msvcrt,
+   but without losing the support for modern format specifiers added by
+   __mingw_*printf.  */
+
+int
+gl_consolesafe_fprintf (FILE *restrict fp, const char *restrict format, ...)
+{
+  va_list args;
+  char *tmpstring;
+  va_start (args, format);
+  int result = vasprintf (&tmpstring, format, args);
+  va_end (args);
+  if (result >= 0)
+    {
+      if (workaround_fwrite0 (tmpstring, result, fp) < result)
+        result = -1;
+    }
+  else
+    fseterr (fp);
+  return result;
+}
+
+int
+gl_consolesafe_printf (const char *restrict format, ...)
+{
+  va_list args;
+  char *tmpstring;
+  va_start (args, format);
+  int result = vasprintf (&tmpstring, format, args);
+  va_end (args);
+  if (result >= 0)
+    {
+      if (workaround_fwrite0 (tmpstring, result, stdout) < result)
+        result = -1;
+    }
+  else
+    fseterr (stdout);
+  return result;
+}
+
+int
+gl_consolesafe_vfprintf (FILE *restrict fp,
+                         const char *restrict format, va_list args)
+{
+  char *tmpstring;
+  int result = vasprintf (&tmpstring, format, args);
+  if (result >= 0)
+    {
+      if (workaround_fwrite0 (tmpstring, result, fp) < result)
+        result = -1;
+    }
+  else
+    fseterr (fp);
+  return result;
+}
+
+int
+gl_consolesafe_vprintf (const char *restrict format, va_list args)
+{
+  char *tmpstring;
+  int result = vasprintf (&tmpstring, format, args);
+  if (result >= 0)
+    {
+      if (workaround_fwrite0 (tmpstring, result, stdout) < result)
+        result = -1;
+    }
+  else
+    fseterr (stdout);
+  return result;
+}
+
+#endif
diff --git a/lib/stdio-write.c b/lib/stdio-write.c
index 1115111eda..59ba8fc4c3 100644
--- a/lib/stdio-write.c
+++ b/lib/stdio-write.c
@@ -162,6 +162,9 @@ vprintf (const char *format, va_list args)
 int
 vfprintf (FILE *stream, const char *format, va_list args)
 #undef vfprintf
+#if defined __MINGW32__ && !defined _UCRT && __USE_MINGW_ANSI_STDIO
+# define vfprintf gl_consolesafe_vfprintf
+#endif
 {
   CALL_WITH_SIGPIPE_EMULATION (int, vfprintf (stream, format, args), ret == EOF)
 }
diff --git a/lib/stdio.in.h b/lib/stdio.in.h
index 08db0290cd..f3ab7969d1 100644
--- a/lib/stdio.in.h
+++ b/lib/stdio.in.h
@@ -276,6 +276,25 @@
 _GL_FUNCDECL_SYS (gl_consolesafe_fwrite, size_t,
                   (const void *ptr, size_t size, size_t nmemb, FILE *fp),
                   _GL_ARG_NONNULL ((1, 4)));
+# if defined __MINGW32__
+_GL_FUNCDECL_SYS (gl_consolesafe_fprintf, int,
+                  (FILE *restrict fp, const char *restrict format, ...),
+                  _GL_ATTRIBUTE_FORMAT_PRINTF_STANDARD (2, 3)
+                  _GL_ARG_NONNULL ((1, 2)));
+_GL_FUNCDECL_SYS (gl_consolesafe_printf, int,
+                  (const char *restrict format, ...),
+                  _GL_ATTRIBUTE_FORMAT_PRINTF_STANDARD (1, 2)
+                  _GL_ARG_NONNULL ((1)));
+_GL_FUNCDECL_SYS (gl_consolesafe_vfprintf, int,
+                  (FILE *restrict fp,
+                   const char *restrict format, va_list args),
+                  _GL_ATTRIBUTE_FORMAT_PRINTF_STANDARD (2, 0)
+                  _GL_ARG_NONNULL ((1, 2)));
+_GL_FUNCDECL_SYS (gl_consolesafe_vprintf, int,
+                  (const char *restrict format, va_list args),
+                  _GL_ATTRIBUTE_FORMAT_PRINTF_STANDARD (1, 0)
+                  _GL_ARG_NONNULL ((1)));
+# endif
 #endif
 
 
@@ -615,6 +634,11 @@ _GL_CXXALIAS_SYS (fprintf, int,
 # if __GLIBC__ >= 2
 _GL_CXXALIASWARN (fprintf);
 # endif
+#elif defined __MINGW32__ && !defined _UCRT && __USE_MINGW_ANSI_STDIO
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#  undef fprintf
+#  define fprintf gl_consolesafe_fprintf
+# endif
 #endif
 #if !@GNULIB_FPRINTF_POSIX@ && defined GNULIB_POSIXCHECK
 # if !GNULIB_overrides_fprintf
@@ -1337,6 +1361,11 @@ _GL_CXXALIAS_SYS (printf, int, (const char *restrict format, ...));
 # if __GLIBC__ >= 2
 _GL_CXXALIASWARN (printf);
 # endif
+#elif defined __MINGW32__ && !defined _UCRT && __USE_MINGW_ANSI_STDIO
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#  undef printf
+#  define printf gl_consolesafe_printf
+# endif
 #endif
 #if !@GNULIB_PRINTF_POSIX@ && defined GNULIB_POSIXCHECK
 # if !GNULIB_overrides_printf
@@ -1889,6 +1918,11 @@ _GL_CXXALIAS_SYS_CAST (vfprintf, int,
 # if __GLIBC__ >= 2
 _GL_CXXALIASWARN (vfprintf);
 # endif
+#elif defined __MINGW32__ && !defined _UCRT && __USE_MINGW_ANSI_STDIO
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#  undef vfprintf
+#  define vfprintf gl_consolesafe_vfprintf
+# endif
 #endif
 #if !@GNULIB_VFPRINTF_POSIX@ && defined GNULIB_POSIXCHECK
 # if !GNULIB_overrides_vfprintf
@@ -1970,6 +2004,11 @@ _GL_CXXALIAS_SYS_CAST (vprintf, int,
 # if __GLIBC__ >= 2
 _GL_CXXALIASWARN (vprintf);
 # endif
+#elif defined __MINGW32__ && !defined _UCRT && __USE_MINGW_ANSI_STDIO
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#  undef vprintf
+#  define vprintf gl_consolesafe_vprintf
+# endif
 #endif
 #if !@GNULIB_VPRINTF_POSIX@ && defined GNULIB_POSIXCHECK
 # if !GNULIB_overrides_vprintf
diff --git a/modules/stdio-h b/modules/stdio-h
index 02a08688ec..619d868430 100644
--- a/modules/stdio-h
+++ b/modules/stdio-h
@@ -20,6 +20,7 @@ ssize_t
 stddef-h
 sys_types-h
 stdckdint-h
+fseterr
 
 configure.ac-early:
 gl_STDIO_H_EARLY
-- 
2.50.1

Reply via email to