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
