>From 58793c6963dcc7587f0ea49be60e9efef6c08015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= Date: Mon, 2 Mar 2015 20:06:17 +0000 Subject: [PATCH] tee: generalize the --write-error option to --output-error Adjust commit v8.23-140-gfdd6ebf to add the --output-error option instead of --write-error, and treat open() errors like write() errors. * doc/coreutils.texi (tee invocation): s/write-error/output-error/. * src/tee.c (main): Exit on open() error if appropriate. * tests/misc/tee.sh: Add a case to test open() errors. * NEWS: Adjust for the more general output error behavior. Suggested by Bernhard Voelker. --- NEWS | 3 ++- doc/coreutils.texi | 14 +++++++------- src/tee.c | 57 +++++++++++++++++++++++++++--------------------------- tests/misc/tee.sh | 26 +++++++++++++++---------- 4 files changed, 53 insertions(+), 47 deletions(-) diff --git a/NEWS b/NEWS index 8aef1a6..1a51235 100644 --- a/NEWS +++ b/NEWS @@ -58,7 +58,8 @@ GNU coreutils NEWS -*- outline -*- sync no longer ignores arguments, and syncs each specified file, or with the --file-system option, the file systems associated with each specified file. - tee accepts a new --write-error option to control operation with pipes. + tee accepts a new --output-error option to control operation with pipes + and output errors in general. ** Changes in behavior diff --git a/doc/coreutils.texi b/doc/coreutils.texi index d3239a5..6110cec 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -13202,29 +13202,29 @@ them. Ignore interrupt signals. @item -p -@itemx --write-error[=@var{mode}] +@itemx --output-error[=@var{mode}] @opindex -p -@opindex --write-error -Select the behavior with write errors on the outputs, +@opindex --output-error +Select the behavior with errors on the outputs, where @var{mode} is one of the following: @table @samp @item warn -Warn on error writing any output, including pipes. +Warn on error opening or writing any output, including pipes. Writing is continued to still open files/pipes. Exit status indicates failure if any output has an error. @item warn-nopipe -Warn on error writing any output, except pipes. +Warn on error opening or writing any output, except pipes. Writing is continued to still open files/pipes. Exit status indicates failure if any non pipe output had an error. This is the default @var{mode} when not specified. @item exit -Exit on error writing any output, including pipes. +Exit on error opening or writing any output, including pipes. @item exit-nopipe -Exit on error writing any output, except pipes. +Exit on error opening or writing any output, except pipes. @end table @end table diff --git a/src/tee.c b/src/tee.c index c163184..3c39a4a 100644 --- a/src/tee.c +++ b/src/tee.c @@ -44,37 +44,37 @@ static bool append; /* If true, ignore interrupts. */ static bool ignore_interrupts; -enum write_error +enum output_error { - write_error_sigpipe, /* traditional behavior, sigpipe enabled. */ - write_error_warn, /* warn on EPIPE, but continue. */ - write_error_warn_nopipe, /* ignore EPIPE, continue. */ - write_error_exit, /* exit on any write error. */ - write_error_exit_nopipe /* exit on any write error except EPIPE. */ + output_error_sigpipe, /* traditional behavior, sigpipe enabled. */ + output_error_warn, /* warn on EPIPE, but continue. */ + output_error_warn_nopipe, /* ignore EPIPE, continue. */ + output_error_exit, /* exit on any output error. */ + output_error_exit_nopipe /* exit on any output error except EPIPE. */ }; -static enum write_error write_error; +static enum output_error output_error; static struct option const long_options[] = { {"append", no_argument, NULL, 'a'}, {"ignore-interrupts", no_argument, NULL, 'i'}, - {"write-error", optional_argument, NULL, 'p'}, + {"output-error", optional_argument, NULL, 'p'}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, {NULL, 0, NULL, 0} }; -static char const *const write_error_args[] = +static char const *const output_error_args[] = { "warn", "warn-nopipe", "exit", "exit-nopipe", NULL }; -static enum write_error const write_error_types[] = +static enum output_error const output_error_types[] = { - write_error_warn, write_error_warn_nopipe, - write_error_exit, write_error_exit_nopipe + output_error_warn, output_error_warn_nopipe, + output_error_exit, output_error_exit_nopipe }; -ARGMATCH_VERIFY (write_error_args, write_error_types); +ARGMATCH_VERIFY (output_error_args, output_error_types); void usage (int status) @@ -91,7 +91,7 @@ Copy standard input to each FILE, and also to standard output.\n\ -i, --ignore-interrupts ignore interrupt signals\n\ "), stdout); fputs (_("\ - -p, --write-error[=MODE] behavior on write error. See MODE details below\n\ + -p, --output-error[=MODE] behavior on write error. See MODE details below\n\ "), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); @@ -103,7 +103,7 @@ MODE determines behavior with write errors on the outputs:\n\ 'exit' exit on error writing to any output\n\ 'exit-nopipe' exit on error writing to any output not a pipe\n\ The default MODE for the -p option is 'warn-nopipe'.\n\ -The default operation when --write-error is not specified, is to\n\ +The default operation when --output-error is not specified, is to\n\ exit immediately on error writing to a pipe, and diagnose errors\n\ writing to non pipe outputs.\n\ "), stdout); @@ -143,10 +143,10 @@ main (int argc, char **argv) case 'p': if (optarg) - write_error = XARGMATCH ("--write-error", optarg, write_error_args, - write_error_types); + output_error = XARGMATCH ("--output-error", optarg, + output_error_args, output_error_types); else - write_error = write_error_warn_nopipe; + output_error = output_error_warn_nopipe; break; case_GETOPT_HELP_CHAR; @@ -161,7 +161,7 @@ main (int argc, char **argv) if (ignore_interrupts) signal (SIGINT, SIG_IGN); - if (write_error != write_error_sigpipe) + if (output_error != output_error_sigpipe) signal (SIGPIPE, SIG_IGN); /* Do *not* warn if tee is given no file arguments. @@ -184,7 +184,7 @@ tee_files (int nfiles, const char **files) size_t n_outputs = 0; FILE **descriptors; char buffer[BUFSIZ]; - ssize_t bytes_read; + ssize_t bytes_read = 0; int i; bool ok = true; char const *mode_string = @@ -219,7 +219,9 @@ tee_files (int nfiles, const char **files) descriptors[i] = fopen (files[i], mode_string); if (descriptors[i] == NULL) { - error (0, errno, "%s", files[i]); + error (output_error == output_error_exit + || output_error == output_error_exit_nopipe, + errno, "%s", files[i]); ok = false; } else @@ -229,7 +231,7 @@ tee_files (int nfiles, const char **files) } } - while (1) + while (n_outputs) { bytes_read = read (0, buffer, sizeof buffer); if (bytes_read < 0 && errno == EINTR) @@ -244,14 +246,14 @@ tee_files (int nfiles, const char **files) && fwrite (buffer, bytes_read, 1, descriptors[i]) != 1) { int w_errno = errno; - bool fail = errno != EPIPE || (write_error == write_error_exit - || write_error == write_error_warn); + bool fail = errno != EPIPE || (output_error == output_error_exit + || output_error == output_error_warn); if (descriptors[i] == stdout) clearerr (stdout); /* Avoid redundant close_stdout diagnostic. */ if (fail) { - error (write_error == write_error_exit - || write_error == write_error_exit_nopipe, + error (output_error == output_error_exit + || output_error == output_error_exit_nopipe, w_errno, "%s", files[i]); } descriptors[i] = NULL; @@ -259,9 +261,6 @@ tee_files (int nfiles, const char **files) ok = false; n_outputs--; } - - if (n_outputs == 0) - break; } if (bytes_read == -1) diff --git a/tests/misc/tee.sh b/tests/misc/tee.sh index 84665cd..f457a0b 100755 --- a/tests/misc/tee.sh +++ b/tests/misc/tee.sh @@ -64,7 +64,7 @@ if test -w /dev/full && test -c /dev/full; then fi -# Ensure tee honors --write-error modes +# Ensure tee honors --output-error modes mkfifo_or_skip_ fifo read_fifo() { timeout 10 dd count=1 if=fifo of=/dev/null status=none & } @@ -73,30 +73,36 @@ read_fifo yes >fifo pipe_status=$? -# Default operation is to exit silently on SIGPIPE +# Default operation is to continue on output errors but exit silently on SIGPIPE read_fifo -yes | returns_ $pipe_status timeout 10 tee 2>err >fifo || fail=1 -test $(wc -l < err) = 0 || { cat err; fail=1; } +yes | returns_ $pipe_status timeout 10 tee ./e/noent 2>err >fifo || fail=1 +test $(wc -l < err) = 1 || { cat err; fail=1; } # With -p, SIGPIPE is suppressed, exit 0 for EPIPE when all outputs finished read_fifo yes | timeout 10 tee -p 2>err >fifo || fail=1 test $(wc -l < err) = 0 || { cat err; fail=1; } -# With --write-error=warn, exit 1 for EPIPE when all outputs finished +# With --output-error=warn, exit 1 for EPIPE when all outputs finished +read_fifo +yes | returns_ 1 timeout 10 tee --output-error=warn 2>err >fifo || fail=1 +test $(wc -l < err) = 1 || { cat err; fail=1; } + +# With --output-error=exit, exit 1 immediately for EPIPE read_fifo -yes | returns_ 1 timeout 10 tee --write-error=warn 2>err >fifo || fail=1 +yes | returns_ 1 timeout 10 tee --output-error=exit /dev/null 2>err >fifo \ + || fail=1 test $(wc -l < err) = 1 || { cat err; fail=1; } -# With --write-error=exit, exit 1 immediately for EPIPE +# With --output-error=exit, exit 1 immediately on output error read_fifo -yes | returns_ 1 timeout 10 tee --write-error=exit /dev/null 2>err >fifo \ +yes | returns_ 1 timeout 10 tee --output-error=exit ./e/noent 2>err >fifo \ || fail=1 test $(wc -l < err) = 1 || { cat err; fail=1; } -# With --write-error=exit-nopipe, exit 0 for EPIPE +# With --output-error=exit-nopipe, exit 0 for EPIPE read_fifo -yes | timeout 10 tee --write-error=exit-nopipe 2>err >fifo || fail=1 +yes | timeout 10 tee --output-error=exit-nopipe 2>err >fifo || fail=1 test $(wc -l < err) = 0 || { cat err; fail=1; } wait -- 2.1.0