/* libstdbuf -- a shared lib to preload to setup stdio buffering for a command
Copyright (C) 2009-2025 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
/* Written by Pádraig Brady. LD_PRELOAD idea from Brian Dessent. */
#include
#include
#include
#include
#include "gettext.h"
#define _(msgid) gettext (msgid)
/* Deactivate config.h's "rpl_"-prefixed definitions, since we don't
link gnulib here, and the replacements aren't needed. */
#undef fprintf
#undef free
#undef malloc
#undef strtoul
/* Note currently for glibc (2.3.5) the following call does not change
the buffer size, and more problematically does not give any indication
that the new size request was ignored:
setvbuf (stdout, nullptr, _IOFBF, 8192);
The ISO C99 standard section 7.19.5.6 on the setvbuf function says:
... If buf is not a null pointer, the array it points to _may_ be used
instead of a buffer allocated by the setvbuf function and the argument
size specifies the size of the array; otherwise, size _may_ determine
the size of a buffer allocated by the setvbuf function. ...
Obviously some interpret the above to mean setvbuf (..., size)
is only a hint from the application which I don't agree with.
FreeBSD's libc seems more sensible in this regard. From the man page:
The size argument may be given as zero to obtain deferred optimal-size
buffer allocation as usual. If it is not zero, then except for
unbuffered files, the buf argument should point to a buffer at least size
bytes long; this buffer will be used instead of the current buffer. (If
the size argument is not zero but buf is null, a buffer of the given size
will be allocated immediately, and released on close. This is an extension
to ANSI C; portable code should use a size of 0 with any null buffer.)
--------------------
Another issue is that on glibc-2.7 the following doesn't buffer
the first write if it's greater than 1 byte.
setvbuf (stdout, buf, _IOFBF, 127);
Now the POSIX standard says that "allocating a buffer of size bytes does
not necessarily imply that all of size bytes are used for the buffer area".
However I think it's just a buggy implementation due to the various
inconsistencies with write sizes and subsequent writes. */
static void
apply_mode (FILE *stream, char const *stream_name, char const *envvar)
{
char *buf = nullptr;
int setvbuf_mode;
unsigned long int size = 0;
char const *mode = getenv (envvar);
if (!mode)
return;
if (*mode == '0')
setvbuf_mode = _IONBF;
else if (*mode == 'L')
setvbuf_mode = _IOLBF; /* FIXME: should we allow 1ML */
else
{
setvbuf_mode = _IOFBF;
char *mode_end;
size = strtoul (mode, &mode_end, 10);
if (size == 0 || *mode_end)
{
fprintf (stderr, _("invalid buffering mode %s for %s\n"),
mode, stream_name);
return;
}
/* If strtoul might have overflowed or if the size is more than
half of size_t range, treat it as an allocation failure.
Huge sizes can cause problems with some stdio implementations. */
buf = (size <= ((unsigned long int) -2 < (size_t) -1 / 2
? (unsigned long int) -2 : (size_t) -1 / 2)
? malloc (size) : nullptr);
if (!buf)
{
/* We could defer the allocation to libc, however since
glibc currently ignores the combination of null buffer
with non zero size, we'll fail here. */
fprintf (stderr,
_("failed to allocate a %lu byte stdio buffer\n"),
size);
return;
}
/* buf will be freed by fclose. */
}
if (setvbuf (stream, buf, setvbuf_mode, size) != 0)
{
fprintf (stderr, _("could not set buffering of %s to mode %s\n"),
stream_name, mode);
free (buf);
}
}
/* Use __attribute to avoid elision of __attribute__ on SUNPRO_C etc. */
static void __attribute ((constructor))
stdbuf (void)
{
/* Do first so can write errors to stderr. */
apply_mode (stderr, "stderr", "_STDBUF_E");
apply_mode (stdin, "stdin", "_STDBUF_I");
apply_mode (stdout, "stdout", "_STDBUF_O");
}