[v2,05/14] stdio-common: Introduce buffers for implementing printf

Message ID a24674e2b8fe6363ebe76b11cebf975dca7ee2a8.1653314701.git.fweimer@redhat.com
State Superseded
Headers
Series vfprintf rework to remove vtables |

Checks

Context Check Description
dj/TryBot-apply_patch success Patch applied to master at the time it was sent

Commit Message

Florian Weimer May 23, 2022, 2:07 p.m. UTC
  These buffers will eventually be used instead of FILE * objects
to implement printf functions.  The multibyte buffer is struct
__printf_buffer, the wide buffer is struct __wprintf_buffer.

To enable writing type-generic code, the header files
printf_buffer-char.h and printf_buffer-wchar_t.h define the
Xprintf macro differently, enabling Xprintf (buffer) to stand
for __printf_buffer and __wprintf_buffer as appropriate.  For
common cases, macros like Xprintf_buffer are provided as a more
syntactically convenient shortcut.

Buffer-specific flush callbacks are implemented with a switch
statement instead of a function pointer, to avoid hardening issues
similar to those of libio vtables.  struct __printf_buffer_as_file
is needed to support custom printf specifiers because the public
interface for that requires passing a FILE *, which is why there
is a trapdoor back from these buffers to FILE * streams.

Since the immediate user of these interfaces knows when processing
has finished, there is no flush callback for the end of processing,
only a flush callback for the intermediate buffer flush.
---
 include/printf_buffer.h               | 281 ++++++++++++++++++++++++++
 stdio-common/Makefile                 |  16 ++
 stdio-common/Xprintf_buffer_done.c    |  40 ++++
 stdio-common/Xprintf_buffer_flush.c   |  72 +++++++
 stdio-common/Xprintf_buffer_pad_1.c   |  44 ++++
 stdio-common/Xprintf_buffer_putc_1.c  |  29 +++
 stdio-common/Xprintf_buffer_puts_1.c  |  37 ++++
 stdio-common/Xprintf_buffer_write.c   |  43 ++++
 stdio-common/printf_buffer-char.h     |  24 +++
 stdio-common/printf_buffer-wchar_t.h  |  24 +++
 stdio-common/printf_buffer_as_file.c  | 148 ++++++++++++++
 stdio-common/printf_buffer_as_file.h  |  87 ++++++++
 stdio-common/printf_buffer_done.c     |  21 ++
 stdio-common/printf_buffer_flush.c    |  42 ++++
 stdio-common/printf_buffer_pad_1.c    |  21 ++
 stdio-common/printf_buffer_putc_1.c   |  21 ++
 stdio-common/printf_buffer_puts_1.c   |  21 ++
 stdio-common/printf_buffer_to_file.c  | 122 +++++++++++
 stdio-common/printf_buffer_to_file.h  |  57 ++++++
 stdio-common/printf_buffer_write.c    |  21 ++
 stdio-common/wprintf_buffer_as_file.c | 153 ++++++++++++++
 stdio-common/wprintf_buffer_done.c    |  21 ++
 stdio-common/wprintf_buffer_flush.c   |  36 ++++
 stdio-common/wprintf_buffer_pad_1.c   |  21 ++
 stdio-common/wprintf_buffer_putc_1.c  |  21 ++
 stdio-common/wprintf_buffer_puts_1.c  |  21 ++
 stdio-common/wprintf_buffer_to_file.c |  55 +++++
 stdio-common/wprintf_buffer_write.c   |  21 ++
 28 files changed, 1520 insertions(+)
 create mode 100644 include/printf_buffer.h
 create mode 100644 stdio-common/Xprintf_buffer_done.c
 create mode 100644 stdio-common/Xprintf_buffer_flush.c
 create mode 100644 stdio-common/Xprintf_buffer_pad_1.c
 create mode 100644 stdio-common/Xprintf_buffer_putc_1.c
 create mode 100644 stdio-common/Xprintf_buffer_puts_1.c
 create mode 100644 stdio-common/Xprintf_buffer_write.c
 create mode 100644 stdio-common/printf_buffer-char.h
 create mode 100644 stdio-common/printf_buffer-wchar_t.h
 create mode 100644 stdio-common/printf_buffer_as_file.c
 create mode 100644 stdio-common/printf_buffer_as_file.h
 create mode 100644 stdio-common/printf_buffer_done.c
 create mode 100644 stdio-common/printf_buffer_flush.c
 create mode 100644 stdio-common/printf_buffer_pad_1.c
 create mode 100644 stdio-common/printf_buffer_putc_1.c
 create mode 100644 stdio-common/printf_buffer_puts_1.c
 create mode 100644 stdio-common/printf_buffer_to_file.c
 create mode 100644 stdio-common/printf_buffer_to_file.h
 create mode 100644 stdio-common/printf_buffer_write.c
 create mode 100644 stdio-common/wprintf_buffer_as_file.c
 create mode 100644 stdio-common/wprintf_buffer_done.c
 create mode 100644 stdio-common/wprintf_buffer_flush.c
 create mode 100644 stdio-common/wprintf_buffer_pad_1.c
 create mode 100644 stdio-common/wprintf_buffer_putc_1.c
 create mode 100644 stdio-common/wprintf_buffer_puts_1.c
 create mode 100644 stdio-common/wprintf_buffer_to_file.c
 create mode 100644 stdio-common/wprintf_buffer_write.c
  

Comments

Adhemerval Zanella June 2, 2022, 5:16 p.m. UTC | #1
On 23/05/2022 11:07, Florian Weimer via Libc-alpha wrote:
> These buffers will eventually be used instead of FILE * objects
> to implement printf functions.  The multibyte buffer is struct
> __printf_buffer, the wide buffer is struct __wprintf_buffer.
> 
> To enable writing type-generic code, the header files
> printf_buffer-char.h and printf_buffer-wchar_t.h define the
> Xprintf macro differently, enabling Xprintf (buffer) to stand
> for __printf_buffer and __wprintf_buffer as appropriate.  For
> common cases, macros like Xprintf_buffer are provided as a more
> syntactically convenient shortcut.
> 
> Buffer-specific flush callbacks are implemented with a switch
> statement instead of a function pointer, to avoid hardening issues
> similar to those of libio vtables.  struct __printf_buffer_as_file
> is needed to support custom printf specifiers because the public
> interface for that requires passing a FILE *, which is why there
> is a trapdoor back from these buffers to FILE * streams.
> 
> Since the immediate user of these interfaces knows when processing
> has finished, there is no flush callback for the end of processing,
> only a flush callback for the intermediate buffer flush.

Some comments below.  I am not very found on this approach, it is *another*
layer on our already convoluted libio/stdio code; although I don't have a
better solution.  I *really* wish we could move away from libio...

> ---
>  include/printf_buffer.h               | 281 ++++++++++++++++++++++++++
>  stdio-common/Makefile                 |  16 ++
>  stdio-common/Xprintf_buffer_done.c    |  40 ++++
>  stdio-common/Xprintf_buffer_flush.c   |  72 +++++++
>  stdio-common/Xprintf_buffer_pad_1.c   |  44 ++++
>  stdio-common/Xprintf_buffer_putc_1.c  |  29 +++
>  stdio-common/Xprintf_buffer_puts_1.c  |  37 ++++
>  stdio-common/Xprintf_buffer_write.c   |  43 ++++
>  stdio-common/printf_buffer-char.h     |  24 +++
>  stdio-common/printf_buffer-wchar_t.h  |  24 +++
>  stdio-common/printf_buffer_as_file.c  | 148 ++++++++++++++
>  stdio-common/printf_buffer_as_file.h  |  87 ++++++++
>  stdio-common/printf_buffer_done.c     |  21 ++
>  stdio-common/printf_buffer_flush.c    |  42 ++++
>  stdio-common/printf_buffer_pad_1.c    |  21 ++
>  stdio-common/printf_buffer_putc_1.c   |  21 ++
>  stdio-common/printf_buffer_puts_1.c   |  21 ++
>  stdio-common/printf_buffer_to_file.c  | 122 +++++++++++
>  stdio-common/printf_buffer_to_file.h  |  57 ++++++
>  stdio-common/printf_buffer_write.c    |  21 ++
>  stdio-common/wprintf_buffer_as_file.c | 153 ++++++++++++++
>  stdio-common/wprintf_buffer_done.c    |  21 ++
>  stdio-common/wprintf_buffer_flush.c   |  36 ++++
>  stdio-common/wprintf_buffer_pad_1.c   |  21 ++
>  stdio-common/wprintf_buffer_putc_1.c  |  21 ++
>  stdio-common/wprintf_buffer_puts_1.c  |  21 ++
>  stdio-common/wprintf_buffer_to_file.c |  55 +++++
>  stdio-common/wprintf_buffer_write.c   |  21 ++
>  28 files changed, 1520 insertions(+)
>  create mode 100644 include/printf_buffer.h
>  create mode 100644 stdio-common/Xprintf_buffer_done.c
>  create mode 100644 stdio-common/Xprintf_buffer_flush.c
>  create mode 100644 stdio-common/Xprintf_buffer_pad_1.c
>  create mode 100644 stdio-common/Xprintf_buffer_putc_1.c
>  create mode 100644 stdio-common/Xprintf_buffer_puts_1.c
>  create mode 100644 stdio-common/Xprintf_buffer_write.c
>  create mode 100644 stdio-common/printf_buffer-char.h
>  create mode 100644 stdio-common/printf_buffer-wchar_t.h
>  create mode 100644 stdio-common/printf_buffer_as_file.c
>  create mode 100644 stdio-common/printf_buffer_as_file.h
>  create mode 100644 stdio-common/printf_buffer_done.c
>  create mode 100644 stdio-common/printf_buffer_flush.c
>  create mode 100644 stdio-common/printf_buffer_pad_1.c
>  create mode 100644 stdio-common/printf_buffer_putc_1.c
>  create mode 100644 stdio-common/printf_buffer_puts_1.c
>  create mode 100644 stdio-common/printf_buffer_to_file.c
>  create mode 100644 stdio-common/printf_buffer_to_file.h
>  create mode 100644 stdio-common/printf_buffer_write.c
>  create mode 100644 stdio-common/wprintf_buffer_as_file.c
>  create mode 100644 stdio-common/wprintf_buffer_done.c
>  create mode 100644 stdio-common/wprintf_buffer_flush.c
>  create mode 100644 stdio-common/wprintf_buffer_pad_1.c
>  create mode 100644 stdio-common/wprintf_buffer_putc_1.c
>  create mode 100644 stdio-common/wprintf_buffer_puts_1.c
>  create mode 100644 stdio-common/wprintf_buffer_to_file.c
>  create mode 100644 stdio-common/wprintf_buffer_write.c
> 
> diff --git a/include/printf_buffer.h b/include/printf_buffer.h
> new file mode 100644
> index 0000000000..6be7723869
> --- /dev/null
> +++ b/include/printf_buffer.h
> @@ -0,0 +1,281 @@
> +/* Multibyte and wide buffers for implementing printf-related functions.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +/* The naming of the multibyte and wide variants is intentionally
> +   consistent, so that it is possible to use the Xprintf macro in
> +   stdio-common/printf_buffer-char.h and
> +   stdio-common/printf_buffer-wchar_t.h to select between them in
> +   type-generic code.  */
> +
> +#ifndef PRINTF_BUFFER_H
> +#define PRINTF_BUFFER_H
> +
> +#include <stdbool.h>
> +#include <stdint.h>
> +#include <wchar.h>
> +
> +/* <printf_buffer_as_file.h> introduces a way to use struct
> +   __printf_buffer objects from FILE * streams.  To avoid storing a
> +   function pointer (or vtable pointer) in struct __printf_buffer
> +   (which would defeat libio vtable hardening), a switch statement
> +   over the different flush implementations is used to implement
> +   __printf_buffer_flush.
> +
> +   __printf_buffer_mode_failed is special: it is the sticky failure
> +   indicator.  Unlike struct alloc_buffer, this is not folded into
> +   write_ptr, so that snprintf and other string-writing functions can
> +   discover the end of the string even in the error case, to be able
> +   to add the null terminator.  */
> +enum __printf_buffer_mode
> +  {
> +    __printf_buffer_mode_failed,
> +    __printf_buffer_mode_to_file,
> +  };

Do we really need to use namespace safe names for types as well? I usually
think it is usually clear to use otherwise.

> +
> +/* Buffer for fast character writing with overflow handling.
> +   Typically embedded in another struct with further data that is used
> +   by the flush function.  */
> +struct __printf_buffer
> +{
> +  /* These pointer members follow FILE streams.  write_ptr and
> +     write_end must be initialized to cover the target buffer.  See
> +     __printf_buffer_init.
> +
> +     Data can be written directly to *write_ptr while write_ptr !=
> +     write_end, and write_ptr can be advanced accordingly.  Note that
> +     is not possible to use the apparently-unused part of the buffer
> +     as scratch space because sprintf (and snprintf, but that is a bit
> +     iffy) must only write the minimum number of characters produced
> +     by the format string and its arguments.
> +
> +     write_base must be initialized to be equal to write_ptr.  The
> +     framework uses this pointer to compute the total number of
> +     written bytes, together with the written field.  See
> +     __printf_buffer_done.
> +
> +     write_base and write_end are only read by the generic functions
> +     after initialization, only the flush implementation called from
> +     __printf_buffer_flush might change these pointers.  See the
> +     comment on Xprintf (buffer_do_flush) in Xprintf_buffer_flush.c
> +     for details regarding the flush operation.  */
> +  char *write_base;
> +  char *write_ptr;
> +  char *write_end;
> +
> +  /* Number of characters written so far (excluding the current
> +     buffer).  Potentially updated on flush.  The actual number of
> +     written bytes also inclused the unflushed-but-written buffer
> +     part, write_ptr - write_base.  A 64-bit value is used to avoid
> +     the need for overflow checks.  */
> +  uint64_t written;
> +
> +  /* Identifies the flush callback.  */
> +  enum __printf_buffer_mode mode;
> +};
> +
> +/* Marks the buffer as failed, so that __printf_buffer_has_failed
> +   returns true and future flush operations are no-ops.  */
> +static inline void
> +__printf_buffer_mark_failed (struct __printf_buffer *buf)

For other internal interfaces it seems that current pratice is to use
non double underscore static inline function wrappers over the namespace
safe symbols.  Maybe we should use the same strategy here.

> +{
> +  buf->mode = __printf_buffer_mode_failed;
> +}
> +
> +/* Returns true if the sticky error indicator of the buffer has been
> +   set to failed.  */
> +static inline bool __attribute_warn_unused_result__
> +__printf_buffer_has_failed (struct __printf_buffer *buf)
> +{
> +  return buf->mode == __printf_buffer_mode_failed;
> +}
> +
> +/* Initialization of a buffer, using the memory region from [BASE, BASE +LEN)
> +   as the initial buffer contents.  LEN can be zero.  */
> +static inline void
> +__printf_buffer_init (struct __printf_buffer *buf, char *base, size_t len,
> +                      enum __printf_buffer_mode mode)
> +{
> +  buf->write_base = base;
> +  buf->write_ptr = base;
> +  buf->write_end = base + len;
> +  buf->written = 0;
> +  buf->mode = mode;
> +}
> +
> +/* Called by printf_buffer_putc for a full buffer.  */
> +void __printf_buffer_putc_1 (struct __printf_buffer *buf, char ch)
> +  attribute_hidden;
> +
> +/* Writes CH to BUF.  */
> +static inline void
> +__printf_buffer_putc (struct __printf_buffer *buf, char ch)
> +{
> +  if (buf->write_ptr != buf->write_end)
> +      *buf->write_ptr++ = ch;
> +  else
> +    __printf_buffer_putc_1 (buf, ch);
> +}
> +
> +/* Writes COUNT repeats of CH to BUF.  */
> +void __printf_buffer_pad_1 (struct __printf_buffer *buf,
> +                            char ch, size_t count) attribute_hidden;
> +
> +/* __printf_buffer_pad with fast path for no padding.  COUNT is a
> +   long int, to accomodate signed uses in printf and elsewhere.  */
> +static inline void
> +__printf_buffer_pad (struct __printf_buffer *buf, char ch, long int count)
> +{
> +  if (count > 0)
> +    __printf_buffer_pad_1 (buf, ch, count);
> +}
> +
> +/* Write COUNT bytes starting at S to BUF.  S must not overlap with
> +   the internal buffer.  */

Maybe add a overlpa check on Xprintf_buffer_write.c ?  Not sure if it is
worth though.

> +void __printf_buffer_write (struct __printf_buffer *buf, const char *s,
> +                            size_t count) attribute_hidden;
> +
> +/* Write S to BUF.  S must not overlap with the internal buffer.  */
> +void __printf_buffer_puts_1 (struct __printf_buffer *buf, const char *s)
> +  attribute_hidden;
> +static inline void
> +__printf_buffer_puts (struct __printf_buffer *buf, const char *s)
> +{
> +  if (__builtin_constant_p (__builtin_strlen (s)))
> +    __printf_buffer_write (buf, s, __builtin_strlen (s));
> +  else
> +    __printf_buffer_puts_1 (buf, s);
> +}
> +
> +/* Returns the number of bytes written through the buffer, or -1 if
> +   there was an error (that is, __printf_buffer_has_failed (BUF) is true).
> +
> +   The number of written bytes includes pending bytes in the buffer
> +   (between BUF->write_base and BUF->write_ptr).
> +
> +   If the number is larger than INT_MAX, returns -1 and sets errno to
> +   EOVERFLOW.  This function does not flush the buffer.  If the caller
> +   needs the side effect of flushing, it has to do this
> +   separately.  */
> +int __printf_buffer_done (struct __printf_buffer *buf) attribute_hidden;
> +
> +/* Internally used to call the flush function.  This can be called
> +   explicitly for certain modes to flush the buffer prematuraly.  In
> +   such cases, it is often the case that the buffer mode is statically
> +   known, and the flush implementation can be called directly.  */
> +bool __printf_buffer_flush (struct __printf_buffer *buf) attribute_hidden;
> +
> +/* Wide version of struct __printf_buffer follows.  */
> +
> +enum __wprintf_buffer_mode
> +  {
> +    __wprintf_buffer_mode_failed,
> +    __wprintf_buffer_mode_to_file,
> +  };
> +
> +struct __wprintf_buffer
> +{
> +  wchar_t *write_base;
> +  wchar_t *write_ptr;
> +  wchar_t *write_end;
> +  uint64_t written;
> +  enum __wprintf_buffer_mode mode;
> +};
> +
> +static inline void
> +__wprintf_buffer_mark_failed (struct __wprintf_buffer *buf)
> +{
> +  buf->mode = __wprintf_buffer_mode_failed;
> +}
> +
> +static inline bool __attribute_warn_unused_result__
> +__wprintf_buffer_has_failed (struct __wprintf_buffer *buf)
> +{
> +  return buf->mode == __wprintf_buffer_mode_failed;
> +}
> +
> +static inline void
> +__wprintf_buffer_init (struct __wprintf_buffer *buf,
> +                       wchar_t *base, size_t len,
> +                       enum __wprintf_buffer_mode mode)
> +{
> +  buf->write_base = base;
> +  buf->write_ptr = base;
> +  buf->write_end = base + len;
> +  buf->written = 0;
> +  buf->mode = mode;
> +}
> +
> +void __wprintf_buffer_putc_1 (struct __wprintf_buffer *buf, wchar_t ch)
> +  attribute_hidden;
> +
> +static inline void
> +__wprintf_buffer_putc (struct __wprintf_buffer *buf, wchar_t ch)
> +{
> +  if (buf->write_ptr != buf->write_end)
> +      *buf->write_ptr++ = ch;
> +  else
> +    __wprintf_buffer_putc_1 (buf, ch);
> +}
> +
> +void __wprintf_buffer_pad_1 (struct __wprintf_buffer *buf,
> +                             wchar_t ch, size_t count) attribute_hidden;
> +
> +static inline void
> +__wprintf_buffer_pad (struct __wprintf_buffer *buf, char ch, long int count)
> +{
> +  if (count > 0)
> +    __wprintf_buffer_pad_1 (buf, ch, count);
> +}
> +
> +void __wprintf_buffer_write (struct __wprintf_buffer *buf, const wchar_t *s,
> +                             size_t count) attribute_hidden;
> +
> +void __wprintf_buffer_puts (struct __wprintf_buffer *buf, const wchar_t *s)
> +  attribute_hidden;
> +
> +int __wprintf_buffer_done (struct __wprintf_buffer *buf) attribute_hidden;
> +
> +bool __wprintf_buffer_flush (struct __wprintf_buffer *buf) attribute_hidden;
> +
> +/* Type-generic convenience macros.  They are useful if
> +   printf_buffer-char.h or printf_buffer-wchar_t.h is included as
> +   well.  */
> +
> +#define Xprintf_buffer Xprintf (buffer)
> +#define Xprintf_buffer_done Xprintf (buffer_done)
> +#define Xprintf_buffer_flush Xprintf (buffer_flush)
> +#define Xprintf_buffer_has_failed Xprintf (buffer_has_failed)
> +#define Xprintf_buffer_mark_failed Xprintf (buffer_mark_failed)
> +#define Xprintf_buffer_pad Xprintf (buffer_pad)
> +#define Xprintf_buffer_putc Xprintf (buffer_putc)
> +#define Xprintf_buffer_puts Xprintf (buffer_puts)
> +#define Xprintf_buffer_write Xprintf (buffer_write)
> +
> +/* 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.  */
> +
> +struct __printf_buffer_to_file;
> +void __printf_buffer_flush_to_file (struct __printf_buffer_to_file *)
> +  attribute_hidden;
> +
> +struct __wprintf_buffer_to_file;
> +void __wprintf_buffer_flush_to_file (struct __wprintf_buffer_to_file *)
> +  attribute_hidden;
> +
> +#endif /* PRINTF_BUFFER_H */
> diff --git a/stdio-common/Makefile b/stdio-common/Makefile
> index da3a3bc0c9..5ba381fe41 100644
> --- a/stdio-common/Makefile
> +++ b/stdio-common/Makefile
> @@ -53,6 +53,14 @@ routines := \
>    perror \
>    printf \
>    printf-prs \
> +  printf_buffer_as_file \
> +  printf_buffer_done \
> +  printf_buffer_flush \
> +  printf_buffer_pad_1 \
> +  printf_buffer_putc_1 \
> +  printf_buffer_puts_1 \
> +  printf_buffer_to_file \
> +  printf_buffer_write \
>    printf_fp \
>    printf_fphex \
>    printf_size \
> @@ -85,6 +93,14 @@ routines := \
>    vfwscanf \
>    vfwscanf-internal \
>    vprintf \
> +  wprintf_buffer_as_file \
> +  wprintf_buffer_done \
> +  wprintf_buffer_flush \
> +  wprintf_buffer_pad_1 \
> +  wprintf_buffer_putc_1 \
> +  wprintf_buffer_puts_1 \
> +  wprintf_buffer_to_file \
> +  wprintf_buffer_write \
>    # routines
>  
>  aux := \
> diff --git a/stdio-common/Xprintf_buffer_done.c b/stdio-common/Xprintf_buffer_done.c
> new file mode 100644
> index 0000000000..c7551979af
> --- /dev/null
> +++ b/stdio-common/Xprintf_buffer_done.c
> @@ -0,0 +1,40 @@
> +/* Final status reporting for struct __*printf_buffer.  Generic version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <errno.h>
> +#include <limits.h>
> +#include <stdint.h>
> +
> +int
> +Xprintf_buffer_done (struct Xprintf_buffer *buf)
> +{
> +  if (Xprintf_buffer_has_failed (buf))
> +    return -1;
> +
> +  /* Use uintptr_t to deal with ptrdiff_t overflows (to some degree).  */

I am not sure using uintptr_t yield any gain in fact, if 
'buf->write_ptr - buf->write_base' can not fit in ptrdiff_t it means the
code already trigger UB in write_ptr update.

> +  uintptr_t written_current = buf->write_ptr - buf->write_base;
> +  unsigned int written_total;
> +  if (__builtin_add_overflow (buf->written, written_current, &written_total)

I think it should use INT_ADD_OVERFLOW since we still support GCC 6.5 and
__builtin_{add,sub}_overflow is not reliable with GCC 6.X [1].

[1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98269

> +      || buf->written > INT_MAX)
> +    {
> +      __set_errno (EOVERFLOW);
> +      return -1;
> +    }
> +  else
> +    return written_total;
> +}
> diff --git a/stdio-common/Xprintf_buffer_flush.c b/stdio-common/Xprintf_buffer_flush.c
> new file mode 100644
> index 0000000000..1368cfe684
> --- /dev/null
> +++ b/stdio-common/Xprintf_buffer_flush.c
> @@ -0,0 +1,72 @@
> +/* Flush wrapper for struct __*printf_buffer.  Generic version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +
> +#include <assert.h>
> +#include <stdint.h>
> +
> +/* Xprintf (buffer_do_flush) (BUF) performs the flush operation.  The
> +   actual implementation is specific to the multibyte and wide
> +   variants.
> +
> +   If the flush fails, Xprintf_buffer_mark_failed (BUF) must be
> +   called, and BUF->write_ptr and BUF->write_end can be left
> +   unchanged.
> +
> +   The function must not do anything if failure has already occurred,
> +   that is, if BUF->mode == Xprintf (buffer_mode_failed).
> +
> +   The framework implicitly invokes flush with BUF->write_ptr ==
> +   BUF->write_end only.  (This is particularly relevant to the
> +   __sprintf_chk flush, which just calls __chk_fail.)  But in some
> +   cases, Xprintf_buffer_flush may be called explicitly (when
> +   BUF->mode/the backing function is known).  In that case, it is
> +   possible that BUF->write_ptr < BUF->write_end is true.
> +
> +   If the flush succeeds, the pointers are changed so that
> +   BUF->write_ptr < BUF->write_end.  It is possible to switch to a
> +   completely different buffer here.  If the buffer is moved, it may
> +   be necessary to updated BUF->write_base and BUF->written from the
> +   flush function as well.
> +
> +   Note that when chaining buffers, in the flush function for the
> +   outer buffer (to which data is written first), it is necessary to
> +   check for BUF->next->failed (for the inner buffer) and set
> +   BUF->base.failed to true (for the outer buffer).  This should come
> +   towards the end of the outer flush function.  Usually, there is
> +   also some unwrapping step afterwards; it has to check the outer
> +   buffer (BUF->base.failed) and propagate any error to the inner
> +   buffer (BUF->next->failed), so essentially in the other
> +   direction.  */
> +static void Xprintf (buffer_do_flush) (struct Xprintf_buffer *buf);
> +
> +bool
> +Xprintf_buffer_flush (struct Xprintf_buffer *buf)
> +{
> +  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
> +    return false;
> +
> +  Xprintf (buffer_do_flush) (buf);
> +  if (Xprintf_buffer_has_failed (buf))
> +    return false;
> +
> +  /* Ensure that the flush has made available some bytes.  */
> +  assert (buf->write_ptr != buf->write_end);
> +  return true;
> +}
> diff --git a/stdio-common/Xprintf_buffer_pad_1.c b/stdio-common/Xprintf_buffer_pad_1.c
> new file mode 100644
> index 0000000000..2be8270ad2
> --- /dev/null
> +++ b/stdio-common/Xprintf_buffer_pad_1.c
> @@ -0,0 +1,44 @@
> +/* Write repeated characters to struct __*printf_buffer.  Generic version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <assert.h>
> +#include <string.h>
> +
> +void
> +Xprintf (buffer_pad_1) (struct Xprintf_buffer *buf, CHAR_T ch, size_t count)
> +{
> +  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
> +    return;
> +
> +  do
> +    {
> +      /* Proactively make room.  __*printf_buffer_pad has already
> +         checked for a zero-length write, so this function is only
> +         called when there is actually data to write.  */
> +      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
> +        return;
> +      assert (buf->write_ptr != buf->write_end);
> +      size_t to_fill = buf->write_end - buf->write_ptr;
> +      if (to_fill > count)
> +        to_fill = count;
> +      MEMSET (buf->write_ptr, ch, to_fill);
> +      buf->write_ptr += to_fill;
> +      count -= to_fill;
> +    }
> +  while (count > 0);
> +}
> diff --git a/stdio-common/Xprintf_buffer_putc_1.c b/stdio-common/Xprintf_buffer_putc_1.c
> new file mode 100644
> index 0000000000..3458775a1c
> --- /dev/null
> +++ b/stdio-common/Xprintf_buffer_putc_1.c
> @@ -0,0 +1,29 @@
> +/* Overflow write function for struct __*printf_buffer.  Generic version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <assert.h>
> +
> +void
> +Xprintf (buffer_putc_1) (struct Xprintf_buffer *buf, CHAR_T ch)
> +{
> +  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf))
> +      || !Xprintf_buffer_flush (buf))
> +    return;
> +  assert (buf->write_ptr < buf->write_end);
> +  *buf->write_ptr++ = ch;
> +}
> diff --git a/stdio-common/Xprintf_buffer_puts_1.c b/stdio-common/Xprintf_buffer_puts_1.c
> new file mode 100644
> index 0000000000..d8ee7ef47e
> --- /dev/null
> +++ b/stdio-common/Xprintf_buffer_puts_1.c
> @@ -0,0 +1,37 @@
> +/* String write function for struct __*printf_buffer.  Generic version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <assert.h>
> +#include <string.h>
> +
> +void
> +Xprintf (buffer_puts_1) (struct Xprintf_buffer *buf, const CHAR_T *s)
> +{
> +  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
> +    return;
> +
> +  while (*s != 0)
> +    {
> +      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
> +        return;
> +      assert (buf->write_ptr != buf->write_end);
> +      size_t to_copy = STRNLEN (s, buf->write_end - buf->write_ptr);
> +      buf->write_ptr = MEMPCPY (buf->write_ptr, s, to_copy);
> +      s += to_copy;
> +    }
> +}

Does it really need a loop here? 

> diff --git a/stdio-common/Xprintf_buffer_write.c b/stdio-common/Xprintf_buffer_write.c
> new file mode 100644
> index 0000000000..fc6ad5fdd1
> --- /dev/null
> +++ b/stdio-common/Xprintf_buffer_write.c
> @@ -0,0 +1,43 @@
> +/* Blob write function for struct __*printf_buffer.  Generic version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +
> +#include <assert.h>
> +#include <string.h>
> +
> +void
> +Xprintf_buffer_write (struct Xprintf_buffer *buf,
> +                        const CHAR_T *s, size_t count)
> +{
> +  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
> +    return;
> +
> +  while (count > 0)
> +    {
> +      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
> +        return;
> +      assert (buf->write_ptr != buf->write_end);
> +      size_t to_copy = buf->write_end - buf->write_ptr;
> +      if (to_copy > count)
> +        to_copy = count;
> +      buf->write_ptr = MEMPCPY (buf->write_ptr, s, to_copy);
> +      s += to_copy;
> +      count -= to_copy;
> +    }
> +}

As before, I trying to understand why we need a loop here.

> diff --git a/stdio-common/printf_buffer-char.h b/stdio-common/printf_buffer-char.h
> new file mode 100644
> index 0000000000..88eb326a45
> --- /dev/null
> +++ b/stdio-common/printf_buffer-char.h
> @@ -0,0 +1,24 @@
> +/* Macros for the multibyte (char) implementation of struct __printf_buffer.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#define Xprintf(n) __printf_##n
> +
> +#define CHAR_T char
> +#define MEMPCPY __mempcpy
> +#define MEMSET memset
> +#define STRNLEN __strnlen
> diff --git a/stdio-common/printf_buffer-wchar_t.h b/stdio-common/printf_buffer-wchar_t.h
> new file mode 100644
> index 0000000000..7597dc2eab
> --- /dev/null
> +++ b/stdio-common/printf_buffer-wchar_t.h
> @@ -0,0 +1,24 @@
> +/* Macros for wide (wchar_t) implementation of struct __wprintf_buffer.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#define Xprintf(n) __wprintf_##n
> +
> +#define CHAR_T wchar_t
> +#define MEMPCPY __wmempcpy
> +#define MEMSET __wmemset
> +#define STRNLEN __wcsnlen
> diff --git a/stdio-common/printf_buffer_as_file.c b/stdio-common/printf_buffer_as_file.c
> new file mode 100644
> index 0000000000..f27b000d78
> --- /dev/null
> +++ b/stdio-common/printf_buffer_as_file.c
> @@ -0,0 +1,148 @@
> +/* FILE * interface to a struct __printf_buffer.  Multibyte version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer_as_file.h>
> +
> +#include <assert.h>
> +#include <printf_buffer.h>
> +
> +/* Commit the data directly written through the stdio stream.  */
> +static void
> +__printf_buffer_as_file_commit (struct __printf_buffer_as_file *file)
> +{
> +  /* Check that the write pointers in the file stream are consistent
> +     with the next buffer.  */
> +  assert (file->stream._IO_write_ptr >= file->next->write_ptr);
> +  assert (file->stream._IO_write_ptr <= file->next->write_end);
> +  assert (file->stream._IO_write_base == file->next->write_base);
> +  assert (file->stream._IO_write_end == file->next->write_end);
> +
> +  file->next->write_ptr = file->stream._IO_write_ptr;
> +}
> +
> +/* Pointer the FILE * write buffer into the active printf_buffer
> +   area.  */
> +static void
> +__printf_buffer_as_file_switch_to_buffer (struct __printf_buffer_as_file *file)
> +{
> +  file->stream._IO_write_base = file->next->write_base;
> +  file->stream._IO_write_ptr = file->next->write_ptr;
> +  file->stream._IO_write_end = file->next->write_end;
> +}
> +
> +/* Only a small subset of the vtable functions is implemented here,
> +   following _IO_obstack_jumps.  */
> +
> +static int
> +__printf_buffer_as_file_overflow (FILE *fp, int ch)
> +{
> +  struct __printf_buffer_as_file *file = (struct __printf_buffer_as_file *) fp;
> +
> +  __printf_buffer_as_file_commit (file);
> +
> +  /* EOF means only a flush is requested.   */
> +  if (ch != EOF)
> +    __printf_buffer_putc (file->next, ch);
> +
> +  /* Ensure that flushing actually produces room.  */
> +  if (!__printf_buffer_has_failed (file->next)
> +      && file->next->write_ptr == file->next->write_end)
> +    __printf_buffer_flush (file->next);
> +
> +  __printf_buffer_as_file_switch_to_buffer (file);
> +
> +  if (!__printf_buffer_has_failed (file->next))
> +    return (unsigned char) ch;
> +  else
> +    return EOF;
> +}
> +
> +static size_t
> +__printf_buffer_as_file_xsputn (FILE *fp, const void *buf, size_t len)
> +{
> +  struct __printf_buffer_as_file *file = (struct __printf_buffer_as_file *) fp;
> +
> +  __printf_buffer_as_file_commit (file);
> +
> +  /* Copy the data.  */
> +  __printf_buffer_write (file->next, buf, len);
> +
> +  __printf_buffer_as_file_switch_to_buffer (file);
> +
> +  if (!__printf_buffer_has_failed (file->next))
> +    return len;
> +  else
> +    /* We may actually have written something.  But the stream is
> +       corrupted in this case anyway, so try not to divine the write
> +       count here.  */
> +    return 0;
> +}
> +
> +static const struct _IO_jump_t _IO_printf_buffer_as_file_jumps libio_vtable =
> +{
> +  JUMP_INIT_DUMMY,
> +  JUMP_INIT(finish, NULL),
> +  JUMP_INIT(overflow, __printf_buffer_as_file_overflow),
> +  JUMP_INIT(underflow, NULL),
> +  JUMP_INIT(uflow, NULL),
> +  JUMP_INIT(pbackfail, NULL),
> +  JUMP_INIT(xsputn, __printf_buffer_as_file_xsputn),
> +  JUMP_INIT(xsgetn, NULL),
> +  JUMP_INIT(seekoff, NULL),
> +  JUMP_INIT(seekpos, NULL),
> +  JUMP_INIT(setbuf, NULL),
> +  JUMP_INIT(sync, NULL),
> +  JUMP_INIT(doallocate, NULL),
> +  JUMP_INIT(read, NULL),
> +  JUMP_INIT(write, NULL),
> +  JUMP_INIT(seek, NULL),
> +  JUMP_INIT(close, NULL),
> +  JUMP_INIT(stat, NULL),
> +  JUMP_INIT(showmanyc, NULL),
> +  JUMP_INIT(imbue, NULL)
> +};
> +
> +void
> +__printf_buffer_as_file_init (struct __printf_buffer_as_file *file,
> +                              struct __printf_buffer *next)
> +{
> +  file->stream._lock = NULL;
> +  _IO_no_init (&file->stream, _IO_USER_LOCK, -1, NULL, NULL);
> +  file->vtable = &_IO_printf_buffer_as_file_jumps;
> +
> +  /* Set up the write buffer from the next buffer.  */
> +  file->next = next;
> +  __printf_buffer_as_file_switch_to_buffer (file);
> +
> +  /* Mark the read area as inactive, by making all pointers equal.  */
> +  file->stream._IO_read_base = file->stream._IO_write_base;
> +  file->stream._IO_read_ptr = file->stream._IO_write_base;
> +  file->stream._IO_read_end = file->stream._IO_write_base;
> +}
> +
> +bool
> +__printf_buffer_as_file_terminate (struct __printf_buffer_as_file *file)
> +{
> +  if (file->stream._flags & _IO_ERR_SEEN)
> +    return false;
> +  else
> +    {
> +      __printf_buffer_as_file_commit (file);
> +      return true;
> +    }
> +}
> diff --git a/stdio-common/printf_buffer_as_file.h b/stdio-common/printf_buffer_as_file.h
> new file mode 100644
> index 0000000000..ab7fa44144
> --- /dev/null
> +++ b/stdio-common/printf_buffer_as_file.h
> @@ -0,0 +1,87 @@
> +/* FILE * interface to a struct __*printf_buffer.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +/* Registered printf format specifier callbacks produce data via a
> +   FILE * stream.  struct __printf_buffer_as_file enables vfprintf to
> +   create a suitable stdio stream.  Likewise struct
> +   __wprintf_buffer_as_file for vfwprintf.  */
> +
> +#ifndef PRINTF_BUFFER_AS_FILE_H
> +#define PRINTF_BUFFER_AS_FILE_H
> +
> +#include <libio/libioP.h>
> +
> +struct __printf_buffer;
> +
> +struct __printf_buffer_as_file
> +{
> +  /* Interface to libio.  */
> +  FILE stream;
> +  const struct _IO_jump_t *vtable;
> +
> +  /* Pointer to the underlying buffer.  */
> +  struct __printf_buffer *next;
> +};
> +
> +/* Initialization *FP so that data written to its FILE * stream ends
> +   up in NEXT.  */
> +void __printf_buffer_as_file_init (struct __printf_buffer_as_file *fp,
> +                                   struct __printf_buffer *next)
> +  attribute_hidden;
> +
> +/* Returns the FILE * that can be used to write data to the
> +   buffer.  */
> +static inline FILE *
> +__printf_buffer_as_file_get (struct __printf_buffer_as_file *file)
> +{
> +  return &file->stream;
> +}
> +
> +/* Transfers all pending data from the FILE * to the underlying
> +   buffer.  Returns true if there have been no errors. */
> +bool __printf_buffer_as_file_terminate (struct __printf_buffer_as_file *)
> +  attribute_hidden;
> +
> +/* Wide variant follows.  */
> +
> +struct __wprintf_buffer;
> +struct __wprintf_buffer_as_file
> +{
> +  /* Interface to libio.  */
> +  FILE stream;
> +  const struct _IO_jump_t *vtable;
> +  struct _IO_wide_data wide_stream;
> +
> +  /* Pointer to the underlying buffer.  */
> +  struct __wprintf_buffer *next;
> +};
> +
> +void __wprintf_buffer_as_file_init (struct __wprintf_buffer_as_file *fp,
> +                                    struct __wprintf_buffer *next)
> +  attribute_hidden;
> +
> +static inline FILE *
> +__wprintf_buffer_as_file_get (struct __wprintf_buffer_as_file *file)
> +{
> +  return &file->stream;
> +}
> +
> +bool __wprintf_buffer_as_file_terminate (struct __wprintf_buffer_as_file *)
> +  attribute_hidden;
> +
> +#endif /* PRINTF_BUFFER_AS_FILE_H */
> diff --git a/stdio-common/printf_buffer_done.c b/stdio-common/printf_buffer_done.c
> new file mode 100644
> index 0000000000..5f3df1daad
> --- /dev/null
> +++ b/stdio-common/printf_buffer_done.c
> @@ -0,0 +1,21 @@
> +/* Final status reporting for struct __printf_buffer.  Multibyte version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-char.h"
> +#include "Xprintf_buffer_done.c"
> diff --git a/stdio-common/printf_buffer_flush.c b/stdio-common/printf_buffer_flush.c
> new file mode 100644
> index 0000000000..9b25c0fde5
> --- /dev/null
> +++ b/stdio-common/printf_buffer_flush.c
> @@ -0,0 +1,42 @@
> +/* Flush a struct __printf_buffer.  Multibyte version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +
> +#include "printf_buffer-char.h"
> +#include "Xprintf_buffer_flush.c"
> +
> +/* The __printf_buffer_flush_* functions are defined together with
> +   functions that are pulled in by strong references.  */
> +#ifndef SHARED
> +# pragma weak __printf_buffer_flush_to_file
> +#endif /* !SHARED */

Is is an optimization to avoid pulling __printf_buffer_flush_to_file?

> +
> +static void
> +__printf_buffer_do_flush (struct __printf_buffer *buf)
> +{
> +  switch (buf->mode)
> +    {
> +    case __printf_buffer_mode_failed:
> +      return;
> +    case __printf_buffer_mode_to_file:
> +      __printf_buffer_flush_to_file ((struct __printf_buffer_to_file *) buf);
> +      return;
> +    }
> +  __builtin_trap ();
> +}
> diff --git a/stdio-common/printf_buffer_pad_1.c b/stdio-common/printf_buffer_pad_1.c
> new file mode 100644
> index 0000000000..c4b9a7af56
> --- /dev/null
> +++ b/stdio-common/printf_buffer_pad_1.c
> @@ -0,0 +1,21 @@
> +/* Write repeated characters to struct __printf_buffer.  Multibyte version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-char.h"
> +#include "Xprintf_buffer_pad_1.c"
> diff --git a/stdio-common/printf_buffer_putc_1.c b/stdio-common/printf_buffer_putc_1.c
> new file mode 100644
> index 0000000000..a15e8d16d1
> --- /dev/null
> +++ b/stdio-common/printf_buffer_putc_1.c
> @@ -0,0 +1,21 @@
> +/* Overflow character write function for struct __printf_buffer.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-char.h"
> +#include "Xprintf_buffer_putc_1.c"
> diff --git a/stdio-common/printf_buffer_puts_1.c b/stdio-common/printf_buffer_puts_1.c
> new file mode 100644
> index 0000000000..ed97fd6b3f
> --- /dev/null
> +++ b/stdio-common/printf_buffer_puts_1.c
> @@ -0,0 +1,21 @@
> +/* String write function for struct __printf_buffer.  Multibyte version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-char.h"
> +#include "Xprintf_buffer_puts_1.c"
> diff --git a/stdio-common/printf_buffer_to_file.c b/stdio-common/printf_buffer_to_file.c
> new file mode 100644
> index 0000000000..6aeb954509
> --- /dev/null
> +++ b/stdio-common/printf_buffer_to_file.c
> @@ -0,0 +1,122 @@
> +/* Multibyte printf buffers writing data to a FILE * stream.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer_to_file.h>
> +
> +#include <assert.h>
> +#include <array_length.h>
> +#include <libio/libioP.h>
> +
> +/* Switch to the file buffer if possible.  If the file has write_ptr
> +   == write_end, use the stage buffer instead.  */
> +void
> +__printf_buffer_to_file_switch (struct __printf_buffer_to_file *buf)
> +{
> +  if (buf->fp->_IO_write_ptr < buf->fp->_IO_write_end)
> +    {
> +      /* buf->fp has a buffer associated with it, so write directly to
> +         it from now on.  */
> +      buf->base.write_ptr = buf->fp->_IO_write_ptr;
> +      buf->base.write_end = buf->fp->_IO_write_end;
> +    }
> +  else
> +    {
> +      /* Use the staging area if no buffer is available in buf->fp.  */
> +      buf->base.write_ptr = buf->stage;
> +      buf->base.write_end = array_end (buf->stage);
> +    }
> +
> +  buf->base.write_base = buf->base.write_ptr;
> +}
> +
> +void
> +__printf_buffer_flush_to_file (struct __printf_buffer_to_file *buf)
> +{
> +  /* The bytes in the buffer are always consumed.  */
> +  buf->base.written += buf->base.write_ptr - buf->base.write_base;
> +
> +  if (buf->base.write_end == array_end (buf->stage))
> +    {
> +      /* If the stage buffer is used, make a copy into the file.  The
> +         stage buffer is always consumed fully, even if just partially
> +         written, to ensure that the file stream has all the data.  */
> +      size_t count = buf->base.write_ptr - buf->stage;
> +      if ((size_t) _IO_sputn (buf->fp, buf->stage, count) != count)
> +            {
> +              __printf_buffer_mark_failed (&buf->base);
> +              return;
> +            }
> +      /* buf->fp may have a buffer now.  */
> +      __printf_buffer_to_file_switch (buf);
> +      return;
> +    }
> +  else if (buf->base.write_end == buf->stage + 1)
> +    {
> +      /* Special one-character buffer case.  This is used to avoid
> +         flush-only overflow below.  */
> +      if (buf->base.write_ptr == buf->base.write_end)
> +        {
> +          if (__overflow (buf->fp, (unsigned char) *buf->stage) == EOF)
> +            {
> +              __printf_buffer_mark_failed (&buf->base);
> +              return;
> +            }
> +          __printf_buffer_to_file_switch (buf);
> +        }
> +      /* Else there is nothing to write.  */
> +      return;
> +    }
> +
> +  /* We have written directly into the buf->fp buffer.  */
> +  assert (buf->base.write_end == buf->fp->_IO_write_end);
> +
> +  /* Mark the bytes as written.  */
> +  buf->fp->_IO_write_ptr = buf->base.write_ptr;
> +
> +  if (buf->base.write_ptr == buf->base.write_end)
> +    {
> +      /* The buffer in buf->fp has been filled.  This should just call
> +         __overflow (buf->fp, EOF), but flush-only overflow is obscure
> +         and not always correctly implemented.  See bug 28949.  Be
> +         conservative and switch to a one-character buffer instead, to
> +         obtain one more character for a regular __overflow call.  */
> +      buf->base.write_ptr = buf->stage;
> +      buf->base.write_end = buf->stage + 1;
> +    }
> +  /* The bytes in the file stream were already marked as written above.  */
> +
> +  buf->base.write_base = buf->base.write_ptr;
> +}
> +
> +void
> +__printf_buffer_to_file_init (struct __printf_buffer_to_file *buf, FILE *fp)
> +{
> +  __printf_buffer_init (&buf->base, buf->stage, array_length (buf->stage),
> +                        __printf_buffer_mode_to_file);
> +  buf->fp = fp;
> +  __printf_buffer_to_file_switch (buf);
> +}
> +
> +int
> +__printf_buffer_to_file_done (struct __printf_buffer_to_file *buf)
> +{
> +  if (__printf_buffer_has_failed (&buf->base))
> +    return -1;
> +  __printf_buffer_flush_to_file (buf);
> +  return __printf_buffer_done (&buf->base);
> +}
> diff --git a/stdio-common/printf_buffer_to_file.h b/stdio-common/printf_buffer_to_file.h
> new file mode 100644
> index 0000000000..97ec1d6d95
> --- /dev/null
> +++ b/stdio-common/printf_buffer_to_file.h
> @@ -0,0 +1,57 @@
> +/* Multibyte and wide printf buffers writing data to a FILE * stream.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#ifndef PRINTF_BUFFER_TO_FILE_H
> +#define PRINTF_BUFFER_TO_FILE_H
> +
> +#include <bits/types/FILE.h>
> +#include <printf_buffer.h>
> +
> +struct __printf_buffer_to_file
> +{
> +  struct __printf_buffer base;
> +  FILE *fp;
> +
> +  /* Staging buffer.  All data goes through this buffer before
> +     reaching the fp stream.  (Any buffer in fp is not yet used.)  */
> +  char stage[128];
> +};
> +
> +/* Initializes *BUF to write data to FP.  */
> +void __printf_buffer_to_file_init (struct __printf_buffer_to_file *buf,
> +                                   FILE *fp) attribute_hidden;
> +
> +/* Transfers any pending data in BUF to BUF->FP.  The return value
> +   follows the printf convention (number bytes written; or -1 for error).  */
> +int __printf_buffer_to_file_done (struct __printf_buffer_to_file *buf)
> +  attribute_hidden;
> +
> +/* Wide version below.  */
> +
> +struct __wprintf_buffer_to_file
> +{
> +  struct __wprintf_buffer base;
> +  FILE *fp;
> +  wchar_t stage[128];
> +};
> +void __wprintf_buffer_to_file_init (struct __wprintf_buffer_to_file *buf,
> +                                    FILE *fp) attribute_hidden;
> +int __wprintf_buffer_to_file_done (struct __wprintf_buffer_to_file *buf)
> +  attribute_hidden;
> +
> +#endif /* PRINTF_BUFFER_TO_FILE_H */
> diff --git a/stdio-common/printf_buffer_write.c b/stdio-common/printf_buffer_write.c
> new file mode 100644
> index 0000000000..1038f69063
> --- /dev/null
> +++ b/stdio-common/printf_buffer_write.c
> @@ -0,0 +1,21 @@
> +/* Blob write function for struct __printf_buffer.  Multibyte version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-char.h"
> +#include "Xprintf_buffer_write.c"
> diff --git a/stdio-common/wprintf_buffer_as_file.c b/stdio-common/wprintf_buffer_as_file.c
> new file mode 100644
> index 0000000000..cd48a7d42a
> --- /dev/null
> +++ b/stdio-common/wprintf_buffer_as_file.c
> @@ -0,0 +1,153 @@
> +/* FILE * interface to a struct __wprintf_buffer.  Wide version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer_as_file.h>
> +
> +#include <assert.h>
> +#include <printf_buffer.h>
> +
> +/* Commit the data directly written through the stdio stream.  */
> +static void
> +__wprintf_buffer_as_file_commit (struct __wprintf_buffer_as_file *file)
> +{
> +  /* Check that the write pointers in the file stream are consistent
> +     with the next buffer.  */
> +  assert (file->wide_stream._IO_write_ptr >= file->next->write_ptr);
> +  assert (file->wide_stream._IO_write_ptr <= file->next->write_end);
> +  assert (file->wide_stream._IO_write_base == file->next->write_base);
> +  assert (file->wide_stream._IO_write_end == file->next->write_end);
> +
> +  file->next->write_ptr = file->wide_stream._IO_write_ptr;
> +}
> +
> +/* Pointer the FILE * write buffer into the active struct __wprintf_buffer
> +   area.  */
> +static void
> +__wprintf_buffer_as_file_switch_to_buffer (struct __wprintf_buffer_as_file *file)
> +{
> +  file->wide_stream._IO_write_base = file->next->write_base;
> +  file->wide_stream._IO_write_ptr = file->next->write_ptr;
> +  file->wide_stream._IO_write_end = file->next->write_end;
> +}
> +
> +/* Only a small subset of the vtable functions is implemented here,
> +   following _IO_obstack_jumps.  */
> +
> +static wint_t
> +__wprintf_buffer_as_file_overflow (FILE *fp, int ch)
> +{
> +  struct __wprintf_buffer_as_file *file
> +    = (struct __wprintf_buffer_as_file *) fp;
> +
> +  __wprintf_buffer_as_file_commit (file);
> +
> +  /* EOF means only a flush is requested.   */
> +  if (ch != WEOF)
> +    __wprintf_buffer_putc (file->next, ch);
> +  else
> +    ch = 0;
> +
> +  /* Ensure that flushing actually produces room.  */
> +  if (!__wprintf_buffer_has_failed (file->next)
> +      && file->next->write_ptr == file->next->write_end)
> +    __wprintf_buffer_flush (file->next);
> +
> +  __wprintf_buffer_as_file_switch_to_buffer (file);
> +
> +  if (!__wprintf_buffer_has_failed (file->next))
> +    return (unsigned char) ch;
> +  else
> +    return WEOF;
> +}
> +
> +static size_t
> +__wprintf_buffer_as_file_xsputn (FILE *fp, const void *buf, size_t len)
> +{
> +  struct __wprintf_buffer_as_file *file
> +    = (struct __wprintf_buffer_as_file *) fp;
> +
> +  __wprintf_buffer_as_file_commit (file);
> +
> +  /* Copy the data.  */
> +  __wprintf_buffer_write (file->next, buf, len);
> +
> +  __wprintf_buffer_as_file_switch_to_buffer (file);
> +
> +  if (!__wprintf_buffer_has_failed (file->next))
> +    return len;
> +  else
> +    /* We may actually have written something.  But the stream is
> +       corrupted in this case anyway, so try not to divine the write
> +       count here.  */
> +    return 0;
> +}
> +
> +static const struct _IO_jump_t _IO_wprintf_buffer_as_file_jumps libio_vtable =
> +{
> +  JUMP_INIT_DUMMY,
> +  JUMP_INIT(finish, NULL),
> +  JUMP_INIT(overflow, (_IO_overflow_t) __wprintf_buffer_as_file_overflow),
> +  JUMP_INIT(underflow, NULL),
> +  JUMP_INIT(uflow, NULL),
> +  JUMP_INIT(pbackfail, NULL),
> +  JUMP_INIT(xsputn, __wprintf_buffer_as_file_xsputn),
> +  JUMP_INIT(xsgetn, NULL),
> +  JUMP_INIT(seekoff, NULL),
> +  JUMP_INIT(seekpos, NULL),
> +  JUMP_INIT(setbuf, NULL),
> +  JUMP_INIT(sync, NULL),
> +  JUMP_INIT(doallocate, NULL),
> +  JUMP_INIT(read, NULL),
> +  JUMP_INIT(write, NULL),
> +  JUMP_INIT(seek, NULL),
> +  JUMP_INIT(close, NULL),
> +  JUMP_INIT(stat, NULL),
> +  JUMP_INIT(showmanyc, NULL),
> +  JUMP_INIT(imbue, NULL)
> +};
> +
> +void
> +__wprintf_buffer_as_file_init (struct __wprintf_buffer_as_file *file,
> +                               struct __wprintf_buffer *next)
> +{
> +  file->stream._lock = NULL;
> +  _IO_no_init (&file->stream, _IO_USER_LOCK, 0, &file->wide_stream,
> +               &_IO_wprintf_buffer_as_file_jumps);
> +  _IO_fwide (&file->stream, 1);
> +
> +  /* Set up the write buffer from the next buffer.  */
> +  file->next = next;
> +  __wprintf_buffer_as_file_switch_to_buffer (file);
> +
> +  /* Mark the read area as inactive, by making all pointers equal.  */
> +  file->stream._IO_read_base = file->stream._IO_write_base;
> +  file->stream._IO_read_ptr = file->stream._IO_write_base;
> +  file->stream._IO_read_end = file->stream._IO_write_base;
> +}
> +
> +bool
> +__wprintf_buffer_as_file_terminate (struct __wprintf_buffer_as_file *file)
> +{
> +  if (file->stream._flags & _IO_ERR_SEEN)
> +    return false;
> +  else
> +    {
> +      __wprintf_buffer_as_file_commit (file);
> +      return true;
> +    }
> +}
> diff --git a/stdio-common/wprintf_buffer_done.c b/stdio-common/wprintf_buffer_done.c
> new file mode 100644
> index 0000000000..60eac03955
> --- /dev/null
> +++ b/stdio-common/wprintf_buffer_done.c
> @@ -0,0 +1,21 @@
> +/* Final status reporting for struct __wprintf_buffer.  Wide version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-wchar_t.h"
> +#include "Xprintf_buffer_done.c"
> diff --git a/stdio-common/wprintf_buffer_flush.c b/stdio-common/wprintf_buffer_flush.c
> new file mode 100644
> index 0000000000..2d91095cca
> --- /dev/null
> +++ b/stdio-common/wprintf_buffer_flush.c
> @@ -0,0 +1,36 @@
> +/* Flush a struct __wprintf_buffer.  Wide version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +
> +#include "printf_buffer-wchar_t.h"
> +#include "Xprintf_buffer_flush.c"
> +
> +static void
> +__wprintf_buffer_do_flush (struct __wprintf_buffer *buf)
> +{
> +  switch (buf->mode)
> +    {
> +    case __wprintf_buffer_mode_failed:
> +      return;
> +    case __wprintf_buffer_mode_to_file:
> +      __wprintf_buffer_flush_to_file ((struct __wprintf_buffer_to_file *) buf);
> +      return;
> +    }
> +  __builtin_trap ();
> +}
> diff --git a/stdio-common/wprintf_buffer_pad_1.c b/stdio-common/wprintf_buffer_pad_1.c
> new file mode 100644
> index 0000000000..9c91126f42
> --- /dev/null
> +++ b/stdio-common/wprintf_buffer_pad_1.c
> @@ -0,0 +1,21 @@
> +/* Write repeated characters to struct __wprintf_buffer.  Wide version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-wchar_t.h"
> +#include "Xprintf_buffer_pad_1.c"
> diff --git a/stdio-common/wprintf_buffer_putc_1.c b/stdio-common/wprintf_buffer_putc_1.c
> new file mode 100644
> index 0000000000..b1af68e721
> --- /dev/null
> +++ b/stdio-common/wprintf_buffer_putc_1.c
> @@ -0,0 +1,21 @@
> +/* Overflow character write function for struct __wprintf_buffer.  Wide version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-wchar_t.h"
> +#include "Xprintf_buffer_putc_1.c"
> diff --git a/stdio-common/wprintf_buffer_puts_1.c b/stdio-common/wprintf_buffer_puts_1.c
> new file mode 100644
> index 0000000000..039aebb56e
> --- /dev/null
> +++ b/stdio-common/wprintf_buffer_puts_1.c
> @@ -0,0 +1,21 @@
> +/* String write function struct __wprintf_buffer.  Wide version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-wchar_t.h"
> +#include "Xprintf_buffer_puts_1.c"
> diff --git a/stdio-common/wprintf_buffer_to_file.c b/stdio-common/wprintf_buffer_to_file.c
> new file mode 100644
> index 0000000000..ac936fab6c
> --- /dev/null
> +++ b/stdio-common/wprintf_buffer_to_file.c
> @@ -0,0 +1,55 @@
> +/* Wide printf buffers writing data to a FILE *.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +/* This implementation is not optimized (unlikely the multibyte
> +   implementation) and always writes to the temporary buffer first.  */
> +
> +#include <printf_buffer_to_file.h>
> +
> +#include <array_length.h>
> +#include <libio/libioP.h>
> +
> +void
> +__wprintf_buffer_flush_to_file (struct __wprintf_buffer_to_file *buf)
> +{
> +  size_t count = buf->base.write_ptr - buf->stage;
> +  if ((size_t) _IO_sputn (buf->fp, buf->stage, count) != count)
> +    {
> +      __wprintf_buffer_mark_failed (&buf->base);
> +      return;
> +    }
> +  buf->base.written += count;
> +  buf->base.write_ptr = buf->stage;
> +}
> +
> +void
> +__wprintf_buffer_to_file_init (struct __wprintf_buffer_to_file *buf, FILE *fp)
> +{
> +  __wprintf_buffer_init (&buf->base, buf->stage, array_length (buf->stage),
> +                         __wprintf_buffer_mode_to_file);
> +  buf->fp = fp;
> +}
> +
> +int
> +__wprintf_buffer_to_file_done (struct __wprintf_buffer_to_file *buf)
> +{
> +  if (__wprintf_buffer_has_failed (&buf->base))
> +    return -1;
> +  __wprintf_buffer_flush_to_file (buf);
> +  return __wprintf_buffer_done (&buf->base);
> +}
> diff --git a/stdio-common/wprintf_buffer_write.c b/stdio-common/wprintf_buffer_write.c
> new file mode 100644
> index 0000000000..06b53c74c3
> --- /dev/null
> +++ b/stdio-common/wprintf_buffer_write.c
> @@ -0,0 +1,21 @@
> +/* Blob write function for struct __wprintf_buffer.  Wide version.
> +   Copyright (C) 2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <printf_buffer.h>
> +#include "printf_buffer-wchar_t.h"
> +#include "Xprintf_buffer_write.c"
  
Florian Weimer June 2, 2022, 5:58 p.m. UTC | #2
* Adhemerval Zanella:

> Some comments below.  I am not very found on this approach, it is *another*
> layer on our already convoluted libio/stdio code; although I don't have a
> better solution.  I *really* wish we could move away from libio...

On the plus side, it completely eliminates libio for sprintf, strfromd,
strfmon.

>> +/* <printf_buffer_as_file.h> introduces a way to use struct
>> +   __printf_buffer objects from FILE * streams.  To avoid storing a
>> +   function pointer (or vtable pointer) in struct __printf_buffer
>> +   (which would defeat libio vtable hardening), a switch statement
>> +   over the different flush implementations is used to implement
>> +   __printf_buffer_flush.
>> +
>> +   __printf_buffer_mode_failed is special: it is the sticky failure
>> +   indicator.  Unlike struct alloc_buffer, this is not folded into
>> +   write_ptr, so that snprintf and other string-writing functions can
>> +   discover the end of the string even in the error case, to be able
>> +   to add the null terminator.  */
>> +enum __printf_buffer_mode
>> +  {
>> +    __printf_buffer_mode_failed,
>> +    __printf_buffer_mode_to_file,
>> +  };
>
> Do we really need to use namespace safe names for types as well? I usually
> think it is usually clear to use otherwise.

It's needed to enable the type-generic implementations.
stdio-common/Xprintf_buffer_flush.c uses Xprintf (buffer_mode_failed).
I used to have two macros (Xprintf and __Xprintf), but I moved away from
this because using __printf_ everywhere seemed more consistent.

>> +/* Marks the buffer as failed, so that __printf_buffer_has_failed
>> +   returns true and future flush operations are no-ops.  */
>> +static inline void
>> +__printf_buffer_mark_failed (struct __printf_buffer *buf)
>
> For other internal interfaces it seems that current pratice is to use
> non double underscore static inline function wrappers over the namespace
> safe symbols.  Maybe we should use the same strategy here.

Same reason as above.
>
>> +/* Write COUNT bytes starting at S to BUF.  S must not overlap with
>> +   the internal buffer.  */
>
> Maybe add a overlpa check on Xprintf_buffer_write.c ?  Not sure if it is
> worth though.

We actually can't because for sprintf, the target buffer (as specified)
extends to the end of the address space, it's unbounded.

>> +int
>> +Xprintf_buffer_done (struct Xprintf_buffer *buf)
>> +{
>> +  if (Xprintf_buffer_has_failed (buf))
>> +    return -1;
>> +
>> +  /* Use uintptr_t to deal with ptrdiff_t overflows (to some degree).  */
>
> I am not sure using uintptr_t yield any gain in fact, if 
> 'buf->write_ptr - buf->write_base' can not fit in ptrdiff_t it means the
> code already trigger UB in write_ptr update.

I should probably remove that.

>> +  uintptr_t written_current = buf->write_ptr - buf->write_base;
>> +  unsigned int written_total;
>> +  if (__builtin_add_overflow (buf->written, written_current, &written_total)
>
> I think it should use INT_ADD_OVERFLOW since we still support GCC 6.5 and
> __builtin_{add,sub}_overflow is not reliable with GCC 6.X [1].
>
> [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98269

We require GCC 7.1 on s390x, see 844b4d8b4b937fe6943d2c0c80ce7d871cdb1eb5.

I'm not sure if INT_ADD_OVERFLOW deals correctly with mixed types.

>> +void
>> +Xprintf (buffer_puts_1) (struct Xprintf_buffer *buf, const CHAR_T *s)
>> +{
>> +  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
>> +    return;
>> +
>> +  while (*s != 0)
>> +    {
>> +      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
>> +        return;
>> +      assert (buf->write_ptr != buf->write_end);
>> +      size_t to_copy = STRNLEN (s, buf->write_end - buf->write_ptr);
>> +      buf->write_ptr = MEMPCPY (buf->write_ptr, s, to_copy);
>> +      s += to_copy;
>> +    }
>> +}
>
> Does it really need a loop here?

Yes, we might reach the end of the buffer with data left to write, so we
have to flush it.  This can happen with asprintf and of course in the
FILE * case.

Thanks,
Florian
  
Adhemerval Zanella June 2, 2022, 6:35 p.m. UTC | #3
On 02/06/2022 14:58, Florian Weimer wrote:
> * Adhemerval Zanella:
> 
>> Some comments below.  I am not very found on this approach, it is *another*
>> layer on our already convoluted libio/stdio code; although I don't have a
>> better solution.  I *really* wish we could move away from libio...
> 
> On the plus side, it completely eliminates libio for sprintf, strfromd,
> strfmon.
> 
>>> +/* <printf_buffer_as_file.h> introduces a way to use struct
>>> +   __printf_buffer objects from FILE * streams.  To avoid storing a
>>> +   function pointer (or vtable pointer) in struct __printf_buffer
>>> +   (which would defeat libio vtable hardening), a switch statement
>>> +   over the different flush implementations is used to implement
>>> +   __printf_buffer_flush.
>>> +
>>> +   __printf_buffer_mode_failed is special: it is the sticky failure
>>> +   indicator.  Unlike struct alloc_buffer, this is not folded into
>>> +   write_ptr, so that snprintf and other string-writing functions can
>>> +   discover the end of the string even in the error case, to be able
>>> +   to add the null terminator.  */
>>> +enum __printf_buffer_mode
>>> +  {
>>> +    __printf_buffer_mode_failed,
>>> +    __printf_buffer_mode_to_file,
>>> +  };
>>
>> Do we really need to use namespace safe names for types as well? I usually
>> think it is usually clear to use otherwise.
> 
> It's needed to enable the type-generic implementations.
> stdio-common/Xprintf_buffer_flush.c uses Xprintf (buffer_mode_failed).
> I used to have two macros (Xprintf and __Xprintf), but I moved away from
> this because using __printf_ everywhere seemed more consistent.

Ok.

> 
>>> +/* Marks the buffer as failed, so that __printf_buffer_has_failed
>>> +   returns true and future flush operations are no-ops.  */
>>> +static inline void
>>> +__printf_buffer_mark_failed (struct __printf_buffer *buf)
>>
>> For other internal interfaces it seems that current pratice is to use
>> non double underscore static inline function wrappers over the namespace
>> safe symbols.  Maybe we should use the same strategy here.
> 
> Same reason as above.
>>
>>> +/* Write COUNT bytes starting at S to BUF.  S must not overlap with
>>> +   the internal buffer.  */
>>
>> Maybe add a overlpa check on Xprintf_buffer_write.c ?  Not sure if it is
>> worth though.
> 
> We actually can't because for sprintf, the target buffer (as specified)
> extends to the end of the address space, it's unbounded.
> 
>>> +int
>>> +Xprintf_buffer_done (struct Xprintf_buffer *buf)
>>> +{
>>> +  if (Xprintf_buffer_has_failed (buf))
>>> +    return -1;
>>> +
>>> +  /* Use uintptr_t to deal with ptrdiff_t overflows (to some degree).  */
>>
>> I am not sure using uintptr_t yield any gain in fact, if 
>> 'buf->write_ptr - buf->write_base' can not fit in ptrdiff_t it means the
>> code already trigger UB in write_ptr update.
> 
> I should probably remove that.
> 
>>> +  uintptr_t written_current = buf->write_ptr - buf->write_base;
>>> +  unsigned int written_total;
>>> +  if (__builtin_add_overflow (buf->written, written_current, &written_total)
>>
>> I think it should use INT_ADD_OVERFLOW since we still support GCC 6.5 and
>> __builtin_{add,sub}_overflow is not reliable with GCC 6.X [1].
>>
>> [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98269
> 
> We require GCC 7.1 on s390x, see 844b4d8b4b937fe6943d2c0c80ce7d871cdb1eb5.
> 
> I'm not sure if INT_ADD_OVERFLOW deals correctly with mixed types.

I think it does have some drawbacks for some usages.  As a side note, I wonder
if should just create another header with just what we use from intprops.h 
(TYPE_MINIMUM, TYPE_MAXIMUM, INT_BUFSIZE_BOUND) so we don't need to keep with
extra unused overflow/wraps macros.  And it is one *less* thing we need to
keep in sync with gnulib.

> 
>>> +void
>>> +Xprintf (buffer_puts_1) (struct Xprintf_buffer *buf, const CHAR_T *s)
>>> +{
>>> +  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
>>> +    return;
>>> +
>>> +  while (*s != 0)
>>> +    {
>>> +      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
>>> +        return;
>>> +      assert (buf->write_ptr != buf->write_end);
>>> +      size_t to_copy = STRNLEN (s, buf->write_end - buf->write_ptr);
>>> +      buf->write_ptr = MEMPCPY (buf->write_ptr, s, to_copy);
>>> +      s += to_copy;
>>> +    }
>>> +}
>>
>> Does it really need a loop here?
> 
> Yes, we might reach the end of the buffer with data left to write, so we
> have to flush it.  This can happen with asprintf and of course in the
> FILE * case.
> 

But in this case case you will just return right? I am trying to understand
where the loop will be executed twice in this case.
  
Florian Weimer June 2, 2022, 6:41 p.m. UTC | #4
* Adhemerval Zanella:

>>>> +void
>>>> +Xprintf (buffer_puts_1) (struct Xprintf_buffer *buf, const CHAR_T *s)
>>>> +{
>>>> +  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
>>>> +    return;
>>>> +
>>>> +  while (*s != 0)
>>>> +    {
>>>> +      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
>>>> +        return;
>>>> +      assert (buf->write_ptr != buf->write_end);
>>>> +      size_t to_copy = STRNLEN (s, buf->write_end - buf->write_ptr);
>>>> +      buf->write_ptr = MEMPCPY (buf->write_ptr, s, to_copy);
>>>> +      s += to_copy;
>>>> +    }
>>>> +}
>>>
>>> Does it really need a loop here?
>> 
>> Yes, we might reach the end of the buffer with data left to write, so we
>> have to flush it.  This can happen with asprintf and of course in the
>> FILE * case.
>> 
>
> But in this case case you will just return right? I am trying to understand
> where the loop will be executed twice in this case.

No, the return happens only if we need to flush (==, no room in the
buffer) and the flush fails, e.g. due to a write system call failure, or
maybe because we have reached the end of the buffer for snprintf.

Thanks,
Florian
  
Adhemerval Zanella June 2, 2022, 6:48 p.m. UTC | #5
On 23/05/2022 11:07, Florian Weimer via Libc-alpha wrote:
> +
> +void
> +__printf_buffer_flush_to_file (struct __printf_buffer_to_file *buf)
> +{
> +  /* The bytes in the buffer are always consumed.  */
> +  buf->base.written += buf->base.write_ptr - buf->base.write_base;
> +
> +  if (buf->base.write_end == array_end (buf->stage))
> +    {
> +      /* If the stage buffer is used, make a copy into the file.  The
> +         stage buffer is always consumed fully, even if just partially
> +         written, to ensure that the file stream has all the data.  */
> +      size_t count = buf->base.write_ptr - buf->stage;
> +      if ((size_t) _IO_sputn (buf->fp, buf->stage, count) != count)
> +            {
> +              __printf_buffer_mark_failed (&buf->base);
> +              return;
> +            }

Indentation seems off here.
  
Florian Weimer June 2, 2022, 6:54 p.m. UTC | #6
* Adhemerval Zanella:

> On 23/05/2022 11:07, Florian Weimer via Libc-alpha wrote:
>> +
>> +void
>> +__printf_buffer_flush_to_file (struct __printf_buffer_to_file *buf)
>> +{
>> +  /* The bytes in the buffer are always consumed.  */
>> +  buf->base.written += buf->base.write_ptr - buf->base.write_base;
>> +
>> +  if (buf->base.write_end == array_end (buf->stage))
>> +    {
>> +      /* If the stage buffer is used, make a copy into the file.  The
>> +         stage buffer is always consumed fully, even if just partially
>> +         written, to ensure that the file stream has all the data.  */
>> +      size_t count = buf->base.write_ptr - buf->stage;
>> +      if ((size_t) _IO_sputn (buf->fp, buf->stage, count) != count)
>> +            {
>> +              __printf_buffer_mark_failed (&buf->base);
>> +              return;
>> +            }
>
> Indentation seems off here.

Right, fixed locally.

Thanks,
Florian
  
Adhemerval Zanella June 2, 2022, 6:55 p.m. UTC | #7
On 02/06/2022 15:41, Florian Weimer wrote:
> * Adhemerval Zanella:
> 
>>>>> +void
>>>>> +Xprintf (buffer_puts_1) (struct Xprintf_buffer *buf, const CHAR_T *s)
>>>>> +{
>>>>> +  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
>>>>> +    return;
>>>>> +
>>>>> +  while (*s != 0)
>>>>> +    {
>>>>> +      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
>>>>> +        return;
>>>>> +      assert (buf->write_ptr != buf->write_end);
>>>>> +      size_t to_copy = STRNLEN (s, buf->write_end - buf->write_ptr);
>>>>> +      buf->write_ptr = MEMPCPY (buf->write_ptr, s, to_copy);
>>>>> +      s += to_copy;
>>>>> +    }
>>>>> +}
>>>>
>>>> Does it really need a loop here?
>>>
>>> Yes, we might reach the end of the buffer with data left to write, so we
>>> have to flush it.  This can happen with asprintf and of course in the
>>> FILE * case.
>>>
>>
>> But in this case case you will just return right? I am trying to understand
>> where the loop will be executed twice in this case.
> 
> No, the return happens only if we need to flush (==, no room in the
> buffer) and the flush fails, e.g. due to a write system call failure, or
> maybe because we have reached the end of the buffer for snprintf.
> 

Sigh, I forgot about the stage buffer you keep for printf_buffer_to_file.
  
Florian Weimer June 2, 2022, 7:12 p.m. UTC | #8
* Adhemerval Zanella:

> On 02/06/2022 15:41, Florian Weimer wrote:
>> * Adhemerval Zanella:
>> 
>>>>>> +void
>>>>>> +Xprintf (buffer_puts_1) (struct Xprintf_buffer *buf, const CHAR_T *s)
>>>>>> +{
>>>>>> +  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
>>>>>> +    return;
>>>>>> +
>>>>>> +  while (*s != 0)
>>>>>> +    {
>>>>>> +      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
>>>>>> +        return;
>>>>>> +      assert (buf->write_ptr != buf->write_end);
>>>>>> +      size_t to_copy = STRNLEN (s, buf->write_end - buf->write_ptr);
>>>>>> +      buf->write_ptr = MEMPCPY (buf->write_ptr, s, to_copy);
>>>>>> +      s += to_copy;
>>>>>> +    }
>>>>>> +}
>>>>>
>>>>> Does it really need a loop here?
>>>>
>>>> Yes, we might reach the end of the buffer with data left to write, so we
>>>> have to flush it.  This can happen with asprintf and of course in the
>>>> FILE * case.
>>>>
>>>
>>> But in this case case you will just return right? I am trying to understand
>>> where the loop will be executed twice in this case.
>> 
>> No, the return happens only if we need to flush (==, no room in the
>> buffer) and the flush fails, e.g. due to a write system call failure, or
>> maybe because we have reached the end of the buffer for snprintf.
>> 
>
> Sigh, I forgot about the stage buffer you keep for printf_buffer_to_file.

This buffer is also used for asprintf, obstack_printf, dprintf.  It's
not just the FILE * interfaces that can produce arbitrary-sized output.

Thanks,
Florian
  
Florian Weimer June 7, 2022, 2:30 p.m. UTC | #9
* Florian Weimer via Libc-alpha:

>> I am not sure using uintptr_t yield any gain in fact, if 
>> 'buf->write_ptr - buf->write_base' can not fit in ptrdiff_t it means the
>> code already trigger UB in write_ptr update.
>
> I should probably remove that.

I'm instead going with this

  /* Use uintptr_t here because for sprintf, the buffer range may
     cover more than half of the address space+.  */
  uintptr_t written_current = buf->write_ptr - buf->write_base;

>>> +  uintptr_t written_current = buf->write_ptr - buf->write_base;
>>> +  unsigned int written_total;
>>> +  if (__builtin_add_overflow (buf->written, written_current, &written_total)
>>
>> I think it should use INT_ADD_OVERFLOW since we still support GCC 6.5 and
>> __builtin_{add,sub}_overflow is not reliable with GCC 6.X [1].
>>
>> [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98269
>
> We require GCC 7.1 on s390x, see 844b4d8b4b937fe6943d2c0c80ce7d871cdb1eb5.
>
> I'm not sure if INT_ADD_OVERFLOW deals correctly with mixed types.

I'll keep __builtin_add_overflow.

Thanks,
Florian
  
Paul Eggert June 7, 2022, 4:55 p.m. UTC | #10
On 6/2/22 10:58, Florian Weimer via Libc-alpha wrote:
> I'm not sure if INT_ADD_OVERFLOW deals correctly with mixed types.

It works with any integer types - though as (I think) Florian mentioned, 
if you can assume GCC 7 you can just use __builtin_add_overflow.

By the way if you want to use __builtin_mul_overflow you'll need at 
least GCC 8.4+, 9.3+ or 10+, due to GCC bug 91450. intprops.h's 
INT_MULTIPLY_WRAPV works around that bug and (like INT_ADD_WRAPV) deals 
with any combination of integer types.
  
Paul Eggert June 7, 2022, 4:57 p.m. UTC | #11
On 5/23/22 07:07, Florian Weimer via Libc-alpha wrote:
> +  /* Use uintptr_t to deal with ptrdiff_t overflows (to some degree).  */
> +  uintptr_t written_current = buf->write_ptr - buf->write_base;
> +  unsigned int written_total;
> +  if (__builtin_add_overflow (buf->written, written_current, &written_total)
> +      || buf->written > INT_MAX)
> +    {

Simpler is to declare written_total to be of type 'int'. Then 
__builtin_add_overflow will do the right thing, with no need to compare 
against INT_MAX.

buf->write_ptr - buf->write_base is of type ptrdiff_t and should never 
be negative, so I don't see why it should be converted to an unsigned 
type. glibc shouldn't allocate objects larger than PTRDIFF_MAX. Any 
attempt by a user to create or use a buffer larger than that should be 
rejected anyway (too many opportunities for user error or glibc internal 
error).
  
Florian Weimer June 7, 2022, 5:11 p.m. UTC | #12
* Paul Eggert:

> On 6/2/22 10:58, Florian Weimer via Libc-alpha wrote:
>> I'm not sure if INT_ADD_OVERFLOW deals correctly with mixed types.

(That should be INT_ADD_WRAPV because INT_ADD_OVERFLOW does not know
about the result type.)

> It works with any integer types

Is this actually true?  I don't see how exactly things are layered, but
there is this requirement for INT_ADD_RANGE_OVERFLOW:

| The arithmetic arguments (including the MIN and MAX arguments) must be
| of the same integer type after the usual arithmetic conversions, and
| the type must have minimum value MIN and maximum MAX.

Thanks,
Florian
  
Florian Weimer June 7, 2022, 5:33 p.m. UTC | #13
* Paul Eggert:

> On 5/23/22 07:07, Florian Weimer via Libc-alpha wrote:
>> +  /* Use uintptr_t to deal with ptrdiff_t overflows (to some degree).  */
>> +  uintptr_t written_current = buf->write_ptr - buf->write_base;
>> +  unsigned int written_total;
>> +  if (__builtin_add_overflow (buf->written, written_current, &written_total)
>> +      || buf->written > INT_MAX)
>> +    {
>
> Simpler is to declare written_total to be of type 'int'. Then
> __builtin_add_overflow will do the right thing, with no need to
> compare against INT_MAX.

Right, I'm going tom make the change and re-test.

> buf->write_ptr - buf->write_base is of type ptrdiff_t and should never
> be negative, so I don't see why it should be converted to an unsigned 
> type. glibc shouldn't allocate objects larger than PTRDIFF_MAX. Any
> attempt by a user to create or use a buffer larger than that should be 
> rejected anyway (too many opportunities for user error or glibc
> internal error).

I think that's not how the specified maximum size for __sprintf_chk
works.  It can be so large that there is address space wraparound.

Thanks,
Florian
  
Paul Eggert June 7, 2022, 5:41 p.m. UTC | #14
On 6/7/22 10:11, Florian Weimer wrote:
> Is this actually true?  I don't see how exactly things are layered, but
> there is this requirement for INT_ADD_RANGE_OVERFLOW:

Yes, it's true.

INT_ADD_RANGE_OVERFLOW has more restrictions, I don't recommend it for 
casual use, and INT_ADD_WRAPV doesn't use INT_ADD_RANGE_OVERFLOW. With 
older compilers INT_ADD_WRAPV uses _GL_INT_ADD_RANGE_OVERFLOW and does 
so safely.

Please use INT_ADD_WRAPV instead; it works like __builtin_add_overflow 
regardless of GCC version (and works for non-GCC too, but glibc doesn't 
care about that).
  

Patch

diff --git a/include/printf_buffer.h b/include/printf_buffer.h
new file mode 100644
index 0000000000..6be7723869
--- /dev/null
+++ b/include/printf_buffer.h
@@ -0,0 +1,281 @@ 
+/* Multibyte and wide buffers for implementing printf-related functions.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+/* The naming of the multibyte and wide variants is intentionally
+   consistent, so that it is possible to use the Xprintf macro in
+   stdio-common/printf_buffer-char.h and
+   stdio-common/printf_buffer-wchar_t.h to select between them in
+   type-generic code.  */
+
+#ifndef PRINTF_BUFFER_H
+#define PRINTF_BUFFER_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <wchar.h>
+
+/* <printf_buffer_as_file.h> introduces a way to use struct
+   __printf_buffer objects from FILE * streams.  To avoid storing a
+   function pointer (or vtable pointer) in struct __printf_buffer
+   (which would defeat libio vtable hardening), a switch statement
+   over the different flush implementations is used to implement
+   __printf_buffer_flush.
+
+   __printf_buffer_mode_failed is special: it is the sticky failure
+   indicator.  Unlike struct alloc_buffer, this is not folded into
+   write_ptr, so that snprintf and other string-writing functions can
+   discover the end of the string even in the error case, to be able
+   to add the null terminator.  */
+enum __printf_buffer_mode
+  {
+    __printf_buffer_mode_failed,
+    __printf_buffer_mode_to_file,
+  };
+
+/* Buffer for fast character writing with overflow handling.
+   Typically embedded in another struct with further data that is used
+   by the flush function.  */
+struct __printf_buffer
+{
+  /* These pointer members follow FILE streams.  write_ptr and
+     write_end must be initialized to cover the target buffer.  See
+     __printf_buffer_init.
+
+     Data can be written directly to *write_ptr while write_ptr !=
+     write_end, and write_ptr can be advanced accordingly.  Note that
+     is not possible to use the apparently-unused part of the buffer
+     as scratch space because sprintf (and snprintf, but that is a bit
+     iffy) must only write the minimum number of characters produced
+     by the format string and its arguments.
+
+     write_base must be initialized to be equal to write_ptr.  The
+     framework uses this pointer to compute the total number of
+     written bytes, together with the written field.  See
+     __printf_buffer_done.
+
+     write_base and write_end are only read by the generic functions
+     after initialization, only the flush implementation called from
+     __printf_buffer_flush might change these pointers.  See the
+     comment on Xprintf (buffer_do_flush) in Xprintf_buffer_flush.c
+     for details regarding the flush operation.  */
+  char *write_base;
+  char *write_ptr;
+  char *write_end;
+
+  /* Number of characters written so far (excluding the current
+     buffer).  Potentially updated on flush.  The actual number of
+     written bytes also inclused the unflushed-but-written buffer
+     part, write_ptr - write_base.  A 64-bit value is used to avoid
+     the need for overflow checks.  */
+  uint64_t written;
+
+  /* Identifies the flush callback.  */
+  enum __printf_buffer_mode mode;
+};
+
+/* Marks the buffer as failed, so that __printf_buffer_has_failed
+   returns true and future flush operations are no-ops.  */
+static inline void
+__printf_buffer_mark_failed (struct __printf_buffer *buf)
+{
+  buf->mode = __printf_buffer_mode_failed;
+}
+
+/* Returns true if the sticky error indicator of the buffer has been
+   set to failed.  */
+static inline bool __attribute_warn_unused_result__
+__printf_buffer_has_failed (struct __printf_buffer *buf)
+{
+  return buf->mode == __printf_buffer_mode_failed;
+}
+
+/* Initialization of a buffer, using the memory region from [BASE, BASE +LEN)
+   as the initial buffer contents.  LEN can be zero.  */
+static inline void
+__printf_buffer_init (struct __printf_buffer *buf, char *base, size_t len,
+                      enum __printf_buffer_mode mode)
+{
+  buf->write_base = base;
+  buf->write_ptr = base;
+  buf->write_end = base + len;
+  buf->written = 0;
+  buf->mode = mode;
+}
+
+/* Called by printf_buffer_putc for a full buffer.  */
+void __printf_buffer_putc_1 (struct __printf_buffer *buf, char ch)
+  attribute_hidden;
+
+/* Writes CH to BUF.  */
+static inline void
+__printf_buffer_putc (struct __printf_buffer *buf, char ch)
+{
+  if (buf->write_ptr != buf->write_end)
+      *buf->write_ptr++ = ch;
+  else
+    __printf_buffer_putc_1 (buf, ch);
+}
+
+/* Writes COUNT repeats of CH to BUF.  */
+void __printf_buffer_pad_1 (struct __printf_buffer *buf,
+                            char ch, size_t count) attribute_hidden;
+
+/* __printf_buffer_pad with fast path for no padding.  COUNT is a
+   long int, to accomodate signed uses in printf and elsewhere.  */
+static inline void
+__printf_buffer_pad (struct __printf_buffer *buf, char ch, long int count)
+{
+  if (count > 0)
+    __printf_buffer_pad_1 (buf, ch, count);
+}
+
+/* Write COUNT bytes starting at S to BUF.  S must not overlap with
+   the internal buffer.  */
+void __printf_buffer_write (struct __printf_buffer *buf, const char *s,
+                            size_t count) attribute_hidden;
+
+/* Write S to BUF.  S must not overlap with the internal buffer.  */
+void __printf_buffer_puts_1 (struct __printf_buffer *buf, const char *s)
+  attribute_hidden;
+static inline void
+__printf_buffer_puts (struct __printf_buffer *buf, const char *s)
+{
+  if (__builtin_constant_p (__builtin_strlen (s)))
+    __printf_buffer_write (buf, s, __builtin_strlen (s));
+  else
+    __printf_buffer_puts_1 (buf, s);
+}
+
+/* Returns the number of bytes written through the buffer, or -1 if
+   there was an error (that is, __printf_buffer_has_failed (BUF) is true).
+
+   The number of written bytes includes pending bytes in the buffer
+   (between BUF->write_base and BUF->write_ptr).
+
+   If the number is larger than INT_MAX, returns -1 and sets errno to
+   EOVERFLOW.  This function does not flush the buffer.  If the caller
+   needs the side effect of flushing, it has to do this
+   separately.  */
+int __printf_buffer_done (struct __printf_buffer *buf) attribute_hidden;
+
+/* Internally used to call the flush function.  This can be called
+   explicitly for certain modes to flush the buffer prematuraly.  In
+   such cases, it is often the case that the buffer mode is statically
+   known, and the flush implementation can be called directly.  */
+bool __printf_buffer_flush (struct __printf_buffer *buf) attribute_hidden;
+
+/* Wide version of struct __printf_buffer follows.  */
+
+enum __wprintf_buffer_mode
+  {
+    __wprintf_buffer_mode_failed,
+    __wprintf_buffer_mode_to_file,
+  };
+
+struct __wprintf_buffer
+{
+  wchar_t *write_base;
+  wchar_t *write_ptr;
+  wchar_t *write_end;
+  uint64_t written;
+  enum __wprintf_buffer_mode mode;
+};
+
+static inline void
+__wprintf_buffer_mark_failed (struct __wprintf_buffer *buf)
+{
+  buf->mode = __wprintf_buffer_mode_failed;
+}
+
+static inline bool __attribute_warn_unused_result__
+__wprintf_buffer_has_failed (struct __wprintf_buffer *buf)
+{
+  return buf->mode == __wprintf_buffer_mode_failed;
+}
+
+static inline void
+__wprintf_buffer_init (struct __wprintf_buffer *buf,
+                       wchar_t *base, size_t len,
+                       enum __wprintf_buffer_mode mode)
+{
+  buf->write_base = base;
+  buf->write_ptr = base;
+  buf->write_end = base + len;
+  buf->written = 0;
+  buf->mode = mode;
+}
+
+void __wprintf_buffer_putc_1 (struct __wprintf_buffer *buf, wchar_t ch)
+  attribute_hidden;
+
+static inline void
+__wprintf_buffer_putc (struct __wprintf_buffer *buf, wchar_t ch)
+{
+  if (buf->write_ptr != buf->write_end)
+      *buf->write_ptr++ = ch;
+  else
+    __wprintf_buffer_putc_1 (buf, ch);
+}
+
+void __wprintf_buffer_pad_1 (struct __wprintf_buffer *buf,
+                             wchar_t ch, size_t count) attribute_hidden;
+
+static inline void
+__wprintf_buffer_pad (struct __wprintf_buffer *buf, char ch, long int count)
+{
+  if (count > 0)
+    __wprintf_buffer_pad_1 (buf, ch, count);
+}
+
+void __wprintf_buffer_write (struct __wprintf_buffer *buf, const wchar_t *s,
+                             size_t count) attribute_hidden;
+
+void __wprintf_buffer_puts (struct __wprintf_buffer *buf, const wchar_t *s)
+  attribute_hidden;
+
+int __wprintf_buffer_done (struct __wprintf_buffer *buf) attribute_hidden;
+
+bool __wprintf_buffer_flush (struct __wprintf_buffer *buf) attribute_hidden;
+
+/* Type-generic convenience macros.  They are useful if
+   printf_buffer-char.h or printf_buffer-wchar_t.h is included as
+   well.  */
+
+#define Xprintf_buffer Xprintf (buffer)
+#define Xprintf_buffer_done Xprintf (buffer_done)
+#define Xprintf_buffer_flush Xprintf (buffer_flush)
+#define Xprintf_buffer_has_failed Xprintf (buffer_has_failed)
+#define Xprintf_buffer_mark_failed Xprintf (buffer_mark_failed)
+#define Xprintf_buffer_pad Xprintf (buffer_pad)
+#define Xprintf_buffer_putc Xprintf (buffer_putc)
+#define Xprintf_buffer_puts Xprintf (buffer_puts)
+#define Xprintf_buffer_write Xprintf (buffer_write)
+
+/* 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.  */
+
+struct __printf_buffer_to_file;
+void __printf_buffer_flush_to_file (struct __printf_buffer_to_file *)
+  attribute_hidden;
+
+struct __wprintf_buffer_to_file;
+void __wprintf_buffer_flush_to_file (struct __wprintf_buffer_to_file *)
+  attribute_hidden;
+
+#endif /* PRINTF_BUFFER_H */
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index da3a3bc0c9..5ba381fe41 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -53,6 +53,14 @@  routines := \
   perror \
   printf \
   printf-prs \
+  printf_buffer_as_file \
+  printf_buffer_done \
+  printf_buffer_flush \
+  printf_buffer_pad_1 \
+  printf_buffer_putc_1 \
+  printf_buffer_puts_1 \
+  printf_buffer_to_file \
+  printf_buffer_write \
   printf_fp \
   printf_fphex \
   printf_size \
@@ -85,6 +93,14 @@  routines := \
   vfwscanf \
   vfwscanf-internal \
   vprintf \
+  wprintf_buffer_as_file \
+  wprintf_buffer_done \
+  wprintf_buffer_flush \
+  wprintf_buffer_pad_1 \
+  wprintf_buffer_putc_1 \
+  wprintf_buffer_puts_1 \
+  wprintf_buffer_to_file \
+  wprintf_buffer_write \
   # routines
 
 aux := \
diff --git a/stdio-common/Xprintf_buffer_done.c b/stdio-common/Xprintf_buffer_done.c
new file mode 100644
index 0000000000..c7551979af
--- /dev/null
+++ b/stdio-common/Xprintf_buffer_done.c
@@ -0,0 +1,40 @@ 
+/* Final status reporting for struct __*printf_buffer.  Generic version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+
+int
+Xprintf_buffer_done (struct Xprintf_buffer *buf)
+{
+  if (Xprintf_buffer_has_failed (buf))
+    return -1;
+
+  /* Use uintptr_t to deal with ptrdiff_t overflows (to some degree).  */
+  uintptr_t written_current = buf->write_ptr - buf->write_base;
+  unsigned int written_total;
+  if (__builtin_add_overflow (buf->written, written_current, &written_total)
+      || buf->written > INT_MAX)
+    {
+      __set_errno (EOVERFLOW);
+      return -1;
+    }
+  else
+    return written_total;
+}
diff --git a/stdio-common/Xprintf_buffer_flush.c b/stdio-common/Xprintf_buffer_flush.c
new file mode 100644
index 0000000000..1368cfe684
--- /dev/null
+++ b/stdio-common/Xprintf_buffer_flush.c
@@ -0,0 +1,72 @@ 
+/* Flush wrapper for struct __*printf_buffer.  Generic version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+
+#include <assert.h>
+#include <stdint.h>
+
+/* Xprintf (buffer_do_flush) (BUF) performs the flush operation.  The
+   actual implementation is specific to the multibyte and wide
+   variants.
+
+   If the flush fails, Xprintf_buffer_mark_failed (BUF) must be
+   called, and BUF->write_ptr and BUF->write_end can be left
+   unchanged.
+
+   The function must not do anything if failure has already occurred,
+   that is, if BUF->mode == Xprintf (buffer_mode_failed).
+
+   The framework implicitly invokes flush with BUF->write_ptr ==
+   BUF->write_end only.  (This is particularly relevant to the
+   __sprintf_chk flush, which just calls __chk_fail.)  But in some
+   cases, Xprintf_buffer_flush may be called explicitly (when
+   BUF->mode/the backing function is known).  In that case, it is
+   possible that BUF->write_ptr < BUF->write_end is true.
+
+   If the flush succeeds, the pointers are changed so that
+   BUF->write_ptr < BUF->write_end.  It is possible to switch to a
+   completely different buffer here.  If the buffer is moved, it may
+   be necessary to updated BUF->write_base and BUF->written from the
+   flush function as well.
+
+   Note that when chaining buffers, in the flush function for the
+   outer buffer (to which data is written first), it is necessary to
+   check for BUF->next->failed (for the inner buffer) and set
+   BUF->base.failed to true (for the outer buffer).  This should come
+   towards the end of the outer flush function.  Usually, there is
+   also some unwrapping step afterwards; it has to check the outer
+   buffer (BUF->base.failed) and propagate any error to the inner
+   buffer (BUF->next->failed), so essentially in the other
+   direction.  */
+static void Xprintf (buffer_do_flush) (struct Xprintf_buffer *buf);
+
+bool
+Xprintf_buffer_flush (struct Xprintf_buffer *buf)
+{
+  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
+    return false;
+
+  Xprintf (buffer_do_flush) (buf);
+  if (Xprintf_buffer_has_failed (buf))
+    return false;
+
+  /* Ensure that the flush has made available some bytes.  */
+  assert (buf->write_ptr != buf->write_end);
+  return true;
+}
diff --git a/stdio-common/Xprintf_buffer_pad_1.c b/stdio-common/Xprintf_buffer_pad_1.c
new file mode 100644
index 0000000000..2be8270ad2
--- /dev/null
+++ b/stdio-common/Xprintf_buffer_pad_1.c
@@ -0,0 +1,44 @@ 
+/* Write repeated characters to struct __*printf_buffer.  Generic version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <string.h>
+
+void
+Xprintf (buffer_pad_1) (struct Xprintf_buffer *buf, CHAR_T ch, size_t count)
+{
+  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
+    return;
+
+  do
+    {
+      /* Proactively make room.  __*printf_buffer_pad has already
+         checked for a zero-length write, so this function is only
+         called when there is actually data to write.  */
+      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
+        return;
+      assert (buf->write_ptr != buf->write_end);
+      size_t to_fill = buf->write_end - buf->write_ptr;
+      if (to_fill > count)
+        to_fill = count;
+      MEMSET (buf->write_ptr, ch, to_fill);
+      buf->write_ptr += to_fill;
+      count -= to_fill;
+    }
+  while (count > 0);
+}
diff --git a/stdio-common/Xprintf_buffer_putc_1.c b/stdio-common/Xprintf_buffer_putc_1.c
new file mode 100644
index 0000000000..3458775a1c
--- /dev/null
+++ b/stdio-common/Xprintf_buffer_putc_1.c
@@ -0,0 +1,29 @@ 
+/* Overflow write function for struct __*printf_buffer.  Generic version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+
+void
+Xprintf (buffer_putc_1) (struct Xprintf_buffer *buf, CHAR_T ch)
+{
+  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf))
+      || !Xprintf_buffer_flush (buf))
+    return;
+  assert (buf->write_ptr < buf->write_end);
+  *buf->write_ptr++ = ch;
+}
diff --git a/stdio-common/Xprintf_buffer_puts_1.c b/stdio-common/Xprintf_buffer_puts_1.c
new file mode 100644
index 0000000000..d8ee7ef47e
--- /dev/null
+++ b/stdio-common/Xprintf_buffer_puts_1.c
@@ -0,0 +1,37 @@ 
+/* String write function for struct __*printf_buffer.  Generic version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <string.h>
+
+void
+Xprintf (buffer_puts_1) (struct Xprintf_buffer *buf, const CHAR_T *s)
+{
+  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
+    return;
+
+  while (*s != 0)
+    {
+      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
+        return;
+      assert (buf->write_ptr != buf->write_end);
+      size_t to_copy = STRNLEN (s, buf->write_end - buf->write_ptr);
+      buf->write_ptr = MEMPCPY (buf->write_ptr, s, to_copy);
+      s += to_copy;
+    }
+}
diff --git a/stdio-common/Xprintf_buffer_write.c b/stdio-common/Xprintf_buffer_write.c
new file mode 100644
index 0000000000..fc6ad5fdd1
--- /dev/null
+++ b/stdio-common/Xprintf_buffer_write.c
@@ -0,0 +1,43 @@ 
+/* Blob write function for struct __*printf_buffer.  Generic version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+
+#include <assert.h>
+#include <string.h>
+
+void
+Xprintf_buffer_write (struct Xprintf_buffer *buf,
+                        const CHAR_T *s, size_t count)
+{
+  if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
+    return;
+
+  while (count > 0)
+    {
+      if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
+        return;
+      assert (buf->write_ptr != buf->write_end);
+      size_t to_copy = buf->write_end - buf->write_ptr;
+      if (to_copy > count)
+        to_copy = count;
+      buf->write_ptr = MEMPCPY (buf->write_ptr, s, to_copy);
+      s += to_copy;
+      count -= to_copy;
+    }
+}
diff --git a/stdio-common/printf_buffer-char.h b/stdio-common/printf_buffer-char.h
new file mode 100644
index 0000000000..88eb326a45
--- /dev/null
+++ b/stdio-common/printf_buffer-char.h
@@ -0,0 +1,24 @@ 
+/* Macros for the multibyte (char) implementation of struct __printf_buffer.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#define Xprintf(n) __printf_##n
+
+#define CHAR_T char
+#define MEMPCPY __mempcpy
+#define MEMSET memset
+#define STRNLEN __strnlen
diff --git a/stdio-common/printf_buffer-wchar_t.h b/stdio-common/printf_buffer-wchar_t.h
new file mode 100644
index 0000000000..7597dc2eab
--- /dev/null
+++ b/stdio-common/printf_buffer-wchar_t.h
@@ -0,0 +1,24 @@ 
+/* Macros for wide (wchar_t) implementation of struct __wprintf_buffer.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#define Xprintf(n) __wprintf_##n
+
+#define CHAR_T wchar_t
+#define MEMPCPY __wmempcpy
+#define MEMSET __wmemset
+#define STRNLEN __wcsnlen
diff --git a/stdio-common/printf_buffer_as_file.c b/stdio-common/printf_buffer_as_file.c
new file mode 100644
index 0000000000..f27b000d78
--- /dev/null
+++ b/stdio-common/printf_buffer_as_file.c
@@ -0,0 +1,148 @@ 
+/* FILE * interface to a struct __printf_buffer.  Multibyte version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer_as_file.h>
+
+#include <assert.h>
+#include <printf_buffer.h>
+
+/* Commit the data directly written through the stdio stream.  */
+static void
+__printf_buffer_as_file_commit (struct __printf_buffer_as_file *file)
+{
+  /* Check that the write pointers in the file stream are consistent
+     with the next buffer.  */
+  assert (file->stream._IO_write_ptr >= file->next->write_ptr);
+  assert (file->stream._IO_write_ptr <= file->next->write_end);
+  assert (file->stream._IO_write_base == file->next->write_base);
+  assert (file->stream._IO_write_end == file->next->write_end);
+
+  file->next->write_ptr = file->stream._IO_write_ptr;
+}
+
+/* Pointer the FILE * write buffer into the active printf_buffer
+   area.  */
+static void
+__printf_buffer_as_file_switch_to_buffer (struct __printf_buffer_as_file *file)
+{
+  file->stream._IO_write_base = file->next->write_base;
+  file->stream._IO_write_ptr = file->next->write_ptr;
+  file->stream._IO_write_end = file->next->write_end;
+}
+
+/* Only a small subset of the vtable functions is implemented here,
+   following _IO_obstack_jumps.  */
+
+static int
+__printf_buffer_as_file_overflow (FILE *fp, int ch)
+{
+  struct __printf_buffer_as_file *file = (struct __printf_buffer_as_file *) fp;
+
+  __printf_buffer_as_file_commit (file);
+
+  /* EOF means only a flush is requested.   */
+  if (ch != EOF)
+    __printf_buffer_putc (file->next, ch);
+
+  /* Ensure that flushing actually produces room.  */
+  if (!__printf_buffer_has_failed (file->next)
+      && file->next->write_ptr == file->next->write_end)
+    __printf_buffer_flush (file->next);
+
+  __printf_buffer_as_file_switch_to_buffer (file);
+
+  if (!__printf_buffer_has_failed (file->next))
+    return (unsigned char) ch;
+  else
+    return EOF;
+}
+
+static size_t
+__printf_buffer_as_file_xsputn (FILE *fp, const void *buf, size_t len)
+{
+  struct __printf_buffer_as_file *file = (struct __printf_buffer_as_file *) fp;
+
+  __printf_buffer_as_file_commit (file);
+
+  /* Copy the data.  */
+  __printf_buffer_write (file->next, buf, len);
+
+  __printf_buffer_as_file_switch_to_buffer (file);
+
+  if (!__printf_buffer_has_failed (file->next))
+    return len;
+  else
+    /* We may actually have written something.  But the stream is
+       corrupted in this case anyway, so try not to divine the write
+       count here.  */
+    return 0;
+}
+
+static const struct _IO_jump_t _IO_printf_buffer_as_file_jumps libio_vtable =
+{
+  JUMP_INIT_DUMMY,
+  JUMP_INIT(finish, NULL),
+  JUMP_INIT(overflow, __printf_buffer_as_file_overflow),
+  JUMP_INIT(underflow, NULL),
+  JUMP_INIT(uflow, NULL),
+  JUMP_INIT(pbackfail, NULL),
+  JUMP_INIT(xsputn, __printf_buffer_as_file_xsputn),
+  JUMP_INIT(xsgetn, NULL),
+  JUMP_INIT(seekoff, NULL),
+  JUMP_INIT(seekpos, NULL),
+  JUMP_INIT(setbuf, NULL),
+  JUMP_INIT(sync, NULL),
+  JUMP_INIT(doallocate, NULL),
+  JUMP_INIT(read, NULL),
+  JUMP_INIT(write, NULL),
+  JUMP_INIT(seek, NULL),
+  JUMP_INIT(close, NULL),
+  JUMP_INIT(stat, NULL),
+  JUMP_INIT(showmanyc, NULL),
+  JUMP_INIT(imbue, NULL)
+};
+
+void
+__printf_buffer_as_file_init (struct __printf_buffer_as_file *file,
+                              struct __printf_buffer *next)
+{
+  file->stream._lock = NULL;
+  _IO_no_init (&file->stream, _IO_USER_LOCK, -1, NULL, NULL);
+  file->vtable = &_IO_printf_buffer_as_file_jumps;
+
+  /* Set up the write buffer from the next buffer.  */
+  file->next = next;
+  __printf_buffer_as_file_switch_to_buffer (file);
+
+  /* Mark the read area as inactive, by making all pointers equal.  */
+  file->stream._IO_read_base = file->stream._IO_write_base;
+  file->stream._IO_read_ptr = file->stream._IO_write_base;
+  file->stream._IO_read_end = file->stream._IO_write_base;
+}
+
+bool
+__printf_buffer_as_file_terminate (struct __printf_buffer_as_file *file)
+{
+  if (file->stream._flags & _IO_ERR_SEEN)
+    return false;
+  else
+    {
+      __printf_buffer_as_file_commit (file);
+      return true;
+    }
+}
diff --git a/stdio-common/printf_buffer_as_file.h b/stdio-common/printf_buffer_as_file.h
new file mode 100644
index 0000000000..ab7fa44144
--- /dev/null
+++ b/stdio-common/printf_buffer_as_file.h
@@ -0,0 +1,87 @@ 
+/* FILE * interface to a struct __*printf_buffer.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+/* Registered printf format specifier callbacks produce data via a
+   FILE * stream.  struct __printf_buffer_as_file enables vfprintf to
+   create a suitable stdio stream.  Likewise struct
+   __wprintf_buffer_as_file for vfwprintf.  */
+
+#ifndef PRINTF_BUFFER_AS_FILE_H
+#define PRINTF_BUFFER_AS_FILE_H
+
+#include <libio/libioP.h>
+
+struct __printf_buffer;
+
+struct __printf_buffer_as_file
+{
+  /* Interface to libio.  */
+  FILE stream;
+  const struct _IO_jump_t *vtable;
+
+  /* Pointer to the underlying buffer.  */
+  struct __printf_buffer *next;
+};
+
+/* Initialization *FP so that data written to its FILE * stream ends
+   up in NEXT.  */
+void __printf_buffer_as_file_init (struct __printf_buffer_as_file *fp,
+                                   struct __printf_buffer *next)
+  attribute_hidden;
+
+/* Returns the FILE * that can be used to write data to the
+   buffer.  */
+static inline FILE *
+__printf_buffer_as_file_get (struct __printf_buffer_as_file *file)
+{
+  return &file->stream;
+}
+
+/* Transfers all pending data from the FILE * to the underlying
+   buffer.  Returns true if there have been no errors. */
+bool __printf_buffer_as_file_terminate (struct __printf_buffer_as_file *)
+  attribute_hidden;
+
+/* Wide variant follows.  */
+
+struct __wprintf_buffer;
+struct __wprintf_buffer_as_file
+{
+  /* Interface to libio.  */
+  FILE stream;
+  const struct _IO_jump_t *vtable;
+  struct _IO_wide_data wide_stream;
+
+  /* Pointer to the underlying buffer.  */
+  struct __wprintf_buffer *next;
+};
+
+void __wprintf_buffer_as_file_init (struct __wprintf_buffer_as_file *fp,
+                                    struct __wprintf_buffer *next)
+  attribute_hidden;
+
+static inline FILE *
+__wprintf_buffer_as_file_get (struct __wprintf_buffer_as_file *file)
+{
+  return &file->stream;
+}
+
+bool __wprintf_buffer_as_file_terminate (struct __wprintf_buffer_as_file *)
+  attribute_hidden;
+
+#endif /* PRINTF_BUFFER_AS_FILE_H */
diff --git a/stdio-common/printf_buffer_done.c b/stdio-common/printf_buffer_done.c
new file mode 100644
index 0000000000..5f3df1daad
--- /dev/null
+++ b/stdio-common/printf_buffer_done.c
@@ -0,0 +1,21 @@ 
+/* Final status reporting for struct __printf_buffer.  Multibyte version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-char.h"
+#include "Xprintf_buffer_done.c"
diff --git a/stdio-common/printf_buffer_flush.c b/stdio-common/printf_buffer_flush.c
new file mode 100644
index 0000000000..9b25c0fde5
--- /dev/null
+++ b/stdio-common/printf_buffer_flush.c
@@ -0,0 +1,42 @@ 
+/* Flush a struct __printf_buffer.  Multibyte version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+
+#include "printf_buffer-char.h"
+#include "Xprintf_buffer_flush.c"
+
+/* The __printf_buffer_flush_* functions are defined together with
+   functions that are pulled in by strong references.  */
+#ifndef SHARED
+# pragma weak __printf_buffer_flush_to_file
+#endif /* !SHARED */
+
+static void
+__printf_buffer_do_flush (struct __printf_buffer *buf)
+{
+  switch (buf->mode)
+    {
+    case __printf_buffer_mode_failed:
+      return;
+    case __printf_buffer_mode_to_file:
+      __printf_buffer_flush_to_file ((struct __printf_buffer_to_file *) buf);
+      return;
+    }
+  __builtin_trap ();
+}
diff --git a/stdio-common/printf_buffer_pad_1.c b/stdio-common/printf_buffer_pad_1.c
new file mode 100644
index 0000000000..c4b9a7af56
--- /dev/null
+++ b/stdio-common/printf_buffer_pad_1.c
@@ -0,0 +1,21 @@ 
+/* Write repeated characters to struct __printf_buffer.  Multibyte version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-char.h"
+#include "Xprintf_buffer_pad_1.c"
diff --git a/stdio-common/printf_buffer_putc_1.c b/stdio-common/printf_buffer_putc_1.c
new file mode 100644
index 0000000000..a15e8d16d1
--- /dev/null
+++ b/stdio-common/printf_buffer_putc_1.c
@@ -0,0 +1,21 @@ 
+/* Overflow character write function for struct __printf_buffer.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-char.h"
+#include "Xprintf_buffer_putc_1.c"
diff --git a/stdio-common/printf_buffer_puts_1.c b/stdio-common/printf_buffer_puts_1.c
new file mode 100644
index 0000000000..ed97fd6b3f
--- /dev/null
+++ b/stdio-common/printf_buffer_puts_1.c
@@ -0,0 +1,21 @@ 
+/* String write function for struct __printf_buffer.  Multibyte version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-char.h"
+#include "Xprintf_buffer_puts_1.c"
diff --git a/stdio-common/printf_buffer_to_file.c b/stdio-common/printf_buffer_to_file.c
new file mode 100644
index 0000000000..6aeb954509
--- /dev/null
+++ b/stdio-common/printf_buffer_to_file.c
@@ -0,0 +1,122 @@ 
+/* Multibyte printf buffers writing data to a FILE * stream.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer_to_file.h>
+
+#include <assert.h>
+#include <array_length.h>
+#include <libio/libioP.h>
+
+/* Switch to the file buffer if possible.  If the file has write_ptr
+   == write_end, use the stage buffer instead.  */
+void
+__printf_buffer_to_file_switch (struct __printf_buffer_to_file *buf)
+{
+  if (buf->fp->_IO_write_ptr < buf->fp->_IO_write_end)
+    {
+      /* buf->fp has a buffer associated with it, so write directly to
+         it from now on.  */
+      buf->base.write_ptr = buf->fp->_IO_write_ptr;
+      buf->base.write_end = buf->fp->_IO_write_end;
+    }
+  else
+    {
+      /* Use the staging area if no buffer is available in buf->fp.  */
+      buf->base.write_ptr = buf->stage;
+      buf->base.write_end = array_end (buf->stage);
+    }
+
+  buf->base.write_base = buf->base.write_ptr;
+}
+
+void
+__printf_buffer_flush_to_file (struct __printf_buffer_to_file *buf)
+{
+  /* The bytes in the buffer are always consumed.  */
+  buf->base.written += buf->base.write_ptr - buf->base.write_base;
+
+  if (buf->base.write_end == array_end (buf->stage))
+    {
+      /* If the stage buffer is used, make a copy into the file.  The
+         stage buffer is always consumed fully, even if just partially
+         written, to ensure that the file stream has all the data.  */
+      size_t count = buf->base.write_ptr - buf->stage;
+      if ((size_t) _IO_sputn (buf->fp, buf->stage, count) != count)
+            {
+              __printf_buffer_mark_failed (&buf->base);
+              return;
+            }
+      /* buf->fp may have a buffer now.  */
+      __printf_buffer_to_file_switch (buf);
+      return;
+    }
+  else if (buf->base.write_end == buf->stage + 1)
+    {
+      /* Special one-character buffer case.  This is used to avoid
+         flush-only overflow below.  */
+      if (buf->base.write_ptr == buf->base.write_end)
+        {
+          if (__overflow (buf->fp, (unsigned char) *buf->stage) == EOF)
+            {
+              __printf_buffer_mark_failed (&buf->base);
+              return;
+            }
+          __printf_buffer_to_file_switch (buf);
+        }
+      /* Else there is nothing to write.  */
+      return;
+    }
+
+  /* We have written directly into the buf->fp buffer.  */
+  assert (buf->base.write_end == buf->fp->_IO_write_end);
+
+  /* Mark the bytes as written.  */
+  buf->fp->_IO_write_ptr = buf->base.write_ptr;
+
+  if (buf->base.write_ptr == buf->base.write_end)
+    {
+      /* The buffer in buf->fp has been filled.  This should just call
+         __overflow (buf->fp, EOF), but flush-only overflow is obscure
+         and not always correctly implemented.  See bug 28949.  Be
+         conservative and switch to a one-character buffer instead, to
+         obtain one more character for a regular __overflow call.  */
+      buf->base.write_ptr = buf->stage;
+      buf->base.write_end = buf->stage + 1;
+    }
+  /* The bytes in the file stream were already marked as written above.  */
+
+  buf->base.write_base = buf->base.write_ptr;
+}
+
+void
+__printf_buffer_to_file_init (struct __printf_buffer_to_file *buf, FILE *fp)
+{
+  __printf_buffer_init (&buf->base, buf->stage, array_length (buf->stage),
+                        __printf_buffer_mode_to_file);
+  buf->fp = fp;
+  __printf_buffer_to_file_switch (buf);
+}
+
+int
+__printf_buffer_to_file_done (struct __printf_buffer_to_file *buf)
+{
+  if (__printf_buffer_has_failed (&buf->base))
+    return -1;
+  __printf_buffer_flush_to_file (buf);
+  return __printf_buffer_done (&buf->base);
+}
diff --git a/stdio-common/printf_buffer_to_file.h b/stdio-common/printf_buffer_to_file.h
new file mode 100644
index 0000000000..97ec1d6d95
--- /dev/null
+++ b/stdio-common/printf_buffer_to_file.h
@@ -0,0 +1,57 @@ 
+/* Multibyte and wide printf buffers writing data to a FILE * stream.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef PRINTF_BUFFER_TO_FILE_H
+#define PRINTF_BUFFER_TO_FILE_H
+
+#include <bits/types/FILE.h>
+#include <printf_buffer.h>
+
+struct __printf_buffer_to_file
+{
+  struct __printf_buffer base;
+  FILE *fp;
+
+  /* Staging buffer.  All data goes through this buffer before
+     reaching the fp stream.  (Any buffer in fp is not yet used.)  */
+  char stage[128];
+};
+
+/* Initializes *BUF to write data to FP.  */
+void __printf_buffer_to_file_init (struct __printf_buffer_to_file *buf,
+                                   FILE *fp) attribute_hidden;
+
+/* Transfers any pending data in BUF to BUF->FP.  The return value
+   follows the printf convention (number bytes written; or -1 for error).  */
+int __printf_buffer_to_file_done (struct __printf_buffer_to_file *buf)
+  attribute_hidden;
+
+/* Wide version below.  */
+
+struct __wprintf_buffer_to_file
+{
+  struct __wprintf_buffer base;
+  FILE *fp;
+  wchar_t stage[128];
+};
+void __wprintf_buffer_to_file_init (struct __wprintf_buffer_to_file *buf,
+                                    FILE *fp) attribute_hidden;
+int __wprintf_buffer_to_file_done (struct __wprintf_buffer_to_file *buf)
+  attribute_hidden;
+
+#endif /* PRINTF_BUFFER_TO_FILE_H */
diff --git a/stdio-common/printf_buffer_write.c b/stdio-common/printf_buffer_write.c
new file mode 100644
index 0000000000..1038f69063
--- /dev/null
+++ b/stdio-common/printf_buffer_write.c
@@ -0,0 +1,21 @@ 
+/* Blob write function for struct __printf_buffer.  Multibyte version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-char.h"
+#include "Xprintf_buffer_write.c"
diff --git a/stdio-common/wprintf_buffer_as_file.c b/stdio-common/wprintf_buffer_as_file.c
new file mode 100644
index 0000000000..cd48a7d42a
--- /dev/null
+++ b/stdio-common/wprintf_buffer_as_file.c
@@ -0,0 +1,153 @@ 
+/* FILE * interface to a struct __wprintf_buffer.  Wide version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer_as_file.h>
+
+#include <assert.h>
+#include <printf_buffer.h>
+
+/* Commit the data directly written through the stdio stream.  */
+static void
+__wprintf_buffer_as_file_commit (struct __wprintf_buffer_as_file *file)
+{
+  /* Check that the write pointers in the file stream are consistent
+     with the next buffer.  */
+  assert (file->wide_stream._IO_write_ptr >= file->next->write_ptr);
+  assert (file->wide_stream._IO_write_ptr <= file->next->write_end);
+  assert (file->wide_stream._IO_write_base == file->next->write_base);
+  assert (file->wide_stream._IO_write_end == file->next->write_end);
+
+  file->next->write_ptr = file->wide_stream._IO_write_ptr;
+}
+
+/* Pointer the FILE * write buffer into the active struct __wprintf_buffer
+   area.  */
+static void
+__wprintf_buffer_as_file_switch_to_buffer (struct __wprintf_buffer_as_file *file)
+{
+  file->wide_stream._IO_write_base = file->next->write_base;
+  file->wide_stream._IO_write_ptr = file->next->write_ptr;
+  file->wide_stream._IO_write_end = file->next->write_end;
+}
+
+/* Only a small subset of the vtable functions is implemented here,
+   following _IO_obstack_jumps.  */
+
+static wint_t
+__wprintf_buffer_as_file_overflow (FILE *fp, int ch)
+{
+  struct __wprintf_buffer_as_file *file
+    = (struct __wprintf_buffer_as_file *) fp;
+
+  __wprintf_buffer_as_file_commit (file);
+
+  /* EOF means only a flush is requested.   */
+  if (ch != WEOF)
+    __wprintf_buffer_putc (file->next, ch);
+  else
+    ch = 0;
+
+  /* Ensure that flushing actually produces room.  */
+  if (!__wprintf_buffer_has_failed (file->next)
+      && file->next->write_ptr == file->next->write_end)
+    __wprintf_buffer_flush (file->next);
+
+  __wprintf_buffer_as_file_switch_to_buffer (file);
+
+  if (!__wprintf_buffer_has_failed (file->next))
+    return (unsigned char) ch;
+  else
+    return WEOF;
+}
+
+static size_t
+__wprintf_buffer_as_file_xsputn (FILE *fp, const void *buf, size_t len)
+{
+  struct __wprintf_buffer_as_file *file
+    = (struct __wprintf_buffer_as_file *) fp;
+
+  __wprintf_buffer_as_file_commit (file);
+
+  /* Copy the data.  */
+  __wprintf_buffer_write (file->next, buf, len);
+
+  __wprintf_buffer_as_file_switch_to_buffer (file);
+
+  if (!__wprintf_buffer_has_failed (file->next))
+    return len;
+  else
+    /* We may actually have written something.  But the stream is
+       corrupted in this case anyway, so try not to divine the write
+       count here.  */
+    return 0;
+}
+
+static const struct _IO_jump_t _IO_wprintf_buffer_as_file_jumps libio_vtable =
+{
+  JUMP_INIT_DUMMY,
+  JUMP_INIT(finish, NULL),
+  JUMP_INIT(overflow, (_IO_overflow_t) __wprintf_buffer_as_file_overflow),
+  JUMP_INIT(underflow, NULL),
+  JUMP_INIT(uflow, NULL),
+  JUMP_INIT(pbackfail, NULL),
+  JUMP_INIT(xsputn, __wprintf_buffer_as_file_xsputn),
+  JUMP_INIT(xsgetn, NULL),
+  JUMP_INIT(seekoff, NULL),
+  JUMP_INIT(seekpos, NULL),
+  JUMP_INIT(setbuf, NULL),
+  JUMP_INIT(sync, NULL),
+  JUMP_INIT(doallocate, NULL),
+  JUMP_INIT(read, NULL),
+  JUMP_INIT(write, NULL),
+  JUMP_INIT(seek, NULL),
+  JUMP_INIT(close, NULL),
+  JUMP_INIT(stat, NULL),
+  JUMP_INIT(showmanyc, NULL),
+  JUMP_INIT(imbue, NULL)
+};
+
+void
+__wprintf_buffer_as_file_init (struct __wprintf_buffer_as_file *file,
+                               struct __wprintf_buffer *next)
+{
+  file->stream._lock = NULL;
+  _IO_no_init (&file->stream, _IO_USER_LOCK, 0, &file->wide_stream,
+               &_IO_wprintf_buffer_as_file_jumps);
+  _IO_fwide (&file->stream, 1);
+
+  /* Set up the write buffer from the next buffer.  */
+  file->next = next;
+  __wprintf_buffer_as_file_switch_to_buffer (file);
+
+  /* Mark the read area as inactive, by making all pointers equal.  */
+  file->stream._IO_read_base = file->stream._IO_write_base;
+  file->stream._IO_read_ptr = file->stream._IO_write_base;
+  file->stream._IO_read_end = file->stream._IO_write_base;
+}
+
+bool
+__wprintf_buffer_as_file_terminate (struct __wprintf_buffer_as_file *file)
+{
+  if (file->stream._flags & _IO_ERR_SEEN)
+    return false;
+  else
+    {
+      __wprintf_buffer_as_file_commit (file);
+      return true;
+    }
+}
diff --git a/stdio-common/wprintf_buffer_done.c b/stdio-common/wprintf_buffer_done.c
new file mode 100644
index 0000000000..60eac03955
--- /dev/null
+++ b/stdio-common/wprintf_buffer_done.c
@@ -0,0 +1,21 @@ 
+/* Final status reporting for struct __wprintf_buffer.  Wide version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-wchar_t.h"
+#include "Xprintf_buffer_done.c"
diff --git a/stdio-common/wprintf_buffer_flush.c b/stdio-common/wprintf_buffer_flush.c
new file mode 100644
index 0000000000..2d91095cca
--- /dev/null
+++ b/stdio-common/wprintf_buffer_flush.c
@@ -0,0 +1,36 @@ 
+/* Flush a struct __wprintf_buffer.  Wide version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+
+#include "printf_buffer-wchar_t.h"
+#include "Xprintf_buffer_flush.c"
+
+static void
+__wprintf_buffer_do_flush (struct __wprintf_buffer *buf)
+{
+  switch (buf->mode)
+    {
+    case __wprintf_buffer_mode_failed:
+      return;
+    case __wprintf_buffer_mode_to_file:
+      __wprintf_buffer_flush_to_file ((struct __wprintf_buffer_to_file *) buf);
+      return;
+    }
+  __builtin_trap ();
+}
diff --git a/stdio-common/wprintf_buffer_pad_1.c b/stdio-common/wprintf_buffer_pad_1.c
new file mode 100644
index 0000000000..9c91126f42
--- /dev/null
+++ b/stdio-common/wprintf_buffer_pad_1.c
@@ -0,0 +1,21 @@ 
+/* Write repeated characters to struct __wprintf_buffer.  Wide version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-wchar_t.h"
+#include "Xprintf_buffer_pad_1.c"
diff --git a/stdio-common/wprintf_buffer_putc_1.c b/stdio-common/wprintf_buffer_putc_1.c
new file mode 100644
index 0000000000..b1af68e721
--- /dev/null
+++ b/stdio-common/wprintf_buffer_putc_1.c
@@ -0,0 +1,21 @@ 
+/* Overflow character write function for struct __wprintf_buffer.  Wide version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-wchar_t.h"
+#include "Xprintf_buffer_putc_1.c"
diff --git a/stdio-common/wprintf_buffer_puts_1.c b/stdio-common/wprintf_buffer_puts_1.c
new file mode 100644
index 0000000000..039aebb56e
--- /dev/null
+++ b/stdio-common/wprintf_buffer_puts_1.c
@@ -0,0 +1,21 @@ 
+/* String write function struct __wprintf_buffer.  Wide version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-wchar_t.h"
+#include "Xprintf_buffer_puts_1.c"
diff --git a/stdio-common/wprintf_buffer_to_file.c b/stdio-common/wprintf_buffer_to_file.c
new file mode 100644
index 0000000000..ac936fab6c
--- /dev/null
+++ b/stdio-common/wprintf_buffer_to_file.c
@@ -0,0 +1,55 @@ 
+/* Wide printf buffers writing data to a FILE *.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+/* This implementation is not optimized (unlikely the multibyte
+   implementation) and always writes to the temporary buffer first.  */
+
+#include <printf_buffer_to_file.h>
+
+#include <array_length.h>
+#include <libio/libioP.h>
+
+void
+__wprintf_buffer_flush_to_file (struct __wprintf_buffer_to_file *buf)
+{
+  size_t count = buf->base.write_ptr - buf->stage;
+  if ((size_t) _IO_sputn (buf->fp, buf->stage, count) != count)
+    {
+      __wprintf_buffer_mark_failed (&buf->base);
+      return;
+    }
+  buf->base.written += count;
+  buf->base.write_ptr = buf->stage;
+}
+
+void
+__wprintf_buffer_to_file_init (struct __wprintf_buffer_to_file *buf, FILE *fp)
+{
+  __wprintf_buffer_init (&buf->base, buf->stage, array_length (buf->stage),
+                         __wprintf_buffer_mode_to_file);
+  buf->fp = fp;
+}
+
+int
+__wprintf_buffer_to_file_done (struct __wprintf_buffer_to_file *buf)
+{
+  if (__wprintf_buffer_has_failed (&buf->base))
+    return -1;
+  __wprintf_buffer_flush_to_file (buf);
+  return __wprintf_buffer_done (&buf->base);
+}
diff --git a/stdio-common/wprintf_buffer_write.c b/stdio-common/wprintf_buffer_write.c
new file mode 100644
index 0000000000..06b53c74c3
--- /dev/null
+++ b/stdio-common/wprintf_buffer_write.c
@@ -0,0 +1,21 @@ 
+/* Blob write function for struct __wprintf_buffer.  Wide version.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <printf_buffer.h>
+#include "printf_buffer-wchar_t.h"
+#include "Xprintf_buffer_write.c"