C2x printf %wN, %wfN support (bug 24466)

Message ID c0597c26-9679-d88e-c257-75a0221e59bb@codesourcery.com
State Committed
Headers
Series C2x printf %wN, %wfN support (bug 24466) |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
redhat-pt-bot/TryBot-32bit success Build for i686
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Testing passed
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Testing passed

Commit Message

Joseph Myers June 9, 2023, 8:48 p.m. UTC
  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.
  

Comments

Adhemerval Zanella June 15, 2023, 2:10 p.m. UTC | #1
On 09/06/23 17:48, Joseph Myers wrote:
> 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.

Maybe we can extend the interface to handle this case, but I don't think
this is a priority.

LGTM, just some minor suggestion below.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> 
> Tested for x86_64 and x86.
> 
> diff --git a/NEWS b/NEWS
> index 23051cf139..77635d5760 100644
> --- 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.
>  

Ok.

> diff --git a/manual/stdio.texi b/manual/stdio.texi> index 3820a24f3e..a981e6512a 100644
> --- a/manual/stdio.texi
> +++ b/manual/stdio.texi
> @@ -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}.

Ok.

> 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 \

Ok.

> 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;
>  
>  
>  

Ok.

> 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))
>      {

Ok.

> @@ -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;

Ok.

> 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)

Ok.

> diff --git a/stdio-common/tst-printf-intn-main.c b/stdio-common/tst-printf-intn-main.c
> new file mode 100644
> index 0000000000..3c311de81c
> --- /dev/null
> +++ b/stdio-common/tst-printf-intn-main.c
> @@ -0,0 +1,619 @@
> +/* 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 <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__);					\

I think we should check the errno value as well:

 TEST_COMPARE (errno, EINVAL);

> +      TEST_VERIFY (ret < 0);						\
> +    }									\
> +  while (0)
> +
> +static void
> +test_w8 (void)
> +{
> +  CHAR buf[1024];
> +  int8_t n;
> +  int_least8_t ln;
> +  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);
> +  n = -1;
> +  CHECK_PRINTF ("12345", "%d%w8n", 12345, &n);
> +  TEST_COMPARE (n, 5);

Maybe use a different scope toi avoid expose 'n' to rest of the
test?

  {
    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);
> +  ln = -1;

Same as before.

> +  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];
> +  int_fast8_t n;
> +  _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);
> +  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");
> +}
> +

Ok.

> +static void
> +test_w16 (void)
> +{
> +  CHAR buf[1024];
> +  int16_t n;
> +  int_least16_t ln;
> +  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);
> +  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);
> +  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");
> +}
> +

Ok.

> +static void
> +test_wf16 (void)
> +{
> +  CHAR buf[1024];
> +  int_fast16_t n;
> +  _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);
> +  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
> +}
> +

Ok.

> +static void
> +test_w32 (void)
> +{
> +  CHAR buf[1024];
> +  int32_t n;
> +  int_least32_t ln;
> +  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);
> +  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);
> +  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");
> +}
> +

Ok.

> +static void
> +test_wf32 (void)
> +{
> +  CHAR buf[1024];
> +  int_fast32_t n;
> +  _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);
> +  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
> +}
> +

Ok.

> +static void
> +test_w64 (void)
> +{
> +  CHAR buf[1024];
> +  int64_t n;
> +  int_least64_t ln;
> +  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);
> +  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);
> +  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");
> +}
> +

Ok.

> +static void
> +test_wf64 (void)
> +{
> +  CHAR buf[1024];
> +  int_fast64_t n;
> +  _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);
> +  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");
> +}
> +

Ok.

> +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];

I think it would be worth do add extra tests for invalid numbers, not
only for numbers in the supported range:

  CHECK_PRINTF_ERR ("%w", 123);
  CHECK_PRINTF_ERR ("%wf", 123);
  CHECK_PRINTF_ERR ("%w-", 123);
  CHECK_PRINTF_ERR ("%wf-", 123);

> +  CHECK_PRINTF_ERR ("%w1d", 123);
> +  CHECK_PRINTF_ERR ("%w123d", 123);
> +  CHECK_PRINTF_ERR ("%wf1d", 123);
> +  CHECK_PRINTF_ERR ("%wf123d", 123);

Same for positional:

  CHECK_PRINTF_ERR ("%1$w", 123);
  CHECK_PRINTF_ERR ("%1$wf", 123);
  CHECK_PRINTF_ERR ("%1$w-", 123);
  CHECK_PRINTF_ERR ("%1$wf-", 123);

> +  CHECK_PRINTF_ERR ("%1$w1d", 123);
> +  CHECK_PRINTF_ERR ("%1$w123d", 123);
> +  CHECK_PRINTF_ERR ("%1$wf1d", 123);
> +  CHECK_PRINTF_ERR ("%1$wf123d", 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>

Ok.

> 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.  */

Ok.

> @@ -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)
>  	{

Ok.

> @@ -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.  */

Ok.

> diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile
> index ea8ea7b3e6..b03423d1e7 100644
> --- a/wcsmbs/Makefile
> +++ b/wcsmbs/Makefile
> @@ -166,6 +166,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
> index 0000000000..0c0eb80c2c
> --- /dev/null
> +++ b/wcsmbs/tst-wprintf-intn.c
> @@ -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"
> 

Ok.
  
Joseph Myers June 15, 2023, 5:56 p.m. UTC | #2
On Thu, 15 Jun 2023, Adhemerval Zanella Netto via Libc-alpha wrote:

> > +#define CHECK_PRINTF_ERR(FMT, ...)					\
> > +  do									\
> > +    {									\
> > +      int ret = SNPRINTF (buf, sizeof buf / sizeof buf[0], L_(FMT),	\
> > +			  __VA_ARGS__);					\
> 
> I think we should check the errno value as well:
> 
>  TEST_COMPARE (errno, EINVAL);

I've added that check (but not documented the choice of errno value).

> > +  n = -1;
> > +  CHECK_PRINTF ("12345", "%d%w8n", 12345, &n);
> > +  TEST_COMPARE (n, 5);
> 
> Maybe use a different scope toi avoid expose 'n' to rest of the
> test?

I've done that for each of these variables.

> I think it would be worth do add extra tests for invalid numbers, not
> only for numbers in the supported range:
> 
>   CHECK_PRINTF_ERR ("%w", 123);
>   CHECK_PRINTF_ERR ("%wf", 123);
>   CHECK_PRINTF_ERR ("%w-", 123);
>   CHECK_PRINTF_ERR ("%wf-", 123);

These are still undefined behavior; they don't match the syntax for which 
there's an error return specified in the C2x working draft.  I think it 
would be questionable to try to define particular error semantics for such 
cases (that don't use a positive decimal integer with no leading 0, in a 
format specifier that's otherwise valid).  Other uses of %w (e.g. for 
_BitInt or _FloatN) might well be added in future to the C standard, it's 
open as space for future features.

I've added tests with a too-large integer, however, since that does match 
the syntax.

Here is version 2 of the patch (only changes are to the test).

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.

diff --git a/NEWS b/NEWS
index 7a4df85f08..7cec03cc76 100644
--- 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.
 
diff --git a/manual/stdio.texi b/manual/stdio.texi
index 3820a24f3e..a981e6512a 100644
--- a/manual/stdio.texi
+++ b/manual/stdio.texi
@@ -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}.
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.  */
diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile
index d8512c8801..22192985e1 100644
--- a/wcsmbs/Makefile
+++ b/wcsmbs/Makefile
@@ -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
index 0000000000..0c0eb80c2c
--- /dev/null
+++ b/wcsmbs/tst-wprintf-intn.c
@@ -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"
  
Adhemerval Zanella June 19, 2023, 6:46 p.m. UTC | #3
On 15/06/23 14:56, Joseph Myers wrote:
> On Thu, 15 Jun 2023, Adhemerval Zanella Netto via Libc-alpha wrote:
> 
>>> +#define CHECK_PRINTF_ERR(FMT, ...)					\
>>> +  do									\
>>> +    {									\
>>> +      int ret = SNPRINTF (buf, sizeof buf / sizeof buf[0], L_(FMT),	\
>>> +			  __VA_ARGS__);					\
>>
>> I think we should check the errno value as well:
>>
>>  TEST_COMPARE (errno, EINVAL);
> 
> I've added that check (but not documented the choice of errno value).
> 
>>> +  n = -1;
>>> +  CHECK_PRINTF ("12345", "%d%w8n", 12345, &n);
>>> +  TEST_COMPARE (n, 5);
>>
>> Maybe use a different scope toi avoid expose 'n' to rest of the
>> test?
> 
> I've done that for each of these variables.
> 
>> I think it would be worth do add extra tests for invalid numbers, not
>> only for numbers in the supported range:
>>
>>   CHECK_PRINTF_ERR ("%w", 123);
>>   CHECK_PRINTF_ERR ("%wf", 123);
>>   CHECK_PRINTF_ERR ("%w-", 123);
>>   CHECK_PRINTF_ERR ("%wf-", 123);
> 
> These are still undefined behavior; they don't match the syntax for which 
> there's an error return specified in the C2x working draft.  I think it 
> would be questionable to try to define particular error semantics for such 
> cases (that don't use a positive decimal integer with no leading 0, in a 
> format specifier that's otherwise valid).  Other uses of %w (e.g. for 
> _BitInt or _FloatN) might well be added in future to the C standard, it's 
> open as space for future features.
> 
> I've added tests with a too-large integer, however, since that does match 
> the syntax.

Fair enough.

> 
> Here is version 2 of the patch (only changes are to the test).
> 
> 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.

LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> 
> diff --git a/NEWS b/NEWS
> index 7a4df85f08..7cec03cc76 100644
> --- 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.
>  
> diff --git a/manual/stdio.texi b/manual/stdio.texi
> index 3820a24f3e..a981e6512a 100644
> --- a/manual/stdio.texi
> +++ b/manual/stdio.texi
> @@ -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}.
> 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.  */
> diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile
> index d8512c8801..22192985e1 100644
> --- a/wcsmbs/Makefile
> +++ b/wcsmbs/Makefile
> @@ -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
> index 0000000000..0c0eb80c2c
> --- /dev/null
> +++ b/wcsmbs/tst-wprintf-intn.c
> @@ -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"
>
  

Patch

diff --git a/NEWS b/NEWS
index 23051cf139..77635d5760 100644
--- 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.
 
diff --git a/manual/stdio.texi b/manual/stdio.texi
index 3820a24f3e..a981e6512a 100644
--- a/manual/stdio.texi
+++ b/manual/stdio.texi
@@ -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}.
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..3c311de81c
--- /dev/null
+++ b/stdio-common/tst-printf-intn-main.c
@@ -0,0 +1,619 @@ 
+/* 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 <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);						\
+    }									\
+  while (0)
+
+static void
+test_w8 (void)
+{
+  CHAR buf[1024];
+  int8_t n;
+  int_least8_t ln;
+  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);
+  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);
+  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];
+  int_fast8_t n;
+  _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);
+  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];
+  int16_t n;
+  int_least16_t ln;
+  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);
+  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);
+  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];
+  int_fast16_t n;
+  _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);
+  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];
+  int32_t n;
+  int_least32_t ln;
+  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);
+  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);
+  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];
+  int_fast32_t n;
+  _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);
+  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];
+  int64_t n;
+  int_least64_t ln;
+  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);
+  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);
+  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];
+  int_fast64_t n;
+  _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);
+  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 ("%wf1d", 123);
+  CHECK_PRINTF_ERR ("%wf123d", 123);
+  CHECK_PRINTF_ERR ("%1$w1d", 123);
+  CHECK_PRINTF_ERR ("%1$w123d", 123);
+  CHECK_PRINTF_ERR ("%1$wf1d", 123);
+  CHECK_PRINTF_ERR ("%1$wf123d", 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.  */
diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile
index ea8ea7b3e6..b03423d1e7 100644
--- a/wcsmbs/Makefile
+++ b/wcsmbs/Makefile
@@ -166,6 +166,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
index 0000000000..0c0eb80c2c
--- /dev/null
+++ b/wcsmbs/tst-wprintf-intn.c
@@ -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"