summary refs log tree commit diff
path: root/stdio-common
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2022-12-19 18:56:54 +0100
committerFlorian Weimer <fweimer@redhat.com>2022-12-19 18:56:54 +0100
commit659fe9fdd14b0772f4e9722b751b9b010665e053 (patch)
tree3098a69345fbd3474154bbba45e8f21de449f266 /stdio-common
parentffde06c915d10c0717a0980508ccb28506c6ec63 (diff)
downloadglibc-659fe9fdd14b0772f4e9722b751b9b010665e053.tar.gz
glibc-659fe9fdd14b0772f4e9722b751b9b010665e053.tar.xz
glibc-659fe9fdd14b0772f4e9722b751b9b010665e053.zip
stdio-common: Introduce buffers for implementing printf
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>
Diffstat (limited to 'stdio-common')
-rw-r--r--stdio-common/Makefile16
-rw-r--r--stdio-common/Xprintf_buffer_done.c40
-rw-r--r--stdio-common/Xprintf_buffer_flush.c72
-rw-r--r--stdio-common/Xprintf_buffer_pad_1.c44
-rw-r--r--stdio-common/Xprintf_buffer_putc_1.c29
-rw-r--r--stdio-common/Xprintf_buffer_puts_1.c38
-rw-r--r--stdio-common/Xprintf_buffer_write.c44
-rw-r--r--stdio-common/printf_buffer-char.h24
-rw-r--r--stdio-common/printf_buffer-wchar_t.h24
-rw-r--r--stdio-common/printf_buffer_as_file.c148
-rw-r--r--stdio-common/printf_buffer_as_file.h87
-rw-r--r--stdio-common/printf_buffer_done.c21
-rw-r--r--stdio-common/printf_buffer_flush.c42
-rw-r--r--stdio-common/printf_buffer_pad_1.c21
-rw-r--r--stdio-common/printf_buffer_putc_1.c21
-rw-r--r--stdio-common/printf_buffer_puts_1.c21
-rw-r--r--stdio-common/printf_buffer_to_file.c122
-rw-r--r--stdio-common/printf_buffer_to_file.h57
-rw-r--r--stdio-common/printf_buffer_write.c21
-rw-r--r--stdio-common/wprintf_buffer_as_file.c153
-rw-r--r--stdio-common/wprintf_buffer_done.c21
-rw-r--r--stdio-common/wprintf_buffer_flush.c36
-rw-r--r--stdio-common/wprintf_buffer_pad_1.c21
-rw-r--r--stdio-common/wprintf_buffer_putc_1.c21
-rw-r--r--stdio-common/wprintf_buffer_puts_1.c21
-rw-r--r--stdio-common/wprintf_buffer_to_file.c55
-rw-r--r--stdio-common/wprintf_buffer_write.c21
27 files changed, 1241 insertions, 0 deletions
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"