1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
/* libstdbuf -- a shared lib to preload to setup stdio buffering for a command
Copyright (C) 2009-2010 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 <http://www.gnu.org/licenses/>. */
/* Written by Pádraig Brady. LD_PRELOAD idea from Brian Dessent. */
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include "system.h"
#include "verify.h"
/* Note currently for glibc (2.3.5) the following call does not change the
the buffer size, and more problematically does not give any indication
that the new size request was ignored:
setvbuf (stdout, (char*)NULL, _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 const char *
fileno_to_name (const int fd)
{
const char *ret = NULL;
switch (fd)
{
case 0:
ret = "stdin";
break;
case 1:
ret = "stdout";
break;
case 2:
ret = "stderr";
break;
default:
ret = "unknown";
break;
}
return ret;
}
static void
apply_mode (FILE *stream, const char *mode)
{
char *buf = NULL;
int setvbuf_mode;
size_t size = 0;
if (*mode == '0')
setvbuf_mode = _IONBF;
else if (*mode == 'L')
setvbuf_mode = _IOLBF; /* FIXME: should we allow 1ML */
else
{
setvbuf_mode = _IOFBF;
verify (SIZE_MAX <= ULONG_MAX);
size = strtoul (mode, NULL, 10);
if (size > 0)
{
if (!(buf = malloc (size))) /* will be freed by fclose() */
{
/* 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 %" PRIuMAX
" byte stdio buffer\n"), (uintmax_t) size);
return;
}
}
else
{
fprintf (stderr, _("invalid buffering mode %s for %s\n"),
mode, fileno_to_name (fileno (stream)));
return;
}
}
if (setvbuf (stream, buf, setvbuf_mode, size) != 0)
{
fprintf (stderr, _("could not set buffering of %s to mode %s\n"),
fileno_to_name (fileno (stream)), mode);
free (buf);
}
}
__attribute__ ((constructor)) static void
stdbuf (void)
{
char *e_mode = getenv ("_STDBUF_E");
char *i_mode = getenv ("_STDBUF_I");
char *o_mode = getenv ("_STDBUF_O");
if (e_mode) /* Do first so can write errors to stderr */
apply_mode (stderr, e_mode);
if (i_mode)
apply_mode (stdin, i_mode);
if (o_mode)
apply_mode (stdout, o_mode);
}
|