Hello Martin, in attachment I'm sending new version (v3) of these changes.
I addressed all issues about which we were discussed and Kirill run CI
tests on all these and also other changes (some other changes are
revered in git history, so do not be confused). CI tests passed:
https://github.com/maiddaisuki/mingw-w64/actions/runs/22307392638
>From 487a95d5aa97acd226fdbee2ad2096df32cbf28e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]>
Date: Sun, 22 Jun 2025 01:25:16 +0200
Subject: [PATCH v3 1/6] crt: Fix support for mingw_pformat %C and %S
Format %C and %S uses opposite wideness as format %c and %s.
So %S in printf uses wchar_t* string and in wprintf uses char* string.
Format %C and %S can be changed by l or h modifiers, like %c and %s.
So for example %hC uses char type in both printf and wprintf calls
and for example %lS uses wchar_t* string in both swprintf and sprintf.
This change fixes the mingw_pformat function to correctly process %C and %S
formats with optional l and h modifiers. This also aligns mingw-w64 support
with crtdll, msvcrt and UCRT *printf functions.
---
mingw-w64-crt/stdio/mingw_pformat.c | 28 ++++++++++++++++++++++------
1 file changed, 22 insertions(+), 6 deletions(-)
diff --git a/mingw-w64-crt/stdio/mingw_pformat.c
b/mingw-w64-crt/stdio/mingw_pformat.c
index 25ceffb5028e..d40d9de29aa2 100644
--- a/mingw-w64-crt/stdio/mingw_pformat.c
+++ b/mingw-w64-crt/stdio/mingw_pformat.c
@@ -2470,10 +2470,18 @@ __pformat (int flags, void *dest, int max, const
APICHAR *fmt, va_list argv)
case 'C':
/*
- * Equivalent to `%lc'; set `length' accordingly,
- * and simply fall through.
+ * If the explicit length modifier is not set
+ * then it is opposite of the default length
+ * modifier used by `%c`.
*/
- length = PFORMAT_LENGTH_LONG;
+ if( length == PFORMAT_LENGTH_INT )
+ {
+ #ifndef __BUILD_WIDEAPI
+ length = PFORMAT_LENGTH_LONG;
+ #else
+ length = PFORMAT_LENGTH_SHORT;
+ #endif
+ }
/* fallthrough */
@@ -2516,10 +2524,18 @@ __pformat (int flags, void *dest, int max, const
APICHAR *fmt, va_list argv)
case 'S':
/*
- * Equivalent to `%ls'; set `length' accordingly,
- * and simply fall through.
+ * If the explicit length modifier is not set
+ * then it is opposite of the default length
+ * modifier used by `%s`.
*/
- length = PFORMAT_LENGTH_LONG;
+ if( length == PFORMAT_LENGTH_INT )
+ {
+ #ifndef __BUILD_WIDEAPI
+ length = PFORMAT_LENGTH_LONG;
+ #else
+ length = PFORMAT_LENGTH_SHORT;
+ #endif
+ }
/* fallthrough */
--
2.20.1
>From 2a227d37993526a34746ab4cc1f367251daff6fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]>
Date: Tue, 24 Jun 2025 22:56:24 +0200
Subject: [PATCH v3 2/6] crt: Fix support for mingw_pformat %c and %s in wide
wprintf
Format %c and %s when used in some wide wprintf function takes the wide
wchar_t character or wide wchar_t* string. This is msvcrt.dll and UCRT behavior.
This change aligns the mingw-w64 wprintf functions to be compatible with MS.
---
mingw-w64-crt/stdio/mingw_pformat.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/mingw-w64-crt/stdio/mingw_pformat.c
b/mingw-w64-crt/stdio/mingw_pformat.c
index d40d9de29aa2..79d5bd46f845 100644
--- a/mingw-w64-crt/stdio/mingw_pformat.c
+++ b/mingw-w64-crt/stdio/mingw_pformat.c
@@ -2505,7 +2505,11 @@ __pformat (int flags, void *dest, int max, const APICHAR
*fmt, va_list argv)
/* Now we invoke the appropriate format handler...
*/
if( (length == PFORMAT_LENGTH_LONG)
- || (length == PFORMAT_LENGTH_LLONG) )
+ || (length == PFORMAT_LENGTH_LLONG)
+ #ifdef __BUILD_WIDEAPI
+ || (length == PFORMAT_LENGTH_INT)
+ #endif
+ )
{
/* considering any `long' type modifier as a reference to
* `wchar_t' data, (which is promoted to an `int' argument)...
@@ -2541,7 +2545,11 @@ __pformat (int flags, void *dest, int max, const APICHAR
*fmt, va_list argv)
case 's':
if( (length == PFORMAT_LENGTH_LONG)
- || (length == PFORMAT_LENGTH_LLONG))
+ || (length == PFORMAT_LENGTH_LLONG)
+ #ifdef __BUILD_WIDEAPI
+ || (length == PFORMAT_LENGTH_INT)
+ #endif
+ )
{
/* considering any `long' type modifier as a reference to
* a `wchar_t' string...
--
2.20.1
>From 30c38d398b8bfe7a44bc1b83f6206b9ec1718476 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]>
Date: Tue, 24 Jun 2025 23:02:03 +0200
Subject: [PATCH v3 3/6] crt: Add tests for printf format %c/%C and %s/%S
---
mingw-w64-crt/testcases/Makefile.am | 3 +
mingw-w64-crt/testcases/t_format_CS.c | 2 +
mingw-w64-crt/testcases/t_format_CS0.c | 2 +
mingw-w64-crt/testcases/t_format_CS1.c | 2 +
mingw-w64-crt/testcases/t_format_CS_tmpl.h | 120 +++++++++++++++++++++
5 files changed, 129 insertions(+)
create mode 100644 mingw-w64-crt/testcases/t_format_CS.c
create mode 100644 mingw-w64-crt/testcases/t_format_CS0.c
create mode 100644 mingw-w64-crt/testcases/t_format_CS1.c
create mode 100644 mingw-w64-crt/testcases/t_format_CS_tmpl.h
diff --git a/mingw-w64-crt/testcases/Makefile.am
b/mingw-w64-crt/testcases/Makefile.am
index eb7044076f11..bcd32f056d2f 100644
--- a/mingw-w64-crt/testcases/Makefile.am
+++ b/mingw-w64-crt/testcases/Makefile.am
@@ -24,6 +24,9 @@ testcase_progs = \
t_ctype \
t_findfirst \
t_float \
+ t_format_CS \
+ t_format_CS0 \
+ t_format_CS1 \
t_fseeki64 \
t_fseeki64_ftelli64 \
t_fstat \
diff --git a/mingw-w64-crt/testcases/t_format_CS.c
b/mingw-w64-crt/testcases/t_format_CS.c
new file mode 100644
index 000000000000..62a568e61044
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_format_CS.c
@@ -0,0 +1,2 @@
+#undef __USE_MINGW_ANSI_STDIO
+#include "t_format_CS_tmpl.h"
diff --git a/mingw-w64-crt/testcases/t_format_CS0.c
b/mingw-w64-crt/testcases/t_format_CS0.c
new file mode 100644
index 000000000000..74eff8f896d8
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_format_CS0.c
@@ -0,0 +1,2 @@
+#define __USE_MINGW_ANSI_STDIO 0
+#include "t_format_CS_tmpl.h"
diff --git a/mingw-w64-crt/testcases/t_format_CS1.c
b/mingw-w64-crt/testcases/t_format_CS1.c
new file mode 100644
index 000000000000..23675a57df3b
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_format_CS1.c
@@ -0,0 +1,2 @@
+#define __USE_MINGW_ANSI_STDIO 1
+#include "t_format_CS_tmpl.h"
diff --git a/mingw-w64-crt/testcases/t_format_CS_tmpl.h
b/mingw-w64-crt/testcases/t_format_CS_tmpl.h
new file mode 100644
index 000000000000..e472adaeb1ec
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_format_CS_tmpl.h
@@ -0,0 +1,120 @@
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+#include <windows.h>
+
+#define ARRAY_COUNT(a) (sizeof(a)/sizeof(a[0]))
+
+#define PRINT_NARROW_BUF(buf) \
+ for (i = 0; i < ARRAY_COUNT(buf); i++) { \
+ if ((buf)[i] == '\n' || ((buf)[i] >= 0x20 && (buf)[i] <= 0x7E
&& (buf)[i] != '\\')) \
+ fputc((buf)[i], stderr); \
+ else \
+ fprintf(stderr, "\\x%02X", (unsigned int)(unsigned
char)(buf)[i]); \
+ }
+
+#define PRINT_WIDE_BUF(buf) \
+ for (i = 0; i < ARRAY_COUNT(buf); i++) { \
+ if ((buf)[i] == L'\n' || ((buf)[i] >= 0x20 && (buf)[i] <= 0x7E
&& (buf)[i] != L'\\')) \
+ fputc((char)(buf)[i], stderr); \
+ else \
+ fprintf(stderr, "\\x%04X", (unsigned
int)(wint_t)(buf)[i]); \
+ } \
+
+int main() {
+ char abuf[37];
+ char abuf2[13];
+ wchar_t wbuf[33];
+ wchar_t wbuf2[13];
+ wchar_t wbuf3[5];
+ int ret;
+ size_t i;
+
+ memset(abuf, 0xff, sizeof(abuf));
+ ret = snprintf(abuf, ARRAY_COUNT(abuf), "%c %C %hc %hC %lc %lC %s %S
%hs %hS %ls %lS", 'c', L'C', 'c', 'c', L'C', L'C', "str", L"STR", "str", "str",
L"STR", L"STR");
+ if (ret != ARRAY_COUNT(abuf)-2 || memcmp(abuf, "c C c c C C str STR str
str STR STR\x00\xFF", ARRAY_COUNT(abuf)) != 0) {
+ fprintf(stderr, "ret: expected=%d got=%d\n",
ARRAY_COUNT(abuf)-2, ret);
+ fprintf(stderr, "abuf:\n");
+ PRINT_NARROW_BUF(abuf);
+ fprintf(stderr, "\n");
+ return 1;
+ }
+ printf("OK abuf\n");
+
+ memset(wbuf, 0xff, sizeof(wbuf));
+ ret = swprintf(wbuf, ARRAY_COUNT(wbuf), L"%c %C %hc %hC %lc %lC %s %S
%hs %hS %ls", L'C', 'c', 'c', 'c', L'C', L'C', L"STR", "str", "str", "str",
L"STR");
+ if (ret != ARRAY_COUNT(wbuf)-2 || wmemcmp(wbuf, L"C c c c C C STR str
str str STR\x0000\xFFFF", ARRAY_COUNT(wbuf)) != 0) {
+ fprintf(stderr, "ret: expected=%d got=%d\n",
ARRAY_COUNT(wbuf)-2, ret);
+ fprintf(stderr, "wbuf:\n");
+ PRINT_WIDE_BUF(wbuf);
+ fprintf(stderr, "\n");
+ return 1;
+ }
+ printf("OK wbuf\n");
+
+#if __MSVCRT_VERSION__ < 0x200 || __MSVCRT_VERSION__ > 0x400
+
+ /*
+ * These two tests do not work with msvcrt20.dll, msvcrt40.dll and
msvcr40d.dll
+ * which do not support w length modifier. All older and new CRT
libraries support it.
+ */
+
+ memset(abuf2, 0xff, sizeof(abuf2));
+ ret = snprintf(abuf2, ARRAY_COUNT(abuf2), "%wc %wC %ws %wS", L'C',
L'C', L"STR", L"STR");
+ if (ret != ARRAY_COUNT(abuf2)-2 || memcmp(abuf2, "C C STR STR\x00\xFF",
ARRAY_COUNT(abuf2)) != 0) {
+ fprintf(stderr, "ret: expected=%d got=%d\n",
ARRAY_COUNT(abuf2)-2, ret);
+ fprintf(stderr, "abuf2:\n");
+ PRINT_NARROW_BUF(abuf2);
+ fprintf(stderr, "\n");
+ return 1;
+ }
+ printf("OK abuf2\n");
+
+ memset(wbuf2, 0xff, sizeof(wbuf2));
+ ret = swprintf(wbuf2, ARRAY_COUNT(wbuf2), L"%wc %wC %ws %wS", L'C',
L'C', L"STR", L"STR");
+ if (ret != ARRAY_COUNT(wbuf2)-2 || wmemcmp(wbuf2, L"C C STR
STR\x0000\xFFFF", ARRAY_COUNT(wbuf2)) != 0) {
+ fprintf(stderr, "ret: expected=%d got=%d\n",
ARRAY_COUNT(wbuf2)-2, ret);
+ fprintf(stderr, "wbuf2:\n");
+ PRINT_WIDE_BUF(wbuf2);
+ fprintf(stderr, "\n");
+ return 1;
+ }
+ printf("OK wbuf2\n");
+
+#endif
+
+#if __MSVCRT_VERSION__ >= 0x600 || __USE_MINGW_ANSI_STDIO == 1
+
+#if __USE_MINGW_ANSI_STDIO == 0 && !defined(_UCRT)
+ HMODULE msvcrt_dll = GetModuleHandleA("msvcrt.dll");
+ IMAGE_DOS_HEADER *msvcrt_dos = (IMAGE_DOS_HEADER *)msvcrt_dll;
+ IMAGE_NT_HEADERS *msvcrt_pe = (msvcrt_dos && msvcrt_dos->e_magic ==
IMAGE_DOS_SIGNATURE) ? (IMAGE_NT_HEADERS *)((BYTE *)msvcrt_dos +
msvcrt_dos->e_lfanew) : NULL;
+ DWORD msvcrt_version = (msvcrt_pe && msvcrt_pe->Signature ==
IMAGE_NT_SIGNATURE) ? ((msvcrt_pe->OptionalHeader.MajorImageVersion << 16) |
msvcrt_pe->OptionalHeader.MinorImageVersion) : 0;
+ /* Win9x, pre-WinXP and VC42-VC60 msvcrt.dll have zero value in
MajorImageVersion/MinorImageVersion */
+ /* When msvcrt_dll is NULL then we are not using msvcrt.dll library,
but some versioned msvcr*.dll library */
+ if (!msvcrt_dll || msvcrt_version >= 0x0501 /* XP+ */)
+#endif
+ {
+
+ /*
+ * This test do not work with crtdll.dll, msvcrt20.dll, msvcrt40.dll,
msvcr40d.dll and msvcrt.dll before Windows XP
+ * which treats %lS format wprintf as narrow char* and not as wide
wchar_t*.
+ */
+
+ memset(wbuf3, 0xff, sizeof(wbuf3));
+ ret = swprintf(wbuf3, ARRAY_COUNT(wbuf3), L"%lS", L"STR");
+ if (ret != ARRAY_COUNT(wbuf3)-2 || wmemcmp(wbuf3, L"STR\x0000\xFFFF",
ARRAY_COUNT(wbuf3)) != 0) {
+ fprintf(stderr, "ret: expected=%d got=%d\n",
ARRAY_COUNT(wbuf3)-2, ret);
+ fprintf(stderr, "wbuf3:\n");
+ PRINT_WIDE_BUF(wbuf3);
+ fprintf(stderr, "\n");
+ return 1;
+ }
+ printf("OK wbuf3\n");
+
+ }
+
+#endif
+
+ return 0;
+}
--
2.20.1
>From 2433e34624e2e31f879ba4a6b844057f6a292bed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]>
Date: Tue, 24 Jun 2025 21:05:32 +0200
Subject: [PATCH v3 4/6] crt: Fix __pformat_wputchars() for swprintf
__pformat_wputchars() for snprintf, __pformat_puchats() for swprintf and
__pformat_puchats() for snprintf prints all characters including nul chars.
So change __pformat_wputchars() for swprintf to align this behavior.
This change is needed for swprintf(%wZ) support in follow up change.
---
mingw-w64-crt/stdio/mingw_pformat.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mingw-w64-crt/stdio/mingw_pformat.c
b/mingw-w64-crt/stdio/mingw_pformat.c
index 79d5bd46f845..32f473a9e662 100644
--- a/mingw-w64-crt/stdio/mingw_pformat.c
+++ b/mingw-w64-crt/stdio/mingw_pformat.c
@@ -680,7 +680,7 @@ void __pformat_wputchars( const wchar_t *s, int count,
__pformat_t *stream )
__pformat_putc( '\x20', stream );
len = count;
- while(len-- > 0 && *s != 0)
+ while(len-- > 0)
{
__pformat_putc(*s++, stream);
}
--
2.20.1
>From ba1b002ba1beac8356129d0374434ceb177ed9c3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]>
Date: Sun, 22 Jun 2025 16:20:15 +0200
Subject: [PATCH v3 5/6] crt: Add support for mingw_pformat %Z, %hZ, %lZ, %wZ
Format %Z takes pointer to ANSI_STRING or UNICODE_STRING based on the
wideness of format.
In this mingw-w64 implementation (same as in UCRT) the uppercase format %Z
has the same default wideness as uppercase format %S when no h, l or w
modifier is specified.
Format %Z is supported by crtdll, msvcrt and UCRT libraries. There are some
differences between individual CRT versions regarding the wide wprintf(%lZ),
narrow printf(%Z/%lZ) and whether nul chars in ANSI_STRING are accepted by
wprintf or not. In mingw-w64 for compatibility reasons are accepted.
---
mingw-w64-crt/stdio/mingw_pformat.c | 66 +++++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/mingw-w64-crt/stdio/mingw_pformat.c
b/mingw-w64-crt/stdio/mingw_pformat.c
index 32f473a9e662..48eec45df54b 100644
--- a/mingw-w64-crt/stdio/mingw_pformat.c
+++ b/mingw-w64-crt/stdio/mingw_pformat.c
@@ -66,6 +66,7 @@
#include <limits.h>
#include <locale.h>
#include <wchar.h>
+#include <winternl.h>
#ifdef __ENABLE_DFP
#ifndef __STDC_WANT_DEC_FP__
@@ -2562,6 +2563,71 @@ __pformat (int flags, void *dest, int max, const APICHAR
*fmt, va_list argv)
*/
__pformat_puts( va_arg( argv, char * ), &stream );
goto format_scan;
+
+ case 'Z':
+ /*
+ * The logic for `%Z` length modifier is quite complicated.
+ *
+ * for printf:
+ * `%Z` - UNICODE_STRING for UCRT; ANSI_STRING for
crtdll,msvcrt10,msvcrt,msvcr80-msvcr120
+ * `%hZ` - ANSI_STRING
+ * `%lZ` - UNICODE_STRING for UCRT; ANSI_STRING for
crtdll,msvcrt10,msvcrt,msvcr80-msvcr120
+ * `%wZ` - UNICODE_STRING
+ *
+ * for wprintf:
+ * `%Z` - ANSI_STRING
+ * `%hZ` - ANSI_STRING
+ * `%lZ` - UNICODE_STRING for UCRT; ANSI_STRING for
crtdll,msvcrt10,msvcrt,msvcr80-msvcr120
+ * `%wZ` - UNICODE_STRING
+ *
+ * There are some other changes between versions regarding nul
chars.
+ * - msvcrt since Vista, msvcr80+ and UCRT do not accept nul chars
in ANSI_STRING for wprintf.
+ * If encountering a nul char, it stops processing the format
string and returns -1.
+ * - msvcrt before Vista, crtdll and msvcrt10 accept nul char in
ANSI_STRING for wprintf,
+ * but the first nul char and everything after it in ANSI_STRING
content is discarded.
+ * - msvcrt20 does not support %Z format at all.
+ * - msvcrt40 from Visual C++ 4.0 and in Win9x systems does not
support %Z format all.
+ * - msvcrt40 in WinNT systems forwards calls to msvcrt, so it
behaves as msvcrt described above.
+ * - ANSI_STRING for printf, and UNICODE_STRING for both printf
and wprintf works fine
+ * in all versions, every nul byte and all following chars in
the ANSI_STRING/UNICODE_STRING
+ * is processed and printed.
+ *
+ * This mingw-w64 implementation uses UCRT behavior of length
modifiers.
+ * And for compatibility with older msvcrt versions, wprintf
discards
+ * nul char and content after it in ANSI_STRING, and continue
processing
+ * of the format string (like msvcrt before Vista).
+ */
+ if( length == PFORMAT_LENGTH_INT )
+ {
+ #ifndef __BUILD_WIDEAPI
+ length = PFORMAT_LENGTH_LONG;
+ #else
+ length = PFORMAT_LENGTH_SHORT;
+ #endif
+ }
+
+ if( (length == PFORMAT_LENGTH_LONG)
+ || (length == PFORMAT_LENGTH_LLONG)
+ )
+ {
+ const UNICODE_STRING *s = va_arg( argv, UNICODE_STRING * );
+ const wchar_t *buf = (s && s->Buffer) ? (const wchar_t
*)s->Buffer : L"(null)";
+ const int len = (s && s->Buffer) ? s->Length/sizeof(wchar_t) /*
accept content after nul char */ : ( sizeof( "(null)" ) - 1 );
+ __pformat_wputchars( buf, len, &stream );
+ }
+ else
+ {
+ const ANSI_STRING *s = va_arg( argv, ANSI_STRING * );
+ const char *buf = (s && s->Buffer) ? (const char *)s->Buffer :
"(null)";
+ #ifndef __BUILD_WIDEAPI
+ const int len = (s && s->Buffer) ? s->Length /* accept content
after nul char */ : ( sizeof( "(null)" ) - 1 );
+ #else
+ const int len = (s && s->Buffer) ? strnlen( (const char
*)s->Buffer, s->Length ) /* discard content after nul char */ : ( sizeof(
"(null)" ) - 1 );
+ #endif
+ __pformat_putchars( buf, len, &stream );
+ }
+ goto format_scan;
+
case 'm': /* strerror (errno) */
__pformat_puts (strerror (saved_errno), &stream);
goto format_scan;
--
2.20.1
>From 01a9c780672fde6712e010a8cf83efcc76993bae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <[email protected]>
Date: Tue, 24 Jun 2025 20:20:35 +0200
Subject: [PATCH v3 6/6] crt: Add tests for printf format %Z
---
mingw-w64-crt/testcases/Makefile.am | 3 +
mingw-w64-crt/testcases/t_format_Z.c | 2 +
mingw-w64-crt/testcases/t_format_Z0.c | 2 +
mingw-w64-crt/testcases/t_format_Z1.c | 2 +
mingw-w64-crt/testcases/t_format_Z_tmpl.h | 110 ++++++++++++++++++++++
5 files changed, 119 insertions(+)
create mode 100644 mingw-w64-crt/testcases/t_format_Z.c
create mode 100644 mingw-w64-crt/testcases/t_format_Z0.c
create mode 100644 mingw-w64-crt/testcases/t_format_Z1.c
create mode 100644 mingw-w64-crt/testcases/t_format_Z_tmpl.h
diff --git a/mingw-w64-crt/testcases/Makefile.am
b/mingw-w64-crt/testcases/Makefile.am
index bcd32f056d2f..1bf780acdeb1 100644
--- a/mingw-w64-crt/testcases/Makefile.am
+++ b/mingw-w64-crt/testcases/Makefile.am
@@ -27,6 +27,9 @@ testcase_progs = \
t_format_CS \
t_format_CS0 \
t_format_CS1 \
+ t_format_Z \
+ t_format_Z0 \
+ t_format_Z1 \
t_fseeki64 \
t_fseeki64_ftelli64 \
t_fstat \
diff --git a/mingw-w64-crt/testcases/t_format_Z.c
b/mingw-w64-crt/testcases/t_format_Z.c
new file mode 100644
index 000000000000..1b5cb5da6ab0
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_format_Z.c
@@ -0,0 +1,2 @@
+#undef __USE_MINGW_ANSI_STDIO
+#include "t_format_Z_tmpl.h"
diff --git a/mingw-w64-crt/testcases/t_format_Z0.c
b/mingw-w64-crt/testcases/t_format_Z0.c
new file mode 100644
index 000000000000..ce1be00accd0
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_format_Z0.c
@@ -0,0 +1,2 @@
+#define __USE_MINGW_ANSI_STDIO 0
+#include "t_format_Z_tmpl.h"
diff --git a/mingw-w64-crt/testcases/t_format_Z1.c
b/mingw-w64-crt/testcases/t_format_Z1.c
new file mode 100644
index 000000000000..a0ef018d3309
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_format_Z1.c
@@ -0,0 +1,2 @@
+#define __USE_MINGW_ANSI_STDIO 1
+#include "t_format_Z_tmpl.h"
diff --git a/mingw-w64-crt/testcases/t_format_Z_tmpl.h
b/mingw-w64-crt/testcases/t_format_Z_tmpl.h
new file mode 100644
index 000000000000..0779190a15b5
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_format_Z_tmpl.h
@@ -0,0 +1,110 @@
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+#include <ntdef.h>
+
+#define ARRAY_COUNT(a) (sizeof(a)/sizeof(a[0]))
+#define DEF_STRING(str) { sizeof(str)-sizeof(str[0]),
sizeof(str)-sizeof(str[0]), str }
+
+#define PRINT_NARROW_BUF(buf) \
+ for (i = 0; i < ARRAY_COUNT(buf); i++) { \
+ if ((buf)[i] == '\n' || ((buf)[i] >= 0x20 && (buf)[i] <= 0x7E
&& (buf)[i] != '\\')) \
+ fputc((buf)[i], stderr); \
+ else \
+ fprintf(stderr, "\\x%02X", (unsigned int)(unsigned
char)(buf)[i]); \
+ }
+
+#define PRINT_WIDE_BUF(buf) \
+ for (i = 0; i < ARRAY_COUNT(buf); i++) { \
+ if ((buf)[i] == L'\n' || ((buf)[i] >= 0x20 && (buf)[i] <= 0x7E
&& (buf)[i] != L'\\')) \
+ fputc((char)(buf)[i], stderr); \
+ else \
+ fprintf(stderr, "\\x%04X", (unsigned
int)(wint_t)(buf)[i]); \
+ } \
+
+int main() {
+ ANSI_STRING anull = {};
+ UNICODE_STRING wnull = {};
+ ANSI_STRING as0 = DEF_STRING("TEST\x00XYZA");
+ ANSI_STRING as1 = DEF_STRING("TEST");
+ UNICODE_STRING ws0 = DEF_STRING(L"TEST\x0000XYZA");
+ UNICODE_STRING ws1 = DEF_STRING(L"TEST");
+ char abuf[70];
+ char abuf2[45];
+ wchar_t wbuf[55];
+ wchar_t wbuf2[50];
+ int ret;
+ size_t i;
+
+ (void)ws1; /* avoid "warning: unused variable" on some builds */
+
+ /*
+ * These tests do not work with msvcrt20.dll, msvcrt40.dll and
msvcr40d.dll
+ * which do not support %Z format. All older and new CRT libraries
support it.
+ *
+ * UCRT does not accept nul characters in ANSI_STRING for wide printf
functions
+ * and treats them as errors (signaled by negative return value and
stopping
+ * formatting). They are accepted in narrow printf functions. Nul
characters
+ * in UNICODE_STRING are accepted by both narrow and wide printf
functions.
+ */
+
+#if __MSVCRT_VERSION__ >= 0x200 && __MSVCRT_VERSION__ <= 0x400
+ return 0;
+#endif
+
+ memset(abuf, 0xff, sizeof(abuf));
+#if __USE_MINGW_ANSI_STDIO == 1 || defined(_UCRT)
+ ret = snprintf(abuf, ARRAY_COUNT(abuf),
"%Z\n%hZ\n%lZ\n%wZ\n%hZ\n%hZ\n%wZ\n%wZ\n", &ws0, &as0, &ws0, &ws0, &anull,
NULL, &wnull, NULL);
+#else
+ ret = snprintf(abuf, ARRAY_COUNT(abuf),
"%Z\n%hZ\n%lZ\n%wZ\n%hZ\n%hZ\n%wZ\n%wZ\n", &as0, &as0, &as0, &ws0, &anull,
NULL, &wnull, NULL);
+#endif
+ if (ret != ARRAY_COUNT(abuf)-2 || memcmp(abuf,
"TEST\x00XYZA\nTEST\x00XYZA\nTEST\x00XYZA\nTEST\x00XYZA\n(null)\n(null)\n(null)\n(null)\n\x00\xFF",
ARRAY_COUNT(abuf)) != 0) {
+ fprintf(stderr, "ret: expected=%d got=%d\n",
ARRAY_COUNT(abuf)-2, ret);
+ fprintf(stderr, "abuf:\n");
+ PRINT_NARROW_BUF(abuf);
+ fprintf(stderr, "\n");
+ return 1;
+ }
+ printf("OK abuf\n");
+
+ memset(abuf2, 0xff, sizeof(abuf2));
+ ret = snprintf(abuf2, ARRAY_COUNT(abuf2),
"%hZ\n%wZ\n%hZ\n%hZ\n%wZ\n%wZ\n", &as1, &ws0, &anull, NULL, &wnull, NULL);
+ if (ret != ARRAY_COUNT(abuf2)-2 || memcmp(abuf2,
"TEST\nTEST\x00XYZA\n(null)\n(null)\n(null)\n(null)\n\x00\xFF",
ARRAY_COUNT(abuf2)) != 0) {
+ fprintf(stderr, "ret: expected=%d got=%d\n",
ARRAY_COUNT(abuf2)-2, ret);
+ fprintf(stderr, "abuf2:\n");
+ PRINT_NARROW_BUF(abuf2);
+ fprintf(stderr, "\n");
+ return 1;
+ }
+ printf("OK abuf2\n");
+
+ memset(wbuf, 0xff, sizeof(wbuf));
+#if __USE_MINGW_ANSI_STDIO == 1
+ ret = swprintf(wbuf, ARRAY_COUNT(wbuf),
L"%Z\n%hZ\n%lZ\n%wZ\n%hZ\n%hZ\n%wZ\n%wZ\n", &as0, &as0, &ws1, &ws0, &anull,
NULL, &wnull, NULL);
+#elif defined(_UCRT)
+ ret = swprintf(wbuf, ARRAY_COUNT(wbuf),
L"%Z\n%hZ\n%lZ\n%wZ\n%hZ\n%hZ\n%wZ\n%wZ\n", &as1, &as1, &ws1, &ws0, &anull,
NULL, &wnull, NULL);
+#else
+ ret = swprintf(wbuf, ARRAY_COUNT(wbuf),
L"%Z\n%hZ\n%lZ\n%wZ\n%hZ\n%hZ\n%wZ\n%wZ\n", &as1, &as1, &as1, &ws0, &anull,
NULL, &wnull, NULL);
+#endif
+ if (ret != ARRAY_COUNT(wbuf)-2 || wmemcmp(wbuf,
L"TEST\nTEST\nTEST\nTEST\x00XYZA\n(null)\n(null)\n(null)\n(null)\n\x0000\xFFFF",
ARRAY_COUNT(wbuf)) != 0) {
+ fprintf(stderr, "ret: expected=%d got=%d\n",
ARRAY_COUNT(wbuf)-2, ret);
+ fprintf(stderr, "wbuf:\n");
+ PRINT_WIDE_BUF(wbuf);
+ fprintf(stderr, "\n");
+ return 1;
+ }
+ printf("OK wbuf\n");
+
+ memset(wbuf2, 0xff, sizeof(wbuf2));
+ ret = swprintf(wbuf2, ARRAY_COUNT(wbuf2),
L"%Z\n%hZ\n%wZ\n%hZ\n%hZ\n%wZ\n%wZ\n", &as1, &as1, &ws0, &anull, NULL, &wnull,
NULL);
+ if (ret != ARRAY_COUNT(wbuf2)-2 || wmemcmp(wbuf2,
L"TEST\nTEST\nTEST\x00XYZA\n(null)\n(null)\n(null)\n(null)\n\x0000\xFFFF",
ARRAY_COUNT(wbuf2)) != 0) {
+ fprintf(stderr, "ret: expected=%d got=%d\n",
ARRAY_COUNT(wbuf2)-2, ret);
+ fprintf(stderr, "wbuf2:\n");
+ PRINT_WIDE_BUF(wbuf2);
+ fprintf(stderr, "\n");
+ return 1;
+ }
+ printf("OK wbuf2\n");
+
+ return 0;
+}
--
2.20.1
_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public