/* tee - read from standard input and write to standard output and files. This is the tee utility
Copyright (C) 1985-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
/* Mike Parker, Richard M. Stallman, and David MacKenzie */
#include <config.h> Provides system specific information
#include <sys/types.h> Provides system data types
#include <signal.h> ...!includes auto-comment...
#include <getopt.h> ...!includes auto-comment...
#include "system.h" ...!includes auto-comment...
#include "argmatch.h" ...!includes auto-comment...
#include "die.h" ...!includes auto-comment...
#include "error.h" ...!includes auto-comment...
#include "fadvise.h" ...!includes auto-comment...
#include "stdio--.h" ...!includes auto-comment...
#include "xbinary-io.h" ...!includes auto-comment...
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "tee" Line 33
#define AUTHORS \ Line 35
proper_name ("Mike Parker"), \ Line 36
proper_name ("Richard M. Stallman"), \ Line 37
proper_name ("David MacKenzie") Line 38
static bool tee_files (int nfiles, char **files); Line 40
/* If true, append to output files rather than truncating them. */
static bool append; Line 43
/* If true, ignore interrupts. */
static bool ignore_interrupts; Line 46
enum output_error Line 48
{
output_error_sigpipe, /* traditional behavior, sigpipe enabled. */ Line 50
output_error_warn, /* warn on EPIPE, but continue. */ Line 51
output_error_warn_nopipe, /* ignore EPIPE, continue. */ Line 52
output_error_exit, /* exit on any output error. */ Line 53
output_error_exit_nopipe /* exit on any output error except EPIPE. */ Line 54
}; Block 1
static enum output_error output_error; Line 57
static struct option const long_options[] = Line 59
{
{"append", no_argument, NULL, 'a'}, Line 61
{"ignore-interrupts", no_argument, NULL, 'i'}, Line 62
{"output-error", optional_argument, NULL, 'p'}, Line 63
{GETOPT_HELP_OPTION_DECL}, Line 64
{GETOPT_VERSION_OPTION_DECL}, Line 65
{NULL, 0, NULL, 0} Line 66
}; Block 2
static char const *const output_error_args[] = Line 69
{
"warn", "warn-nopipe", "exit", "exit-nopipe", NULL Line 71
}; Block 3
static enum output_error const output_error_types[] = Line 73
{
output_error_warn, output_error_warn_nopipe, Line 75
output_error_exit, output_error_exit_nopipe Line 76
}; Block 4
ARGMATCH_VERIFY (output_error_args, output_error_types); Line 78
void Line 80
usage (int status) Line 81
{
if (status != EXIT_SUCCESS) Line 83
emit_try_help (); ...!common auto-comment...
else Line 85
{
printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name); Line 87
fputs (_("\ Line 88
Copy standard input to each FILE, and also to standard output.\n\ Line 89
\n\
-a, --append append to the given FILEs, do not overwrite\n\ Line 91
-i, --ignore-interrupts ignore interrupt signals\n\ Line 92
"), stdout); Line 93
fputs (_("\ Line 94
-p diagnose errors writing to non pipes\n\ Line 95
--output-error[=MODE] set behavior on write error. See MODE below\n\ Line 96
"), stdout); Line 97
fputs (HELP_OPTION_DESCRIPTION, stdout); Line 98
fputs (VERSION_OPTION_DESCRIPTION, stdout); Line 99
fputs (_("\ Line 100
\n\
MODE determines behavior with write errors on the outputs:\n\ Line 102
'warn' diagnose errors writing to any output\n\ Line 103
'warn-nopipe' diagnose errors writing to any output not a pipe\n\ Line 104
'exit' exit on error writing to any output\n\ Line 105
'exit-nopipe' exit on error writing to any output not a pipe\n\ Line 106
The default MODE for the -p option is 'warn-nopipe'.\n\ Line 107
The default operation when --output-error is not specified, is to\n\ Line 108
exit immediately on error writing to a pipe, and diagnose errors\n\ Line 109
writing to non pipe outputs.\n\ Line 110
"), stdout); Line 111
emit_ancillary_info (PROGRAM_NAME); Line 112
}
exit (status); Line 114
} Block 5
int
main (int argc, char **argv) Line 118
{
bool ok; Line 120
int optc; Line 121
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
atexit (close_stdout); Close stdout on exit (see gnulib)
append = false; Line 131
ignore_interrupts = false; Line 132
while ((optc = getopt_long (argc, argv, "aip", long_options, NULL)) != -1) Line 134
{
switch (optc) Line 136
{
case 'a': Line 138
append = true; Line 139
break; Line 140
case 'i': Line 142
ignore_interrupts = true; Line 143
break; Line 144
case 'p': Line 146
if (optarg) Line 147
output_error = XARGMATCH ("--output-error", optarg, Line 148
output_error_args, output_error_types); Line 149
else Line 150
output_error = output_error_warn_nopipe; Line 151
break; Line 152
case_GETOPT_HELP_CHAR; Line 154
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); Line 156
default: Line 158
usage (EXIT_FAILURE); Line 159
}
}
if (ignore_interrupts) Line 163
signal (SIGINT, SIG_IGN); Line 164
if (output_error != output_error_sigpipe) Line 166
signal (SIGPIPE, SIG_IGN); Line 167
/* Do *not* warn if tee is given no file arguments.
POSIX requires that it work when given no arguments. */
ok = tee_files (argc - optind, &argv[optind]); Line 172
if (close (STDIN_FILENO) != 0) Line 173...!syscalls auto-comment...
die (EXIT_FAILURE, errno, "%s", _("standard input")); Line 174
return ok ? EXIT_SUCCESS : EXIT_FAILURE; Line 176
} Block 6
/* Copy the standard input into each of the NFILES files in FILES
and into the standard output. As a side effect, modify FILES[-1].
Return true if successful. */
static bool Line 183
tee_files (int nfiles, char **files) Line 184
{
size_t n_outputs = 0; Line 186
FILE **descriptors; Line 187
char buffer[BUFSIZ]; Line 188
ssize_t bytes_read = 0; Line 189
int i; Line 190
bool ok = true; Line 191
char const *mode_string = Line 192
(O_BINARY Line 193
? (append ? "ab" : "wb") Line 194
: (append ? "a" : "w")); Line 195
xset_binary_mode (STDIN_FILENO, O_BINARY); Line 197
xset_binary_mode (STDOUT_FILENO, O_BINARY); Line 198
fadvise (stdin, FADVISE_SEQUENTIAL); Line 199...!syscalls auto-comment...
/* Set up FILES[0 .. NFILES] and DESCRIPTORS[0 .. NFILES].
In both arrays, entry 0 corresponds to standard output. */
descriptors = xnmalloc (nfiles + 1, sizeof *descriptors); Line 204
files--; Line 205
descriptors[0] = stdout; Line 206
files[0] = bad_cast (_("standard output")); Line 207
setvbuf (stdout, NULL, _IONBF, 0); Line 208
n_outputs++; Line 209
for (i = 1; i <= nfiles; i++) Line 211
{
/* Do not treat "-" specially - as mandated by POSIX. */
descriptors[i] = fopen (files[i], mode_string); Line 214...!syscalls auto-comment...
if (descriptors[i] == NULL) Line 215
{
error (output_error == output_error_exit Line 217
|| output_error == output_error_exit_nopipe, Line 218
errno, "%s", quotef (files[i])); Line 219
ok = false; Line 220
}
else Line 222
{
setvbuf (descriptors[i], NULL, _IONBF, 0); Line 224
n_outputs++; Line 225
}
}
while (n_outputs) Line 229
{
bytes_read = read (0, buffer, sizeof buffer); Line 231...!syscalls auto-comment...
if (bytes_read < 0 && errno == EINTR) Line 232
continue; Line 233
if (bytes_read <= 0) Line 234
break; Line 235
/* Write to all NFILES + 1 descriptors.
Standard output is the first one. */
for (i = 0; i <= nfiles; i++) Line 239
if (descriptors[i] Line 240
&& fwrite (buffer, bytes_read, 1, descriptors[i]) != 1) Line 241...!syscalls auto-comment...
{
int w_errno = errno; Line 243
bool fail = errno != EPIPE || (output_error == output_error_exit Line 244
|| output_error == output_error_warn);Line 245
if (descriptors[i] == stdout) Line 246
clearerr (stdout); /* Avoid redundant close_stdout diagnostic. */Line 247
if (fail) Line 248
{
error (output_error == output_error_exit Line 250
|| output_error == output_error_exit_nopipe, Line 251
w_errno, "%s", quotef (files[i])); Line 252
}
descriptors[i] = NULL; Line 254
if (fail) Line 255
ok = false; Line 256
n_outputs--; Line 257
}
}
if (bytes_read == -1) Line 261
{
error (0, errno, _("read error")); Line 263
ok = false; Line 264
}
/* Close the files, but not standard output. */
for (i = 1; i <= nfiles; i++) Line 268
if (descriptors[i] && fclose (descriptors[i]) != 0) Line 269...!syscalls auto-comment...
{
error (0, errno, "%s", quotef (files[i])); Line 271
ok = false; Line 272
}
free (descriptors); Line 275
return ok; Line 277
} Block 7