about summary refs log tree commit diff
path: root/stdio-common
diff options
context:
space:
mode:
authorJoseph Myers <joseph@codesourcery.com>2023-06-19 18:52:12 +0000
committerJoseph Myers <joseph@codesourcery.com>2023-06-19 18:52:12 +0000
commit5f83b2674e42cd74257731b281f66d0442bf045f (patch)
treecb4d3f19d481d718c4800218f20afc93441199e3 /stdio-common
parent8022fc7d5119a22e9e0ac72798f649385b0e167a (diff)
downloadglibc-5f83b2674e42cd74257731b281f66d0442bf045f.tar.gz
glibc-5f83b2674e42cd74257731b281f66d0442bf045f.tar.xz
glibc-5f83b2674e42cd74257731b281f66d0442bf045f.zip
C2x printf %wN, %wfN support (bug 24466)
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.
Diffstat (limited to 'stdio-common')
-rw-r--r--stdio-common/Makefile1
-rw-r--r--stdio-common/printf-parse.h9
-rw-r--r--stdio-common/printf-parsemb.c56
-rw-r--r--stdio-common/printf-prs.c3
-rw-r--r--stdio-common/tst-printf-intn-main.c637
-rw-r--r--stdio-common/tst-printf-intn.c26
-rw-r--r--stdio-common/vfprintf-internal.c82
7 files changed, 799 insertions, 15 deletions
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 4c15b97683..8871ec7668 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -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 \
diff --git a/stdio-common/printf-parse.h b/stdio-common/printf-parse.h
index b25181496c..57673a331d 100644
--- a/stdio-common/printf-parse.h
+++ b/stdio-common/printf-parse.h
@@ -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;
 
 
 
diff --git a/stdio-common/printf-parsemb.c b/stdio-common/printf-parsemb.c
index c5d2704b02..414cbc7223 100644
--- a/stdio-common/printf-parsemb.c
+++ b/stdio-common/printf-parsemb.c
@@ -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;
diff --git a/stdio-common/printf-prs.c b/stdio-common/printf-prs.c
index 2408a2e328..9d8bf306e4 100644
--- a/stdio-common/printf-prs.c
+++ b/stdio-common/printf-prs.c
@@ -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
index 0000000000..bd70e9de73
--- /dev/null
+++ b/stdio-common/tst-printf-intn-main.c
@@ -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
index 0000000000..975aebecf7
--- /dev/null
+++ b/stdio-common/tst-printf-intn.c
@@ -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>
diff --git a/stdio-common/vfprintf-internal.c b/stdio-common/vfprintf-internal.c
index c76c06e49b..f30a9e9f3a 100644
--- a/stdio-common/vfprintf-internal.c
+++ b/stdio-common/vfprintf-internal.c
@@ -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.  */