[v6,02/11] stdio-common: Introduce buffers for implementing printf

Message ID 9994e8bfc24aa00576d230451e1552318397bef0.1671221440.git.fweimer@redhat.com
State Committed
Commit 659fe9fdd14b0772f4e9722b751b9b010665e053
Headers
Series vfprintf refactor |

Checks

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

Commit Message

Florian Weimer Dec. 16, 2022, 8:15 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.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
---
 elf/Makefile                          |   2 +
 include/printf_buffer.h               | 291 ++++++++++++++++++++++++++
 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  |  38 ++++
 stdio-common/Xprintf_buffer_write.c   |  44 ++++
 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 ++
 29 files changed, 1534 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

Noah Goldstein Dec. 16, 2022, 8:41 p.m. UTC | #1
On Fri, Dec 16, 2022 at 12:18 PM Florian Weimer via Libc-alpha
<libc-alpha@sourceware.org> 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.
>
> Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
> ---
>  elf/Makefile                          |   2 +
>  include/printf_buffer.h               | 291 ++++++++++++++++++++++++++
>  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  |  38 ++++
>  stdio-common/Xprintf_buffer_write.c   |  44 ++++
>  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 ++
>  29 files changed, 1534 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/elf/Makefile b/elf/Makefile
> index eca7b28ab5..0bfaffbd42 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -602,6 +602,7 @@ $(objpfx)tst-relro-libc.out: tst-relro-symbols.py $(..)/scripts/glibcelf.py \
>             --required=_IO_helper_jumps \
>             --required=_IO_mem_jumps \
>             --required=_IO_obstack_jumps \
> +           --required=_IO_printf_buffer_as_file_jumps \
>             --required=_IO_proc_jumps \
>             --required=_IO_str_chk_jumps \
>             --required=_IO_str_jumps \
> @@ -610,6 +611,7 @@ $(objpfx)tst-relro-libc.out: tst-relro-symbols.py $(..)/scripts/glibcelf.py \
>             --required=_IO_wfile_jumps_maybe_mmap \
>             --required=_IO_wfile_jumps_mmap \
>             --required=_IO_wmem_jumps \
> +           --required=_IO_wprintf_buffer_as_file_jumps \
>             --required=_IO_wstr_jumps \
>             --required=_IO_wstrn_jumps \
>             --optional=_IO_old_cookie_jumps \
> diff --git a/include/printf_buffer.h b/include/printf_buffer.h
> new file mode 100644
> index 0000000000..e27f2a899c
> --- /dev/null
> +++ b/include/printf_buffer.h
> @@ -0,0 +1,291 @@
> +/* 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 <sys/types.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 includes 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
> +   ssize_t to accomodate signed uses in printf and elsewhere.  */
> +static inline void
> +__printf_buffer_pad (struct __printf_buffer *buf, char ch, ssize_t 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, ssize_t 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;
> +
> +/* Buffer sizes.  These can be tuned as necessary.  There is a tension
> +   here between stack consumption, cache usage, and additional system
> +   calls or heap allocations (if the buffer is too small).  */
> +
> +/* Fallback buffer if the underlying FILE * stream does not provide
> +   buffer space.  */
> +#define PRINTF_BUFFER_SIZE_TO_FILE_STAGE 128
> +
> +#endif /* PRINTF_BUFFER_H */
> diff --git a/stdio-common/Makefile b/stdio-common/Makefile
> index 8f2524959d..120d66ea93 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..a3f51c43ef
> --- /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 <intprops.h>
> +#include <stdint.h>
> +
> +int
> +Xprintf_buffer_done (struct Xprintf_buffer *buf)
> +{
> +  if (Xprintf_buffer_has_failed (buf))
> +    return -1;
> +
> +  /* 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;
> +  int written_total;
> +  if (INT_ADD_WRAPV (buf->written, written_current, &written_total))
> +    {
> +      __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..88d0b8b2bd
> --- /dev/null
> +++ b/stdio-common/Xprintf_buffer_puts_1.c
> @@ -0,0 +1,38 @@
> +/* 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);
> +      MEMCPY (buf->write_ptr, s, to_copy);
> +      buf->write_ptr += 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..9db2b5066a
> --- /dev/null
> +++ b/stdio-common/Xprintf_buffer_write.c
> @@ -0,0 +1,44 @@
> +/* 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;
> +      MEMCPY (buf->write_ptr, s, to_copy);
> +      buf->write_ptr += 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..44264e24c0
> --- /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 MEMCPY memcpy
> +#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..d8d0c5da46
> --- /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 MEMCPY __wmemcpy
> +#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..8ac707f2d0
> --- /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..3576771f70
> --- /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)
> +    {

Think maybe this should have a minimum bound so we don't end up
in a situation where the strnlen / memcpy loop is only doing 1/2
bytes at a time.
Probably something like 32/64 would make sense.
> +      /* 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..7e597fd992
> --- /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.  Used if fp does not have any available buffer
> +     space.  */
> +  char stage[PRINTF_BUFFER_SIZE_TO_FILE_STAGE];
> +};
> +
> +/* 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[PRINTF_BUFFER_SIZE_TO_FILE_STAGE];
> +};
> +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"
> --
> 2.38.1
>
>
  
Florian Weimer Dec. 16, 2022, 8:59 p.m. UTC | #2
* Noah Goldstein:

>> +/* 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)
>> +    {
>
> Think maybe this should have a minimum bound so we don't end up
> in a situation where the strnlen / memcpy loop is only doing 1/2
> bytes at a time.
> Probably something like 32/64 would make sense.

I think this would only matter with setvbuf and a small buffer.  We can
use half of the staging buffer size (PRINTF_BUFFER_SIZE_TO_FILE_STAGE),
that would be your 64 number.

Thanks,
Florian
  
Noah Goldstein Dec. 16, 2022, 9:14 p.m. UTC | #3
On Fri, Dec 16, 2022 at 12:59 PM Florian Weimer <fweimer@redhat.com> wrote:
>
> * Noah Goldstein:
>
> >> +/* 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)
> >> +    {
> >
> > Think maybe this should have a minimum bound so we don't end up
> > in a situation where the strnlen / memcpy loop is only doing 1/2
> > bytes at a time.
> > Probably something like 32/64 would make sense.
>
> I think this would only matter with setvbuf and a small buffer.  We can
> use half of the staging buffer size (PRINTF_BUFFER_SIZE_TO_FILE_STAGE),
> that would be your 64 number.

I don't follow, is it impossible for `_IO_write_ptr < _IO_write_end` and for
the buffer to be less than 64?

Otherwise I it seems `write_ptr` will be set to `_IO_write_ptr` and
`write_end` to `_IO_write_end` so the loop:
```
+      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);
+      MEMCPY (buf->write_ptr, s, to_copy);
+      buf->write_ptr += to_copy;
+      s += to_copy;
```

Will be doing 1/2 bytes at a time.

>
> Thanks,
> Florian
>
  
Florian Weimer Dec. 16, 2022, 10:32 p.m. UTC | #4
* Noah Goldstein:

> On Fri, Dec 16, 2022 at 12:59 PM Florian Weimer <fweimer@redhat.com> wrote:
>>
>> * Noah Goldstein:
>>
>> >> +/* 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)
>> >> +    {
>> >
>> > Think maybe this should have a minimum bound so we don't end up
>> > in a situation where the strnlen / memcpy loop is only doing 1/2
>> > bytes at a time.
>> > Probably something like 32/64 would make sense.
>>
>> I think this would only matter with setvbuf and a small buffer.  We can
>> use half of the staging buffer size (PRINTF_BUFFER_SIZE_TO_FILE_STAGE),
>> that would be your 64 number.
>
> I don't follow, is it impossible for `_IO_write_ptr < _IO_write_end` and for
> the buffer to be less than 64?

Typical buffers are 8192 bytes large, I believe, but when printf is
called, an arbitrary amount can already be consumed.

> Otherwise I it seems `write_ptr` will be set to `_IO_write_ptr` and
> `write_end` to `_IO_write_end` so the loop:
> ```
> +      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);
> +      MEMCPY (buf->write_ptr, s, to_copy);
> +      buf->write_ptr += to_copy;
> +      s += to_copy;
> ```
>
> Will be doing 1/2 bytes at a time.

There are two buffers here: the staging buffer, and the (usually larger,
but optional) FILE * buffer.  If there is no FILE * buffer (yet), we
must use the staging buffer.  But we can instruct printf to write
directly to the FILE * buffer if it is there.  Once the buffer is full,
we need to use the staging buffer for a bit, but flushing that into the
FILE * buffer will most likely make the FILE * buffer available again.

The core issue here is that there is no real way to flush the FILE *
once it's full.  We always have to write a character:

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

This is basically a backwards-compatibility kludge for certain C++
programs compiled with GCC 2.95.

Thanks,
Florian
  
Noah Goldstein Dec. 17, 2022, 12:32 a.m. UTC | #5
On Fri, Dec 16, 2022 at 2:32 PM Florian Weimer <fweimer@redhat.com> wrote:
>
> * Noah Goldstein:
>
> > On Fri, Dec 16, 2022 at 12:59 PM Florian Weimer <fweimer@redhat.com> wrote:
> >>
> >> * Noah Goldstein:
> >>
> >> >> +/* 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)
> >> >> +    {
> >> >
> >> > Think maybe this should have a minimum bound so we don't end up
> >> > in a situation where the strnlen / memcpy loop is only doing 1/2
> >> > bytes at a time.
> >> > Probably something like 32/64 would make sense.
> >>
> >> I think this would only matter with setvbuf and a small buffer.  We can
> >> use half of the staging buffer size (PRINTF_BUFFER_SIZE_TO_FILE_STAGE),
> >> that would be your 64 number.
> >
> > I don't follow, is it impossible for `_IO_write_ptr < _IO_write_end` and for
> > the buffer to be less than 64?
>
> Typical buffers are 8192 bytes large, I believe, but when printf is
> called, an arbitrary amount can already be consumed.
>
> > Otherwise I it seems `write_ptr` will be set to `_IO_write_ptr` and
> > `write_end` to `_IO_write_end` so the loop:
> > ```
> > +      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);
> > +      MEMCPY (buf->write_ptr, s, to_copy);
> > +      buf->write_ptr += to_copy;
> > +      s += to_copy;
> > ```
> >
> > Will be doing 1/2 bytes at a time.
>
> There are two buffers here: the staging buffer, and the (usually larger,
> but optional) FILE * buffer.  If there is no FILE * buffer (yet), we
> must use the staging buffer.  But we can instruct printf to write
> directly to the FILE * buffer if it is there.  Once the buffer is full,
> we need to use the staging buffer for a bit, but flushing that into the
> FILE * buffer will most likely make the FILE * buffer available again.

Si,

I guess my point is maybe better to use the staging buffer -> IO buffer
if there is only minimal room left in the FILE * buffer.

But thinking more on it, it doesn't really make sense.
>
> The core issue here is that there is no real way to flush the FILE *
> once it's full.  We always have to write a character:
>
>       /* 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.  */
>
> This is basically a backwards-compatibility kludge for certain C++
> programs compiled with GCC 2.95.
>
> Thanks,
> Florian
>
  
Florian Weimer Dec. 17, 2022, 12:55 p.m. UTC | #6
* Noah Goldstein:

>> There are two buffers here: the staging buffer, and the (usually larger,
>> but optional) FILE * buffer.  If there is no FILE * buffer (yet), we
>> must use the staging buffer.  But we can instruct printf to write
>> directly to the FILE * buffer if it is there.  Once the buffer is full,
>> we need to use the staging buffer for a bit, but flushing that into the
>> FILE * buffer will most likely make the FILE * buffer available again.

> I guess my point is maybe better to use the staging buffer -> IO buffer
> if there is only minimal room left in the FILE * buffer.

But the libio machinery behind the _IO_sputn call still needs to split
the staging buffer when transfering it into the FILE * buffer, so it may
be more beneficial to write it directly there.

> But thinking more on it, it doesn't really make sense.

Right, I'll leave it as is for now.  If we can fix the input-less flush
case in libio, I think it's more clearly beneficial to write to the
FILE * buffer directly as long it exists.

Thanks,
Florian
  

Patch

diff --git a/elf/Makefile b/elf/Makefile
index eca7b28ab5..0bfaffbd42 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -602,6 +602,7 @@  $(objpfx)tst-relro-libc.out: tst-relro-symbols.py $(..)/scripts/glibcelf.py \
 	    --required=_IO_helper_jumps \
 	    --required=_IO_mem_jumps \
 	    --required=_IO_obstack_jumps \
+	    --required=_IO_printf_buffer_as_file_jumps \
 	    --required=_IO_proc_jumps \
 	    --required=_IO_str_chk_jumps \
 	    --required=_IO_str_jumps \
@@ -610,6 +611,7 @@  $(objpfx)tst-relro-libc.out: tst-relro-symbols.py $(..)/scripts/glibcelf.py \
 	    --required=_IO_wfile_jumps_maybe_mmap \
 	    --required=_IO_wfile_jumps_mmap \
 	    --required=_IO_wmem_jumps \
+	    --required=_IO_wprintf_buffer_as_file_jumps \
 	    --required=_IO_wstr_jumps \
 	    --required=_IO_wstrn_jumps \
 	    --optional=_IO_old_cookie_jumps \
diff --git a/include/printf_buffer.h b/include/printf_buffer.h
new file mode 100644
index 0000000000..e27f2a899c
--- /dev/null
+++ b/include/printf_buffer.h
@@ -0,0 +1,291 @@ 
+/* 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 <sys/types.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 includes 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
+   ssize_t to accomodate signed uses in printf and elsewhere.  */
+static inline void
+__printf_buffer_pad (struct __printf_buffer *buf, char ch, ssize_t 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, ssize_t 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;
+
+/* Buffer sizes.  These can be tuned as necessary.  There is a tension
+   here between stack consumption, cache usage, and additional system
+   calls or heap allocations (if the buffer is too small).  */
+
+/* Fallback buffer if the underlying FILE * stream does not provide
+   buffer space.  */
+#define PRINTF_BUFFER_SIZE_TO_FILE_STAGE 128
+
+#endif /* PRINTF_BUFFER_H */
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 8f2524959d..120d66ea93 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..a3f51c43ef
--- /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 <intprops.h>
+#include <stdint.h>
+
+int
+Xprintf_buffer_done (struct Xprintf_buffer *buf)
+{
+  if (Xprintf_buffer_has_failed (buf))
+    return -1;
+
+  /* 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;
+  int written_total;
+  if (INT_ADD_WRAPV (buf->written, written_current, &written_total))
+    {
+      __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..88d0b8b2bd
--- /dev/null
+++ b/stdio-common/Xprintf_buffer_puts_1.c
@@ -0,0 +1,38 @@ 
+/* 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);
+      MEMCPY (buf->write_ptr, s, to_copy);
+      buf->write_ptr += 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..9db2b5066a
--- /dev/null
+++ b/stdio-common/Xprintf_buffer_write.c
@@ -0,0 +1,44 @@ 
+/* 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;
+      MEMCPY (buf->write_ptr, s, to_copy);
+      buf->write_ptr += 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..44264e24c0
--- /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 MEMCPY memcpy
+#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..d8d0c5da46
--- /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 MEMCPY __wmemcpy
+#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..8ac707f2d0
--- /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..3576771f70
--- /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..7e597fd992
--- /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.  Used if fp does not have any available buffer
+     space.  */
+  char stage[PRINTF_BUFFER_SIZE_TO_FILE_STAGE];
+};
+
+/* 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[PRINTF_BUFFER_SIZE_TO_FILE_STAGE];
+};
+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"