]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
C2x printf %wN, %wfN support (bug 24466)
authorJoseph Myers <joseph@codesourcery.com>
Mon, 19 Jun 2023 18:52:12 +0000 (18:52 +0000)
committerJoseph Myers <joseph@codesourcery.com>
Mon, 19 Jun 2023 18:52:12 +0000 (18:52 +0000)
ISO C2x defines printf length modifiers wN (for intN_t / int_leastN_t
/ uintN_t / uint_leastN_t) and wfN (for int_fastN_t / uint_fastN_t).
Add support for those length modifiers (such a feature was previously
requested in bug 24466).  scanf support is to be added separately.
GCC 13 has format checking support for these modifiers.

When used with the support for registering format specifiers, these
modifiers are translated to existing flags in struct printf_info,
rather than trying to add some way of distinguishing them without
breaking the printf_info ABI.  C2x requires an error to be returned
for unsupported values of N; this is implemented for printf-family
functions, but the parse_printf_format interface doesn't support error
returns, so such an error gets discarded by that function.

Tested for x86_64 and x86.

NEWS
manual/stdio.texi
stdio-common/Makefile
stdio-common/printf-parse.h
stdio-common/printf-parsemb.c
stdio-common/printf-prs.c
stdio-common/tst-printf-intn-main.c [new file with mode: 0644]
stdio-common/tst-printf-intn.c [new file with mode: 0644]
stdio-common/vfprintf-internal.c
wcsmbs/Makefile
wcsmbs/tst-wprintf-intn.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 7a4df85f081b68dc468068fd1b739d37b24079f1..7cec03cc76938f43aff70ed94b0bd56f9fde6b62 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,13 @@ Major new features:
 
 * PRIb* and PRIB* macros from C2X have been added to <inttypes.h>.
 
+* printf-family functions now support the wN format length modifiers for
+  arguments of type intN_t, int_leastN_t, uintN_t or uint_leastN_t (for
+  example, %w32d to print int32_t or int_least32_t in decimal, or %w32x
+  to print uint32_t or uint_least32_t in hexadecimal) and the wfN format
+  length modifiers for arguments of type int_fastN_t or uint_fastN_t, as
+  specified in draft ISO C2X.
+
 * A new tunable, glibc.pthread.stack_hugetlb, can be used to disable
   Transparent Huge Pages (THP) in stack allocation at pthread_create.
 
index 3820a24f3e164f7536299c5ac01ab0c69df6fe0a..a981e6512a1dc35f7ae9dd1095dbcf500b179b12 100644 (file)
@@ -2028,6 +2028,24 @@ Specifies that the argument is a @code{ptrdiff_t}.
 
 This modifier was introduced in @w{ISO C99}.
 
+@item w@var{n}
+Specifies that the argument is a @code{int@var{n}_t} or
+@code{int_least@var{n}_t} (which are the same type), for conversions
+taking signed integers, or @code{uint@var{n}_t} or
+@code{uint_least@var{n}_t} (which are the same type), for conversions
+taking unsigned integers.  If the type is narrower than @code{int},
+the promoted argument is converted back to the specified type.
+
+This modifier was introduced in @w{ISO C2X}.
+
+@item wf@var{n}
+Specifies that the argument is a @code{int_fast@var{n}_t} or
+@code{uint_fast@var{n}_t}, as appropriate.  If the type is narrower
+than @code{int}, the promoted argument is converted back to the
+specified type.
+
+This modifier was introduced in @w{ISO C2X}.
+
 @item z
 @itemx Z
 Specifies that the argument is a @code{size_t}.
index 4c15b97683c7c03b830d849345654429136e18e0..8871ec76689134f10178d260336dfa7c5ef24e1f 100644 (file)
@@ -219,6 +219,7 @@ tests := \
   tst-printf-bz25691 \
   tst-printf-fp-free \
   tst-printf-fp-leak \
+  tst-printf-intn \
   tst-printf-oct \
   tst-printf-round \
   tst-printfsz \
index b25181496c2ba874fcc3644ae14a808167a439c2..57673a331dcfca52cf968adccd870dfd0a989649 100644 (file)
@@ -93,14 +93,17 @@ __find_specwc (const unsigned int *format)
    with the parsed details.  POSN is the number of arguments already
    consumed.  At most MAXTYPES - POSN types are filled in TYPES.  Return
    the number of args consumed by this spec; *MAX_REF_ARG is updated so it
-   remains the highest argument index used.  */
+   remains the highest argument index used.  *FAILED is set to indicate
+   whether parsing failed and printf should return with an error status.  */
 extern size_t __parse_one_specmb (const unsigned char *format, size_t posn,
                                  struct printf_spec *spec,
-                                 size_t *max_ref_arg) attribute_hidden;
+                                 size_t *max_ref_arg,
+                                 bool *failed) attribute_hidden;
 
 extern size_t __parse_one_specwc (const unsigned int *format, size_t posn,
                                  struct printf_spec *spec,
-                                 size_t *max_ref_arg) attribute_hidden;
+                                 size_t *max_ref_arg,
+                                 bool *failed) attribute_hidden;
 
 
 
index c5d2704b02393f81f6612c13e7a5d1d00271147e..414cbc722397aeed941b3cdb06e96ef959653e94 100644 (file)
@@ -56,14 +56,17 @@ size_t
 attribute_hidden
 #ifdef COMPILE_WPRINTF
 __parse_one_specwc (const UCHAR_T *format, size_t posn,
-                   struct printf_spec *spec, size_t *max_ref_arg)
+                   struct printf_spec *spec, size_t *max_ref_arg,
+                   bool *failed)
 #else
 __parse_one_specmb (const UCHAR_T *format, size_t posn,
-                   struct printf_spec *spec, size_t *max_ref_arg)
+                   struct printf_spec *spec, size_t *max_ref_arg,
+                   bool *failed)
 #endif
 {
   unsigned int n;
   size_t nargs = 0;
+  bool is_fast;
 
   /* Skip the '%'.  */
   ++format;
@@ -81,6 +84,8 @@ __parse_one_specmb (const UCHAR_T *format, size_t posn,
   spec->info.wide = sizeof (UCHAR_T) > 1;
   spec->info.is_binary128 = 0;
 
+  *failed = false;
+
   /* Test for positional argument.  */
   if (ISDIGIT (*format))
     {
@@ -298,6 +303,53 @@ __parse_one_specmb (const UCHAR_T *format, size_t posn,
 #endif
        spec->info.is_long = sizeof (uintmax_t) > sizeof (unsigned int);
        break;
+      case L_('w'):
+       is_fast = false;
+       if (*format == L_('f'))
+         {
+           ++format;
+           is_fast = true;
+         }
+       int bitwidth = 0;
+       if (ISDIGIT (*format))
+         bitwidth = read_int (&format);
+       if (is_fast)
+         switch (bitwidth)
+           {
+           case 8:
+             bitwidth = INT_FAST8_WIDTH;
+             break;
+           case 16:
+             bitwidth = INT_FAST16_WIDTH;
+             break;
+           case 32:
+             bitwidth = INT_FAST32_WIDTH;
+             break;
+           case 64:
+             bitwidth = INT_FAST64_WIDTH;
+             break;
+           }
+       switch (bitwidth)
+         {
+         case 8:
+           spec->info.is_char = 1;
+           break;
+         case 16:
+           spec->info.is_short = 1;
+           break;
+         case 32:
+           break;
+         case 64:
+           spec->info.is_long_double = 1;
+           spec->info.is_long = 1;
+           break;
+         default:
+           /* ISO C requires this error to be detected.  */
+           __set_errno (EINVAL);
+           *failed = true;
+           break;
+         }
+       break;
       default:
        /* Not a recognized modifier.  Backup.  */
        --format;
index 2408a2e328b931b301d711532754e26c28a70d77..9d8bf306e459642c9fa400c4aaacd03cee9466d2 100644 (file)
@@ -63,6 +63,7 @@ parse_printf_format (const char *fmt, size_t n, int *argtypes)
   size_t max_ref_arg;          /* Highest index used in a positional arg.  */
   struct printf_spec spec;
   const unsigned char *f = (const unsigned char *) fmt;
+  bool failed;
 
   nargs = 0;
   max_ref_arg = 0;
@@ -71,7 +72,7 @@ parse_printf_format (const char *fmt, size_t n, int *argtypes)
   for (f = __find_specmb (f); *f != '\0'; f = spec.next_fmt)
     {
       /* Parse this spec.  */
-      nargs += __parse_one_specmb (f, nargs, &spec, &max_ref_arg);
+      nargs += __parse_one_specmb (f, nargs, &spec, &max_ref_arg, &failed);
 
       /* If the width is determined by an argument, it is an int.  */
       if (spec.width_arg != -1 && (size_t) spec.width_arg < n)
diff --git a/stdio-common/tst-printf-intn-main.c b/stdio-common/tst-printf-intn-main.c
new file mode 100644 (file)
index 0000000..bd70e9d
--- /dev/null
@@ -0,0 +1,637 @@
+/* Test printf formats for intN_t, int_leastN_t and int_fastN_t types.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <libc-diag.h>
+#include <support/check.h>
+
+/* GCC does not know the %wN or %wfN length modifiers before GCC 13.  */
+DIAG_PUSH_NEEDS_COMMENT;
+#if !__GNUC_PREREQ (13, 0)
+DIAG_IGNORE_NEEDS_COMMENT (12, "-Wformat");
+DIAG_IGNORE_NEEDS_COMMENT (12, "-Wformat-extra-args");
+#endif
+
+#define CHECK_PRINTF(EXPECTED, FMT, ...)                               \
+  do                                                                   \
+    {                                                                  \
+      int ret = SNPRINTF (buf, sizeof buf / sizeof buf[0], L_(FMT),    \
+                         __VA_ARGS__);                                 \
+      TEST_COMPARE_STRING_MACRO (buf, L_(EXPECTED));                   \
+      TEST_COMPARE (ret, STRLEN (L_(EXPECTED)));                       \
+    }                                                                  \
+  while (0)
+
+#define CHECK_PRINTF_ERR(FMT, ...)                                     \
+  do                                                                   \
+    {                                                                  \
+      int ret = SNPRINTF (buf, sizeof buf / sizeof buf[0], L_(FMT),    \
+                         __VA_ARGS__);                                 \
+      TEST_VERIFY (ret < 0);                                           \
+      TEST_COMPARE (errno, EINVAL);                                    \
+    }                                                                  \
+  while (0)
+
+static void
+test_w8 (void)
+{
+  CHAR buf[1024];
+  CHECK_PRINTF ("123", "%w8d", (int8_t) 123);
+  CHECK_PRINTF ("-123", "%w8d", (int8_t) -123);
+  CHECK_PRINTF ("123", "%w8i", (int8_t) 123);
+  CHECK_PRINTF ("-123", "%w8i", (int8_t) -123);
+  CHECK_PRINTF ("1111011", "%w8b", (uint8_t) 123);
+  CHECK_PRINTF ("1111011", "%w8B", (uint8_t) 123);
+  CHECK_PRINTF ("173", "%w8o", (uint8_t) 123);
+  CHECK_PRINTF ("123", "%w8u", (uint8_t) 123);
+  CHECK_PRINTF ("7b", "%w8x", (uint8_t) 123);
+  CHECK_PRINTF ("7B", "%w8X", (uint8_t) 123);
+  CHECK_PRINTF ("  123", "%5w8d", (int8_t) 123);
+  CHECK_PRINTF ("  123", "%*w8d", 5, (int8_t) 123);
+  CHECK_PRINTF ("0x7b", "%#w8x", (uint8_t) 123);
+  CHECK_PRINTF ("00123", "%.5w8d", (int8_t) 123);
+  CHECK_PRINTF ("00123", "%.*w8d", 5, (int8_t) 123);
+  CHECK_PRINTF ("   00123", "%8.5w8d", (int8_t) 123);
+  CHECK_PRINTF ("   00123", "%*.5w8d", 8, (int8_t) 123);
+  CHECK_PRINTF ("   00123", "%8.*w8d", 5, (int8_t) 123);
+  CHECK_PRINTF ("   00123", "%*.*w8d", 8, 5, (int8_t) 123);
+  CHECK_PRINTF ("00123   ", "%-8.5w8d", (int8_t) 123);
+  CHECK_PRINTF ("00123   ", "%-*.5w8d", 8, (int8_t) 123);
+  CHECK_PRINTF ("00123   ", "%-8.*w8d", 5, (int8_t) 123);
+  CHECK_PRINTF ("00123   ", "%-*.*w8d", 8, 5, (int8_t) 123);
+  {
+    int8_t n = -1;
+    CHECK_PRINTF ("12345", "%d%w8n", 12345, &n);
+    TEST_COMPARE (n, 5);
+  }
+  CHECK_PRINTF ("123", "%w8d", (int_least8_t) 123);
+  CHECK_PRINTF ("-123", "%w8d", (int_least8_t) -123);
+  CHECK_PRINTF ("123", "%w8i", (int_least8_t) 123);
+  CHECK_PRINTF ("-123", "%w8i", (int_least8_t) -123);
+  CHECK_PRINTF ("1111011", "%w8b", (uint_least8_t) 123);
+  CHECK_PRINTF ("1111011", "%w8B", (uint_least8_t) 123);
+  CHECK_PRINTF ("173", "%w8o", (uint_least8_t) 123);
+  CHECK_PRINTF ("123", "%w8u", (uint_least8_t) 123);
+  CHECK_PRINTF ("7b", "%w8x", (uint_least8_t) 123);
+  CHECK_PRINTF ("7B", "%w8X", (uint_least8_t) 123);
+  CHECK_PRINTF ("  123", "%5w8d", (int_least8_t) 123);
+  CHECK_PRINTF ("  123", "%*w8d", 5, (int_least8_t) 123);
+  CHECK_PRINTF ("0x7b", "%#w8x", (uint_least8_t) 123);
+  CHECK_PRINTF ("00123", "%.5w8d", (int_least8_t) 123);
+  CHECK_PRINTF ("00123", "%.*w8d", 5, (int_least8_t) 123);
+  CHECK_PRINTF ("   00123", "%8.5w8d", (int_least8_t) 123);
+  CHECK_PRINTF ("   00123", "%*.5w8d", 8, (int_least8_t) 123);
+  CHECK_PRINTF ("   00123", "%8.*w8d", 5, (int_least8_t) 123);
+  CHECK_PRINTF ("   00123", "%*.*w8d", 8, 5, (int_least8_t) 123);
+  CHECK_PRINTF ("00123   ", "%-8.5w8d", (int_least8_t) 123);
+  CHECK_PRINTF ("00123   ", "%-*.5w8d", 8, (int_least8_t) 123);
+  CHECK_PRINTF ("00123   ", "%-8.*w8d", 5, (int_least8_t) 123);
+  CHECK_PRINTF ("00123   ", "%-*.*w8d", 8, 5, (int_least8_t) 123);
+  {
+    int_least8_t ln = -1;
+    CHECK_PRINTF ("12345", "%d%w8n", 12345, &ln);
+    TEST_COMPARE (ln, 5);
+  }
+  /* Test truncation of value in promoted type not representable in
+     narrower type.  */
+  CHECK_PRINTF ("57", "%w8d", 12345);
+  CHECK_PRINTF ("-57", "%w8d", -12345);
+  CHECK_PRINTF ("-121", "%w8d", 1234567);
+  CHECK_PRINTF ("121", "%w8d", -1234567);
+  CHECK_PRINTF ("135", "%w8u", 1234567);
+  /* Test positional argument handling.  */
+  CHECK_PRINTF ("test 10 test2 20", "%4$s %3$w8d %2$s %1$w8d",
+               276, "test2", 266, "test");
+}
+
+static void
+test_wf8 (void)
+{
+  CHAR buf[1024];
+  _Static_assert (sizeof (int_fast8_t) == sizeof (char),
+                 "test assumes size of int_fast8_t");
+  CHECK_PRINTF ("123", "%wf8d", (int_fast8_t) 123);
+  CHECK_PRINTF ("-123", "%wf8d", (int_fast8_t) -123);
+  CHECK_PRINTF ("123", "%wf8i", (int_fast8_t) 123);
+  CHECK_PRINTF ("-123", "%wf8i", (int_fast8_t) -123);
+  CHECK_PRINTF ("1111011", "%wf8b", (uint_fast8_t) 123);
+  CHECK_PRINTF ("1111011", "%wf8B", (uint_fast8_t) 123);
+  CHECK_PRINTF ("173", "%wf8o", (uint_fast8_t) 123);
+  CHECK_PRINTF ("123", "%wf8u", (uint_fast8_t) 123);
+  CHECK_PRINTF ("7b", "%wf8x", (uint_fast8_t) 123);
+  CHECK_PRINTF ("7B", "%wf8X", (uint_fast8_t) 123);
+  CHECK_PRINTF ("  123", "%5w8d", (int_fast8_t) 123);
+  CHECK_PRINTF ("  123", "%*w8d", 5, (int_fast8_t) 123);
+  CHECK_PRINTF ("0x7b", "%#w8x", (uint_fast8_t) 123);
+  CHECK_PRINTF ("00123", "%.5w8d", (int_fast8_t) 123);
+  CHECK_PRINTF ("00123", "%.*w8d", 5, (int_fast8_t) 123);
+  CHECK_PRINTF ("   00123", "%8.5w8d", (int_fast8_t) 123);
+  CHECK_PRINTF ("   00123", "%*.5w8d", 8, (int_fast8_t) 123);
+  CHECK_PRINTF ("   00123", "%8.*w8d", 5, (int_fast8_t) 123);
+  CHECK_PRINTF ("   00123", "%*.*w8d", 8, 5, (int_fast8_t) 123);
+  CHECK_PRINTF ("00123   ", "%-8.5w8d", (int_fast8_t) 123);
+  CHECK_PRINTF ("00123   ", "%-*.5w8d", 8, (int_fast8_t) 123);
+  CHECK_PRINTF ("00123   ", "%-8.*w8d", 5, (int_fast8_t) 123);
+  CHECK_PRINTF ("00123   ", "%-*.*w8d", 8, 5, (int_fast8_t) 123);
+  {
+    int_fast8_t n = -1;
+    CHECK_PRINTF ("12345", "%d%wf8n", 12345, &n);
+    TEST_COMPARE (n, 5);
+  }
+  /* Test truncation of value in promoted type not representable in
+     narrower type.  */
+  CHECK_PRINTF ("57", "%wf8d", 12345);
+  CHECK_PRINTF ("-57", "%wf8d", -12345);
+  CHECK_PRINTF ("-121", "%wf8d", 1234567);
+  CHECK_PRINTF ("121", "%wf8d", -1234567);
+  CHECK_PRINTF ("135", "%wf8u", 1234567);
+  /* Test positional argument handling.  */
+  CHECK_PRINTF ("test 10 test2 20", "%4$s %3$wf8d %2$s %1$wf8d",
+               276, "test2", 266, "test");
+}
+
+static void
+test_w16 (void)
+{
+  CHAR buf[1024];
+  CHECK_PRINTF ("12345", "%w16d", (int16_t) 12345);
+  CHECK_PRINTF ("-12345", "%w16d", (int16_t) -12345);
+  CHECK_PRINTF ("12345", "%w16i", (int16_t) 12345);
+  CHECK_PRINTF ("-12345", "%w16i", (int16_t) -12345);
+  CHECK_PRINTF ("11000000111001", "%w16b", (uint16_t) 12345);
+  CHECK_PRINTF ("11000000111001", "%w16B", (uint16_t) 12345);
+  CHECK_PRINTF ("30071", "%w16o", (uint16_t) 12345);
+  CHECK_PRINTF ("12345", "%w16u", (uint16_t) 12345);
+  CHECK_PRINTF ("303a", "%w16x", (uint16_t) 12346);
+  CHECK_PRINTF ("303A", "%w16X", (uint16_t) 12346);
+  CHECK_PRINTF ("  12345", "%7w16d", (int16_t) 12345);
+  CHECK_PRINTF ("  12345", "%*w16d", 7, (int16_t) 12345);
+  CHECK_PRINTF ("0x3039", "%#w16x", (uint16_t) 12345);
+  CHECK_PRINTF ("0012345", "%.7w16d", (int16_t) 12345);
+  CHECK_PRINTF ("0012345", "%.*w16d", 7, (int16_t) 12345);
+  CHECK_PRINTF ("   0012345", "%10.7w16d", (int16_t) 12345);
+  CHECK_PRINTF ("   0012345", "%*.7w16d", 10, (int16_t) 12345);
+  CHECK_PRINTF ("   0012345", "%10.*w16d", 7, (int16_t) 12345);
+  CHECK_PRINTF ("   0012345", "%*.*w16d", 10, 7, (int16_t) 12345);
+  CHECK_PRINTF ("0012345   ", "%-10.7w16d", (int16_t) 12345);
+  CHECK_PRINTF ("0012345   ", "%-*.7w16d", 10, (int16_t) 12345);
+  CHECK_PRINTF ("0012345   ", "%-10.*w16d", 7, (int16_t) 12345);
+  CHECK_PRINTF ("0012345   ", "%-*.*w16d", 10, 7, (int16_t) 12345);
+  {
+    int16_t n = -1;
+    CHECK_PRINTF ("12345", "%d%w16n", 12345, &n);
+    TEST_COMPARE (n, 5);
+  }
+  CHECK_PRINTF ("12345", "%w16d", (int_least16_t) 12345);
+  CHECK_PRINTF ("-12345", "%w16d", (int_least16_t) -12345);
+  CHECK_PRINTF ("12345", "%w16i", (int_least16_t) 12345);
+  CHECK_PRINTF ("-12345", "%w16i", (int_least16_t) -12345);
+  CHECK_PRINTF ("11000000111001", "%w16b", (uint_least16_t) 12345);
+  CHECK_PRINTF ("11000000111001", "%w16B", (uint_least16_t) 12345);
+  CHECK_PRINTF ("30071", "%w16o", (uint_least16_t) 12345);
+  CHECK_PRINTF ("12345", "%w16u", (uint_least16_t) 12345);
+  CHECK_PRINTF ("303a", "%w16x", (uint_least16_t) 12346);
+  CHECK_PRINTF ("303A", "%w16X", (uint_least16_t) 12346);
+  CHECK_PRINTF ("  12345", "%7w16d", (int_least16_t) 12345);
+  CHECK_PRINTF ("  12345", "%*w16d", 7, (int_least16_t) 12345);
+  CHECK_PRINTF ("0x3039", "%#w16x", (uint_least16_t) 12345);
+  CHECK_PRINTF ("0012345", "%.7w16d", (int_least16_t) 12345);
+  CHECK_PRINTF ("0012345", "%.*w16d", 7, (int_least16_t) 12345);
+  CHECK_PRINTF ("   0012345", "%10.7w16d", (int_least16_t) 12345);
+  CHECK_PRINTF ("   0012345", "%*.7w16d", 10, (int_least16_t) 12345);
+  CHECK_PRINTF ("   0012345", "%10.*w16d", 7, (int_least16_t) 12345);
+  CHECK_PRINTF ("   0012345", "%*.*w16d", 10, 7, (int_least16_t) 12345);
+  CHECK_PRINTF ("0012345   ", "%-10.7w16d", (int_least16_t) 12345);
+  CHECK_PRINTF ("0012345   ", "%-*.7w16d", 10, (int_least16_t) 12345);
+  CHECK_PRINTF ("0012345   ", "%-10.*w16d", 7, (int_least16_t) 12345);
+  CHECK_PRINTF ("0012345   ", "%-*.*w16d", 10, 7, (int_least16_t) 12345);
+  {
+    int_least16_t ln = -1;
+    CHECK_PRINTF ("12345", "%d%w16n", 12345, &ln);
+    TEST_COMPARE (ln, 5);
+  }
+  /* Test truncation of value in promoted type not representable in
+     narrower type.  */
+  CHECK_PRINTF ("4464", "%w16d", 70000);
+  CHECK_PRINTF ("-4464", "%w16d", -70000);
+  CHECK_PRINTF ("-7616", "%w16d", 123456);
+  CHECK_PRINTF ("7616", "%w16d", -123456);
+  CHECK_PRINTF ("57920", "%w16u", 123456);
+  /* Test positional argument handling.  */
+  CHECK_PRINTF ("test 10 test2 20", "%4$s %3$w16d %2$s %1$w16d",
+               65556, "test2", 65546, "test");
+}
+
+static void
+test_wf16 (void)
+{
+  CHAR buf[1024];
+  _Static_assert (sizeof (int_fast16_t) == sizeof (long int),
+                 "test assumes size of int_fast16_t");
+  CHECK_PRINTF ("1234567", "%wf16d", (int_fast16_t) 1234567);
+  CHECK_PRINTF ("-1234567", "%wf16d", (int_fast16_t) -1234567);
+  CHECK_PRINTF ("1234567", "%wf16i", (int_fast16_t) 1234567);
+  CHECK_PRINTF ("-1234567", "%wf16i", (int_fast16_t) -1234567);
+  CHECK_PRINTF ("100101101011010000111", "%wf16b", (uint_fast16_t) 1234567);
+  CHECK_PRINTF ("100101101011010000111", "%wf16B", (uint_fast16_t) 1234567);
+  CHECK_PRINTF ("4553207", "%wf16o", (uint_fast16_t) 1234567);
+  CHECK_PRINTF ("1234567", "%wf16u", (uint_fast16_t) 1234567);
+  CHECK_PRINTF ("12d687", "%wf16x", (uint_fast16_t) 1234567);
+  CHECK_PRINTF ("12D687", "%wf16X", (uint_fast16_t) 1234567);
+  CHECK_PRINTF ("  1234567", "%9wf16d", (int_fast16_t) 1234567);
+  CHECK_PRINTF ("  1234567", "%*wf16d", 9, (int_fast16_t) 1234567);
+  CHECK_PRINTF ("0x12d687", "%#wf16x", (uint_fast16_t) 1234567);
+  CHECK_PRINTF ("001234567", "%.9wf16d", (int_fast16_t) 1234567);
+  CHECK_PRINTF ("001234567", "%.*wf16d", 9, (int_fast16_t) 1234567);
+  CHECK_PRINTF ("   001234567", "%12.9wf16d", (int_fast16_t) 1234567);
+  CHECK_PRINTF ("   001234567", "%*.9wf16d", 12, (int_fast16_t) 1234567);
+  CHECK_PRINTF ("   001234567", "%12.*wf16d", 9, (int_fast16_t) 1234567);
+  CHECK_PRINTF ("   001234567", "%*.*wf16d", 12, 9, (int_fast16_t) 1234567);
+  CHECK_PRINTF ("001234567   ", "%-12.9wf16d", (int_fast16_t) 1234567);
+  CHECK_PRINTF ("001234567   ", "%-*.9wf16d", 12, (int_fast16_t) 1234567);
+  CHECK_PRINTF ("001234567   ", "%-12.*wf16d", 9, (int_fast16_t) 1234567);
+  CHECK_PRINTF ("001234567   ", "%-*.*wf16d", 12, 9, (int_fast16_t) 1234567);
+  {
+    int_fast16_t n = -1;
+    CHECK_PRINTF ("12345", "%d%wf16n", 12345, &n);
+    TEST_COMPARE (n, 5);
+  }
+  /* Test positional argument handling.  */
+  CHECK_PRINTF ("test 123456 test2 234567", "%4$s %3$wf16d %2$s %1$wf16d",
+               (int_fast16_t) 234567, "test2", (int_fast16_t) 123456, "test");
+#if INT_FAST16_MAX > 0x7fffffff
+  CHECK_PRINTF ("12345678901", "%wf16d", (int_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("-12345678901", "%wf16d", (int_fast16_t) -12345678901LL);
+  CHECK_PRINTF ("12345678901", "%wf16i", (int_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("-12345678901", "%wf16i", (int_fast16_t) -12345678901LL);
+  CHECK_PRINTF ("1011011111110111000001110000110101", "%wf16b",
+               (uint_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("1011011111110111000001110000110101", "%wf16B",
+               (uint_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("133767016065", "%wf16o", (uint_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("12345678901", "%wf16u", (uint_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("2dfdc1c35", "%wf16x", (uint_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("2DFDC1C35", "%wf16X", (uint_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("  12345678901", "%13wf16d", (int_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("  12345678901", "%*wf16d", 13, (int_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("0x2dfdc1c35", "%#wf16x", (uint_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901", "%.13wf16d", (int_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901", "%.*wf16d", 13, (int_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%16.13wf16d",
+               (int_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%*.13wf16d", 16,
+               (int_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%16.*wf16d", 13,
+               (int_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%*.*wf16d", 16, 13,
+               (int_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-16.13wf16d",
+               (int_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-*.13wf16d", 16,
+               (int_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-16.*wf16d", 13,
+               (int_fast16_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-*.*wf16d", 16, 13,
+               (int_fast16_t) 12345678901LL);
+  /* Test positional argument handling.  */
+  CHECK_PRINTF ("test 123456789012 test2 234567890123",
+               "%4$s %3$wf16d %2$s %1$wf16d",
+               (int_fast16_t) 234567890123ULL, "test2",
+               (int_fast16_t) 123456789012ULL, "test");
+#endif
+}
+
+static void
+test_w32 (void)
+{
+  CHAR buf[1024];
+  CHECK_PRINTF ("1234567", "%w32d", (int32_t) 1234567);
+  CHECK_PRINTF ("-1234567", "%w32d", (int32_t) -1234567);
+  CHECK_PRINTF ("1234567", "%w32i", (int32_t) 1234567);
+  CHECK_PRINTF ("-1234567", "%w32i", (int32_t) -1234567);
+  CHECK_PRINTF ("100101101011010000111", "%w32b", (uint32_t) 1234567);
+  CHECK_PRINTF ("100101101011010000111", "%w32B", (uint32_t) 1234567);
+  CHECK_PRINTF ("4553207", "%w32o", (uint32_t) 1234567);
+  CHECK_PRINTF ("1234567", "%w32u", (uint32_t) 1234567);
+  CHECK_PRINTF ("12d687", "%w32x", (uint32_t) 1234567);
+  CHECK_PRINTF ("12D687", "%w32X", (uint32_t) 1234567);
+  CHECK_PRINTF ("  1234567", "%9w32d", (int32_t) 1234567);
+  CHECK_PRINTF ("  1234567", "%*w32d", 9, (int32_t) 1234567);
+  CHECK_PRINTF ("0x12d687", "%#w32x", (uint32_t) 1234567);
+  CHECK_PRINTF ("001234567", "%.9w32d", (int32_t) 1234567);
+  CHECK_PRINTF ("001234567", "%.*w32d", 9, (int32_t) 1234567);
+  CHECK_PRINTF ("   001234567", "%12.9w32d", (int32_t) 1234567);
+  CHECK_PRINTF ("   001234567", "%*.9w32d", 12, (int32_t) 1234567);
+  CHECK_PRINTF ("   001234567", "%12.*w32d", 9, (int32_t) 1234567);
+  CHECK_PRINTF ("   001234567", "%*.*w32d", 12, 9, (int32_t) 1234567);
+  CHECK_PRINTF ("001234567   ", "%-12.9w32d", (int32_t) 1234567);
+  CHECK_PRINTF ("001234567   ", "%-*.9w32d", 12, (int32_t) 1234567);
+  CHECK_PRINTF ("001234567   ", "%-12.*w32d", 9, (int32_t) 1234567);
+  CHECK_PRINTF ("001234567   ", "%-*.*w32d", 12, 9, (int32_t) 1234567);
+  {
+    int32_t n = -1;
+    CHECK_PRINTF ("12345", "%d%w32n", 12345, &n);
+    TEST_COMPARE (n, 5);
+  }
+  CHECK_PRINTF ("1234567", "%w32d", (int_least32_t) 1234567);
+  CHECK_PRINTF ("-1234567", "%w32d", (int_least32_t) -1234567);
+  CHECK_PRINTF ("1234567", "%w32i", (int_least32_t) 1234567);
+  CHECK_PRINTF ("-1234567", "%w32i", (int_least32_t) -1234567);
+  CHECK_PRINTF ("100101101011010000111", "%w32b", (uint_least32_t) 1234567);
+  CHECK_PRINTF ("100101101011010000111", "%w32B", (uint_least32_t) 1234567);
+  CHECK_PRINTF ("4553207", "%w32o", (uint_least32_t) 1234567);
+  CHECK_PRINTF ("1234567", "%w32u", (uint_least32_t) 1234567);
+  CHECK_PRINTF ("12d687", "%w32x", (uint_least32_t) 1234567);
+  CHECK_PRINTF ("12D687", "%w32X", (uint_least32_t) 1234567);
+  CHECK_PRINTF ("  1234567", "%9w32d", (int_least32_t) 1234567);
+  CHECK_PRINTF ("  1234567", "%*w32d", 9, (int_least32_t) 1234567);
+  CHECK_PRINTF ("0x12d687", "%#w32x", (uint_least32_t) 1234567);
+  CHECK_PRINTF ("001234567", "%.9w32d", (int_least32_t) 1234567);
+  CHECK_PRINTF ("001234567", "%.*w32d", 9, (int_least32_t) 1234567);
+  CHECK_PRINTF ("   001234567", "%12.9w32d", (int_least32_t) 1234567);
+  CHECK_PRINTF ("   001234567", "%*.9w32d", 12, (int_least32_t) 1234567);
+  CHECK_PRINTF ("   001234567", "%12.*w32d", 9, (int_least32_t) 1234567);
+  CHECK_PRINTF ("   001234567", "%*.*w32d", 12, 9, (int_least32_t) 1234567);
+  CHECK_PRINTF ("001234567   ", "%-12.9w32d", (int_least32_t) 1234567);
+  CHECK_PRINTF ("001234567   ", "%-*.9w32d", 12, (int_least32_t) 1234567);
+  CHECK_PRINTF ("001234567   ", "%-12.*w32d", 9, (int_least32_t) 1234567);
+  CHECK_PRINTF ("001234567   ", "%-*.*w32d", 12, 9, (int_least32_t) 1234567);
+  {
+    int_least32_t ln = -1;
+    CHECK_PRINTF ("12345", "%d%w32n", 12345, &ln);
+    TEST_COMPARE (ln, 5);
+  }
+  /* Test positional argument handling.  */
+  CHECK_PRINTF ("test 123456 test2 234567", "%4$s %3$w32d %2$s %1$w32d",
+               INT32_C (234567), "test2", INT32_C (123456), "test");
+}
+
+static void
+test_wf32 (void)
+{
+  CHAR buf[1024];
+  _Static_assert (sizeof (int_fast32_t) == sizeof (long int),
+                 "test assumes size of int_fast32_t");
+  CHECK_PRINTF ("1234567", "%wf32d", (int_fast32_t) 1234567);
+  CHECK_PRINTF ("-1234567", "%wf32d", (int_fast32_t) -1234567);
+  CHECK_PRINTF ("1234567", "%wf32i", (int_fast32_t) 1234567);
+  CHECK_PRINTF ("-1234567", "%wf32i", (int_fast32_t) -1234567);
+  CHECK_PRINTF ("100101101011010000111", "%wf32b", (uint_fast32_t) 1234567);
+  CHECK_PRINTF ("100101101011010000111", "%wf32B", (uint_fast32_t) 1234567);
+  CHECK_PRINTF ("4553207", "%wf32o", (uint_fast32_t) 1234567);
+  CHECK_PRINTF ("1234567", "%wf32u", (uint_fast32_t) 1234567);
+  CHECK_PRINTF ("12d687", "%wf32x", (uint_fast32_t) 1234567);
+  CHECK_PRINTF ("12D687", "%wf32X", (uint_fast32_t) 1234567);
+  CHECK_PRINTF ("  1234567", "%9wf32d", (int_fast32_t) 1234567);
+  CHECK_PRINTF ("  1234567", "%*wf32d", 9, (int_fast32_t) 1234567);
+  CHECK_PRINTF ("0x12d687", "%#wf32x", (uint_fast32_t) 1234567);
+  CHECK_PRINTF ("001234567", "%.9wf32d", (int_fast32_t) 1234567);
+  CHECK_PRINTF ("001234567", "%.*wf32d", 9, (int_fast32_t) 1234567);
+  CHECK_PRINTF ("   001234567", "%12.9wf32d", (int_fast32_t) 1234567);
+  CHECK_PRINTF ("   001234567", "%*.9wf32d", 12, (int_fast32_t) 1234567);
+  CHECK_PRINTF ("   001234567", "%12.*wf32d", 9, (int_fast32_t) 1234567);
+  CHECK_PRINTF ("   001234567", "%*.*wf32d", 12, 9, (int_fast32_t) 1234567);
+  CHECK_PRINTF ("001234567   ", "%-12.9wf32d", (int_fast32_t) 1234567);
+  CHECK_PRINTF ("001234567   ", "%-*.9wf32d", 12, (int_fast32_t) 1234567);
+  CHECK_PRINTF ("001234567   ", "%-12.*wf32d", 9, (int_fast32_t) 1234567);
+  CHECK_PRINTF ("001234567   ", "%-*.*wf32d", 12, 9, (int_fast32_t) 1234567);
+  {
+    int_fast32_t n = -1;
+    CHECK_PRINTF ("12345", "%d%wf32n", 12345, &n);
+    TEST_COMPARE (n, 5);
+  }
+  /* Test positional argument handling.  */
+  CHECK_PRINTF ("test 123456 test2 234567", "%4$s %3$wf32d %2$s %1$wf32d",
+               (int_fast32_t) 234567, "test2", (int_fast32_t) 123456, "test");
+#if INT_FAST32_MAX > 0x7fffffff
+  CHECK_PRINTF ("12345678901", "%wf32d", (int_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("-12345678901", "%wf32d", (int_fast32_t) -12345678901LL);
+  CHECK_PRINTF ("12345678901", "%wf32i", (int_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("-12345678901", "%wf32i", (int_fast32_t) -12345678901LL);
+  CHECK_PRINTF ("1011011111110111000001110000110101", "%wf32b",
+               (uint_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("1011011111110111000001110000110101", "%wf32B",
+               (uint_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("133767016065", "%wf32o", (uint_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("12345678901", "%wf32u", (uint_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("2dfdc1c35", "%wf32x", (uint_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("2DFDC1C35", "%wf32X", (uint_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("  12345678901", "%13wf32d", (int_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("  12345678901", "%*wf32d", 13, (int_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("0x2dfdc1c35", "%#wf32x", (uint_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901", "%.13wf32d", (int_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901", "%.*wf32d", 13, (int_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%16.13wf32d",
+               (int_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%*.13wf32d", 16,
+               (int_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%16.*wf32d", 13,
+               (int_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%*.*wf32d", 16, 13,
+               (int_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-16.13wf32d",
+               (int_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-*.13wf32d", 16,
+               (int_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-16.*wf32d", 13,
+               (int_fast32_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-*.*wf32d", 16, 13,
+               (int_fast32_t) 12345678901LL);
+  /* Test positional argument handling.  */
+  CHECK_PRINTF ("test 123456789012 test2 234567890123",
+               "%4$s %3$wf32d %2$s %1$wf32d",
+               (int_fast32_t) 234567890123ULL, "test2",
+               (int_fast32_t) 123456789012ULL, "test");
+#endif
+}
+
+static void
+test_w64 (void)
+{
+  CHAR buf[1024];
+  CHECK_PRINTF ("12345678901", "%w64d", (int64_t) 12345678901LL);
+  CHECK_PRINTF ("-12345678901", "%w64d", (int64_t) -12345678901LL);
+  CHECK_PRINTF ("12345678901", "%w64i", (int64_t) 12345678901LL);
+  CHECK_PRINTF ("-12345678901", "%w64i", (int64_t) -12345678901LL);
+  CHECK_PRINTF ("1011011111110111000001110000110101", "%w64b",
+               (uint64_t) 12345678901LL);
+  CHECK_PRINTF ("1011011111110111000001110000110101", "%w64B",
+               (uint64_t) 12345678901LL);
+  CHECK_PRINTF ("133767016065", "%w64o", (uint64_t) 12345678901LL);
+  CHECK_PRINTF ("12345678901", "%w64u", (uint64_t) 12345678901LL);
+  CHECK_PRINTF ("2dfdc1c35", "%w64x", (uint64_t) 12345678901LL);
+  CHECK_PRINTF ("2DFDC1C35", "%w64X", (uint64_t) 12345678901LL);
+  CHECK_PRINTF ("  12345678901", "%13w64d", (int64_t) 12345678901LL);
+  CHECK_PRINTF ("  12345678901", "%*w64d", 13, (int64_t) 12345678901LL);
+  CHECK_PRINTF ("0x2dfdc1c35", "%#w64x", (uint64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901", "%.13w64d", (int64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901", "%.*w64d", 13, (int64_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%16.13w64d", (int64_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%*.13w64d", 16, (int64_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%16.*w64d", 13, (int64_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%*.*w64d", 16, 13,
+               (int64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-16.13w64d", (int64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-*.13w64d", 16, (int64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-16.*w64d", 13, (int64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-*.*w64d", 16, 13,
+               (int64_t) 12345678901LL);
+  {
+    int64_t n = -1;
+    CHECK_PRINTF ("12345", "%d%w64n", 12345, &n);
+    TEST_COMPARE (n, 5);
+  }
+  CHECK_PRINTF ("12345678901", "%w64d", (int_least64_t) 12345678901LL);
+  CHECK_PRINTF ("-12345678901", "%w64d", (int_least64_t) -12345678901LL);
+  CHECK_PRINTF ("12345678901", "%w64i", (int_least64_t) 12345678901LL);
+  CHECK_PRINTF ("-12345678901", "%w64i", (int_least64_t) -12345678901LL);
+  CHECK_PRINTF ("1011011111110111000001110000110101", "%w64b",
+               (uint_least64_t) 12345678901LL);
+  CHECK_PRINTF ("1011011111110111000001110000110101", "%w64B",
+               (uint_least64_t) 12345678901LL);
+  CHECK_PRINTF ("133767016065", "%w64o", (uint_least64_t) 12345678901LL);
+  CHECK_PRINTF ("12345678901", "%w64u", (uint_least64_t) 12345678901LL);
+  CHECK_PRINTF ("2dfdc1c35", "%w64x", (uint_least64_t) 12345678901LL);
+  CHECK_PRINTF ("2DFDC1C35", "%w64X", (uint_least64_t) 12345678901LL);
+  CHECK_PRINTF ("  12345678901", "%13w64d", (int_least64_t) 12345678901LL);
+  CHECK_PRINTF ("  12345678901", "%*w64d", 13, (int_least64_t) 12345678901LL);
+  CHECK_PRINTF ("0x2dfdc1c35", "%#w64x", (uint_least64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901", "%.13w64d", (int_least64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901", "%.*w64d", 13, (int_least64_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%16.13w64d",
+               (int_least64_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%*.13w64d", 16,
+               (int_least64_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%16.*w64d", 13,
+               (int_least64_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%*.*w64d", 16, 13,
+               (int_least64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-16.13w64d",
+               (int_least64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-*.13w64d", 16,
+               (int_least64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-16.*w64d", 13,
+               (int_least64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-*.*w64d", 16, 13,
+               (int_least64_t) 12345678901LL);
+  {
+    int_least64_t ln = -1;
+    CHECK_PRINTF ("12345", "%d%w64n", 12345, &ln);
+    TEST_COMPARE (ln, 5);
+  }
+  /* Test positional argument handling.  */
+  CHECK_PRINTF ("test 123456789012 test2 234567890123",
+               "%4$s %3$w64d %2$s %1$w64d",
+               INT64_C (234567890123), "test2",
+               INT64_C (123456789012), "test");
+}
+
+static void
+test_wf64 (void)
+{
+  CHAR buf[1024];
+  _Static_assert (sizeof (int_fast64_t) == sizeof (long long int),
+                 "test assumes size of int_fast64_t");
+  CHECK_PRINTF ("12345678901", "%wf64d", (int_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("-12345678901", "%wf64d", (int_fast64_t) -12345678901LL);
+  CHECK_PRINTF ("12345678901", "%wf64i", (int_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("-12345678901", "%wf64i", (int_fast64_t) -12345678901LL);
+  CHECK_PRINTF ("1011011111110111000001110000110101", "%wf64b",
+               (uint_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("1011011111110111000001110000110101", "%wf64B",
+               (uint_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("133767016065", "%wf64o", (uint_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("12345678901", "%wf64u", (uint_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("2dfdc1c35", "%wf64x", (uint_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("2DFDC1C35", "%wf64X", (uint_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("  12345678901", "%13wf64d", (int_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("  12345678901", "%*wf64d", 13, (int_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("0x2dfdc1c35", "%#wf64x", (uint_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901", "%.13wf64d", (int_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901", "%.*wf64d", 13, (int_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%16.13wf64d",
+               (int_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%*.13wf64d", 16,
+               (int_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%16.*wf64d", 13,
+               (int_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("   0012345678901", "%*.*wf64d", 16, 13,
+               (int_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-16.13wf64d",
+               (int_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-*.13wf64d", 16,
+               (int_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-16.*wf64d", 13,
+               (int_fast64_t) 12345678901LL);
+  CHECK_PRINTF ("0012345678901   ", "%-*.*wf64d", 16, 13,
+               (int_fast64_t) 12345678901LL);
+  {
+    int_fast64_t n = -1;
+    CHECK_PRINTF ("12345", "%d%wf64n", 12345, &n);
+    TEST_COMPARE (n, 5);
+  }
+  /* Test positional argument handling.  */
+  CHECK_PRINTF ("test 123456789012 test2 234567890123",
+               "%4$s %3$wf64d %2$s %1$wf64d",
+               (int_fast64_t) 234567890123ULL, "test2",
+               (int_fast64_t) 123456789012ULL, "test");
+}
+
+static int
+do_test (void)
+{
+  test_w8 ();
+  test_wf8 ();
+  test_w16 ();
+  test_wf16 ();
+  test_w32 ();
+  test_wf32 ();
+  test_w64 ();
+  test_wf64 ();
+  /* Bad N in %wN and %wfN are required to produce an error return
+     from printf functions (and can also be seen to be invalid at
+     compile time).  */
+  DIAG_PUSH_NEEDS_COMMENT;
+  DIAG_IGNORE_NEEDS_COMMENT (13, "-Wformat");
+  DIAG_IGNORE_NEEDS_COMMENT (13, "-Wformat-extra-args");
+  CHAR buf[1024];
+  CHECK_PRINTF_ERR ("%w1d", 123);
+  CHECK_PRINTF_ERR ("%w123d", 123);
+  CHECK_PRINTF_ERR ("%w99999999999999999999d", 123);
+  CHECK_PRINTF_ERR ("%wf1d", 123);
+  CHECK_PRINTF_ERR ("%wf123d", 123);
+  CHECK_PRINTF_ERR ("%wf99999999999999999999d", 123);
+  CHECK_PRINTF_ERR ("%1$w1d", 123);
+  CHECK_PRINTF_ERR ("%1$w123d", 123);
+  CHECK_PRINTF_ERR ("%1$w99999999999999999999d", 123);
+  CHECK_PRINTF_ERR ("%1$wf1d", 123);
+  CHECK_PRINTF_ERR ("%1$wf123d", 123);
+  CHECK_PRINTF_ERR ("%1$wf99999999999999999999d", 123);
+  DIAG_POP_NEEDS_COMMENT;
+  return 0;
+}
+
+DIAG_POP_NEEDS_COMMENT;
+
+#include <support/test-driver.c>
diff --git a/stdio-common/tst-printf-intn.c b/stdio-common/tst-printf-intn.c
new file mode 100644 (file)
index 0000000..975aebe
--- /dev/null
@@ -0,0 +1,26 @@
+/* Test printf formats for intN_t, int_leastN_t and int_fastN_t types.
+   Narrow string version.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#define SNPRINTF snprintf
+#define TEST_COMPARE_STRING_MACRO TEST_COMPARE_STRING
+#define STRLEN strlen
+#define CHAR char
+#define L_(C) C
+
+#include <tst-printf-intn-main.c>
index c76c06e49b0b9ffe97f73d17e35de2fdf6b2c0d0..f30a9e9f3a0e551d49894f1d6f812a4fbe5f8417 100644 (file)
@@ -315,7 +315,7 @@ static const uint8_t jump_table[] =
     /* 'h' */ 10, /* 'i' */ 15, /* 'j' */ 28,            0,
     /* 'l' */ 11, /* 'm' */ 24, /* 'n' */ 23, /* 'o' */ 17,
     /* 'p' */ 22, /* 'q' */ 12,            0, /* 's' */ 21,
-    /* 't' */ 27, /* 'u' */ 16,            0,            0,
+    /* 't' */ 27, /* 'u' */ 16,            0, /* 'w' */ 31,
     /* 'x' */ 18,            0, /* 'z' */ 13
   };
 
@@ -356,7 +356,7 @@ static const uint8_t jump_table[] =
 
 #define STEP0_3_TABLE                                                        \
     /* Step 0: at the beginning.  */                                         \
-    static JUMP_TABLE_TYPE step0_jumps[31] =                                 \
+    static JUMP_TABLE_TYPE step0_jumps[32] =                                 \
     {                                                                        \
       REF (form_unknown),                                                    \
       REF (flag_space),                /* for ' ' */                                 \
@@ -389,9 +389,10 @@ static const uint8_t jump_table[] =
       REF (mod_intmax_t),       /* for 'j' */                                \
       REF (flag_i18n),         /* for 'I' */                                 \
       REF (form_binary),       /* for 'B', 'b' */                            \
+      REF (mod_bitwidth),      /* for 'w' */                                 \
     };                                                                       \
     /* Step 1: after processing width.  */                                   \
-    static JUMP_TABLE_TYPE step1_jumps[31] =                                 \
+    static JUMP_TABLE_TYPE step1_jumps[32] =                                 \
     {                                                                        \
       REF (form_unknown),                                                    \
       REF (form_unknown),      /* for ' ' */                                 \
@@ -424,9 +425,10 @@ static const uint8_t jump_table[] =
       REF (mod_intmax_t),       /* for 'j' */                                \
       REF (form_unknown),       /* for 'I' */                                \
       REF (form_binary),       /* for 'B', 'b' */                            \
+      REF (mod_bitwidth),      /* for 'w' */                                 \
     };                                                                       \
     /* Step 2: after processing precision.  */                               \
-    static JUMP_TABLE_TYPE step2_jumps[31] =                                 \
+    static JUMP_TABLE_TYPE step2_jumps[32] =                                 \
     {                                                                        \
       REF (form_unknown),                                                    \
       REF (form_unknown),      /* for ' ' */                                 \
@@ -459,9 +461,10 @@ static const uint8_t jump_table[] =
       REF (mod_intmax_t),       /* for 'j' */                                \
       REF (form_unknown),       /* for 'I' */                                \
       REF (form_binary),       /* for 'B', 'b' */                            \
+      REF (mod_bitwidth),      /* for 'w' */                                 \
     };                                                                       \
     /* Step 3a: after processing first 'h' modifier.  */                     \
-    static JUMP_TABLE_TYPE step3a_jumps[31] =                                \
+    static JUMP_TABLE_TYPE step3a_jumps[32] =                                \
     {                                                                        \
       REF (form_unknown),                                                    \
       REF (form_unknown),      /* for ' ' */                                 \
@@ -494,9 +497,10 @@ static const uint8_t jump_table[] =
       REF (form_unknown),       /* for 'j' */                                \
       REF (form_unknown),       /* for 'I' */                                \
       REF (form_binary),       /* for 'B', 'b' */                            \
+      REF (form_unknown),      /* for 'w' */                                 \
     };                                                                       \
     /* Step 3b: after processing first 'l' modifier.  */                     \
-    static JUMP_TABLE_TYPE step3b_jumps[31] =                                \
+    static JUMP_TABLE_TYPE step3b_jumps[32] =                                \
     {                                                                        \
       REF (form_unknown),                                                    \
       REF (form_unknown),      /* for ' ' */                                 \
@@ -529,11 +533,12 @@ static const uint8_t jump_table[] =
       REF (form_unknown),       /* for 'j' */                                \
       REF (form_unknown),       /* for 'I' */                                \
       REF (form_binary),       /* for 'B', 'b' */                            \
+      REF (form_unknown),      /* for 'w' */                                 \
     }
 
 #define STEP4_TABLE                                                          \
     /* Step 4: processing format specifier.  */                                      \
-    static JUMP_TABLE_TYPE step4_jumps[31] =                                 \
+    static JUMP_TABLE_TYPE step4_jumps[32] =                                 \
     {                                                                        \
       REF (form_unknown),                                                    \
       REF (form_unknown),      /* for ' ' */                                 \
@@ -566,6 +571,7 @@ static const uint8_t jump_table[] =
       REF (form_unknown),       /* for 'j' */                                \
       REF (form_unknown),       /* for 'I' */                                \
       REF (form_binary),       /* for 'B', 'b' */                            \
+      REF (form_unknown),      /* for 'w' */                                 \
     }
 
 /* Handle positional format specifiers.  */
@@ -886,6 +892,56 @@ Xprintf_buffer (struct Xprintf_buffer *buf, const CHAR_T *format,
       is_long = sizeof (intmax_t) > sizeof (unsigned int);
       JUMP (*++f, step4_jumps);
 
+      /* Process 'wN' or 'wfN' modifier.  */
+    LABEL (mod_bitwidth):
+      ++f;
+      bool is_fast = false;
+      if (*f == L_('f'))
+       {
+         ++f;
+         is_fast = true;
+       }
+      int bitwidth = 0;
+      if (ISDIGIT (*f))
+       bitwidth = read_int (&f);
+      if (is_fast)
+       switch (bitwidth)
+         {
+         case 8:
+           bitwidth = INT_FAST8_WIDTH;
+           break;
+         case 16:
+           bitwidth = INT_FAST16_WIDTH;
+           break;
+         case 32:
+           bitwidth = INT_FAST32_WIDTH;
+           break;
+         case 64:
+           bitwidth = INT_FAST64_WIDTH;
+           break;
+         }
+      switch (bitwidth)
+       {
+       case 8:
+         is_char = 1;
+         break;
+       case 16:
+         is_short = 1;
+         break;
+       case 32:
+         break;
+       case 64:
+         is_long_double = 1;
+         is_long = 1;
+         break;
+       default:
+         /* ISO C requires this error to be detected.  */
+         __set_errno (EINVAL);
+         Xprintf_buffer_mark_failed (buf);
+         goto all_done;
+       }
+      JUMP (*f, step4_jumps);
+
       /* Process current format.  */
       while (1)
        {
@@ -1053,11 +1109,19 @@ printf_positional (struct Xprintf_buffer * buf, const CHAR_T *format,
        }
 
       /* Parse the format specifier.  */
+      bool failed;
 #ifdef COMPILE_WPRINTF
-      nargs += __parse_one_specwc (f, nargs, &specs[nspecs], &max_ref_arg);
+      nargs += __parse_one_specwc (f, nargs, &specs[nspecs], &max_ref_arg,
+                                  &failed);
 #else
-      nargs += __parse_one_specmb (f, nargs, &specs[nspecs], &max_ref_arg);
+      nargs += __parse_one_specmb (f, nargs, &specs[nspecs], &max_ref_arg,
+                                  &failed);
 #endif
+      if (failed)
+       {
+         Xprintf_buffer_mark_failed (buf);
+         goto all_done;
+       }
     }
 
   /* Determine the number of arguments the format string consumes.  */
index d8512c880168efcfd0e403763f497f94bc6785f2..22192985e1284facdd6ce2b5ec5ae4d0f1e2301a 100644 (file)
@@ -170,6 +170,7 @@ tests := \
   tst-wcstol-binary-gnu2x \
   tst-wcstol-locale \
   tst-wprintf-binary \
+  tst-wprintf-intn \
   tst-wscanf-binary-c11 \
   tst-wscanf-binary-c2x \
   tst-wscanf-binary-gnu11 \
diff --git a/wcsmbs/tst-wprintf-intn.c b/wcsmbs/tst-wprintf-intn.c
new file mode 100644 (file)
index 0000000..0c0eb80
--- /dev/null
@@ -0,0 +1,26 @@
+/* Test printf formats for intN_t, int_leastN_t and int_fastN_t types.
+   Wide string version.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#define SNPRINTF swprintf
+#define TEST_COMPARE_STRING_MACRO TEST_COMPARE_STRING_WIDE
+#define STRLEN wcslen
+#define CHAR wchar_t
+#define L_(C) L ## C
+
+#include "../stdio-common/tst-printf-intn-main.c"