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

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Mon Jun 19 18:46:45 GMT 2023



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


More information about the Libc-alpha mailing list