public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
From: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
To: Florian Weimer <fweimer@redhat.com>, libc-alpha@sourceware.org
Subject: Re: [PATCH v5 05/11] stdio-common: Convert vfprintf and related functions to buffers
Date: Wed, 14 Dec 2022 17:54:29 -0300	[thread overview]
Message-ID: <32c237bd-1f1d-671c-71df-99005dea6746@linaro.org> (raw)
In-Reply-To: <2706c12ba5db0ed2c845c991bb1577a7e5be2f07.1670858473.git.fweimer@redhat.com>



On 12/12/22 12:23, Florian Weimer via Libc-alpha wrote:
> vfprintf is entangled with vfwprintf (of course), __printf_fp,
> __printf_fphex, __vstrfmon_l_internal, and the strfrom family of
> functions.  The latter use the internal snprintf functionality,
> so vsnprintf is converted as well.
> 
> The simples conversion is __printf_fphex, followed by
> __vstrfmon_l_internal and __printf_fp, and finally
> __vfprintf_internal and __vfwprintf_internal.  __vsnprintf_internal
> and strfrom* are mostly consuming the new interfaces, so they
> are comparatively simple.
> 
> __printf_fp is a public symbol, so the FILE *-based interface
> had to preserved.
> 
> The __printf_fp rewrite does not change the actual binary-to-decimal
> conversion algorithm, and digits are still not emitted directly to
> the target buffer.  However, the staging buffer now uses bytes
> instead of wide characters, and one buffer copy is eliminated.
> 
> The changes are at least performance-neutral in my testing.
> Floating point printing and snprintf improved measurably, so that
> this Lua script
> 
>   for i=1,5000000 do
>       print(i, i * math.pi)
>   end
> 
> runs about 5% faster for me.  To preserve fprintf performance for
> a simple "%d" format, this commit has some logic changes under
> LABEL (unsigned_number) to avoid additional function calls.  There
> are certainly some very easy performance improvements here: binary,
> octal and hexadecimal formatting can easily avoid the temporary work
> buffer (the number of digits can be computed ahead-of-time using one
> of the __builtin_clz* built-ins). Decimal formatting can use a
> specialized version of _itoa_word for base 10.
> 
> The existing (inconsistent) width handling between strfmon and printf
> is preserved here.  __print_fp_buffer_1 would have to use
> __translated_number_width to achieve ISO conformance for printf.
> 
> Test expectations in libio/tst-vtables-common.c are adjusted because
> the internal staging buffer merges all virtual function calls into
> one.
> 
> In general, stack buffer usage is greatly reduced, particularly for
> unbuffered input streams.  __printf_fp can still use a large buffer
> in binary128 mode for %g, though.

Patch looks good, there is only a small nit on the libio vtable removal
that in turn showed an issue on elf/tst-relro-symbols.py.

LGTM, thanks.

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

> ---
>  include/printf.h                              |  29 +-
>  include/printf_buffer.h                       |  36 +
>  libio/tst-vtables-common.c                    |   9 +-
>  libio/vsnprintf.c                             | 131 ++--
>  stdio-common/printf_buffer_flush.c            |  23 +
>  stdio-common/printf_fp.c                      | 736 ++++++++----------
>  stdio-common/printf_fphex.c                   | 260 +++----
>  stdio-common/vfprintf-internal.c              | 664 +++++-----------
>  stdio-common/vfprintf-process-arg.c           | 172 ++--
>  stdlib/strfmon_l.c                            | 196 ++---
>  stdlib/strfrom-skeleton.c                     |  38 +-
>  sysdeps/ia64/fpu/printf_fphex.c               |   8 +-
>  .../ieee754/ldbl-128/printf_fphex_macros.h    |  36 +-
>  sysdeps/ieee754/ldbl-128ibm/printf_fphex.c    |  36 +-
>  sysdeps/ieee754/ldbl-96/printf_fphex.c        |  22 +-
>  sysdeps/x86_64/fpu/printf_fphex.c             |  21 +-
>  16 files changed, 943 insertions(+), 1474 deletions(-)
> 
> diff --git a/include/printf.h b/include/printf.h
> index 5127a45f9b..2c998059d4 100644
> --- a/include/printf.h
> +++ b/include/printf.h
> @@ -65,18 +65,31 @@ int __translated_number_width (locale_t loc,
>  			       const char *first, const char *last)
>    attribute_hidden;
>  
> -extern int __printf_fphex (FILE *, const struct printf_info *,
> -			   const void *const *) attribute_hidden;
> +
> +struct __printf_buffer;
> +void __printf_buffer (struct __printf_buffer *buf, const char *format,
> +		      va_list ap, unsigned int mode_flags);
> +struct __wprintf_buffer;
> +void __wprintf_buffer (struct __wprintf_buffer *buf, const wchar_t *format,
> +		       va_list ap, unsigned int mode_flags);
> +
>  extern int __printf_fp (FILE *, const struct printf_info *,
>  			const void *const *);
>  libc_hidden_proto (__printf_fp)
> -extern int __printf_fp_l (FILE *, locale_t, const struct printf_info *,
> -			  const void *const *);
> -libc_hidden_proto (__printf_fp_l)
>  
> -extern unsigned int __guess_grouping (unsigned int intdig_max,
> -				      const char *grouping)
> -     attribute_hidden;
> +void __printf_fphex_l_buffer (struct __printf_buffer *, locale_t,
> +			      const struct printf_info *,
> +			      const void *const *) attribute_hidden;
> +void __printf_fp_l_buffer (struct __printf_buffer *, locale_t,
> +			   const struct printf_info *,
> +			   const void *const *) attribute_hidden;
> +struct __wprintf_buffer;
> +void __wprintf_fphex_l_buffer (struct __wprintf_buffer *, locale_t,
> +			       const struct printf_info *,
> +			       const void *const *) attribute_hidden;
> +void __wprintf_fp_l_buffer (struct __wprintf_buffer *, locale_t,
> +			    const struct printf_info *,
> +			    const void *const *) attribute_hidden;
>  
>  # endif /* !_ISOMAC */
>  #endif

Ok.

> diff --git a/include/printf_buffer.h b/include/printf_buffer.h
> index ad08a72b71..e89f984aca 100644
> --- a/include/printf_buffer.h
> +++ b/include/printf_buffer.h
> @@ -44,7 +44,12 @@
>  enum __printf_buffer_mode
>    {
>      __printf_buffer_mode_failed,
> +    __printf_buffer_mode_snprintf,
>      __printf_buffer_mode_to_file,
> +    __printf_buffer_mode_strfmon,
> +    __printf_buffer_mode_fp,         /* For __printf_fp_l_buffer.  */
> +    __printf_buffer_mode_fp_to_wide, /* For __wprintf_fp_l_buffer.  */
> +    __printf_buffer_mode_fphex_to_wide, /* For __wprintf_fphex_l_buffer.  */
>    };
>  
>  /* Buffer for fast character writing with overflow handling.
> @@ -266,13 +271,44 @@ bool __wprintf_buffer_flush (struct __wprintf_buffer *buf) attribute_hidden;
>  #define Xprintf_buffer_puts Xprintf (buffer_puts)
>  #define Xprintf_buffer_write Xprintf (buffer_write)
>  
> +/* Commonly used buffers.  */
> +
> +struct __printf_buffer_snprintf
> +{
> +  struct __printf_buffer base;
> +  char discard[128];            /* Used in counting mode.  */
> +};
> +
> +/* Sets up [BUFFER, BUFFER + LENGTH) as the write target.  If LENGTH
> +   is positive, also writes a NUL byte to *BUFFER.  */
> +void __printf_buffer_snprintf_init (struct __printf_buffer_snprintf *,
> +                                    char *buffer, size_t length)
> +  attribute_hidden;
> +
> +/* Add the null terminator after everything has been written.  The
> +   return value is the one expected by printf (see __printf_buffer_done).  */
> +int __printf_buffer_snprintf_done (struct __printf_buffer_snprintf *)
> +  attribute_hidden;
> +
>  /* Flush function implementations follow.  They are called from
>     __printf_buffer_flush.  Generic code should not call these flush
>     functions directly.  Some modes have inline implementations.  */
>  
> +void __printf_buffer_flush_snprintf (struct __printf_buffer_snprintf *)
> +  attribute_hidden;
>  struct __printf_buffer_to_file;
>  void __printf_buffer_flush_to_file (struct __printf_buffer_to_file *)
>    attribute_hidden;
> +struct __printf_buffer_fp;
> +void __printf_buffer_flush_fp (struct __printf_buffer_fp *)
> +  attribute_hidden;
> +struct __printf_buffer_fp_to_wide;
> +void __printf_buffer_flush_fp_to_wide (struct __printf_buffer_fp_to_wide *)
> +  attribute_hidden;
> +struct __printf_buffer_fphex_to_wide;
> +void __printf_buffer_flush_fphex_to_wide (struct
> +                                          __printf_buffer_fphex_to_wide *)
> +  attribute_hidden;
>  
>  struct __wprintf_buffer_to_file;
>  void __wprintf_buffer_flush_to_file (struct __wprintf_buffer_to_file *)

Ok.

> diff --git a/libio/tst-vtables-common.c b/libio/tst-vtables-common.c
> index d18df23e55..a310e516f2 100644
> --- a/libio/tst-vtables-common.c
> +++ b/libio/tst-vtables-common.c
> @@ -409,11 +409,14 @@ void _IO_init (FILE *fp, int flags);
>  static void
>  with_compatibility_fprintf (void *closure)
>  {
> +  /* A temporary staging buffer is used in the current fprintf
> +     implementation, which is why there is just one call to
> +     xsputn.  */
>    TEST_COMPARE (fprintf_ptr (shared->fp, "A%sCD", "B"), 4);
> -  TEST_COMPARE (shared->calls, 3);
> -  TEST_COMPARE (shared->calls_xsputn, 3);
> +  TEST_COMPARE (shared->calls, 1);
> +  TEST_COMPARE (shared->calls_xsputn, 1);
>    TEST_COMPARE_BLOB (shared->buffer, shared->buffer_length,
> -                     "CD", 2);
> +                     "ABCD", 4);
>  }
>  
>  static void

Ok.

> diff --git a/libio/vsnprintf.c b/libio/vsnprintf.c
> index 8dae66761d..7a9667f966 100644
> --- a/libio/vsnprintf.c
> +++ b/libio/vsnprintf.c
> @@ -25,97 +25,76 @@
>     in files containing the exception.  */
>  
>  #include "libioP.h"
> -#include "strfile.h"
>  
> -static int _IO_strn_overflow (FILE *fp, int c) __THROW;
> +#include <array_length.h>
> +#include <printf.h>
> +#include <printf_buffer.h>
>  
> -static int
> -_IO_strn_overflow (FILE *fp, int c)
> +void
> +__printf_buffer_flush_snprintf (struct __printf_buffer_snprintf *buf)
>  {
> -  /* When we come to here this means the user supplied buffer is
> -     filled.  But since we must return the number of characters which
> -     would have been written in total we must provide a buffer for
> -     further use.  We can do this by writing on and on in the overflow
> -     buffer in the _IO_strnfile structure.  */
> -  _IO_strnfile *snf = (_IO_strnfile *) fp;
> -
> -  if (fp->_IO_buf_base != snf->overflow_buf)
> +  /* Record the bytes written so far, before switching buffers.  */
> +  buf->base.written += buf->base.write_ptr - buf->base.write_base;
> +
> +  if (buf->base.write_base != buf->discard)
>      {
> -      /* Terminate the string.  We know that there is room for at
> -	 least one more character since we initialized the stream with
> -	 a size to make this possible.  */
> -      *fp->_IO_write_ptr = '\0';
> -
> -      _IO_setb (fp, snf->overflow_buf,
> -		snf->overflow_buf + sizeof (snf->overflow_buf), 0);
> -
> -      fp->_IO_write_base = snf->overflow_buf;
> -      fp->_IO_read_base = snf->overflow_buf;
> -      fp->_IO_read_ptr = snf->overflow_buf;
> -      fp->_IO_read_end = snf->overflow_buf + sizeof (snf->overflow_buf);
> -    }
> +      /* We just finished writing the caller-supplied buffer.  Force
> +	 NUL termination if the string length is not zero.  */
> +      if (buf->base.write_base != buf->base.write_end)
> +	buf->base.write_end[-1] = '\0';
>  
> -  fp->_IO_write_ptr = snf->overflow_buf;
> -  fp->_IO_write_end = snf->overflow_buf;
>  
> -  /* Since we are not really interested in storing the characters
> -     which do not fit in the buffer we simply ignore it.  */
> -  return c;
> -}
> +      /* Switch to the discard buffer.  */
> +      buf->base.write_base = buf->discard;
> +      buf->base.write_ptr = buf->discard;
> +      buf->base.write_end = array_end (buf->discard);
> +    }
>  
> +  buf->base.write_base = buf->discard;
> +  buf->base.write_ptr = buf->discard;
> +}
>  
> -const struct _IO_jump_t _IO_strn_jumps libio_vtable attribute_hidden =

We need to remove both _IO_strn_jumps definition from libio/strfile.h and
from tst-relro-libc.out.

> +void
> +__printf_buffer_snprintf_init (struct __printf_buffer_snprintf *buf,
> +			       char *buffer, size_t length)
>  {
> -  JUMP_INIT_DUMMY,
> -  JUMP_INIT(finish, _IO_str_finish),
> -  JUMP_INIT(overflow, _IO_strn_overflow),
> -  JUMP_INIT(underflow, _IO_str_underflow),
> -  JUMP_INIT(uflow, _IO_default_uflow),
> -  JUMP_INIT(pbackfail, _IO_str_pbackfail),
> -  JUMP_INIT(xsputn, _IO_default_xsputn),
> -  JUMP_INIT(xsgetn, _IO_default_xsgetn),
> -  JUMP_INIT(seekoff, _IO_str_seekoff),
> -  JUMP_INIT(seekpos, _IO_default_seekpos),
> -  JUMP_INIT(setbuf, _IO_default_setbuf),
> -  JUMP_INIT(sync, _IO_default_sync),
> -  JUMP_INIT(doallocate, _IO_default_doallocate),
> -  JUMP_INIT(read, _IO_default_read),
> -  JUMP_INIT(write, _IO_default_write),
> -  JUMP_INIT(seek, _IO_default_seek),
> -  JUMP_INIT(close, _IO_default_close),
> -  JUMP_INIT(stat, _IO_default_stat),
> -  JUMP_INIT(showmanyc, _IO_default_showmanyc),
> -  JUMP_INIT(imbue, _IO_default_imbue)
> -};
> +  __printf_buffer_init (&buf->base, buffer, length,
> +			__printf_buffer_mode_snprintf);
> +  if (length > 0)
> +    /* Historic behavior for trivially overlapping buffers (checked by
> +       the test suite).  */
> +    *buffer = '\0';
> +}
>  
> +int
> +__printf_buffer_snprintf_done (struct __printf_buffer_snprintf *buf)
> +{
> +  /* NB: Do not check for buf->base.fail here.  Write the null
> +     terminator even in case of errors. */
> +
> +  if (buf->base.write_ptr < buf->base.write_end)
> +    *buf->base.write_ptr = '\0';
> +  else if (buf->base.write_ptr > buf->base.write_base)
> +    /* If write_ptr == write_base, nothing has been written.  No null
> +       termination is needed because of the early truncation in
> +       __printf_buffer_snprintf_init (the historic behavior).
> +
> +       We might also be at the start of the discard buffer, but in
> +       this case __printf_buffer_flush_snprintf has already written
> +       the NUL terminator.  */
> +    buf->base.write_ptr[-1] = '\0';
> +
> +  return __printf_buffer_done (&buf->base);
> +}
>  
>  int
>  __vsnprintf_internal (char *string, size_t maxlen, const char *format,
>  		      va_list args, unsigned int mode_flags)
>  {
> -  _IO_strnfile sf;
> -  int ret;
> -#ifdef _IO_MTSAFE_IO
> -  sf.f._sbf._f._lock = NULL;
> -#endif
> -
> -  /* We need to handle the special case where MAXLEN is 0.  Use the
> -     overflow buffer right from the start.  */
> -  if (maxlen == 0)
> -    {
> -      string = sf.overflow_buf;
> -      maxlen = sizeof (sf.overflow_buf);
> -    }
> -
> -  _IO_no_init (&sf.f._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
> -  _IO_JUMPS (&sf.f._sbf) = &_IO_strn_jumps;
> -  string[0] = '\0';
> -  _IO_str_init_static_internal (&sf.f, string, maxlen - 1, string);
> -  ret = __vfprintf_internal (&sf.f._sbf._f, format, args, mode_flags);
> -
> -  if (sf.f._sbf._f._IO_buf_base != sf.overflow_buf)
> -    *sf.f._sbf._f._IO_write_ptr = '\0';
> -  return ret;
> +  struct __printf_buffer_snprintf buf;
> +  __printf_buffer_snprintf_init (&buf, string, maxlen);
> +  __printf_buffer (&buf.base, format, args, mode_flags);
> +  return __printf_buffer_snprintf_done (&buf);
>  }
>  
>  int

Ok.

> diff --git a/stdio-common/printf_buffer_flush.c b/stdio-common/printf_buffer_flush.c
> index 9b25c0fde5..bfd1f9d733 100644
> --- a/stdio-common/printf_buffer_flush.c
> +++ b/stdio-common/printf_buffer_flush.c
> @@ -16,6 +16,7 @@
>     License along with the GNU C Library; if not, see
>     <https://www.gnu.org/licenses/>.  */
>  
> +#include <errno.h>
>  #include <printf_buffer.h>
>  
>  #include "printf_buffer-char.h"
> @@ -24,7 +25,11 @@
>  /* The __printf_buffer_flush_* functions are defined together with
>     functions that are pulled in by strong references.  */
>  #ifndef SHARED
> +# pragma weak __printf_buffer_flush_snprintf
>  # pragma weak __printf_buffer_flush_to_file
> +# pragma weak __printf_buffer_flush_fp
> +# pragma weak __printf_buffer_flush_fp_to_wide
> +# pragma weak __printf_buffer_flush_fphex_to_wide
>  #endif /* !SHARED */
>  
>  static void
> @@ -34,9 +39,27 @@ __printf_buffer_do_flush (struct __printf_buffer *buf)
>      {
>      case __printf_buffer_mode_failed:
>        return;
> +    case __printf_buffer_mode_snprintf:
> +      __printf_buffer_flush_snprintf ((struct __printf_buffer_snprintf *) buf);
> +      return;
>      case __printf_buffer_mode_to_file:
>        __printf_buffer_flush_to_file ((struct __printf_buffer_to_file *) buf);
>        return;
> +    case __printf_buffer_mode_strfmon:
> +      __set_errno (E2BIG);
> +      __printf_buffer_mark_failed (buf);
> +      return;
> +    case __printf_buffer_mode_fp:
> +      __printf_buffer_flush_fp ((struct __printf_buffer_fp *) buf);
> +      return;
> +    case __printf_buffer_mode_fp_to_wide:
> +      __printf_buffer_flush_fp_to_wide
> +        ((struct __printf_buffer_fp_to_wide *) buf);
> +      return;
> +    case __printf_buffer_mode_fphex_to_wide:
> +      __printf_buffer_flush_fphex_to_wide
> +        ((struct __printf_buffer_fphex_to_wide *) buf);
> +      return;
>      }
>    __builtin_trap ();
>  }

Ok.

> diff --git a/stdio-common/printf_fp.c b/stdio-common/printf_fp.c
> index 3a5560fc16..5c9c30aaee 100644
> --- a/stdio-common/printf_fp.c
> +++ b/stdio-common/printf_fp.c
> @@ -41,90 +41,12 @@
>  #include <wchar.h>
>  #include <stdbool.h>
>  #include <rounding-mode.h>
> +#include <printf_buffer.h>
> +#include <printf_buffer_to_file.h>
> +#include <grouping_iterator.h>
>  
> -#ifdef COMPILE_WPRINTF
> -# define CHAR_T        wchar_t
> -#else
> -# define CHAR_T        char
> -#endif
> -
> -#include "_i18n_number.h"
> -
> -#ifndef NDEBUG
> -# define NDEBUG			/* Undefine this for debugging assertions.  */
> -#endif
>  #include <assert.h>
>  
> -#define PUT(f, s, n) _IO_sputn (f, s, n)
> -#define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : _IO_padn (f, c, n))
> -#undef putc
> -#define putc(c, f) (wide \
> -		    ? (int)_IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f))
> -
> -\f
> -/* Macros for doing the actual output.  */
> -
> -#define outchar(ch)							      \
> -  do									      \
> -    {									      \
> -      const int outc = (ch);						      \
> -      if (putc (outc, fp) == EOF)					      \
> -	{								      \
> -	  if (buffer_malloced)						      \
> -	    {								      \
> -	      free (buffer);						      \
> -	      free (wbuffer);						      \
> -	    }								      \
> -	  return -1;							      \
> -	}								      \
> -      ++done;								      \
> -    } while (0)
> -
> -#define PRINT(ptr, wptr, len)						      \
> -  do									      \
> -    {									      \
> -      size_t outlen = (len);						      \
> -      if (len > 20)							      \
> -	{								      \
> -	  if (PUT (fp, wide ? (const char *) wptr : ptr, outlen) != outlen)   \
> -	    {								      \
> -	      if (buffer_malloced)					      \
> -		{							      \
> -		  free (buffer);					      \
> -		  free (wbuffer);					      \
> -		}							      \
> -	      return -1;						      \
> -	    }								      \
> -	  ptr += outlen;						      \
> -	  done += outlen;						      \
> -	}								      \
> -      else								      \
> -	{								      \
> -	  if (wide)							      \
> -	    while (outlen-- > 0)					      \
> -	      outchar (*wptr++);					      \
> -	  else								      \
> -	    while (outlen-- > 0)					      \
> -	      outchar (*ptr++);						      \
> -	}								      \
> -    } while (0)
> -
> -#define PADN(ch, len)							      \
> -  do									      \
> -    {									      \
> -      if (PAD (fp, ch, len) != len)					      \
> -	{								      \
> -	  if (buffer_malloced)						      \
> -	    {								      \
> -	      free (buffer);						      \
> -	      free (wbuffer);						      \
> -	    }								      \
> -	  return -1;							      \
> -	}								      \
> -      done += len;							      \
> -    }									      \
> -  while (0)
> -\f
>  /* We use the GNU MP library to handle large numbers.
>  
>     An MP variable occupies a varying number of entries in its array.  We keep
> @@ -145,10 +67,6 @@ extern mp_size_t __mpn_extract_long_double (mp_ptr res_ptr, mp_size_t size,
>  					    long double value);
>  
>  
> -static wchar_t *group_number (wchar_t *buf, wchar_t *bufend,
> -			      unsigned int intdig_no, const char *grouping,
> -			      wchar_t thousands_sep, int ngroups);
> -
>  struct hack_digit_param
>  {
>    /* Sign of the exponent.  */
> @@ -165,7 +83,7 @@ struct hack_digit_param
>    MPN_VAR(tmp);
>  };
>  
> -static wchar_t
> +static char
>  hack_digit (struct hack_digit_param *p)
>  {
>    mp_limb_t hi;
> @@ -197,7 +115,7 @@ hack_digit (struct hack_digit_param *p)
>  	      /* We're not prepared for an mpn variable with zero
>  		 limbs.  */
>  	      p->fracsize = 1;
> -	      return L'0' + hi;
> +	      return '0' + hi;
>  	    }
>  	}
>  
> @@ -206,13 +124,22 @@ hack_digit (struct hack_digit_param *p)
>  	p->frac[p->fracsize++] = _cy;
>      }
>  
> -  return L'0' + hi;
> +  return '0' + hi;
>  }
>  
> -int
> -__printf_fp_l (FILE *fp, locale_t loc,
> -	       const struct printf_info *info,
> -	       const void *const *args)
> +/* Version that performs grouping (if INFO->group && THOUSANDS_SEP != 0),
> +   but not i18n digit translation.
> +
> +   The output buffer is always multibyte (not wide) at this stage.
> +   Wide conversion and i18n digit translation happen later, with a
> +   temporary buffer.  To prepare for that, THOUSANDS_SEP_LENGTH is the
> +   final length of the thousands separator.  */
> +static void
> +__printf_fp_buffer_1 (struct __printf_buffer *buf, locale_t loc,
> +		      char thousands_sep, char decimal,
> +		      unsigned int thousands_sep_length,
> +		      const struct printf_info *info,
> +		      const void *const *args)
>  {
>    /* The floating-point value to output.  */
>    union
> @@ -225,18 +152,11 @@ __printf_fp_l (FILE *fp, locale_t loc,
>      }
>    fpnum;
>  
> -  /* Locale-dependent representation of decimal point.	*/
> -  const char *decimal;
> -  wchar_t decimalwc;
> -
> -  /* Locale-dependent thousands separator and grouping specification.  */
> -  const char *thousands_sep = NULL;
> -  wchar_t thousands_sepwc = 0;
> -  const char *grouping;
> -
>    /* "NaN" or "Inf" for the special cases.  */
>    const char *special = NULL;
> -  const wchar_t *wspecial = NULL;
> +
> +  /* Used to determine grouping rules.  */
> +  int lc_category = info->extra ? LC_MONETARY : LC_NUMERIC;
>  
>    /* When _Float128 is enabled in the library and ABI-distinct from long
>       double, we need mp_limbs enough for any of them.  */

Ok.

> @@ -256,90 +176,16 @@ __printf_fp_l (FILE *fp, locale_t loc,
>    /* Sign of float number.  */
>    int is_neg = 0;
>  
> -  /* Counter for number of written characters.	*/
> -  int done = 0;
> -
>    /* General helper (carry limb).  */
>    mp_limb_t cy;
>  
> -  /* Nonzero if this is output on a wide character stream.  */
> -  int wide = info->wide;
> -
>    /* Buffer in which we produce the output.  */
> -  wchar_t *wbuffer = NULL;
> -  char *buffer = NULL;
> +  char *wbuffer = NULL;
>    /* Flag whether wbuffer and buffer are malloc'ed or not.  */
>    int buffer_malloced = 0;
>  
>    p.expsign = 0;
>  
> -  /* Figure out the decimal point character.  */
> -  if (info->extra == 0)
> -    {
> -      decimal = _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT);
> -      decimalwc = _nl_lookup_word
> -	(loc, LC_NUMERIC, _NL_NUMERIC_DECIMAL_POINT_WC);
> -    }
> -  else
> -    {
> -      decimal = _nl_lookup (loc, LC_MONETARY, MON_DECIMAL_POINT);
> -      if (*decimal == '\0')
> -	decimal = _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT);
> -      decimalwc = _nl_lookup_word (loc, LC_MONETARY,
> -				    _NL_MONETARY_DECIMAL_POINT_WC);
> -      if (decimalwc == L'\0')
> -	decimalwc = _nl_lookup_word (loc, LC_NUMERIC,
> -				      _NL_NUMERIC_DECIMAL_POINT_WC);
> -    }
> -  /* The decimal point character must not be zero.  */
> -  assert (*decimal != '\0');
> -  assert (decimalwc != L'\0');
> -
> -  if (info->group)
> -    {
> -      if (info->extra == 0)
> -	grouping = _nl_lookup (loc, LC_NUMERIC, GROUPING);
> -      else
> -	grouping = _nl_lookup (loc, LC_MONETARY, MON_GROUPING);
> -
> -      if (*grouping <= 0 || *grouping == CHAR_MAX)
> -	grouping = NULL;
> -      else
> -	{
> -	  /* Figure out the thousands separator character.  */
> -	  if (wide)
> -	    {
> -	      if (info->extra == 0)
> -		thousands_sepwc = _nl_lookup_word
> -		  (loc, LC_NUMERIC, _NL_NUMERIC_THOUSANDS_SEP_WC);
> -	      else
> -		thousands_sepwc =
> -		  _nl_lookup_word (loc, LC_MONETARY,
> -				    _NL_MONETARY_THOUSANDS_SEP_WC);
> -	    }
> -	  else
> -	    {
> -	      if (info->extra == 0)
> -		thousands_sep = _nl_lookup (loc, LC_NUMERIC, THOUSANDS_SEP);
> -	      else
> -		thousands_sep = _nl_lookup
> -		  (loc, LC_MONETARY, MON_THOUSANDS_SEP);
> -	    }
> -
> -	  if ((wide && thousands_sepwc == L'\0')
> -	      || (! wide && *thousands_sep == '\0'))
> -	    grouping = NULL;
> -	  else if (thousands_sepwc == L'\0')
> -	    /* If we are printing multibyte characters and there is a
> -	       multibyte representation for the thousands separator,
> -	       we must ensure the wide character thousands separator
> -	       is available, even if it is fake.  */
> -	    thousands_sepwc = 0xfffffffe;
> -	}
> -    }
> -  else
> -    grouping = NULL;
> -
>  #define PRINTF_FP_FETCH(FLOAT, VAR, SUFFIX, MANT_DIG)			\
>    {									\
>      (VAR) = *(const FLOAT *) args[0];					\
> @@ -349,29 +195,17 @@ __printf_fp_l (FILE *fp, locale_t loc,
>        {									\
>  	is_neg = signbit (VAR);						\
>  	if (isupper (info->spec))					\
> -	  {								\
> -	    special = "NAN";						\
> -	    wspecial = L"NAN";						\
> -	  }								\
> +	  special = "NAN";						\
>  	else								\
> -	  {								\
> -	    special = "nan";						\
> -	    wspecial = L"nan";						\
> -	  }								\
> +	  special = "nan";						\
>        }									\
>      else if (isinf (VAR))						\
>        {									\
>  	is_neg = signbit (VAR);						\
>  	if (isupper (info->spec))					\
> -	  {								\
> -	    special = "INF";						\
> -	    wspecial = L"INF";						\
> -	  }								\
> +	  special = "INF";						\
>  	else								\
> -	  {								\
> -	    special = "inf";						\
> -	    wspecial = L"inf";						\
> -	  }								\
> +	  special = "inf";						\
>        }									\
>      else								\
>        {									\

Ok.

> @@ -405,22 +239,22 @@ __printf_fp_l (FILE *fp, locale_t loc,
>  	--width;
>        width -= 3;
>  
> -      if (!info->left && width > 0)
> -	PADN (' ', width);
> +      if (!info->left)
> +	__printf_buffer_pad (buf, ' ', width);
>  
>        if (is_neg)
> -	outchar ('-');
> +	__printf_buffer_putc (buf, '-');
>        else if (info->showsign)
> -	outchar ('+');
> +	__printf_buffer_putc (buf, '+');
>        else if (info->space)
> -	outchar (' ');
> +	__printf_buffer_putc (buf, ' ');
>  
> -      PRINT (special, wspecial, 3);
> +      __printf_buffer_puts (buf, special);
>  
> -      if (info->left && width > 0)
> -	PADN (' ', width);
> +      if (info->left)
> +	__printf_buffer_pad (buf, ' ', width);
>  
> -      return done;
> +      return;
>      }
>  
>  

Ok.

> @@ -829,7 +663,7 @@ __printf_fp_l (FILE *fp, locale_t loc,
>  
>    {
>      int width = info->width;
> -    wchar_t *wstartp, *wcp;
> +    char *wstartp, *wcp;
>      size_t chars_needed;
>      int expscale;
>      int intdig_max, intdig_no = 0;
> @@ -837,7 +671,6 @@ __printf_fp_l (FILE *fp, locale_t loc,
>      int fracdig_max;
>      int dig_max;
>      int significant;
> -    int ngroups = 0;
>      char spec = _tolower (info->spec);
>  
>      if (spec == 'e')
> @@ -898,38 +731,32 @@ __printf_fp_l (FILE *fp, locale_t loc,
>  	significant = 0;		/* We count significant digits.	 */
>        }
>  
> -    if (grouping)
> -      {
> -	/* Guess the number of groups we will make, and thus how
> -	   many spaces we need for separator characters.  */
> -	ngroups = __guess_grouping (intdig_max, grouping);
> -	/* Allocate one more character in case rounding increases the
> -	   number of groups.  */
> -	chars_needed += ngroups + 1;
> -      }
> -
>      /* Allocate buffer for output.  We need two more because while rounding
>         it is possible that we need two more characters in front of all the
>         other output.  If the amount of memory we have to allocate is too
>         large use `malloc' instead of `alloca'.  */
> -    if (__builtin_expect (chars_needed >= (size_t) -1 / sizeof (wchar_t) - 2
> -			  || chars_needed < fracdig_max, 0))
> +    if (__glibc_unlikely (chars_needed >= (size_t) -1 - 2
> +			  || chars_needed < fracdig_max))
>        {
>  	/* Some overflow occurred.  */
>  	__set_errno (ERANGE);
> -	return -1;
> +	__printf_buffer_mark_failed (buf);
> +	return;
>        }
> -    size_t wbuffer_to_alloc = (2 + chars_needed) * sizeof (wchar_t);
> +    size_t wbuffer_to_alloc = 2 + chars_needed;
>      buffer_malloced = ! __libc_use_alloca (wbuffer_to_alloc);
>      if (__builtin_expect (buffer_malloced, 0))
>        {
> -	wbuffer = (wchar_t *) malloc (wbuffer_to_alloc);
> +	wbuffer = malloc (wbuffer_to_alloc);
>  	if (wbuffer == NULL)
> -	  /* Signal an error to the caller.  */
> -	  return -1;
> +	  {
> +	    /* Signal an error to the caller.  */
> +	    __printf_buffer_mark_failed (buf);
> +	    return;
> +	  }
>        }
>      else
> -      wbuffer = (wchar_t *) alloca (wbuffer_to_alloc);
> +      wbuffer = alloca (wbuffer_to_alloc);
>      wcp = wstartp = wbuffer + 2;	/* Let room for rounding.  */
>  
>      /* Do the real work: put digits in allocated buffer.  */

Ok.

> @@ -945,15 +772,15 @@ __printf_fp_l (FILE *fp, locale_t loc,
>  	if (info->alt
>  	    || fracdig_min > 0
>  	    || (fracdig_max > 0 && (p.fracsize > 1 || p.frac[0] != 0)))
> -	  *wcp++ = decimalwc;
> +	  *wcp++ = decimal;
>        }
>      else
>        {
>  	/* |fp| < 1.0 and the selected p.type is 'f', so put "0."
>  	   in the buffer.  */
> -	*wcp++ = L'0';
> +	*wcp++ = '0';
>  	--p.exponent;
> -	*wcp++ = decimalwc;
> +	*wcp++ = decimal;
>        }
>  
>      /* Generate the needed number of fractional digits.	 */
> @@ -964,7 +791,7 @@ __printf_fp_l (FILE *fp, locale_t loc,
>        {
>  	++fracdig_no;
>  	*wcp = hack_digit (&p);
> -	if (*wcp++ != L'0')
> +	if (*wcp++ != '0')
>  	  significant = 1;
>  	else if (significant == 0)
>  	  {
> @@ -975,10 +802,10 @@ __printf_fp_l (FILE *fp, locale_t loc,
>        }
>  
>      /* Do rounding.  */
> -    wchar_t last_digit = wcp[-1] != decimalwc ? wcp[-1] : wcp[-2];
> -    wchar_t next_digit = hack_digit (&p);
> +    char last_digit = wcp[-1] != decimal ? wcp[-1] : wcp[-2];
> +    char next_digit = hack_digit (&p);
>      bool more_bits;
> -    if (next_digit != L'0' && next_digit != L'5')
> +    if (next_digit != '0' && next_digit != '5')
>        more_bits = true;
>      else if (p.fracsize == 1 && p.frac[0] == 0)
>        /* Rest of the number is zero.  */
> @@ -995,29 +822,29 @@ __printf_fp_l (FILE *fp, locale_t loc,
>      else
>        more_bits = true;
>      int rounding_mode = get_rounding_mode ();
> -    if (round_away (is_neg, (last_digit - L'0') & 1, next_digit >= L'5',
> +    if (round_away (is_neg, (last_digit - '0') & 1, next_digit >= '5',
>  		    more_bits, rounding_mode))
>        {
> -	wchar_t *wtp = wcp;
> +	char *wtp = wcp;
>  
>  	if (fracdig_no > 0)
>  	  {
>  	    /* Process fractional digits.  Terminate if not rounded or
>  	       radix character is reached.  */
>  	    int removed = 0;
> -	    while (*--wtp != decimalwc && *wtp == L'9')
> +	    while (*--wtp != decimal && *wtp == '9')
>  	      {
> -		*wtp = L'0';
> +		*wtp = '0';
>  		++removed;
>  	      }
>  	    if (removed == fracdig_min && added_zeros > 0)
>  	      --added_zeros;
> -	    if (*wtp != decimalwc)
> +	    if (*wtp != decimal)
>  	      /* Round up.  */
>  	      (*wtp)++;
>  	    else if (__builtin_expect (spec == 'g' && p.type == 'f' && info->alt
>  				       && wtp == wstartp + 1
> -				       && wstartp[0] == L'0',
> +				       && wstartp[0] == '0',
>  				       0))
>  	      /* This is a special case: the rounded number is 1.0,
>  		 the format is 'g' or 'G', and the alternative format
> @@ -1025,14 +852,14 @@ __printf_fp_l (FILE *fp, locale_t loc,
>  	      --added_zeros;
>  	  }
>  
> -	if (fracdig_no == 0 || *wtp == decimalwc)
> +	if (fracdig_no == 0 || *wtp == decimal)
>  	  {
>  	    /* Round the integer digits.  */
> -	    if (*(wtp - 1) == decimalwc)
> +	    if (*(wtp - 1) == decimal)
>  	      --wtp;
>  
> -	    while (--wtp >= wstartp && *wtp == L'9')
> -	      *wtp = L'0';
> +	    while (--wtp >= wstartp && *wtp == '9')
> +	      *wtp = '0';
>  
>  	    if (wtp >= wstartp)
>  	      /* Round up.  */
> @@ -1056,13 +883,13 @@ __printf_fp_l (FILE *fp, locale_t loc,
>  		    /* This is the case where for p.type %g the number fits
>  		       really in the range for %f output but after rounding
>  		       the number of digits is too big.	 */
> -		    *--wstartp = decimalwc;
> -		    *--wstartp = L'1';
> +		    *--wstartp = decimal;
> +		    *--wstartp = '1';
>  
>  		    if (info->alt || fracdig_no > 0)
>  		      {
>  			/* Overwrite the old radix character.  */
> -			wstartp[intdig_no + 2] = L'0';
> +			wstartp[intdig_no + 2] = '0';
>  			++fracdig_no;
>  		      }
>  
> @@ -1077,7 +904,7 @@ __printf_fp_l (FILE *fp, locale_t loc,
>  		  {
>  		    /* We can simply add another another digit before the
>  		       radix.  */
> -		    *--wstartp = L'1';
> +		    *--wstartp = '1';
>  		    ++intdig_no;
>  		  }
>  

Ok.

> @@ -1094,28 +921,16 @@ __printf_fp_l (FILE *fp, locale_t loc,
>        }
>  
>      /* Now remove unnecessary '0' at the end of the string.  */
> -    while (fracdig_no > fracdig_min + added_zeros && *(wcp - 1) == L'0')
> +    while (fracdig_no > fracdig_min + added_zeros && *(wcp - 1) == '0')
>        {
>  	--wcp;
>  	--fracdig_no;
>        }
>      /* If we eliminate all fractional digits we perhaps also can remove
>         the radix character.  */
> -    if (fracdig_no == 0 && !info->alt && *(wcp - 1) == decimalwc)
> +    if (fracdig_no == 0 && !info->alt && *(wcp - 1) == decimal)
>        --wcp;
>  
> -    if (grouping)
> -      {
> -	/* Rounding might have changed the number of groups.  We allocated
> -	   enough memory but we need here the correct number of groups.  */
> -	if (intdig_no != intdig_max)
> -	  ngroups = __guess_grouping (intdig_no, grouping);
> -
> -	/* Add in separator characters, overwriting the same buffer.  */
> -	wcp = group_number (wstartp, wcp, intdig_no, grouping, thousands_sepwc,
> -			    ngroups);
> -      }
> -
>      /* Write the p.exponent if it is needed.  */
>      if (p.type != 'f')
>        {
> @@ -1125,12 +940,12 @@ __printf_fp_l (FILE *fp, locale_t loc,
>  	       really smaller than -4, which requires the 'e'/'E' format.
>  	       But after rounding the number has an p.exponent of -4.  */
>  	    assert (wcp >= wstartp + 1);
> -	    assert (wstartp[0] == L'1');
> -	    __wmemcpy (wstartp, L"0.0001", 6);
> -	    wstartp[1] = decimalwc;
> +	    assert (wstartp[0] == '1');
> +	    memcpy (wstartp, "0.0001", 6);
> +	    wstartp[1] = decimal;
>  	    if (wcp >= wstartp + 2)
>  	      {
> -		__wmemset (wstartp + 6, L'0', wcp - (wstartp + 2));
> +		memset (wstartp + 6, '0', wcp - (wstartp + 2));
>  		wcp += 4;
>  	      }
>  	    else
> @@ -1138,8 +953,8 @@ __printf_fp_l (FILE *fp, locale_t loc,
>  	  }
>  	else
>  	  {
> -	    *wcp++ = (wchar_t) p.type;
> -	    *wcp++ = p.expsign ? L'-' : L'+';
> +	    *wcp++ = p.type;
> +	    *wcp++ = p.expsign ? '-' : '+';
>  
>  	    /* Find the magnitude of the p.exponent.	*/
>  	    expscale = 10;
> @@ -1148,220 +963,293 @@ __printf_fp_l (FILE *fp, locale_t loc,
>  
>  	    if (p.exponent < 10)
>  	      /* Exponent always has at least two digits.  */
> -	      *wcp++ = L'0';
> +	      *wcp++ = '0';
>  	    else
>  	      do
>  		{
>  		  expscale /= 10;
> -		  *wcp++ = L'0' + (p.exponent / expscale);
> +		  *wcp++ = '0' + (p.exponent / expscale);
>  		  p.exponent %= expscale;
>  		}
>  	      while (expscale > 10);
> -	    *wcp++ = L'0' + p.exponent;
> +	    *wcp++ = '0' + p.exponent;
>  	  }
>        }
>  

Ok.

> +    struct grouping_iterator iter;
> +    if (thousands_sep != '\0' && info->group)
> +      __grouping_iterator_init (&iter, lc_category, loc, intdig_no);
> +    else
> +      iter.separators = 0;
> +
>      /* Compute number of characters which must be filled with the padding
>         character.  */
>      if (is_neg || info->showsign || info->space)
>        --width;
> +    /* To count bytes, we would have to use __translated_number_width
> +       for info->i18n && !info->wide.  See bug 28943.  */
>      width -= wcp - wstartp;
> +    /* For counting bytes, we would have to multiply by
> +       thousands_sep_length.  */
> +    width -= iter.separators;
>  
> -    if (!info->left && info->pad != '0' && width > 0)
> -      PADN (info->pad, width);
> +    if (!info->left && info->pad != '0')
> +      __printf_buffer_pad (buf, info->pad, width);
>  
>      if (is_neg)
> -      outchar ('-');
> +      __printf_buffer_putc (buf, '-');
>      else if (info->showsign)
> -      outchar ('+');
> +      __printf_buffer_putc (buf, '+');
>      else if (info->space)
> -      outchar (' ');
> +      __printf_buffer_putc (buf, ' ');
>  
> -    if (!info->left && info->pad == '0' && width > 0)
> -      PADN ('0', width);
> +    if (!info->left && info->pad == '0')
> +      __printf_buffer_pad (buf, '0', width);
>  
> -    {
> -      char *buffer_end = NULL;
> -      char *cp = NULL;
> -      char *tmpptr;
> +    if (iter.separators > 0)
> +      {
> +	char *cp = wstartp;
> +	for (int i = 0; i < intdig_no; ++i)
> +	  {
> +	    if (__grouping_iterator_next (&iter))
> +	      __printf_buffer_putc (buf, thousands_sep);
> +	    __printf_buffer_putc (buf, *cp);
> +	    ++cp;
> +	  }
> +	__printf_buffer_write (buf, cp, wcp - cp);
> +      }
> +    else
> +      __printf_buffer_write (buf, wstartp, wcp - wstartp);
>  
> -      if (! wide)
> -	{
> -	  /* Create the single byte string.  */
> -	  size_t decimal_len;
> -	  size_t thousands_sep_len;
> -	  wchar_t *copywc;
> -	  size_t factor;
> -	  if (info->i18n)
> -	    factor = _nl_lookup_word (loc, LC_CTYPE, _NL_CTYPE_MB_CUR_MAX);
> -	  else
> -	    factor = 1;
> +    if (info->left)
> +      __printf_buffer_pad (buf, info->pad, width);
> +  }
>  
> -	  decimal_len = strlen (decimal);
> +  if (buffer_malloced)
> +    free (wbuffer);
> +}
>  
> -	  if (thousands_sep == NULL)
> -	    thousands_sep_len = 0;
> -	  else
> -	    thousands_sep_len = strlen (thousands_sep);
> +/* ASCII to localization translation.  Multibyte version.  */
> +struct __printf_buffer_fp
> +{
> +  struct __printf_buffer base;
>  
> -	  size_t nbuffer = (2 + chars_needed * factor + decimal_len
> -			    + ngroups * thousands_sep_len);
> -	  if (__glibc_unlikely (buffer_malloced))
> -	    {
> -	      buffer = (char *) malloc (nbuffer);
> -	      if (buffer == NULL)
> -		{
> -		  /* Signal an error to the caller.  */
> -		  free (wbuffer);
> -		  return -1;
> -		}
> -	    }
> -	  else
> -	    buffer = (char *) alloca (nbuffer);
> -	  buffer_end = buffer + nbuffer;
> -
> -	  /* Now copy the wide character string.  Since the character
> -	     (except for the decimal point and thousands separator) must
> -	     be coming from the ASCII range we can esily convert the
> -	     string without mapping tables.  */
> -	  for (cp = buffer, copywc = wstartp; copywc < wcp; ++copywc)
> -	    if (*copywc == decimalwc)
> -	      cp = (char *) __mempcpy (cp, decimal, decimal_len);
> -	    else if (*copywc == thousands_sepwc)
> -	      cp = (char *) __mempcpy (cp, thousands_sep, thousands_sep_len);
> -	    else
> -	      *cp++ = (char) *copywc;
> -	}
> +  /* Replacement for ',' and '.'.  */
> +  const char *thousands_sep;
> +  const char *decimal;
> +  unsigned char decimal_point_bytes;
> +  unsigned char thousands_sep_length;
>  
> -      tmpptr = buffer;
> -      if (__glibc_unlikely (info->i18n))
> -	{
> -#ifdef COMPILE_WPRINTF
> -	  wstartp = _i18n_number_rewrite (wstartp, wcp,
> -					  wbuffer + wbuffer_to_alloc);
> -	  wcp = wbuffer + wbuffer_to_alloc;
> -	  assert ((uintptr_t) wbuffer <= (uintptr_t) wstartp);
> -	  assert ((uintptr_t) wstartp
> -		  < (uintptr_t) wbuffer + wbuffer_to_alloc);
> -#else
> -	  tmpptr = _i18n_number_rewrite (tmpptr, cp, buffer_end);
> -	  cp = buffer_end;
> -	  assert ((uintptr_t) buffer <= (uintptr_t) tmpptr);
> -	  assert ((uintptr_t) tmpptr < (uintptr_t) buffer_end);
> -#endif
> -	}
> +  /* Buffer to write to.   */
> +  struct __printf_buffer *next;
> +
> +  /* Activates outdigit translation if not NULL.  */
> +  struct __locale_data *ctype;
>  
> -      PRINT (tmpptr, wstartp, wide ? wcp - wstartp : cp - tmpptr);
> +  /* Buffer to which the untranslated ASCII digits are written.  */
> +  char untranslated[64];
> +};
>  
> -      /* Free the memory if necessary.  */
> -      if (__glibc_unlikely (buffer_malloced))
> +/*  Multibyte buffer-to-buffer flush function with full translation.  */
> +void
> +__printf_buffer_flush_fp (struct __printf_buffer_fp *buf)
> +{
> +  /* No need to update buf->base.written; the actual count is
> +     maintained in buf->next->written.  */
> +  for (char *p = buf->untranslated; p < buf->base.write_ptr; ++p)
> +    {
> +      char ch = *p;
> +      const char *replacement = NULL;
> +      unsigned int replacement_bytes;
> +      if (ch == ',')
> +	{
> +	  replacement = buf->thousands_sep;
> +	  replacement_bytes = buf->thousands_sep_length;
> +	}
> +      else if (ch == '.')
> +	{
> +	  replacement = buf->decimal;
> +	  replacement_bytes = buf->decimal_point_bytes;
> +	}
> +      else if (buf->ctype != NULL && '0' <= ch && ch <= '9')
>  	{
> -	  free (buffer);
> -	  free (wbuffer);
> -	  /* Avoid a double free if the subsequent PADN encounters an
> -	     I/O error.  */
> -	  buffer = NULL;
> -	  wbuffer = NULL;
> +	  int digit = ch - '0';
> +	  replacement
> +	    = buf->ctype->values[_NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT0_MB)
> +				 + digit].string;
> +	  struct lc_ctype_data *ctype = buf->ctype->private;
> +	  replacement_bytes = ctype->outdigit_bytes[digit];
>  	}
> +      if (replacement == NULL)
> +	__printf_buffer_putc (buf->next, ch);
> +      else
> +	__printf_buffer_write (buf->next, replacement, replacement_bytes);
>      }
>  

Ok.

> -    if (info->left && width > 0)
> -      PADN (info->pad, width);
> -  }
> -  return done;
> +  if (!__printf_buffer_has_failed (buf->next))
> +    buf->base.write_ptr = buf->untranslated;
> +  else
> +    __printf_buffer_mark_failed (&buf->base);
>  }
> -libc_hidden_def (__printf_fp_l)
>  
> -int
> -___printf_fp (FILE *fp, const struct printf_info *info,
> -	      const void *const *args)
> +void
> +__printf_fp_l_buffer (struct __printf_buffer *buf, locale_t loc,
> +		      const struct printf_info *info,
> +		      const void *const *args)
>  {
> -  return __printf_fp_l (fp, _NL_CURRENT_LOCALE, info, args);
> +  struct __printf_buffer_fp tmp;
> +
> +  if (info->extra)
> +    {
> +      tmp.thousands_sep = _nl_lookup (loc, LC_MONETARY, MON_THOUSANDS_SEP);
> +      tmp.decimal = _nl_lookup (loc, LC_MONETARY, MON_DECIMAL_POINT);
> +      if (tmp.decimal[0] == '\0')
> +	tmp.decimal = _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT);
> +    }
> +  else
> +    {
> +      tmp.thousands_sep = _nl_lookup (loc, LC_NUMERIC, THOUSANDS_SEP);
> +      tmp.decimal = _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT);
> +    }
> +
> +  tmp.thousands_sep_length = strlen (tmp.thousands_sep);
> +  if (tmp.decimal[1] == '\0' && tmp.thousands_sep_length <= 1
> +      && !info->i18n)
> +    {
> +      /* Emit the the characters directly.  This is only possible if the
> +	 separators have length 1 (or 0 in case of thousands_sep).  i18n
> +	 digit translation still needs the full conversion.  */
> +      __printf_fp_buffer_1 (buf, loc,
> +			    tmp.thousands_sep[0], tmp.decimal[0],
> +			    tmp.thousands_sep_length,
> +			    info, args);
> +      return;
> +    }
> +
> +  tmp.decimal_point_bytes = strlen (tmp.decimal);
> +
> +  if (info->i18n)
> +    tmp.ctype = loc->__locales[LC_CTYPE];
> +  else
> +    tmp.ctype = NULL;
> +  tmp.next = buf;
> +
> +  __printf_buffer_init (&tmp.base, tmp.untranslated, sizeof (tmp.untranslated),
> +			__printf_buffer_mode_fp);
> +  __printf_fp_buffer_1 (&tmp.base, loc, ',', '.',
> +			tmp.thousands_sep_length, info, args);
> +  if (__printf_buffer_has_failed (&tmp.base))
> +    {
> +      __printf_buffer_mark_failed (tmp.next);
> +      return;
> +    }
> +  __printf_buffer_flush_fp (&tmp);
>  }
> -ldbl_hidden_def (___printf_fp, __printf_fp)
> -ldbl_strong_alias (___printf_fp, __printf_fp)
>  
> -\f
> -/* Return the number of extra grouping characters that will be inserted
> -   into a number with INTDIG_MAX integer digits.  */
> +/* The wide version is implemented on top of the multibyte version using
> +   translation.  */
>  
> -unsigned int
> -__guess_grouping (unsigned int intdig_max, const char *grouping)
> +struct __printf_buffer_fp_to_wide
>  {
> -  unsigned int groups;
> +  struct __printf_buffer base;
> +  wchar_t thousands_sep_wc;
> +  wchar_t decimalwc;
> +  struct __wprintf_buffer *next;
>  
> -  /* We treat all negative values like CHAR_MAX.  */
> +  /* Activates outdigit translation if not NULL.  */
> +  struct __locale_data *ctype;
>  
> -  if (*grouping == CHAR_MAX || *grouping <= 0)
> -    /* No grouping should be done.  */
> -    return 0;
> +  char untranslated[64];
> +};
>  
> -  groups = 0;
> -  while (intdig_max > (unsigned int) *grouping)
> +void
> +__printf_buffer_flush_fp_to_wide (struct __printf_buffer_fp_to_wide *buf)
> +{
> +  /* No need to update buf->base.written; the actual count is
> +     maintained in buf->next->written.  */
> +  for (char *p = buf->untranslated; p < buf->base.write_ptr; ++p)
>      {
> -      ++groups;
> -      intdig_max -= *grouping++;
> -
> -      if (*grouping == CHAR_MAX
> -#if CHAR_MIN < 0
> -	  || *grouping < 0
> -#endif
> -	  )
> -	/* No more grouping should be done.  */
> -	break;
> -      else if (*grouping == 0)
> +      /* wchar_t overlaps with char in the ASCII range.  */
> +      wchar_t ch = *p;
> +      if (ch == L',')
>  	{
> -	  /* Same grouping repeats.  */
> -	  groups += (intdig_max - 1) / grouping[-1];
> -	  break;
> +	  ch = buf->thousands_sep_wc;
> +	  if (ch == 0)
> +	    continue;
>  	}
> +      else if (ch == L'.')
> +	ch = buf->decimalwc;
> +      else if (buf->ctype != NULL && L'0' <= ch && ch <= L'9')
> +	ch = buf->ctype->values[_NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT0_WC)
> +				+ ch - L'0'].word;
> +      __wprintf_buffer_putc (buf->next, ch);
>      }
>  
> -  return groups;
> +  if (!__wprintf_buffer_has_failed (buf->next))
> +    buf->base.write_ptr = buf->untranslated;
> +  else
> +    __printf_buffer_mark_failed (&buf->base);
>  }
>  
> -/* Group the INTDIG_NO integer digits of the number in [BUF,BUFEND).
> -   There is guaranteed enough space past BUFEND to extend it.
> -   Return the new end of buffer.  */
> -
> -static wchar_t *
> -group_number (wchar_t *buf, wchar_t *bufend, unsigned int intdig_no,
> -	      const char *grouping, wchar_t thousands_sep, int ngroups)
> +void
> +__wprintf_fp_l_buffer (struct __wprintf_buffer *buf, locale_t loc,
> +		       const struct printf_info *info,
> +		       const void *const *args)
>  {
> -  wchar_t *p;
> -
> -  if (ngroups == 0)
> -    return bufend;
> +  struct __printf_buffer_fp_to_wide tmp;
> +  if (info->extra)
> +    {
> +      tmp.decimalwc = _nl_lookup_word (loc, LC_MONETARY,
> +				       _NL_MONETARY_DECIMAL_POINT_WC);
> +      tmp.thousands_sep_wc = _nl_lookup_word (loc, LC_MONETARY,
> +					      _NL_MONETARY_THOUSANDS_SEP_WC);
> +      if (tmp.decimalwc == 0)
> +	tmp.decimalwc = _nl_lookup_word (loc, LC_NUMERIC,
> +					 _NL_NUMERIC_DECIMAL_POINT_WC);
> +    }
> +  else
> +    {
> +      tmp.decimalwc = _nl_lookup_word (loc, LC_NUMERIC,
> +				       _NL_NUMERIC_DECIMAL_POINT_WC);
> +      tmp.thousands_sep_wc = _nl_lookup_word (loc, LC_NUMERIC,
> +					      _NL_NUMERIC_THOUSANDS_SEP_WC);
> +    }
>  
> -  /* Move the fractional part down.  */
> -  __wmemmove (buf + intdig_no + ngroups, buf + intdig_no,
> -	      bufend - (buf + intdig_no));
> +  if (info->i18n)
> +    tmp.ctype = loc->__locales[LC_CTYPE];
> +  else
> +    tmp.ctype = NULL;
> +  tmp.next = buf;
>  
> -  p = buf + intdig_no + ngroups - 1;
> -  do
> +  __printf_buffer_init (&tmp.base, tmp.untranslated, sizeof (tmp.untranslated),
> +			__printf_buffer_mode_fp_to_wide);
> +  __printf_fp_buffer_1 (&tmp.base, loc, ',', '.', 1, info, args);
> +  if (__printf_buffer_has_failed (&tmp.base))
>      {
> -      unsigned int len = *grouping++;
> -      do
> -	*p-- = buf[--intdig_no];
> -      while (--len > 0);
> -      *p-- = thousands_sep;
> +      __wprintf_buffer_mark_failed (tmp.next);
> +      return;
> +    }
> +  __printf_buffer_flush (&tmp.base);
> +}
>  
> -      if (*grouping == CHAR_MAX
> -#if CHAR_MIN < 0
> -	  || *grouping < 0
> -#endif
> -	  )
> -	/* No more grouping should be done.  */
> -	break;
> -      else if (*grouping == 0)
> -	/* Same grouping repeats.  */
> -	--grouping;
> -    } while (intdig_no > (unsigned int) *grouping);
> -
> -  /* Copy the remaining ungrouped digits.  */
> -  do
> -    *p-- = buf[--intdig_no];
> -  while (p > buf);
> -
> -  return bufend + ngroups;
> +int
> +___printf_fp (FILE *fp, const struct printf_info *info,
> +	      const void *const *args)
> +{
> +  if (info->wide)
> +    {
> +      struct __wprintf_buffer_to_file buf;
> +      __wprintf_buffer_to_file_init (&buf, fp);
> +      __wprintf_fp_l_buffer (&buf.base, _NL_CURRENT_LOCALE, info, args);
> +      return __wprintf_buffer_to_file_done (&buf);
> +    }
> +  else
> +    {
> +      struct __printf_buffer_to_file buf;
> +      __printf_buffer_to_file_init (&buf, fp);
> +      __printf_fp_l_buffer (&buf.base, _NL_CURRENT_LOCALE, info, args);
> +      return __printf_buffer_to_file_done (&buf);
> +    }
>  }
> +ldbl_hidden_def (___printf_fp, __printf_fp)
> +ldbl_strong_alias (___printf_fp, __printf_fp)

Ok.

> diff --git a/stdio-common/printf_fphex.c b/stdio-common/printf_fphex.c
> index 5af380da62..957796f00b 100644
> --- a/stdio-common/printf_fphex.c
> +++ b/stdio-common/printf_fphex.c
> @@ -17,10 +17,12 @@
>     <https://www.gnu.org/licenses/>.  */
>  
>  #include <array_length.h>
> +#include <assert.h>
>  #include <ctype.h>
>  #include <ieee754.h>
>  #include <math.h>
>  #include <printf.h>
> +#include <libioP.h>
>  #include <stdlib.h>
>  #include <stdio.h>
>  #include <string.h>
> @@ -30,6 +32,9 @@
>  #include <locale/localeinfo.h>
>  #include <stdbool.h>
>  #include <rounding-mode.h>
> +#include <sys/param.h>
> +#include <printf_buffer.h>
> +#include <errno.h>
>  
>  #if __HAVE_DISTINCT_FLOAT128
>  # include "ieee754_float128.h"
> @@ -39,58 +44,11 @@
>  		IEEE854_FLOAT128_BIAS)
>  #endif
>  
> -/* #define NDEBUG 1*/		/* Undefine this for debugging assertions.  */
> -#include <assert.h>
> -
> -#include <libioP.h>
> -#define PUT(f, s, n) _IO_sputn (f, s, n)
> -#define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : _IO_padn (f, c, n))
> -#undef putc
> -#define putc(c, f) (wide \
> -		     ? (int)_IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f))
> -
> -\f
> -/* Macros for doing the actual output.  */
> -
> -#define outchar(ch)							      \
> -  do									      \
> -    {									      \
> -      const int outc = (ch);						      \
> -      if (putc (outc, fp) == EOF)					      \
> -	return -1;							      \
> -      ++done;								      \
> -    } while (0)
> -
> -#define PRINT(ptr, wptr, len)						      \
> -  do									      \
> -    {									      \
> -      size_t outlen = (len);						      \
> -      if (wide)								      \
> -	while (outlen-- > 0)						      \
> -	  outchar (*wptr++);						      \
> -      else								      \
> -	while (outlen-- > 0)						      \
> -	  outchar (*ptr++);						      \
> -    } while (0)
> -
> -#define PADN(ch, len)							      \
> -  do									      \
> -    {									      \
> -      if (PAD (fp, ch, len) != len)					      \
> -	return -1;							      \
> -      done += len;							      \
> -    }									      \
> -  while (0)
> -
> -#ifndef MIN
> -# define MIN(a,b) ((a)<(b)?(a):(b))
> -#endif
> -\f
> -
> -int
> -__printf_fphex (FILE *fp,
> -		const struct printf_info *info,
> -		const void *const *args)
> +static void
> +__printf_fphex_buffer (struct __printf_buffer *buf,
> +		       const char *decimal,
> +		       const struct printf_info *info,
> +		       const void *const *args)
>  {
>    /* The floating-point value to output.  */
>    union
> @@ -106,34 +64,19 @@ __printf_fphex (FILE *fp,
>    /* This function always uses LC_NUMERIC.  */
>    assert (info->extra == 0);
>  
> -  /* Locale-dependent representation of decimal point. Hexadecimal
> -     formatting always using LC_NUMERIC (disregarding info->extra).  */
> -  const char *decimal = _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
> -  wchar_t decimalwc = _NL_CURRENT_WORD (LC_NUMERIC,
> -					_NL_NUMERIC_DECIMAL_POINT_WC);
> -
> -  /* The decimal point character must never be zero.  */
> -  assert (*decimal != '\0' && decimalwc != L'\0');
> -
>    /* "NaN" or "Inf" for the special cases.  */
>    const char *special = NULL;
> -  const wchar_t *wspecial = NULL;
>  
>    /* Buffer for the generated number string for the mantissa.  The
>       maximal size for the mantissa is 128 bits.  */
>    char numbuf[32];
>    char *numstr;
>    char *numend;
> -  wchar_t wnumbuf[32];
> -  wchar_t *wnumstr;
> -  wchar_t *wnumend;
>    int negative;
>  
>    /* The maximal exponent of two in decimal notation has 5 digits.  */
>    char expbuf[5];
>    char *expstr;
> -  wchar_t wexpbuf[5];
> -  wchar_t *wexpstr;
>    int expnegative;
>    int exponent;
>  
> @@ -149,12 +92,6 @@ __printf_fphex (FILE *fp,
>    /* Width.  */
>    int width = info->width;
>  
> -  /* Number of characters written.  */
> -  int done = 0;
> -
> -  /* Nonzero if this is output on a wide character stream.  */
> -  int wide = info->wide;
> -
>  #define PRINTF_FPHEX_FETCH(FLOAT, VAR)					\
>    {									\
>      (VAR) = *(const FLOAT *) args[0];					\
> @@ -163,30 +100,18 @@ __printf_fphex (FILE *fp,
>      if (isnan (VAR))							\
>        {									\
>  	if (isupper (info->spec))					\
> -	  {								\
> -	    special = "NAN";						\
> -	    wspecial = L"NAN";						\
> -	  }								\
> +	  special = "NAN";						\
>  	else								\
> -	  {								\
> -	    special = "nan";						\
> -	    wspecial = L"nan";						\
> -	  }								\
> +	  special = "nan";						\
>        }									\
>      else								\
>        {									\
>  	if (isinf (VAR))						\
>  	  {								\
>  	    if (isupper (info->spec))					\
> -	      {								\
> -		special = "INF";					\
> -		wspecial = L"INF";					\
> -	      }								\
> +	      special = "INF";						\
>  	    else							\
> -	      {								\
> -		special = "inf";					\
> -		wspecial = L"inf";					\
> -	      }								\
> +	      special = "inf";						\
>  	  }								\
>        }									\
>      negative = signbit (VAR);						\
> @@ -215,22 +140,22 @@ __printf_fphex (FILE *fp,
>  	--width;
>        width -= 3;
>  
> -      if (!info->left && width > 0)
> -	PADN (' ', width);
> +      if (!info->left)
> +	__printf_buffer_pad (buf, ' ', width);
>  
>        if (negative)
> -	outchar ('-');
> +	__printf_buffer_putc (buf, '-');
>        else if (info->showsign)
> -	outchar ('+');
> +	__printf_buffer_putc (buf, '+');
>        else if (info->space)
> -	outchar (' ');
> +	__printf_buffer_putc (buf, ' ');
>  
> -      PRINT (special, wspecial, 3);
> +      __printf_buffer_puts (buf, special);
>  
> -      if (info->left && width > 0)
> -	PADN (' ', width);
> +      if (info->left)
> +	__printf_buffer_pad (buf, ' ', width);
>  
> -      return done;
> +      return;
>      }
>  
>  #if __HAVE_DISTINCT_FLOAT128
> @@ -252,26 +177,15 @@ __printf_fphex (FILE *fp,
>        zero_mantissa = num == 0;
>  
>        if (sizeof (unsigned long int) > 6)
> -	{
> -	  wnumstr = _itowa_word (num, wnumbuf + (sizeof wnumbuf) / sizeof (wchar_t), 16,
> -				 info->spec == 'A');
>  	  numstr = _itoa_word (num, numbuf + sizeof numbuf, 16,
>  			       info->spec == 'A');
> -	}
>        else
> -	{
> -	  wnumstr = _itowa (num, wnumbuf + sizeof wnumbuf / sizeof (wchar_t), 16,
> -			    info->spec == 'A');
>  	  numstr = _itoa (num, numbuf + sizeof numbuf, 16,
>  			  info->spec == 'A');
> -	}
>  
>        /* Fill with zeroes.  */
> -      while (wnumstr > wnumbuf + (sizeof wnumbuf - 52) / sizeof (wchar_t))
> -	{
> -	  *--wnumstr = L'0';
> -	  *--numstr = '0';
> -	}
> +      while (numstr > numbuf + (sizeof numbuf - 13))
> +	*--numstr = '0';
>  
>        leading = fpnum.dbl.ieee.exponent == 0 ? '0' : '1';
>  
> @@ -307,13 +221,9 @@ __printf_fphex (FILE *fp,
>    /* Look for trailing zeroes.  */
>    if (! zero_mantissa)
>      {
> -      wnumend = array_end (wnumbuf);
>        numend = array_end (numbuf);
> -      while (wnumend[-1] == L'0')
> -	{
> -	  --wnumend;
> +      while (numend[-1] == '0')
>  	  --numend;
> -	}
>  
>        bool do_round_away = false;
>  
> @@ -352,7 +262,6 @@ __printf_fphex (FILE *fp,
>  		 like in ASCII.  This is true for the rest of GNU, too.  */
>  	      if (ch == '9')
>  		{
> -		  wnumstr[cnt] = (wchar_t) info->spec;
>  		  numstr[cnt] = info->spec;	/* This is tricky,
>  						   think about it!  */
>  		  break;
> @@ -360,14 +269,10 @@ __printf_fphex (FILE *fp,
>  	      else if (tolower (ch) < 'f')
>  		{
>  		  ++numstr[cnt];
> -		  ++wnumstr[cnt];
>  		  break;
>  		}
>  	      else
> -		{
> -		  numstr[cnt] = '0';
> -		  wnumstr[cnt] = L'0';
> -		}
> +		numstr[cnt] = '0';
>  	    }
>  	  if (cnt < 0)
>  	    {
> @@ -401,13 +306,10 @@ __printf_fphex (FILE *fp,
>        if (precision == -1)
>  	precision = 0;
>        numend = numstr;
> -      wnumend = wnumstr;
>      }
>  
>    /* Now we can compute the exponent string.  */
>    expstr = _itoa_word (exponent, expbuf + sizeof expbuf, 10, 0);
> -  wexpstr = _itowa_word (exponent,
> -			 wexpbuf + sizeof wexpbuf / sizeof (wchar_t), 10, 0);
>  
>    /* Now we have all information to compute the size.  */
>    width -= ((negative || info->showsign || info->space)
> @@ -421,54 +323,110 @@ __printf_fphex (FILE *fp,
>       A special case when the mantissa or the precision is zero and the `#'
>       is not given.  In this case we must not print the decimal point.  */
>    if (precision > 0 || info->alt)
> -    width -= wide ? 1 : strlen (decimal);
> +    --width;
>  
> -  if (!info->left && info->pad != '0' && width > 0)
> -    PADN (' ', width);
> +  if (!info->left && info->pad != '0')
> +    __printf_buffer_pad (buf, ' ', width);
>  
>    if (negative)
> -    outchar ('-');
> +    __printf_buffer_putc (buf, '-');
>    else if (info->showsign)
> -    outchar ('+');
> +    __printf_buffer_putc (buf, '+');
>    else if (info->space)
> -    outchar (' ');
> +    __printf_buffer_putc (buf, ' ');
>  
> -  outchar ('0');
> +  __printf_buffer_putc (buf, '0');
>    if ('X' - 'A' == 'x' - 'a')
> -    outchar (info->spec + ('x' - 'a'));
> +    __printf_buffer_putc (buf, info->spec + ('x' - 'a'));
>    else
> -    outchar (info->spec == 'A' ? 'X' : 'x');
> +    __printf_buffer_putc (buf, info->spec == 'A' ? 'X' : 'x');
>  
> -  if (!info->left && info->pad == '0' && width > 0)
> -    PADN ('0', width);
> +  if (!info->left && info->pad == '0')
> +    __printf_buffer_pad (buf, '0', width);
>  
> -  outchar (leading);
> +  __printf_buffer_putc (buf, leading);
>  
>    if (precision > 0 || info->alt)
> -    {
> -      const wchar_t *wtmp = &decimalwc;
> -      PRINT (decimal, wtmp, wide ? 1 : strlen (decimal));
> -    }
> +    __printf_buffer_puts (buf, decimal);
>  
>    if (precision > 0)
>      {
>        ssize_t tofill = precision - (numend - numstr);
> -      PRINT (numstr, wnumstr, MIN (numend - numstr, precision));
> -      if (tofill > 0)
> -	PADN ('0', tofill);
> +      __printf_buffer_write (buf, numstr, MIN (numend - numstr, precision));
> +      __printf_buffer_pad (buf, '0', tofill);
>      }
>  
>    if ('P' - 'A' == 'p' - 'a')
> -    outchar (info->spec + ('p' - 'a'));
> +    __printf_buffer_putc (buf, info->spec + ('p' - 'a'));
>    else
> -    outchar (info->spec == 'A' ? 'P' : 'p');
> +    __printf_buffer_putc (buf, info->spec == 'A' ? 'P' : 'p');
> +
> +  __printf_buffer_putc (buf, expnegative ? '-' : '+');
> +
> +  __printf_buffer_write (buf, expstr, (expbuf + sizeof expbuf) - expstr);
> +
> +  if (info->left && info->pad != '0')
> +    __printf_buffer_pad (buf, info->pad, width);
> +}
>

Ok.

 +
> +void
> +__printf_fphex_l_buffer (struct __printf_buffer *buf, locale_t loc,
> +			 const struct printf_info *info,
> +			 const void *const *args)
> +{
> +  __printf_fphex_buffer (buf, _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT),
> +			 info, args);
> +}
>  
> -  outchar (expnegative ? '-' : '+');
>  
> -  PRINT (expstr, wexpstr, (expbuf + sizeof expbuf) - expstr);
> +/* The wide buffer version is implemented by translating the output of
> +   the multibyte verison.  */
>  
> -  if (info->left && info->pad != '0' && width > 0)
> -    PADN (info->pad, width);
> +struct __printf_buffer_fphex_to_wide
> +{
> +  struct __printf_buffer base;
> +  wchar_t decimalwc;
> +  struct __wprintf_buffer *next;
> +  char untranslated[64];
> +};
> +
> +/* Translate to wide characters, rewriting "." to the actual decimal
> +   point.  */
> +void
> +__printf_buffer_flush_fphex_to_wide (struct __printf_buffer_fphex_to_wide *buf)
> +{
> +  /* No need to adjust buf->base.written, only buf->next->written matters.  */
> +  for (char *p = buf->untranslated; p < buf->base.write_ptr; ++p)
> +    {
> +      /* wchar_t overlaps with char in the ASCII range.  */
> +      wchar_t ch = *p;
> +      if (ch == L'.')
> +	ch = buf->decimalwc;
> +      __wprintf_buffer_putc (buf->next, ch);
> +    }
>  
> -  return done;
> +  if (!__wprintf_buffer_has_failed (buf->next))
> +    buf->base.write_ptr = buf->untranslated;
> +  else
> +    __printf_buffer_mark_failed (&buf->base);
> +}
> +
> +void
> +__wprintf_fphex_l_buffer (struct __wprintf_buffer *next, locale_t loc,
> +			  const struct printf_info *info,
> +			  const void *const *args)
> +{
> +  struct __printf_buffer_fphex_to_wide buf;
> +  __printf_buffer_init (&buf.base, buf.untranslated, sizeof (buf.untranslated),
> +			__printf_buffer_mode_fphex_to_wide);
> +  buf.decimalwc = _nl_lookup_word (loc, LC_NUMERIC,
> +				   _NL_NUMERIC_DECIMAL_POINT_WC);
> +  buf.next = next;
> +  __printf_fphex_buffer (&buf.base, ".", info, args);
> +  if (__printf_buffer_has_failed (&buf.base))
> +    {
> +      __wprintf_buffer_mark_failed (buf.next);
> +      return;
> +    }
> +  __printf_buffer_flush_fphex_to_wide (&buf);
>  }

Ok.

> diff --git a/stdio-common/vfprintf-internal.c b/stdio-common/vfprintf-internal.c
> index fb94961f37..83a6aea510 100644
> --- a/stdio-common/vfprintf-internal.c
> +++ b/stdio-common/vfprintf-internal.c
> @@ -16,6 +16,7 @@
>     <https://www.gnu.org/licenses/>.  */
>  
>  #include <array_length.h>
> +#include <assert.h>
>  #include <ctype.h>
>  #include <limits.h>
>  #include <printf.h>
> @@ -29,9 +30,12 @@
>  #include <sys/param.h>
>  #include <_itoa.h>
>  #include <locale/localeinfo.h>
> +#include <grouping_iterator.h>
>  #include <stdio.h>
>  #include <scratch_buffer.h>
>  #include <intprops.h>
> +#include <printf_buffer.h>
> +#include <printf_buffer_to_file.h>
>  
>  /* This code is shared between the standard stdio implementation found
>     in GNU C library and the libio implementation originally found in
> @@ -47,21 +51,21 @@
>  #endif
>  
>  #define ARGCHECK(S, Format) \
> -  do									      \
> -    {									      \
> -      /* Check file argument for consistence.  */			      \
> -      CHECK_FILE (S, -1);						      \
> -      if (S->_flags & _IO_NO_WRITES)					      \
> -	{								      \
> -	  S->_flags |= _IO_ERR_SEEN;					      \
> -	  __set_errno (EBADF);						      \
> -	  return -1;							      \
> -	}								      \
> -      if (Format == NULL)						      \
> -	{								      \
> -	  __set_errno (EINVAL);						      \
> -	  return -1;							      \
> -	}								      \
> +  do									     \
> +    {									     \
> +      /* Check file argument for consistence.  */			     \
> +      CHECK_FILE (S, -1);						     \
> +      if (S->_flags & _IO_NO_WRITES)					     \
> +       {								     \
> +	 S->_flags |= _IO_ERR_SEEN;					     \
> +	 __set_errno (EBADF);						     \
> +	 return -1;							     \
> +       }								     \
> +      if (Format == NULL)						     \
> +       {								     \
> +	 __set_errno (EINVAL);						     \
> +	 return -1;							     \
> +       }								     \
>      } while (0)
>  #define UNBUFFERED_P(S) ((S)->_flags & _IO_UNBUFFERED)
>  
> @@ -116,37 +120,9 @@
>    while (0)
>  #endif
>  
> -/* Add LENGTH to DONE.  Return the new value of DONE, or -1 on
> -   overflow (and set errno accordingly).  */
> -static inline int
> -done_add_func (size_t length, int done)
> -{
> -  if (done < 0)
> -    return done;
> -  int ret;
> -  if (INT_ADD_WRAPV (done, length, &ret))
> -    {
> -      __set_errno (EOVERFLOW);
> -      return -1;
> -    }
> -  return ret;
> -}
> -
> -#define done_add(val)							\
> -  do									\
> -    {									\
> -      /* Ensure that VAL has a type similar to int.  */			\
> -      _Static_assert (sizeof (val) == sizeof (int), "value int size");	\
> -      _Static_assert ((__typeof__ (val)) -1 < 0, "value signed");	\
> -      done = done_add_func ((val), done);				\
> -      if (done < 0)							\
> -	goto all_done;							\
> -    }									\
> -  while (0)
> -
>  #ifndef COMPILE_WPRINTF
> +# include "printf_buffer-char.h"
>  # define vfprintf	__vfprintf_internal
> -# define CHAR_T		char
>  # define OTHER_CHAR_T   wchar_t
>  # define UCHAR_T	unsigned char
>  # define INT_T		int
> @@ -155,14 +131,12 @@ typedef const char *THOUSANDS_SEP_T;
>  # define ISDIGIT(Ch)	((unsigned int) ((Ch) - '0') < 10)
>  # define STR_LEN(Str)	strlen (Str)
>  
> -# define PUT(F, S, N)	_IO_sputn ((F), (S), (N))
> -# define PUTC(C, F)	_IO_putc_unlocked (C, F)
>  # define ORIENT		if (_IO_vtable_offset (s) == 0 && _IO_fwide (s, -1) != -1)\
>  			  return -1
>  # define CONVERT_FROM_OTHER_STRING __wcsrtombs
>  #else
> +# include "printf_buffer-wchar_t.h"
>  # define vfprintf	__vfwprintf_internal
> -# define CHAR_T		wchar_t
>  # define OTHER_CHAR_T   char
>  /* This is a hack!!!  There should be a type uwchar_t.  */
>  # define UCHAR_T	unsigned int /* uwchar_t */
> @@ -174,8 +148,6 @@ typedef wchar_t THOUSANDS_SEP_T;
>  
>  # include <_itowa.h>
>  
> -# define PUT(F, S, N)	_IO_sputn ((F), (S), (N))
> -# define PUTC(C, F)	_IO_putwc_unlocked (C, F)
>  # define ORIENT		if (_IO_fwide (s, 1) != 1) return -1
>  # define CONVERT_FROM_OTHER_STRING __mbsrtowcs
>  
> @@ -186,76 +158,16 @@ typedef wchar_t THOUSANDS_SEP_T;
>  # define EOF WEOF
>  #endif
>  
> -static inline int
> -pad_func (FILE *s, CHAR_T padchar, int width, int done)
> -{
> -  if (width > 0)
> -    {
> -      ssize_t written;
> -#ifndef COMPILE_WPRINTF
> -      written = _IO_padn (s, padchar, width);
> -#else
> -      written = _IO_wpadn (s, padchar, width);
> -#endif
> -      if (__glibc_unlikely (written != width))
> -	return -1;
> -      return done_add_func (width, done);
> -    }
> -  return done;
> -}
> -
> -#define PAD(Padchar)							\
> -  do									\
> -    {									\
> -      done = pad_func (s, (Padchar), width, done);			\
> -      if (done < 0)							\
> -	goto all_done;							\
> -    }									\
> -  while (0)
> -
> -#include "_i18n_number.h"
> -
>  /* Include the shared code for parsing the format string.  */
>  #include "printf-parse.h"
>  
>  
> -#define	outchar(Ch)							      \
> -  do									      \
> -    {									      \
> -      const INT_T outc = (Ch);						      \
> -      if (PUTC (outc, s) == EOF || done == INT_MAX)			      \
> -	{								      \
> -	  done = -1;							      \
> -	  goto all_done;						      \
> -	}								      \
> -      ++done;								      \
> -    }									      \
> -  while (0)
> -
> -static inline int
> -outstring_func (FILE *s, const UCHAR_T *string, size_t length, int done)
> -{
> -  assert ((size_t) done <= (size_t) INT_MAX);
> -  if ((size_t) PUT (s, string, length) != (size_t) (length))
> -    return -1;
> -  return done_add_func (length, done);
> -}
> -
> -#define outstring(String, Len)						\
> -  do									\
> -    {									\
> -      const void *string_ = (String);					\
> -      done = outstring_func (s, string_, (Len), done);			\
> -      if (done < 0)							\
> -	goto all_done;							\
> -    }									\
> -   while (0)
> -
>  /* Write the string SRC to S.  If PREC is non-negative, write at most
>     PREC bytes.  If LEFT is true, perform left justification.  */
> -static int
> -outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
> -				 int width, bool left, int done)
> +static void
> +outstring_converted_wide_string (struct Xprintf_buffer *target,
> +				 const OTHER_CHAR_T *src, int prec,
> +				 int width, bool left)
>  {
>    /* Use a small buffer to combine processing of multiple characters.
>       CONVERT_FROM_OTHER_STRING expects the buffer size in (wide)
> @@ -290,7 +202,10 @@ outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
>  	      size_t written = CONVERT_FROM_OTHER_STRING
>  		(buf, &src_copy, write_limit, &mbstate);
>  	      if (written == (size_t) -1)
> -		return -1;
> +		{
> +		  Xprintf_buffer_mark_failed (target);
> +		  return;
> +		}
>  	      if (written == 0)
>  		break;
>  	      total_written += written;
> @@ -299,12 +214,9 @@ outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
>  	}
>  
>        /* Output initial padding.  */
> -      if (total_written < width)
> -	{
> -	  done = pad_func (s, L_(' '), width - total_written, done);
> -	  if (done < 0)
> -	    return done;
> -	}
> +      Xprintf_buffer_pad (target, L_(' '), width - total_written);
> +      if (Xprintf_buffer_has_failed (target))
> +	return;
>      }
>  
>    /* Convert the input string, piece by piece.  */
> @@ -324,12 +236,13 @@ outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
>  	size_t written = CONVERT_FROM_OTHER_STRING
>  	  (buf, &src, write_limit, &mbstate);
>  	if (written == (size_t) -1)
> -	  return -1;
> +	  {
> +	    Xprintf_buffer_mark_failed (target);
> +	    return;
> +	  }
>  	if (written == 0)
>  	  break;
> -	done = outstring_func (s, (const UCHAR_T *) buf, written, done);
> -	if (done < 0)
> -	  return done;
> +	Xprintf_buffer_write (target, buf, written);
>  	total_written += written;
>  	if (prec >= 0)
>  	  remaining -= written;
> @@ -337,21 +250,20 @@ outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
>    }
>  
>    /* Add final padding.  */
> -  if (width > 0 && left && total_written < width)
> -    return pad_func (s, L_(' '), width - total_written, done);
> -  return done;
> +  if (width > 0 && left)
> +    Xprintf_buffer_pad (target, L_(' '), width - total_written);
>  }
>  
>  /* Calls __printf_fp or __printf_fphex based on the value of the
>     format specifier INFO->spec.  */
> -static inline int
> -__printf_fp_spec (FILE *fp, const struct printf_info *info,
> -		  const void *const *args)
> +static inline void
> +__printf_fp_spec (struct Xprintf_buffer *target,
> +		  const struct printf_info *info, const void *const *args)
>  {
>    if (info->spec == 'a' || info->spec == 'A')
> -    return __printf_fphex (fp, info, args);
> +    Xprintf (fphex_l_buffer) (target, _NL_CURRENT_LOCALE, info, args);
>    else
> -    return __printf_fp (fp, info, args);
> +    Xprintf (fp_l_buffer) (target, _NL_CURRENT_LOCALE, info, args);
>  }
>  
>  /* For handling long_double and longlong we use the same flag.  If
> @@ -656,31 +568,29 @@ static const uint8_t jump_table[] =
>        REF (form_binary),	/* for 'B', 'b' */			      \
>      }
>  
> -/* Helper function to provide temporary buffering for unbuffered streams.  */
> -static int buffered_vfprintf (FILE *stream, const CHAR_T *fmt, va_list,
> -			      unsigned int)
> -     __THROW __attribute__ ((noinline));
> -
>  /* Handle positional format specifiers.  */
> -static int printf_positional (FILE *s,
> -			      const CHAR_T *format, int readonly_format,
> -			      va_list ap, va_list *ap_savep, int done,
> -			      int nspecs_done, const UCHAR_T *lead_str_end,
> -			      CHAR_T *work_buffer, int save_errno,
> -			      const char *grouping,
> -			      THOUSANDS_SEP_T thousands_sep,
> -			      unsigned int mode_flags);
> +static void printf_positional (struct Xprintf_buffer *buf,
> +			       const CHAR_T *format, int readonly_format,
> +			       va_list ap, va_list *ap_savep,
> +			       int nspecs_done, const UCHAR_T *lead_str_end,
> +			       CHAR_T *work_buffer, int save_errno,
> +			       const char *grouping,
> +			       THOUSANDS_SEP_T thousands_sep,
> +			       unsigned int mode_flags);
>  
>  /* Handle unknown format specifier.  */
> -static int printf_unknown (FILE *, const struct printf_info *) __THROW;
> -
> -/* Group digits of number string.  */
> -static CHAR_T *group_number (CHAR_T *, CHAR_T *, CHAR_T *, const char *,
> -			     THOUSANDS_SEP_T);
> -
> -/* The function itself.  */
> -int
> -vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
> +static void printf_unknown (struct Xprintf_buffer *,
> +			    const struct printf_info *) __THROW;
> +
> +static void group_number (struct Xprintf_buffer *buf,
> +			  struct grouping_iterator *iter,
> +			  CHAR_T *from, CHAR_T *to,
> +			  THOUSANDS_SEP_T thousands_sep, bool i18n);
> +
> +/* The buffer-based function itself.  */
> +void
> +Xprintf_buffer (struct Xprintf_buffer *buf, const CHAR_T *format,
> +		  va_list ap, unsigned int mode_flags)
>  {
>    /* The character used as thousands separator.  */
>    THOUSANDS_SEP_T thousands_sep = 0;
> @@ -688,9 +598,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
>    /* The string describing the size of groups of digits.  */
>    const char *grouping;
>  
> -  /* Place to accumulate the result.  */
> -  int done;
> -
>    /* Current character in format string.  */
>    const UCHAR_T *f;
>  
> @@ -717,30 +624,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
>       0 if unknown.  */
>    int readonly_format = 0;
>  
> -  /* Orient the stream.  */
> -#ifdef ORIENT
> -  ORIENT;
> -#endif
> -
> -  /* Sanity check of arguments.  */
> -  ARGCHECK (s, format);
> -
> -#ifdef ORIENT
> -  /* Check for correct orientation.  */
> -  if (_IO_vtable_offset (s) == 0
> -      && _IO_fwide (s, sizeof (CHAR_T) == 1 ? -1 : 1)
> -      != (sizeof (CHAR_T) == 1 ? -1 : 1))
> -    /* The stream is already oriented otherwise.  */
> -    return EOF;
> -#endif
> -
> -  if (UNBUFFERED_P (s))
> -    /* Use a helper function which will allocate a local temporary buffer
> -       for the stream and then call us again.  */
> -    return buffered_vfprintf (s, format, ap, mode_flags);
> -
>    /* Initialize local variables.  */
> -  done = 0;
>    grouping = (const char *) -1;
>  #ifdef __va_copy
>    /* This macro will be available soon in gcc's <stdarg.h>.  We need it
> @@ -759,17 +643,15 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
>    f = lead_str_end = __find_specmb ((const UCHAR_T *) format);
>  #endif
>  
> -  /* Lock stream.  */
> -  _IO_cleanup_region_start ((void (*) (void *)) &_IO_funlockfile, s);
> -  _IO_flockfile (s);
> -
>    /* Write the literal text before the first format.  */
> -  outstring ((const UCHAR_T *) format,
> -	     lead_str_end - (const UCHAR_T *) format);
> +  Xprintf_buffer_write (buf, format,
> +			  lead_str_end - (const UCHAR_T *) format);
> +  if (Xprintf_buffer_has_failed (buf))
> +    return;
>  
>    /* If we only have to print a simple string, return now.  */
>    if (*f == L_('\0'))
> -    goto all_done;
> +    return;
>  
>    /* Use the slow path in case any printf handler is registered.  */
>    if (__glibc_unlikely (__printf_function_table != NULL
> @@ -885,7 +767,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
>  	    if (pos == -1)
>  	      {
>  		__set_errno (EOVERFLOW);
> -		done = -1;
> +		Xprintf_buffer_mark_failed (buf);
>  		goto all_done;
>  	      }
>  
> @@ -912,7 +794,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
>        if (__glibc_unlikely (width == -1))
>  	{
>  	  __set_errno (EOVERFLOW);
> -	  done = -1;
> +	  Xprintf_buffer_mark_failed (buf);
>  	  goto all_done;
>  	}
>  
> @@ -935,7 +817,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
>  	      if (pos == -1)
>  		{
>  		  __set_errno (EOVERFLOW);
> -		  done = -1;
> +		  Xprintf_buffer_mark_failed (buf);
>  		  goto all_done;
>  		}
>  
> @@ -958,7 +840,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
>  	  if (prec == -1)
>  	    {
>  	      __set_errno (EOVERFLOW);
> -	      done = -1;
> +	      Xprintf_buffer_mark_failed (buf);
>  	      goto all_done;
>  	    }
>  	}
> @@ -1058,13 +940,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
>  	    PARSE_FLOAT_VA_ARG_EXTENDED (info);
>  	    const void *ptr = &the_arg;
>  
> -	    int function_done = __printf_fp_spec (s, &info, &ptr);
> -	    if (function_done < 0)
> -	      {
> -		done = -1;
> -		goto all_done;
> -	      }
> -	    done_add (function_done);
> +	    __printf_fp_spec (buf, &info, &ptr);
>  	  }
>  	  break;
>  
> @@ -1073,7 +949,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
>  	    {
>  	      /* The format string ended before the specifier is complete.  */
>  	      __set_errno (EINVAL);
> -	      done = -1;
> +	      Xprintf_buffer_mark_failed (buf);
>  	      goto all_done;
>  	    }
>  
> @@ -1093,30 +969,28 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
>  #endif
>  
>        /* Write the following constant string.  */
> -      outstring (end_of_spec, f - end_of_spec);
> +      Xprintf_buffer_write (buf, (const CHAR_T *) end_of_spec,
> +			      f - end_of_spec);
>      }
> -  while (*f != L_('\0'));
> +  while (*f != L_('\0') && !Xprintf_buffer_has_failed (buf));
>  
> -  /* Unlock stream and return.  */
> -  goto all_done;
> + all_done:
> +  /* printf_positional performs cleanup under its all_done label, so
> +     vfprintf-process-arg.c uses it for this function and
> +     printf_positional below.  */
> +  return;
>  
>    /* Hand off processing for positional parameters.  */
>  do_positional:
> -  done = printf_positional (s, format, readonly_format, ap, &ap_save,
> -			    done, nspecs_done, lead_str_end, work_buffer,
> -			    save_errno, grouping, thousands_sep, mode_flags);
> -
> - all_done:
> -  /* Unlock the stream.  */
> -  _IO_funlockfile (s);
> -  _IO_cleanup_region_end (0);
> -
> -  return done;
> +  printf_positional (buf, format, readonly_format, ap, &ap_save,
> +		     nspecs_done, lead_str_end, work_buffer,
> +		     save_errno, grouping, thousands_sep, mode_flags);
>  }
>  \f
> -static int
> -printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
> -		   va_list ap, va_list *ap_savep, int done, int nspecs_done,
> +static void
> +printf_positional (struct Xprintf_buffer * buf, const CHAR_T *format,
> +		   int readonly_format,
> +		   va_list ap, va_list *ap_savep, int nspecs_done,
>  		   const UCHAR_T *lead_str_end,
>  		   CHAR_T *work_buffer, int save_errno,
>  		   const char *grouping, THOUSANDS_SEP_T thousands_sep,
> @@ -1171,7 +1045,7 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
>  	{
>  	  if (!scratch_buffer_grow_preserve (&specsbuf))
>  	    {
> -	      done = -1;
> +	      Xprintf_buffer_mark_failed (buf);
>  	      goto all_done;
>  	    }
>  	  specs = specsbuf.data;
> @@ -1199,7 +1073,7 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
>        = sizeof (*args_value) + sizeof (*args_size) + sizeof (*args_type);
>      if (!scratch_buffer_set_array_size (&argsbuf, nargs, bytes_per_arg))
>        {
> -	done = -1;
> +	Xprintf_buffer_mark_failed (buf);
>  	goto all_done;
>        }
>      args_value = argsbuf.data;
> @@ -1312,7 +1186,8 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
>        }
>  
>    /* Now walk through all format specifiers and process them.  */
> -  for (; (size_t) nspecs_done < nspecs; ++nspecs_done)
> +  for (; (size_t) nspecs_done < nspecs && !Xprintf_buffer_has_failed (buf);
> +       ++nspecs_done)
>      {
>        STEP4_TABLE;
>  
> @@ -1376,26 +1251,19 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
>  	}
>  
>        /* Process format specifiers.  */
> -      while (1)
> +      do
>  	{
> -	  int function_done;
> -
>  	  if (spec <= UCHAR_MAX
>  	      && __printf_function_table != NULL
>  	      && __printf_function_table[(size_t) spec] != NULL)
>  	    {
> -	      const void **ptr = alloca (specs[nspecs_done].ndata_args
> -					 * sizeof (const void *));
> -
> -	      /* Fill in an array of pointers to the argument values.  */
> -	      for (unsigned int i = 0; i < specs[nspecs_done].ndata_args;
> -		   ++i)
> -		ptr[i] = &args_value[specs[nspecs_done].data_arg + i];
> -
> -	      /* Call the function.  */
> -	      function_done = __printf_function_table[(size_t) spec]
> -		(s, &specs[nspecs_done].info, ptr);
> -
> +	      int function_done
> +		= Xprintf (function_invoke) (buf,
> +					     __printf_function_table[(size_t) spec],
> +					     &args_value[specs[nspecs_done]
> +							 .data_arg],
> +					     specs[nspecs_done].ndata_args,
> +					     &specs[nspecs_done].info);
>  	      if (function_done != -2)
>  		{
>  		  /* If an error occurred we don't have information
> @@ -1403,11 +1271,9 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
>  		  if (function_done < 0)
>  		    {
>  		      /* Function has set errno.  */
> -		      done = -1;
> +		      Xprintf_buffer_mark_failed (buf);
>  		      goto all_done;
>  		    }
> -
> -		  done_add (function_done);
>  		  break;
>  		}
>  	    }
> @@ -1450,327 +1316,159 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
>  	      }
>  	    SETUP_FLOAT128_INFO (specs[nspecs_done].info);
>  
> -	    int function_done
> -	      = __printf_fp_spec (s, &specs[nspecs_done].info, &ptr);
> -	    if (function_done < 0)
> -	      {
> -		/* Error in print handler; up to handler to set errno.  */
> -		done = -1;
> -		goto all_done;
> -	      }
> -	    done_add (function_done);
> +	    __printf_fp_spec (buf, &specs[nspecs_done].info, &ptr);
>  	  }
>  	  break;
>  
>  	  LABEL (form_unknown):
>  	  {
> -	    int function_done = printf_unknown (s, &specs[nspecs_done].info);
> -
> -	    /* If an error occurred we don't have information about #
> -	       of chars.  */
> -	    if (function_done < 0)
> -	      {
> -		/* Function has set errno.  */
> -		done = -1;
> -		goto all_done;
> -	      }
> -
> -	    done_add (function_done);
> +	    printf_unknown (buf, &specs[nspecs_done].info);
>  	  }
>  	  break;
>  	}
> +      while (Xprintf_buffer_has_failed (buf));
>  
>        /* Write the following constant string.  */
> -      outstring (specs[nspecs_done].end_of_fmt,
> -		 specs[nspecs_done].next_fmt
> -		 - specs[nspecs_done].end_of_fmt);
> +      Xprintf_buffer_write (buf,
> +			      (const CHAR_T *) specs[nspecs_done].end_of_fmt,
> +			      (specs[nspecs_done].next_fmt
> +			       - specs[nspecs_done].end_of_fmt));
>      }
>   all_done:
>    scratch_buffer_free (&argsbuf);
>    scratch_buffer_free (&specsbuf);
> -  return done;
>  }
>  \f
>  /* Handle an unknown format specifier.  This prints out a canonicalized
>     representation of the format spec itself.  */
> -static int
> -printf_unknown (FILE *s, const struct printf_info *info)
> +static void
> +printf_unknown (struct Xprintf_buffer *buf, const struct printf_info *info)
>  {
> -  int done = 0;
>    CHAR_T work_buffer[MAX (sizeof (info->width), sizeof (info->prec)) * 3];
>    CHAR_T *const workend
>      = &work_buffer[sizeof (work_buffer) / sizeof (CHAR_T)];
>    CHAR_T *w;
>  
> -  outchar (L_('%'));
> +  Xprintf_buffer_putc (buf, L_('%'));
>  
>    if (info->alt)
> -    outchar (L_('#'));
> +    Xprintf_buffer_putc (buf, L_('#'));
>    if (info->group)
> -    outchar (L_('\''));
> +    Xprintf_buffer_putc (buf, L_('\''));
>    if (info->showsign)
> -    outchar (L_('+'));
> +    Xprintf_buffer_putc (buf, L_('+'));
>    else if (info->space)
> -    outchar (L_(' '));
> +    Xprintf_buffer_putc (buf, L_(' '));
>    if (info->left)
> -    outchar (L_('-'));
> +    Xprintf_buffer_putc (buf, L_('-'));
>    if (info->pad == L_('0'))
> -    outchar (L_('0'));
> +    Xprintf_buffer_putc (buf, L_('0'));
>    if (info->i18n)
> -    outchar (L_('I'));
> +    Xprintf_buffer_putc (buf, L_('I'));
>  
>    if (info->width != 0)
>      {
>        w = _itoa_word (info->width, workend, 10, 0);
>        while (w < workend)
> -	outchar (*w++);
> +	Xprintf_buffer_putc (buf, *w++);
>      }
>  
>    if (info->prec != -1)
>      {
> -      outchar (L_('.'));
> +      Xprintf_buffer_putc (buf, L_('.'));
>        w = _itoa_word (info->prec, workend, 10, 0);
>        while (w < workend)
> -	outchar (*w++);
> +	Xprintf_buffer_putc (buf, *w++);
>      }
>  
>    if (info->spec != L_('\0'))
> -    outchar (info->spec);
> -
> - all_done:
> -  return done;
> +    Xprintf_buffer_putc (buf, info->spec);
>  }
> -\f
> -/* Group the digits from W to REAR_PTR according to the grouping rules
> -   of the current locale.  The interpretation of GROUPING is as in
> -   `struct lconv' from <locale.h>.  The grouped number extends from
> -   the returned pointer until REAR_PTR.  FRONT_PTR to W is used as a
> -   scratch area.  */
> -static CHAR_T *
> -group_number (CHAR_T *front_ptr, CHAR_T *w, CHAR_T *rear_ptr,
> -	      const char *grouping, THOUSANDS_SEP_T thousands_sep)
> +
> +static void
> +group_number (struct Xprintf_buffer *buf,
> +	      struct grouping_iterator *iter,
> +	      CHAR_T *from, CHAR_T *to, THOUSANDS_SEP_T thousands_sep,
> +	      bool i18n)
>  {
> -  /* Length of the current group.  */
> -  int len;
> -#ifndef COMPILE_WPRINTF
> -  /* Length of the separator (in wide mode, the separator is always a
> -     single wide character).  */
> -  int tlen = strlen (thousands_sep);
> +  if (!i18n)
> +    for (CHAR_T *cp = from; cp != to; ++cp)
> +      {
> +	if (__grouping_iterator_next (iter))
> +	  {
> +#ifdef COMPILE_WPRINTF
> +	    __wprintf_buffer_putc (buf, thousands_sep);
> +#else
> +	    __printf_buffer_puts (buf, thousands_sep);
>  #endif
> -
> -  /* We treat all negative values like CHAR_MAX.  */
> -
> -  if (*grouping == CHAR_MAX || *grouping <= 0)
> -    /* No grouping should be done.  */
> -    return w;
> -
> -  len = *grouping++;
> -
> -  /* Copy existing string so that nothing gets overwritten.  */
> -  memmove (front_ptr, w, (rear_ptr - w) * sizeof (CHAR_T));
> -  CHAR_T *s = front_ptr + (rear_ptr - w);
> -
> -  w = rear_ptr;
> -
> -  /* Process all characters in the string.  */
> -  while (s > front_ptr)
> +	  }
> +	Xprintf_buffer_putc (buf, *cp);
> +      }
> +  else
>      {
> -      *--w = *--s;
> -
> -      if (--len == 0 && s > front_ptr)
> +      /* Apply digit translation and grouping.  */
> +      for (CHAR_T *cp = from; cp != to; ++cp)
>  	{
> -	  /* A new group begins.  */
> +	  if (__grouping_iterator_next (iter))
> +	    {
>  #ifdef COMPILE_WPRINTF
> -	  if (w != s)
> -	    *--w = thousands_sep;
> -	  else
> -	    /* Not enough room for the separator.  */
> -	    goto copy_rest;
> +	      __wprintf_buffer_putc (buf, thousands_sep);
>  #else
> -	  int cnt = tlen;
> -	  if (tlen < w - s)
> -	    do
> -	      *--w = thousands_sep[--cnt];
> -	    while (cnt > 0);
> -	  else
> -	    /* Not enough room for the separator.  */
> -	    goto copy_rest;
> -#endif
> -
> -	  if (*grouping == CHAR_MAX
> -#if CHAR_MIN < 0
> -		   || *grouping < 0
> +	      __printf_buffer_puts (buf, thousands_sep);
>  #endif
> -		   )
> -	    {
> -	    copy_rest:
> -	      /* No further grouping to be done.  Copy the rest of the
> -		 number.  */
> -	      w -= s - front_ptr;
> -	      memmove (w, front_ptr, (s - front_ptr) * sizeof (CHAR_T));
> -	      break;
>  	    }
> -	  else if (*grouping != '\0')
> -	    len = *grouping++;
> -	  else
> -	    /* The previous grouping repeats ad infinitum.  */
> -	    len = grouping[-1];
> -	}
> -    }
> -  return w;
> -}
> -\f
> -/* Helper "class" for `fprintf to unbuffered': creates a temporary buffer.  */
> -struct helper_file
> -  {
> -    struct _IO_FILE_plus _f;
> +	  int digit = *cp - '0';
>  #ifdef COMPILE_WPRINTF
> -    struct _IO_wide_data _wide_data;
> -#endif
> -    FILE *_put_stream;
> -#ifdef _IO_MTSAFE_IO
> -    _IO_lock_t lock;
> -#endif
> -  };
> -
> -static int
> -_IO_helper_overflow (FILE *s, int c)
> -{
> -  FILE *target = ((struct helper_file*) s)->_put_stream;
> -#ifdef COMPILE_WPRINTF
> -  int used = s->_wide_data->_IO_write_ptr - s->_wide_data->_IO_write_base;
> -  if (used)
> -    {
> -      size_t written = _IO_sputn (target, s->_wide_data->_IO_write_base, used);
> -      if (written == 0 || written == WEOF)
> -	return WEOF;
> -      __wmemmove (s->_wide_data->_IO_write_base,
> -		  s->_wide_data->_IO_write_base + written,
> -		  used - written);
> -      s->_wide_data->_IO_write_ptr -= written;
> -    }
> +	  __wprintf_buffer_putc
> +	    (buf, _NL_CURRENT_WORD (LC_CTYPE,
> +				    _NL_CTYPE_OUTDIGIT0_WC + digit));
>  #else
> -  int used = s->_IO_write_ptr - s->_IO_write_base;
> -  if (used)
> -    {
> -      size_t written = _IO_sputn (target, s->_IO_write_base, used);
> -      if (written == 0 || written == EOF)
> -	return EOF;
> -      memmove (s->_IO_write_base, s->_IO_write_base + written,
> -	       used - written);
> -      s->_IO_write_ptr -= written;
> -    }
> +	  __printf_buffer_puts
> +	    (buf, _NL_CURRENT (LC_CTYPE, _NL_CTYPE_OUTDIGIT0_MB + digit));
>  #endif
> -  return PUTC (c, s);
> +	}
> +    }
>  }
>  
> -#ifdef COMPILE_WPRINTF
> -static const struct _IO_jump_t _IO_helper_jumps libio_vtable =
> -{
> -  JUMP_INIT_DUMMY,
> -  JUMP_INIT (finish, _IO_wdefault_finish),
> -  JUMP_INIT (overflow, _IO_helper_overflow),
> -  JUMP_INIT (underflow, _IO_default_underflow),
> -  JUMP_INIT (uflow, _IO_default_uflow),
> -  JUMP_INIT (pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
> -  JUMP_INIT (xsputn, _IO_wdefault_xsputn),
> -  JUMP_INIT (xsgetn, _IO_wdefault_xsgetn),
> -  JUMP_INIT (seekoff, _IO_default_seekoff),
> -  JUMP_INIT (seekpos, _IO_default_seekpos),
> -  JUMP_INIT (setbuf, _IO_default_setbuf),
> -  JUMP_INIT (sync, _IO_default_sync),
> -  JUMP_INIT (doallocate, _IO_wdefault_doallocate),
> -  JUMP_INIT (read, _IO_default_read),
> -  JUMP_INIT (write, _IO_default_write),
> -  JUMP_INIT (seek, _IO_default_seek),
> -  JUMP_INIT (close, _IO_default_close),
> -  JUMP_INIT (stat, _IO_default_stat)
> -};
> -#else
> -static const struct _IO_jump_t _IO_helper_jumps libio_vtable =
> -{
> -  JUMP_INIT_DUMMY,
> -  JUMP_INIT (finish, _IO_default_finish),
> -  JUMP_INIT (overflow, _IO_helper_overflow),
> -  JUMP_INIT (underflow, _IO_default_underflow),
> -  JUMP_INIT (uflow, _IO_default_uflow),
> -  JUMP_INIT (pbackfail, _IO_default_pbackfail),
> -  JUMP_INIT (xsputn, _IO_default_xsputn),
> -  JUMP_INIT (xsgetn, _IO_default_xsgetn),
> -  JUMP_INIT (seekoff, _IO_default_seekoff),
> -  JUMP_INIT (seekpos, _IO_default_seekpos),
> -  JUMP_INIT (setbuf, _IO_default_setbuf),
> -  JUMP_INIT (sync, _IO_default_sync),
> -  JUMP_INIT (doallocate, _IO_default_doallocate),
> -  JUMP_INIT (read, _IO_default_read),
> -  JUMP_INIT (write, _IO_default_write),
> -  JUMP_INIT (seek, _IO_default_seek),
> -  JUMP_INIT (close, _IO_default_close),
> -  JUMP_INIT (stat, _IO_default_stat)
> -};
> -#endif
>  

The _IO_helper_jumps should be removed from tst-relro-symbols.py required list,
and tst-relro-symbols.py not showing a regression raised some questions.  It turned
out that it is not really testing the required symbols due a bug, with the fix below:

diff --git a/elf/tst-relro-symbols.py b/elf/tst-relro-symbols.py
index 368ea3349f..41e87b37ea 100644
--- a/elf/tst-relro-symbols.py
+++ b/elf/tst-relro-symbols.py
@@ -56,10 +56,10 @@ def get_parser():
     """Return an argument parser for this script."""
     parser = argparse.ArgumentParser(description=__doc__)
     parser.add_argument('object', help='path to object file to check')
-    parser.add_argument('--required', metavar='NAME', default=(),
-                        help='required symbol names', nargs='*')
-    parser.add_argument('--optional', metavar='NAME', default=(),
-                        help='required symbol names', nargs='*')
+    parser.add_argument('--required', metavar='NAME', action='append',
+                        help='required symbol names')
+    parser.add_argument('--optional', metavar='NAME', action='append',
+                        help='required symbol names')
     return parser

 def main(argv):

$ make test t=elf/tst-relro-libc
[...]
FAIL: elf/tst-relro-libc
original exit status 1
[...]/libc.so: error: symbol '_IO_helper_jumps' not found
[...]/libc.so: error: symbol '_IO_strn_jumps' not found

(I will send a fix for this)

> -static int
> -buffered_vfprintf (FILE *s, const CHAR_T *format, va_list args,
> -		   unsigned int mode_flags)
> +/* The FILE-based function.  */
> +int
> +vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
>  {
> -  CHAR_T buf[BUFSIZ];
> -  struct helper_file helper;
> -  FILE *hp = (FILE *) &helper._f;
> -  int result, to_flush;
> -
>    /* Orient the stream.  */
>  #ifdef ORIENT
>    ORIENT;
>  #endif
>  
> -  /* Initialize helper.  */
> -  helper._put_stream = s;
> -#ifdef COMPILE_WPRINTF
> -  hp->_wide_data = &helper._wide_data;
> -  _IO_wsetp (hp, buf, buf + sizeof buf / sizeof (CHAR_T));
> -  hp->_mode = 1;
> -#else
> -  _IO_setp (hp, buf, buf + sizeof buf);
> -  hp->_mode = -1;
> -#endif
> -  hp->_flags = _IO_MAGIC|_IO_NO_READS|_IO_USER_LOCK;
> -#if _IO_JUMPS_OFFSET
> -  hp->_vtable_offset = 0;
> -#endif
> -#ifdef _IO_MTSAFE_IO
> -  hp->_lock = NULL;
> +  /* Sanity check of arguments.  */
> +  ARGCHECK (s, format);
> +
> +#ifdef ORIENT
> +  /* Check for correct orientation.  */
> +  if (_IO_vtable_offset (s) == 0
> +      && _IO_fwide (s, sizeof (CHAR_T) == 1 ? -1 : 1)
> +      != (sizeof (CHAR_T) == 1 ? -1 : 1))
> +    /* The stream is already oriented otherwise.  */
> +    return EOF;
>  #endif
> -  hp->_flags2 = s->_flags2;
> -  _IO_JUMPS (&helper._f) = (struct _IO_jump_t *) &_IO_helper_jumps;
>  
> -  /* Now print to helper instead.  */
> -  result = vfprintf (hp, format, args, mode_flags);
> +  int done;
>  
>    /* Lock stream.  */
> -  __libc_cleanup_region_start (1, (void (*) (void *)) &_IO_funlockfile, s);
> +  _IO_cleanup_region_start ((void (*) (void *)) &_IO_funlockfile, s);
>    _IO_flockfile (s);
>  
> -  /* Now flush anything from the helper to the S. */
> -#ifdef COMPILE_WPRINTF
> -  if ((to_flush = (hp->_wide_data->_IO_write_ptr
> -		   - hp->_wide_data->_IO_write_base)) > 0)
> -    {
> -      if ((int) _IO_sputn (s, hp->_wide_data->_IO_write_base, to_flush)
> -	  != to_flush)
> -	result = -1;
> -    }
> -#else
> -  if ((to_flush = hp->_IO_write_ptr - hp->_IO_write_base) > 0)
> -    {
> -      if ((int) _IO_sputn (s, hp->_IO_write_base, to_flush) != to_flush)
> -	result = -1;
> -    }
> -#endif
> +  /* Set up the wrapping buffer.  */
> +  struct Xprintf (buffer_to_file) wrap;
> +  Xprintf (buffer_to_file_init) (&wrap, s);
> +
> +  /* Perform the printing operation on the buffer.  */
> +  Xprintf_buffer (&wrap.base, format, ap, mode_flags);
> +  done = Xprintf (buffer_to_file_done) (&wrap);
>  
>    /* Unlock the stream.  */
>    _IO_funlockfile (s);
> -  __libc_cleanup_region_end (0);
> +  _IO_cleanup_region_end (0);
>  
> -  return result;
> +  return done;
>  }

Ok.

> diff --git a/stdio-common/vfprintf-process-arg.c b/stdio-common/vfprintf-process-arg.c
> index 4fe369e111..ca6402449d 100644
> --- a/stdio-common/vfprintf-process-arg.c
> +++ b/stdio-common/vfprintf-process-arg.c
> @@ -27,7 +27,7 @@
>       now process the wanted format specifier.  */
>  LABEL (form_percent):
>    /* Write a literal "%".  */
> -  outchar (L_('%'));
> +  Xprintf_buffer_putc (buf, L_('%'));
>    break;
>  
>  LABEL (form_integer):
> @@ -116,16 +116,8 @@ LABEL (unsigned_number):      /* Unsigned number of base BASE.  */
>              *--string = L_('0');
>          }
>        else
> -        {
> -          /* Put the number in WORK.  */
> -          string = _itoa (number.longlong, workend, base,
> -                          spec == L_('X'));
> -          if (group && grouping)
> -            string = group_number (work_buffer, string, workend,
> -                                   grouping, thousands_sep);
> -          if (use_outdigits && base == 10)
> -            string = _i18n_number_rewrite (string, workend, workend);
> -        }
> +        /* Put the number in WORK.  */
> +        string = _itoa (number.longlong, workend, base, spec == L_('X'));
>        /* Simplify further test for num != 0.  */
>        number.word = number.longlong != 0;
>      }
> @@ -159,27 +151,46 @@ LABEL (unsigned_number):      /* Unsigned number of base BASE.  */
>              *--string = L_('0');
>          }
>        else
> -        {
> -          /* Put the number in WORK.  */
> -          string = _itoa_word (number.word, workend, base,
> -                               spec == L_('X'));
> -          if (group && grouping)
> -            string = group_number (work_buffer, string, workend,
> -                                   grouping, thousands_sep);
> -          if (use_outdigits && base == 10)
> -            string = _i18n_number_rewrite (string, workend, workend);
> -        }
> +        /* Put the number in WORK.  */
> +        string = _itoa_word (number.word, workend, base,
> +                             spec == L_('X'));
>      }
>  
> -  if (prec <= workend - string && number.word != 0 && alt && base == 8)
> -    /* Add octal marker.  */
> -    *--string = L_('0');
> +  /* Grouping is also used for outdigits translation.  */
> +  struct grouping_iterator iter;
> +  bool number_slow_path = group || (use_outdigits && base == 10);
> +  if (group)
> +    __grouping_iterator_init (&iter, LC_NUMERIC, _NL_CURRENT_LOCALE,
> +                              workend - string);
> +  else if (use_outdigits && base == 10)
> +    __grouping_iterator_init_none (&iter, workend - string);
> +
> +  int number_length;
> +#ifndef COMPILE_WPRINTF
> +  if (use_outdigits && base == 10)
> +    number_length = __translated_number_width (_NL_CURRENT_LOCALE,
> +                                               string, workend);
> +  else
> +    number_length = workend - string;
> +  if (group)
> +    number_length += iter.separators * strlen (thousands_sep);
> +#else
> +  number_length = workend - string;
> +  /* All wide separators have length 1.  */
> +  if (group && thousands_sep != L'\0')
> +    number_length += iter.separators;
> +#endif
> +
> +  /* The marker comes right before the number, but is not subject
> +     to grouping.  */
> +  bool octal_marker = (prec <= number_length && number.word != 0
> +                       && alt && base == 8);
>  
>    prec = MAX (0, prec - (workend - string));
>  
>    if (!left)
>      {
> -      width -= workend - string + prec;
> +      width -= number_length + prec;
>  
>        if (number.word != 0 && alt && (base == 16 || base == 2))
>          /* Account for 0X, 0x, 0B or 0b hex or binary marker.  */
> @@ -190,27 +201,34 @@ LABEL (unsigned_number):      /* Unsigned number of base BASE.  */
>  
>        if (pad == L_(' '))
>          {
> -          PAD (L_(' '));
> +          Xprintf_buffer_pad (buf, L_(' '), width);
>            width = 0;
>          }
>  
>        if (is_negative)
> -        outchar (L_('-'));
> +        Xprintf_buffer_putc (buf, L_('-'));
>        else if (showsign)
> -        outchar (L_('+'));
> +        Xprintf_buffer_putc (buf, L_('+'));
>        else if (space)
> -        outchar (L_(' '));
> +        Xprintf_buffer_putc (buf, L_(' '));
>  
>        if (number.word != 0 && alt && (base == 16 || base == 2))
>          {
> -          outchar (L_('0'));
> -          outchar (spec);
> +          Xprintf_buffer_putc (buf, L_('0'));
> +          Xprintf_buffer_putc (buf, spec);
>          }
>  
>        width += prec;
> -      PAD (L_('0'));
> +      Xprintf_buffer_pad (buf, L_('0'), width);
> +
> +      if (octal_marker)
> +        Xprintf_buffer_putc (buf, L_('0'));
>  
> -      outstring (string, workend - string);
> +      if (number_slow_path)
> +        group_number (buf, &iter, string, workend, thousands_sep,
> +                      use_outdigits && base == 10);
> +      else
> +        Xprintf_buffer_write (buf, string, workend - string);
>  
>        break;
>      }
> @@ -218,40 +236,41 @@ LABEL (unsigned_number):      /* Unsigned number of base BASE.  */
>      {
>        if (is_negative)
>          {
> -          outchar (L_('-'));
> +          Xprintf_buffer_putc (buf, L_('-'));
>            --width;
>          }
>        else if (showsign)
>          {
> -          outchar (L_('+'));
> +          Xprintf_buffer_putc (buf, L_('+'));
>            --width;
>          }
>        else if (space)
>          {
> -          outchar (L_(' '));
> +          Xprintf_buffer_putc (buf, L_(' '));
>            --width;
>          }
>  
>        if (number.word != 0 && alt && (base == 16 || base == 2))
>          {
> -          outchar (L_('0'));
> -          outchar (spec);
> +          Xprintf_buffer_putc (buf, L_('0'));
> +          Xprintf_buffer_putc (buf, spec);
>            width -= 2;
>          }
>  
>        width -= workend - string + prec;
>  
> -      if (prec > 0)
> -        {
> -          int temp = width;
> -          width = prec;
> -          PAD (L_('0'));
> -          width = temp;
> -        }
> +      Xprintf_buffer_pad (buf, L_('0'), prec);
> +
> +      if (octal_marker)
> +        Xprintf_buffer_putc (buf, L_('0'));
>  
> -      outstring (string, workend - string);
> +      if (number_slow_path)
> +        group_number (buf, &iter, string, workend, thousands_sep,
> +                      use_outdigits && base == 10);
> +      else
> +        Xprintf_buffer_write (buf, string, workend - string);
>  
> -      PAD (L_(' '));
> +      Xprintf_buffer_pad (buf, L_(' '), width);
>        break;
>      }
>  
> @@ -300,16 +319,17 @@ LABEL (form_number):
>      }
>    /* Answer the count of characters written.  */
>    void *ptrptr = process_arg_pointer ();
> +  unsigned int written = Xprintf_buffer_done (buf);
>    if (is_longlong)
> -    *(long long int *) ptrptr = done;
> +    *(long long int *) ptrptr = written;
>    else if (is_long_num)
> -    *(long int *) ptrptr = done;
> +    *(long int *) ptrptr = written;
>    else if (is_char)
> -    *(char *) ptrptr = done;
> +    *(char *) ptrptr = written;
>    else if (!is_short)
> -    *(int *) ptrptr = done;
> +    *(int *) ptrptr = written;
>    else
> -    *(short int *) ptrptr = done;
> +    *(short int *) ptrptr = written;
>    break;
>  
>  LABEL (form_strerror):
> @@ -341,14 +361,16 @@ LABEL (form_character):
>      goto LABEL (form_wcharacter);
>    --width;  /* Account for the character itself.  */
>    if (!left)
> -    PAD (L_(' '));
> +    Xprintf_buffer_pad (buf, L_(' '), width);
>  #ifdef COMPILE_WPRINTF
> -  outchar (__btowc ((unsigned char) process_arg_int ())); /* Promoted. */
> +  __wprintf_buffer_putc (buf, __btowc ((unsigned char) /* Promoted. */
> +                                       process_arg_int ()));
>  #else
> -  outchar ((unsigned char) process_arg_int ()); /* Promoted.  */
> +  __printf_buffer_putc (buf, (unsigned char) /* Promoted.  */
> +                        process_arg_int ());
>  #endif
>    if (left)
> -    PAD (L_(' '));
> +    Xprintf_buffer_pad (buf, L_(' '), width);
>    break;
>  
>  LABEL (form_string):
> @@ -382,10 +404,8 @@ LABEL (form_string):
>      else if (!is_long && spec != L_('S'))
>        {
>  #ifdef COMPILE_WPRINTF
> -        done = outstring_converted_wide_string
> -          (s, (const char *) string, prec, width, left, done);
> -        if (done < 0)
> -          goto all_done;
> +        outstring_converted_wide_string (buf, (const char *) string,
> +                                         prec, width, left);
>          /* The padding has already been written.  */
>          break;
>  #else
> @@ -407,10 +427,8 @@ LABEL (form_string):
>          else
>            len = __wcslen (string);
>  #else
> -        done = outstring_converted_wide_string
> -          (s, (const wchar_t *) string, prec, width, left, done);
> -        if (done < 0)
> -          goto all_done;
> +        outstring_converted_wide_string (buf, (const wchar_t *) string,
> +                                         prec, width, left);
>          /* The padding has already been written.  */
>          break;
>  #endif
> @@ -418,15 +436,15 @@ LABEL (form_string):
>  
>      if ((width -= len) < 0)
>        {
> -        outstring (string, len);
> +        Xprintf_buffer_write (buf, string, len);
>          break;
>        }
>  
>      if (!left)
> -      PAD (L_(' '));
> -    outstring (string, len);
> +      Xprintf_buffer_pad (buf, L_(' '), width);
> +    Xprintf_buffer_write (buf, string, len);
>      if (left)
> -      PAD (L_(' '));
> +      Xprintf_buffer_pad (buf, L_(' '), width);
>    }
>    break;
>  
> @@ -436,10 +454,10 @@ LABEL (form_wcharacter):
>      /* Wide character.  */
>      --width;
>      if (!left)
> -      PAD (L' ');
> -    outchar (process_arg_wchar_t ());
> +      Xprintf_buffer_pad (buf, L_(' '), width);
> +    Xprintf_buffer_putc (buf, process_arg_wchar_t ());
>      if (left)
> -      PAD (L' ');
> +      Xprintf_buffer_pad (buf, L_(' '), width);
>    }
>    break;
>  
> @@ -447,24 +465,24 @@ LABEL (form_wcharacter):
>  LABEL (form_wcharacter):
>    {
>      /* Wide character.  */
> -    char buf[MB_LEN_MAX];
> +    char wcbuf[MB_LEN_MAX];
>      mbstate_t mbstate;
>      size_t len;
>  
>      memset (&mbstate, '\0', sizeof (mbstate_t));
> -    len = __wcrtomb (buf, process_arg_wchar_t (), &mbstate);
> +    len = __wcrtomb (wcbuf, process_arg_wchar_t (), &mbstate);
>      if (len == (size_t) -1)
>        {
>          /* Something went wrong during the conversion.  Bail out.  */
> -        done = -1;
> +        __printf_buffer_mark_failed (buf);
>          goto all_done;
>        }
>      width -= len;
>      if (!left)
> -      PAD (' ');
> -    outstring (buf, len);
> +      Xprintf_buffer_pad (buf, L_(' '), width);
> +    Xprintf_buffer_write (buf, wcbuf, len);
>      if (left)
> -      PAD (' ');
> +      Xprintf_buffer_pad (buf, L_(' '), width);
>    }
>    break;
>  #endif /* !COMPILE_WPRINTF */

Ok.

> diff --git a/stdlib/strfmon_l.c b/stdlib/strfmon_l.c
> index d9b22088c7..6dc36e07cc 100644
> --- a/stdlib/strfmon_l.c
> +++ b/stdlib/strfmon_l.c
> @@ -29,33 +29,8 @@
>  #include <string.h>
>  #include "../locale/localeinfo.h"
>  #include <bits/floatn.h>
> -
> -
> -#define out_char(Ch)							      \
> -  do {									      \
> -    if (dest >= s + maxsize - 1)					      \
> -      {									      \
> -	__set_errno (E2BIG);						      \
> -	va_end (ap);							      \
> -	return -1;							      \
> -      }									      \
> -    *dest++ = (Ch);							      \
> -  } while (0)
> -
> -#define out_string(String)						      \
> -  do {									      \
> -    const char *_s = (String);						      \
> -    while (*_s)								      \
> -      out_char (*_s++);							      \
> -  } while (0)
> -
> -#define out_nstring(String, N)						      \
> -  do {									      \
> -    int _n = (N);							      \
> -    const char *_s = (String);						      \
> -    while (_n-- > 0)							      \
> -      out_char (*_s++);							      \
> -  } while (0)
> +#include <stdio-common/grouping_iterator.h>
> +#include <printf_buffer.h>
>  
>  #define to_digit(Ch) ((Ch) - '0')
>  
> @@ -75,21 +50,15 @@
>     some information in the LC_MONETARY category which should be used,
>     too.  Some of the information contradicts the information which can
>     be specified in format string.  */
> -ssize_t
> -__vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> -		       const char *format, va_list ap, unsigned int flags)
> +static void
> +__vstrfmon_l_buffer (struct __printf_buffer *buf, locale_t loc,
> +		     const char *fmt, va_list ap, unsigned int flags)
>  {
>    struct __locale_data *current = loc->__locales[LC_MONETARY];
> -  _IO_strfile f;
>    struct printf_info info;
> -  char *dest;			/* Pointer so copy the output.  */
> -  const char *fmt;		/* Pointer that walks through format.  */
> -
> -  dest = s;
> -  fmt = format;
>  
>    /* Loop through the format-string.  */
> -  while (*fmt != '\0')
> +  while (*fmt != '\0' && !__printf_buffer_has_failed (buf))
>      {
>        /* The floating-point value to output.  */
>        union
> @@ -122,11 +91,9 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
>        int other_cs_precedes;
>        const char *sign_string;
>        const char *other_sign_string;
> -      int done;
>        const char *currency_symbol;
>        size_t currency_symbol_len;
>        long int width;
> -      char *startp;
>        const void *ptr;
>        char space_char;
>  
> @@ -134,14 +101,14 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
>  	 specification.  */
>        if (*fmt != '%')
>  	{
> -	  out_char (*fmt++);
> +	  __printf_buffer_putc (buf, *fmt++);
>  	  continue;
>  	}
>  
>        /* "%%" means a single '%' character.  */
>        if (fmt[1] == '%')
>  	{
> -	  out_char (*++fmt);
> +	  __printf_buffer_putc (buf, *++fmt);
>  	  ++fmt;
>  	  continue;
>  	}
> @@ -171,7 +138,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
>  		{
>  		  /* Premature EOS.  */
>  		  __set_errno (EINVAL);
> -		  return -1;
> +		  __printf_buffer_mark_failed (buf);
> +		  return;
>  		}
>  	      continue;
>  	    case '^':			/* Don't group digits.  */
> @@ -181,7 +149,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
>  	      if (n_sign_posn != -2)
>  		{
>  		  __set_errno (EINVAL);
> -		  return -1;
> +		  __printf_buffer_mark_failed (buf);
> +		  return;
>  		}
>  	      p_sign_posn = *_NL_CURRENT (LC_MONETARY, P_SIGN_POSN);
>  	      n_sign_posn = *_NL_CURRENT (LC_MONETARY, N_SIGN_POSN);
> @@ -190,7 +159,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
>  	      if (n_sign_posn != -2)
>  		{
>  		  __set_errno (EINVAL);
> -		  return -1;
> +		  __printf_buffer_mark_failed (buf);
> +		  return;
>  		}
>  	      p_sign_posn = 0;
>  	      n_sign_posn = 0;
> @@ -220,19 +190,12 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
>  		  || (width == LONG_MAX && val > LONG_MAX % 10))
>  		{
>  		  __set_errno (E2BIG);
> -		  return -1;
> +		  __printf_buffer_mark_failed (buf);
> +		  return;
>  		}
>  
>  	      width = width * 10 + val;
>  	    }
> -
> -	  /* If we don't have enough room for the demanded width we
> -	     can stop now and return an error.  */
> -	  if (width >= maxsize - (dest - s))
> -	    {
> -	      __set_errno (E2BIG);
> -	      return -1;
> -	    }
>  	}
>  
>        /* Recognize left precision.  */
> @@ -241,7 +204,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
>  	  if (!isdigit (*++fmt))
>  	    {
>  	      __set_errno (EINVAL);
> -	      return -1;
> +	      __printf_buffer_mark_failed (buf);
> +	      return;
>  	    }
>  	  left_prec = to_digit (*fmt);
>  
> @@ -258,7 +222,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
>  	  if (!isdigit (*++fmt))
>  	    {
>  	      __set_errno (EINVAL);
> -	      return -1;
> +	      __printf_buffer_mark_failed (buf);
> +	      return;
>  	    }
>  	  right_prec = to_digit (*fmt);
>  
> @@ -306,7 +271,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
>  	  break;
>  	default:		/* Any unrecognized format is an error.  */
>  	  __set_errno (EINVAL);
> -	  return -1;
> +	  __printf_buffer_mark_failed (buf);
> +	  return;
>  	}
>  
>        /* If not specified by the format string now find the values for
> @@ -327,8 +293,11 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
>        /* If we have to print the digits grouped determine how many
>  	 extra characters this means.  */
>        if (group && left_prec != -1)
> -	left_prec += __guess_grouping (left_prec,
> -				       _NL_CURRENT (LC_MONETARY, MON_GROUPING));
> +	{
> +	  struct grouping_iterator it;
> +	  __grouping_iterator_init (&it, LC_MONETARY, loc, left_prec);
> +	  left_prec += it.separators;
> +	}
>  
>        /* Now it's time to get the value.  */
>        if (is_long_double == 1)
> @@ -482,57 +451,46 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
>  #define left_paren '('
>  #define right_paren ')'
>  
> -      startp = dest;		/* Remember start so we can compute length.  */
> +      char *startp = buf->write_ptr;
>  
> -      while (left_pad-- > 0)
> -	out_char (' ');
> +      __printf_buffer_pad (buf, ' ', left_pad);
>  
>        if (sign_posn == 0 && is_negative)
> -	out_char (left_paren);
> +	__printf_buffer_putc (buf, left_paren);
>  
>        if (cs_precedes)
>  	{
>  	  if (sign_posn != 0 && sign_posn != 2 && sign_posn != 4
>  	      && sign_posn != 5)
>  	    {
> -	      out_string (sign_string);
> +	      __printf_buffer_puts (buf, sign_string);
>  	      if (sep_by_space == 2)
> -		out_char (' ');
> +		__printf_buffer_putc (buf, ' ');
>  	    }
>  
>  	  if (print_curr_symbol)
> -	    out_string (currency_symbol);
> +	    __printf_buffer_puts (buf, currency_symbol);
>  
>  	  if (sign_posn == 4)
>  	    {
>  	      if (print_curr_symbol && sep_by_space == 2)
> -		out_char (space_char);
> -	      out_string (sign_string);
> +		__printf_buffer_putc (buf, space_char);
> +	      __printf_buffer_puts (buf, sign_string);
>  	      if (sep_by_space == 1)
>  		/* POSIX.2 and SUS are not clear on this case, but C99
>  		   says a space follows the adjacent-symbol-and-sign */
> -		out_char (' ');
> +		__printf_buffer_putc (buf, ' ');
>  	    }
>  	  else
>  	    if (print_curr_symbol && sep_by_space == 1)
> -	      out_char (space_char);
> +	      __printf_buffer_putc (buf, space_char);
>  	}
>        else
>  	if (sign_posn != 0 && sign_posn != 2 && sign_posn != 3
>  	    && sign_posn != 4 && sign_posn != 5)
> -	  out_string (sign_string);
> +	  __printf_buffer_puts (buf, sign_string);
>  
>        /* Print the number.  */
> -#ifdef _IO_MTSAFE_IO
> -      f._sbf._f._lock = NULL;
> -#endif
> -      _IO_init_internal (&f._sbf._f, 0);
> -      _IO_JUMPS (&f._sbf) = &_IO_str_jumps;
> -      _IO_str_init_static_internal (&f, dest, (s + maxsize) - dest, dest);
> -      /* We clear the last available byte so we can find out whether
> -	 the numeric representation is too long.  */
> -      s[maxsize - 1] = '\0';
> -
>        memset (&info, '\0', sizeof (info));
>        info.prec = right_prec;
>        info.width = left_prec + (right_prec ? (right_prec + 1) : 0);
> @@ -544,25 +502,17 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
>        info.extra = 1;		/* This means use values from LC_MONETARY.  */
>  
>        ptr = &fpnum;
> -      done = __printf_fp_l (&f._sbf._f, loc, &info, &ptr);
> -      if (done < 0)
> -	return -1;
> -
> -      if (s[maxsize - 1] != '\0')
> -	{
> -	  __set_errno (E2BIG);
> -	  return -1;
> -	}
> -
> -      dest += done;
> +      __printf_fp_l_buffer (buf, loc, &info, &ptr);
> +      if (__printf_buffer_has_failed (buf))
> +	return;
>  
>        if (!cs_precedes)
>  	{
>  	  if (sign_posn == 3)
>  	    {
>  	      if (sep_by_space == 1)
> -		out_char (' ');
> -	      out_string (sign_string);
> +		__printf_buffer_putc (buf, ' ');
> +	      __printf_buffer_puts (buf, sign_string);
>  	    }
>  
>  	  if (print_curr_symbol)
> @@ -572,55 +522,61 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
>  		  || (sign_posn == 2 && sep_by_space == 1)
>  		  || (sign_posn == 1 && sep_by_space == 1)
>  		  || (sign_posn == 0 && sep_by_space == 1))
> -		out_char (space_char);
> -	      out_nstring (currency_symbol, currency_symbol_len);
> +		__printf_buffer_putc (buf, space_char);
> +	      __printf_buffer_write (buf, currency_symbol,
> +				       __strnlen (currency_symbol,
> +						  currency_symbol_len));
>  	    }
>  
>  	  if (sign_posn == 4)
>  	    {
>  	      if (sep_by_space == 2)
> -		out_char (' ');
> -	      out_string (sign_string);
> +		__printf_buffer_putc (buf, ' ');
> +	      __printf_buffer_puts (buf, sign_string);
>  	    }
>  	}
>  
>        if (sign_posn == 2)
>  	{
>  	  if (sep_by_space == 2)
> -	    out_char (' ');
> -	  out_string (sign_string);
> +	    __printf_buffer_putc (buf, ' ');
> +	  __printf_buffer_puts (buf, sign_string);
>  	}
>  
>        if (sign_posn == 0 && is_negative)
> -	out_char (right_paren);
> +	__printf_buffer_putc (buf, right_paren);
>  
>        /* Now test whether the output width is filled.  */
> -      if (dest - startp < width)
> +      if (buf->write_ptr - startp < width)
>  	{
> -	  if (left)
> -	    /* We simply have to fill using spaces.  */
> -	    do
> -	      out_char (' ');
> -	    while (dest - startp < width);
> -	  else
> +	  size_t pad_width = width - (buf->write_ptr - startp);
> +	  __printf_buffer_pad (buf, ' ', pad_width);
> +	  if (__printf_buffer_has_failed (buf))
> +	    /* Implies length check.  */
> +	    return;
> +	  /* Left padding is already in the correct position.
> +	     Otherwise move the field contents in place.  */
> +	  if (!left)
>  	    {
> -	      long int dist = width - (dest - startp);
> -	      for (char *cp = dest - 1; cp >= startp; --cp)
> -		cp[dist] = cp[0];
> -
> -	      dest += dist;
> -
> -	      do
> -		startp[--dist] = ' ';
> -	      while (dist > 0);
> +	      memmove (startp + pad_width, startp, buf->write_ptr - startp);
> +	      memset (startp, ' ', pad_width);
>  	    }
>  	}
>      }
> +}
>  
> -  /* Terminate the string.  */
> -  *dest = '\0';
> -
> -  return dest - s;
> +ssize_t
> +__vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> +		       const char *format, va_list ap, unsigned int flags)
> +{
> +  struct __printf_buffer buf;
> +  __printf_buffer_init (&buf, s, maxsize, __printf_buffer_mode_strfmon);
> +  __vstrfmon_l_buffer (&buf, loc, format, ap, flags);
> +  __printf_buffer_putc (&buf, '\0'); /* Terminate the string.  */
> +  if (__printf_buffer_has_failed (&buf))
> +    return -1;
> +  else
> +    return buf.write_ptr - buf.write_base - 1; /* Exclude NUL byte.  */
>  }
>  
>  ssize_t

Ok.

> diff --git a/stdlib/strfrom-skeleton.c b/stdlib/strfrom-skeleton.c
> index 36e9adcad5..810eb315d0 100644
> --- a/stdlib/strfrom-skeleton.c
> +++ b/stdlib/strfrom-skeleton.c
> @@ -28,6 +28,7 @@
>  #include <string.h>
>  #include <locale/localeinfo.h>
>  #include <fix-float-double-convert-nan.h>
> +#include <printf_buffer.h>
>  
>  #define UCHAR_T char
>  #define L_(Str) Str
> @@ -37,12 +38,7 @@
>  int
>  STRFROM (char *dest, size_t size, const char *format, FLOAT f)
>  {
> -  _IO_strnfile sfile;
> -#ifdef _IO_MTSAFE_IO
> -  sfile.f._sbf._f._lock = NULL;
> -#endif
> -
> -  int done;
> +  struct __printf_buffer_snprintf buf;
>  
>    /* Single-precision values need to be stored in a double type, because
>       __printf_fp_l and __printf_fphex do not accept the float type.  */
> @@ -106,23 +102,8 @@ STRFROM (char *dest, size_t size, const char *format, FLOAT f)
>        abort ();
>      }
>  
> -  /* The following code to prepare the virtual file has been adapted from the
> -     function __vsnprintf_internal from libio.  */
> -
> -  if (size == 0)
> -    {
> -    /* When size is zero, nothing is written and dest may be a null pointer.
> -       This is specified for snprintf in ISO/IEC 9899:2011, Section 7.21.6.5,
> -       in the second paragraph.  Thus, if size is zero, prepare to use the
> -       overflow buffer right from the start.  */
> -      dest = sfile.overflow_buf;
> -      size = sizeof (sfile.overflow_buf);
> -    }
> -
> -  /* Prepare the virtual string file.  */
> -  _IO_no_init (&sfile.f._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
> -  _IO_JUMPS (&sfile.f._sbf) = &_IO_strn_jumps;
> -  _IO_str_init_static_internal (&sfile.f, dest, size - 1, dest);
> +  /* Prepare the string buffer.  */
> +  __printf_buffer_snprintf_init (&buf, dest, size);
>  
>    /* Prepare the format specification for printf_fp.  */
>    memset (&info, '\0', sizeof (info));
> @@ -144,13 +125,8 @@ STRFROM (char *dest, size_t size, const char *format, FLOAT f)
>    info.spec = specifier;
>  
>    if (info.spec != 'a' && info.spec != 'A')
> -    done = __printf_fp_l (&sfile.f._sbf._f, _NL_CURRENT_LOCALE, &info, &fpptr);
> +    __printf_fp_l_buffer (&buf.base, _NL_CURRENT_LOCALE, &info, &fpptr);
>    else
> -    done = __printf_fphex (&sfile.f._sbf._f, &info, &fpptr);
> -
> -  /* Terminate the string.  */
> -  if (sfile.f._sbf._f._IO_buf_base != sfile.overflow_buf)
> -    *sfile.f._sbf._f._IO_write_ptr = '\0';
> -
> -  return done;
> +    __printf_fphex_l_buffer (&buf.base, _NL_CURRENT_LOCALE, &info, &fpptr);
> +  return __printf_buffer_snprintf_done (&buf);
>  }

Ok.

> diff --git a/sysdeps/ia64/fpu/printf_fphex.c b/sysdeps/ia64/fpu/printf_fphex.c
> index e7c3109510..91347b7e3d 100644
> --- a/sysdeps/ia64/fpu/printf_fphex.c
> +++ b/sysdeps/ia64/fpu/printf_fphex.c
> @@ -35,16 +35,10 @@ do {									      \
>  									      \
>        numstr = _itoa_word (num, numbuf + sizeof numbuf, 16,		      \
>  			   info->spec == 'A');				      \
> -      wnumstr = _itowa_word (num,					      \
> -			     wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),   \
> -			     16, info->spec == 'A');			      \
>  									      \
>        /* Fill with zeroes.  */						      \
>        while (numstr > numbuf + (sizeof numbuf - 64 / 4))		      \
> -	{								      \
> -	  *--numstr = '0';						      \
> -	  *--wnumstr = L'0';						      \
> -	}								      \
> +	*--numstr = '0';						      \
>  									      \
>        /* We use a full nibble for the leading digit.  */		      \
>        leading = *numstr++;						      \

Ok.

> diff --git a/sysdeps/ieee754/ldbl-128/printf_fphex_macros.h b/sysdeps/ieee754/ldbl-128/printf_fphex_macros.h
> index 28dccb1170..bc712a2527 100644
> --- a/sysdeps/ieee754/ldbl-128/printf_fphex_macros.h
> +++ b/sysdeps/ieee754/ldbl-128/printf_fphex_macros.h
> @@ -36,45 +36,23 @@ do {									      \
>        zero_mantissa = (num0|num1) == 0;					      \
>  									      \
>        if (sizeof (unsigned long int) > 6)				      \
> -	{								      \
> -	  numstr = _itoa_word (num1, numbuf + sizeof numbuf, 16,	      \
> -			       info->spec == 'A');			      \
> -	  wnumstr = _itowa_word (num1,					      \
> -				 wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),\
> -				 16, info->spec == 'A');		      \
> -	}								      \
> +	numstr = _itoa_word (num1, numbuf + sizeof numbuf, 16,		      \
> +			     info->spec == 'A');			      \
>        else								      \
> -	{								      \
> -	  numstr = _itoa (num1, numbuf + sizeof numbuf, 16,		      \
> -			  info->spec == 'A');				      \
> -	  wnumstr = _itowa (num1,					      \
> -			    wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),    \
> -			    16, info->spec == 'A');			      \
> -	}								      \
> +	numstr = _itoa (num1, numbuf + sizeof numbuf, 16,		      \
> +			info->spec == 'A');				      \
>  									      \
>        while (numstr > numbuf + (sizeof numbuf - 64 / 4))		      \
> -	{								      \
> -	  *--numstr = '0';						      \
> -	  *--wnumstr = L'0';						      \
> -	}								      \
> +	*--numstr = '0';						      \
>  									      \
>        if (sizeof (unsigned long int) > 6)				      \
> -	{								      \
> -	  numstr = _itoa_word (num0, numstr, 16, info->spec == 'A');	      \
> -	  wnumstr = _itowa_word (num0, wnumstr, 16, info->spec == 'A');	      \
> -	}								      \
> +	numstr = _itoa_word (num0, numstr, 16, info->spec == 'A');	      \
>        else								      \
> -	{								      \
> -	  numstr = _itoa (num0, numstr, 16, info->spec == 'A');		      \
> -	  wnumstr = _itowa (num0, wnumstr, 16, info->spec == 'A');	      \
> -	}								      \
> +	numstr = _itoa (num0, numstr, 16, info->spec == 'A');		      \
>  									      \
>        /* Fill with zeroes.  */						      \
>        while (numstr > numbuf + (sizeof numbuf - 112 / 4))		      \
> -	{								      \
>  	  *--numstr = '0';						      \
> -	  *--wnumstr = L'0';						      \
> -	}								      \
>  									      \
>        leading = u.ieee.exponent == 0 ? '0' : '1';			      \
>  									      \

Ok.

> diff --git a/sysdeps/ieee754/ldbl-128ibm/printf_fphex.c b/sysdeps/ieee754/ldbl-128ibm/printf_fphex.c
> index 58733f85e5..a06cbe7cb6 100644
> --- a/sysdeps/ieee754/ldbl-128ibm/printf_fphex.c
> +++ b/sysdeps/ieee754/ldbl-128ibm/printf_fphex.c
> @@ -68,45 +68,23 @@ do {									      \
>        zero_mantissa = (num0|num1) == 0;					      \
>  									      \
>        if (sizeof (unsigned long int) > 6)				      \
> -	{								      \
> -	  numstr = _itoa_word (num1, numbuf + sizeof numbuf, 16,	      \
> -			       info->spec == 'A');			      \
> -	  wnumstr = _itowa_word (num1,					      \
> -				 wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),\
> -				 16, info->spec == 'A');		      \
> -	}								      \
> +	numstr = _itoa_word (num1, numbuf + sizeof numbuf, 16,		      \
> +			     info->spec == 'A');			      \
>        else								      \
> -	{								      \
> -	  numstr = _itoa (num1, numbuf + sizeof numbuf, 16,		      \
> +	numstr = _itoa (num1, numbuf + sizeof numbuf, 16,		      \
>  			  info->spec == 'A');				      \
> -	  wnumstr = _itowa (num1,					      \
> -			    wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),    \
> -			    16, info->spec == 'A');			      \
> -	}								      \
>  									      \
>        while (numstr > numbuf + (sizeof numbuf - 64 / 4))		      \
> -	{								      \
> -	  *--numstr = '0';						      \
> -	  *--wnumstr = L'0';						      \
> -	}								      \
> +	*--numstr = '0';						      \
>  									      \
>        if (sizeof (unsigned long int) > 6)				      \
> -	{								      \
> -	  numstr = _itoa_word (num0, numstr, 16, info->spec == 'A');	      \
> -	  wnumstr = _itowa_word (num0, wnumstr, 16, info->spec == 'A');	      \
> -	}								      \
> +	numstr = _itoa_word (num0, numstr, 16, info->spec == 'A');	      \
>        else								      \
> -	{								      \
> -	  numstr = _itoa (num0, numstr, 16, info->spec == 'A');		      \
> -	  wnumstr = _itowa (num0, wnumstr, 16, info->spec == 'A');	      \
> -	}								      \
> +	numstr = _itoa (num0, numstr, 16, info->spec == 'A');		      \
>  									      \
>        /* Fill with zeroes.  */						      \
>        while (numstr > numbuf + (sizeof numbuf - 112 / 4))		      \
> -	{								      \
> -	  *--numstr = '0';						      \
> -	  *--wnumstr = L'0';						      \
> -	}								      \
> +	*--numstr = '0';						      \
>  									      \
>        leading = u.d[0].ieee.exponent == 0 ? '0' : '1';			      \
>  									      \

Ok.

> diff --git a/sysdeps/ieee754/ldbl-96/printf_fphex.c b/sysdeps/ieee754/ldbl-96/printf_fphex.c
> index 26bc60682e..1f09b5f08b 100644
> --- a/sysdeps/ieee754/ldbl-96/printf_fphex.c
> +++ b/sysdeps/ieee754/ldbl-96/printf_fphex.c
> @@ -36,31 +36,17 @@ do {									      \
>        zero_mantissa = num == 0;						      \
>  									      \
>        if (sizeof (unsigned long int) > 6)				      \
> -	{								      \
> -	  numstr = _itoa_word (num, numbuf + sizeof numbuf, 16,		      \
> -			       info->spec == 'A');			      \
> -	  wnumstr = _itowa_word (num,					      \
> -				 wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),\
> -				 16, info->spec == 'A');		      \
> -	}								      \
> +	numstr = _itoa_word (num, numbuf + sizeof numbuf, 16,		      \
> +			     info->spec == 'A');			      \
>        else								      \
> -	{								      \
> -	  numstr = _itoa (num, numbuf + sizeof numbuf, 16, info->spec == 'A');\
> -	  wnumstr = _itowa (num,					      \
> -			    wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),    \
> -			    16, info->spec == 'A');			      \
> -	}								      \
> +	numstr = _itoa (num, numbuf + sizeof numbuf, 16, info->spec == 'A');  \
>  									      \
>        /* Fill with zeroes.  */						      \
>        while (numstr > numbuf + (sizeof numbuf - 64 / 4))		      \
> -	{								      \
> -	  *--numstr = '0';						      \
> -	  *--wnumstr = L'0';						      \
> -	}								      \
> +	*--numstr = '0';						      \
>  									      \
>        /* We use a full nibble for the leading digit.  */		      \
>        leading = *numstr++;						      \
> -      wnumstr++;							      \
>  									      \
>        /* We have 3 bits from the mantissa in the leading nibble.	      \
>  	 Therefore we are here using `IEEE854_LONG_DOUBLE_BIAS + 3'.  */      \

Ok.

> diff --git a/sysdeps/x86_64/fpu/printf_fphex.c b/sysdeps/x86_64/fpu/printf_fphex.c
> index d2de75d4ac..bb702ec7c5 100644
> --- a/sysdeps/x86_64/fpu/printf_fphex.c
> +++ b/sysdeps/x86_64/fpu/printf_fphex.c
> @@ -34,31 +34,16 @@ do {									      \
>        zero_mantissa = num == 0;						      \
>  									      \
>        if (sizeof (unsigned long int) > 6)				      \
> -	{								      \
> -	  numstr = _itoa_word (num, numbuf + sizeof numbuf, 16,		      \
> -			       info->spec == 'A');			      \
> -	  wnumstr = _itowa_word (num,					      \
> -				 wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),\
> -				 16, info->spec == 'A');		      \
> -	}								      \
> +	numstr = _itoa_word (num, numbuf + sizeof numbuf, 16, info->spec == 'A');\
>        else								      \
> -	{								      \
> -	  numstr = _itoa (num, numbuf + sizeof numbuf, 16, info->spec == 'A');\
> -	  wnumstr = _itowa (num,					      \
> -			    wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),    \
> -			    16, info->spec == 'A');			      \
> -	}								      \
> +	numstr = _itoa (num, numbuf + sizeof numbuf, 16, info->spec == 'A');  \
>  									      \
>        /* Fill with zeroes.  */						      \
>        while (numstr > numbuf + (sizeof numbuf - 64 / 4))		      \
> -	{								      \
> -	  *--numstr = '0';						      \
> -	  *--wnumstr = L'0';						      \
> -	}								      \
> +	*--numstr = '0';						      \
>  									      \
>        /* We use a full nibble for the leading digit.  */		      \
>        leading = *numstr++;						      \
> -      wnumstr++;							      \
>  									      \
>        /* We have 3 bits from the mantissa in the leading nibble.	      \
>  	 Therefore we are here using `IEEE854_LONG_DOUBLE_BIAS + 3'.  */      \

Ok.

  reply	other threads:[~2022-12-14 20:54 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-12 15:22 [PATCH v5 00/11] vfprintf refactor Florian Weimer
2022-12-12 15:22 ` [PATCH v5 01/11] locale: Implement struct grouping_iterator Florian Weimer
2022-12-12 15:22 ` [PATCH v5 02/11] stdio-common: Introduce buffers for implementing printf Florian Weimer
2022-12-14 14:10   ` Adhemerval Zanella Netto
2022-12-16 17:51     ` Florian Weimer
2022-12-12 15:23 ` [PATCH v5 03/11] stdio-common: Add __printf_function_invoke Florian Weimer
2022-12-14 14:28   ` Adhemerval Zanella Netto
2022-12-12 15:23 ` [PATCH v5 04/11] stdio-common: Add __translated_number_width Florian Weimer
2022-12-14 14:38   ` Adhemerval Zanella Netto
2022-12-12 15:23 ` [PATCH v5 05/11] stdio-common: Convert vfprintf and related functions to buffers Florian Weimer
2022-12-14 20:54   ` Adhemerval Zanella Netto [this message]
2022-12-16 17:58     ` Florian Weimer
2022-12-12 15:23 ` [PATCH v5 06/11] stdio-common: Add lock optimization to vfprintf and vfwprintf Florian Weimer
2022-12-15 15:58   ` Adhemerval Zanella Netto
2022-12-12 15:23 ` [PATCH v5 07/11] libio: Convert __vsprintf_internal to buffers Florian Weimer
2022-12-15 18:16   ` Adhemerval Zanella Netto
2022-12-16 18:07     ` Florian Weimer
2022-12-12 15:23 ` [PATCH v5 08/11] libio: Convert __vasprintf_internal " Florian Weimer
2022-12-15 18:40   ` Adhemerval Zanella Netto
2022-12-16 18:22     ` Florian Weimer
2022-12-12 15:23 ` [PATCH v5 09/11] libio: Convert __vdprintf_internal " Florian Weimer
2022-12-15 19:11   ` Adhemerval Zanella Netto
2022-12-12 15:24 ` [PATCH v5 10/11] libio: Convert __obstack_vprintf_internal to buffers (bug 27124) Florian Weimer
2022-12-15 19:14   ` Adhemerval Zanella Netto
2022-12-16 18:25     ` Florian Weimer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=32c237bd-1f1d-671c-71df-99005dea6746@linaro.org \
    --to=adhemerval.zanella@linaro.org \
    --cc=fweimer@redhat.com \
    --cc=libc-alpha@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).