aboutsummaryrefslogtreecommitdiff
path: root/stdio-common/printf_buffer_to_file.c
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/printf_buffer_to_file.c
parentffde06c915d10c0717a0980508ccb28506c6ec63 (diff)
downloadglibc-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/printf_buffer_to_file.c')
-rw-r--r--stdio-common/printf_buffer_to_file.c122
1 files changed, 122 insertions, 0 deletions
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);
+}