public inbox for glibc-cvs@sourceware.org
help / color / mirror / Atom feed
* [glibc] stdio-common: Introduce buffers for implementing printf
@ 2022-12-19 18:56 Florian Weimer
  0 siblings, 0 replies; only message in thread
From: Florian Weimer @ 2022-12-19 18:56 UTC (permalink / raw)
  To: glibc-cvs

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=659fe9fdd14b0772f4e9722b751b9b010665e053

commit 659fe9fdd14b0772f4e9722b751b9b010665e053
Author: Florian Weimer <fweimer@redhat.com>
Date:   Mon Dec 19 18:56:54 2022 +0100

    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>

Diff:
---
 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(+)

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"

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-12-19 18:56 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-12-19 18:56 [glibc] stdio-common: Introduce buffers for implementing printf Florian Weimer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).