/* stdbuf -- setup the standard streams for a command This is the stdbuf utility
Copyright (C) 2009-2018 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 <https://www.gnu.org/licenses/>. */ The GNUv3 license
/* Written by Pádraig Brady. */
#include <config.h> Provides system specific information
#include <stdio.h> Provides standard I/O capability
#include <getopt.h> ...!includes auto-comment...
#include <sys/types.h> Provides system data types
#include <assert.h> ...!includes auto-comment...
#include "system.h" ...!includes auto-comment...
#include "die.h" ...!includes auto-comment...
#include "error.h" ...!includes auto-comment...
#include "filenamecat.h" ...!includes auto-comment...
#include "quote.h" ...!includes auto-comment...
#include "xreadlink.h" ...!includes auto-comment...
#include "xstrtol.h" ...!includes auto-comment...
#include "c-ctype.h" ...!includes auto-comment...
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "stdbuf" Line 35
#define LIB_NAME "libstdbuf.so" /* FIXME: don't hardcode */ Line 36
#define AUTHORS proper_name ("Padraig Brady") Line 38
static char *program_path; Line 40
static struct Line 42
{
size_t size; Line 44
int optc; Line 45
char *optarg; Line 46
} stdbuf[3]; Line 47Block 1
static struct option const longopts[] = Line 49
{
{"input", required_argument, NULL, 'i'}, Line 51
{"output", required_argument, NULL, 'o'}, Line 52
{"error", required_argument, NULL, 'e'}, Line 53
{GETOPT_HELP_OPTION_DECL}, Line 54
{GETOPT_VERSION_OPTION_DECL}, Line 55
{NULL, 0, NULL, 0} Line 56
}; Block 2
/* Set size to the value of STR, interpreted as a decimal integer,
optionally multiplied by various values.
Return -1 on error, 0 on success.
This supports dd BLOCK size suffixes.
Note we don't support dd's b=512, c=1, w=2 or 21x512MiB formats. */
static int Line 65
parse_size (char const *str, size_t *size) Line 66
{
uintmax_t tmp_size; Line 68
enum strtol_error e = xstrtoumax (str, NULL, 10, &tmp_size, "EGkKMPTYZ0"); Line 69
if (e == LONGINT_OK && SIZE_MAX < tmp_size) Line 70
e = LONGINT_OVERFLOW; Line 71
if (e == LONGINT_OK) Line 73
{
errno = 0; Line 75
*size = tmp_size; Line 76
return 0; Line 77
}
errno = (e == LONGINT_OVERFLOW ? EOVERFLOW : errno); Line 80
return -1; Line 81
} Block 3
void Line 84
usage (int status) Line 85
{
if (status != EXIT_SUCCESS) Line 87
emit_try_help (); ...!common auto-comment...
else Line 89
{
printf (_("Usage: %s OPTION... COMMAND\n"), program_name); Line 91
fputs (_("\ Line 92
Run COMMAND, with modified buffering operations for its standard streams.\n\ Line 93
"), stdout); Line 94
emit_mandatory_arg_note (); ...!common auto-comment...
fputs (_("\ Line 98
-i, --input=MODE adjust standard input stream buffering\n\ Line 99
-o, --output=MODE adjust standard output stream buffering\n\ Line 100
-e, --error=MODE adjust standard error stream buffering\n\ Line 101
"), stdout); Line 102
fputs (HELP_OPTION_DESCRIPTION, stdout); Line 103
fputs (VERSION_OPTION_DESCRIPTION, stdout); Line 104
fputs (_("\n\ Line 105
If MODE is 'L' the corresponding stream will be line buffered.\n\ Line 106
This option is invalid with standard input.\n"), stdout); Line 107
fputs (_("\n\ Line 108
If MODE is '0' the corresponding stream will be unbuffered.\n\ Line 109
"), stdout); Line 110
fputs (_("\n\ Line 111
Otherwise MODE is a number which may be followed by one of the following:\n\ Line 112
KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\ Line 113
Binary prefixes can be used, too: KiB=K, MiB=M, and so on.\n\ Line 114
In this case the corresponding stream will be fully buffered with the buffer\n\ Line 115
size set to MODE bytes.\n\ Line 116
"), stdout); Line 117
fputs (_("\n\ Line 118
NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does\n\ Line 119
for example) then that will override corresponding changes by 'stdbuf'.\n\ Line 120
Also some filters (like 'dd' and 'cat' etc.) don't use streams for I/O,\n\ Line 121
and are thus unaffected by 'stdbuf' settings.\n\ Line 122
"), stdout); Line 123
emit_ancillary_info (PROGRAM_NAME); Line 124
}
exit (status); Line 126
} Block 4
/* argv[0] can be anything really, but generally it contains
the path to the executable or just a name if it was executed
using $PATH. In the latter case to get the path we can:
search getenv("PATH"), readlink("/prof/self/exe"), getenv("_"),
dladdr(), pstat_getpathname(), etc. */
static void Line 135
set_program_path (const char *arg) Line 136
{
if (strchr (arg, '/')) /* Use absolute or relative paths directly. */ Line 138
{
program_path = dir_name (arg); Line 140
}
else Line 142
{
char *path = xreadlink ("/proc/self/exe"); Line 144...!syscalls auto-comment......!syscalls auto-comment...
if (path) Line 145
program_path = dir_name (path); Line 146
else if ((path = getenv ("PATH"))) Line 147
{
char *dir; Line 149
path = xstrdup (path); Line 150
for (dir = strtok (path, ":"); dir != NULL; dir = strtok (NULL, ":")) Line 151
{
char *candidate = file_name_concat (dir, arg, NULL); Line 153
if (access (candidate, X_OK) == 0) Line 154
{
program_path = dir_name (candidate); Line 156
free (candidate); Line 157
break; Line 158
}
free (candidate); Line 160
}
}
free (path); Line 163
}
} Block 5
static int Line 167
optc_to_fileno (int c) Line 168
{
int ret = -1; Line 170
switch (c) Line 172
{
case 'e': Line 174
ret = STDERR_FILENO; Line 175
break; Line 176
case 'i': Line 177
ret = STDIN_FILENO; Line 178
break; Line 179
case 'o': Line 180
ret = STDOUT_FILENO; Line 181
break; Line 182
}
return ret; Line 185
} Block 6
static void Line 188
set_LD_PRELOAD (void) Line 189
{
int ret; Line 191
#ifdef __APPLE__ Line 192
char const *preload_env = "DYLD_INSERT_LIBRARIES"; Line 193
#else Line 194
char const *preload_env = "LD_PRELOAD"; Line 195
#endif Line 196
char *old_libs = getenv (preload_env); Line 197
char *LD_PRELOAD; Line 198
/* Note this would auto add the appropriate search path for "libstdbuf.so":
gcc stdbuf.c -Wl,-rpath,'$ORIGIN' -Wl,-rpath,$PKGLIBEXECDIR
However we want the lookup done for the exec'd command not stdbuf.
Since we don't link against libstdbuf.so add it to PKGLIBEXECDIR
rather than to LIBDIR.
Note we could add "" as the penultimate item in the following list
to enable searching for libstdbuf.so in the default system lib paths.
However that would not indicate an error if libstdbuf.so was not found.
Also while this could support auto selecting the right arch in a multilib
environment, what we really want is to auto select based on the arch of the
command being run, rather than that of stdbuf itself. This is currently
not supported due to the unusual need for controlling the stdio buffering
of programs that are a different architecture to the default on the
system (and that of stdbuf itself). */
char const *const search_path[] = { Line 216
program_path, Line 217
PKGLIBEXECDIR, Line 218
NULL Line 219
};
char const *const *path = search_path; Line 222
char *libstdbuf; Line 223
while (true) Line 225
{
struct stat sb; Line 227
if (!**path) /* system default */ Line 229
{
libstdbuf = xstrdup (LIB_NAME); Line 231
break; Line 232
}
ret = asprintf (&libstdbuf, "%s/%s", *path, LIB_NAME); Line 234
if (ret < 0) Line 235
xalloc_die (); ...!common auto-comment...
if (stat (libstdbuf, &sb) == 0) /* file_exists */ Line 237...!syscalls auto-comment...
break; Line 238
free (libstdbuf); Line 239
++path; Line 241
if ( ! *path) Line 242
die (EXIT_CANCELED, 0, _("failed to find %s"), quote (LIB_NAME)); Line 243
}
/* FIXME: Do we need to support libstdbuf.dll, c:, '\' separators etc? */
if (old_libs) Line 248
ret = asprintf (&LD_PRELOAD, "%s=%s:%s", preload_env, old_libs, libstdbuf); Line 249
else Line 250
ret = asprintf (&LD_PRELOAD, "%s=%s", preload_env, libstdbuf); Line 251
if (ret < 0) Line 253
xalloc_die (); ...!common auto-comment...
free (libstdbuf); Line 256
ret = putenv (LD_PRELOAD); Line 258
#ifdef __APPLE__ Line 259
if (ret == 0) Line 260
ret = setenv ("DYLD_FORCE_FLAT_NAMESPACE", "y", 1); Line 261
#endif Line 262
if (ret != 0) Line 264
{
die (EXIT_CANCELED, errno, Line 266
_("failed to update the environment with %s"), Line 267
quote (LD_PRELOAD)); Line 268
}
} Block 7
/* Populate environ with _STDBUF_I=$MODE _STDBUF_O=$MODE _STDBUF_E=$MODE.
Return TRUE if any environment variables set. */
static bool Line 275
set_libstdbuf_options (void) Line 276
{
bool env_set = false; Line 278
for (size_t i = 0; i < ARRAY_CARDINALITY (stdbuf); i++) Line 280
{
if (stdbuf[i].optarg) Line 282
{
char *var; Line 284
int ret; Line 285
if (*stdbuf[i].optarg == 'L') Line 287
ret = asprintf (&var, "%s%c=L", "_STDBUF_", Line 288
toupper (stdbuf[i].optc)); Line 289
else Line 290
ret = asprintf (&var, "%s%c=%" PRIuMAX, "_STDBUF_", Line 291
toupper (stdbuf[i].optc), Line 292
(uintmax_t) stdbuf[i].size); Line 293
if (ret < 0) Line 294
xalloc_die (); ...!common auto-comment...
if (putenv (var) != 0) Line 297
{
die (EXIT_CANCELED, errno, Line 299
_("failed to update the environment with %s"), Line 300
quote (var)); Line 301
}
env_set = true; Line 304
}
}
return env_set; Line 308
} Block 8
int
main (int argc, char **argv) Line 312
{
int c; Line 314
initialize_main (&argc, &argv); VMS-specific entry point handling wildcard expansion
set_program_name (argv[0]); Retains program name and discards path
setlocale (LC_ALL, ""); Sets up internationalization (i18n)
bindtextdomain (PACKAGE, LOCALEDIR); Assigns i18n directorySets text domain for _() [gettext()] function
textdomain (PACKAGE); Sets text domain for _() [gettext()] function
initialize_exit_failure (EXIT_CANCELED); Line 322
atexit (close_stdout); Close stdout on exit (see gnulib)
while ((c = getopt_long (argc, argv, "+i:o:e:", longopts, NULL)) != -1) Line 325
{
int opt_fileno; Line 327
switch (c) Line 329
{
/* Old McDonald had a farm ei... */
case 'e': Line 332
case 'i': Line 333
case 'o': Line 334
opt_fileno = optc_to_fileno (c); Line 335
assert (0 <= opt_fileno && opt_fileno < ARRAY_CARDINALITY (stdbuf)); Line 336
stdbuf[opt_fileno].optc = c; Line 337
while (c_isspace (*optarg)) Line 338
optarg++; Line 339
stdbuf[opt_fileno].optarg = optarg; Line 340
if (c == 'i' && *optarg == 'L') Line 341
{
/* -oL will be by far the most common use of this utility,
but one could easily think -iL might have the same affect,
so disallow it as it could be confusing. */
error (0, 0, _("line buffering stdin is meaningless")); Line 346
usage (EXIT_CANCELED); Line 347
}
if (!STREQ (optarg, "L") Line 350
&& parse_size (optarg, &stdbuf[opt_fileno].size) == -1) Line 351
die (EXIT_CANCELED, errno, _("invalid mode %s"), quote (optarg)); Line 352
break; Line 354
case_GETOPT_HELP_CHAR; Line 356
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); Line 358
default: Line 360
usage (EXIT_CANCELED); Line 361
}
}
argv += optind; Line 365
argc -= optind; Line 366
/* must specify at least 1 command. */
if (argc < 1) Line 369
{
error (0, 0, _("missing operand")); Line 371
usage (EXIT_CANCELED); Line 372
}
if (! set_libstdbuf_options ()) Line 375
{
error (0, 0, _("you must specify a buffering mode option")); Line 377
usage (EXIT_CANCELED); Line 378
}
/* Try to preload libstdbuf first from the same path as
stdbuf is running from. */
set_program_path (program_name); Line 383
if (!program_path) Line 384
program_path = xstrdup (PKGLIBDIR); /* Need to init to non-NULL. */ Line 385
set_LD_PRELOAD (); Line 386
free (program_path); Line 387
execvp (*argv, argv); Line 389
int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE; Line 391
error (0, errno, _("failed to run command %s"), quote (argv[0])); Line 392
return exit_status; Line 393
} Block 9