1 /* tee - read from standard input and write to standard output and files.
2    Copyright (C) 1985-2018 Free Software Foundation, Inc.
3 
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16 
17 /* Mike Parker, Richard M. Stallman, and David MacKenzie */
18 
19 #include <config.h>
20 #include <sys/types.h>
21 #include <signal.h>
22 #include <getopt.h>
23 
24 #include "system.h"
25 #include "argmatch.h"
26 #include "die.h"
27 #include "error.h"
28 #include "fadvise.h"
29 #include "stdio--.h"
30 #include "xbinary-io.h"
31 
32 /* The official name of this program (e.g., no 'g' prefix).  */
33 #define PROGRAM_NAME "tee"
34 
35 #define AUTHORS \
36   proper_name ("Mike Parker"), \
37   proper_name ("Richard M. Stallman"), \
38   proper_name ("David MacKenzie")
39 
40 static bool tee_files (int nfiles, char **files);
41 
42 /* If true, append to output files rather than truncating them. */
43 static bool append;
44 
45 /* If true, ignore interrupts. */
46 static bool ignore_interrupts;
47 
48 enum output_error
49   {
50     output_error_sigpipe,      /* traditional behavior, sigpipe enabled.  */
51     output_error_warn,         /* warn on EPIPE, but continue.  */
52     output_error_warn_nopipe,  /* ignore EPIPE, continue.  */
53     output_error_exit,         /* exit on any output error.  */
54     output_error_exit_nopipe   /* exit on any output error except EPIPE.  */
55   };
56 
57 static enum output_error output_error;
58 
59 static struct option const long_options[] =
60 {
61   {"append", no_argument, NULL, 'a'},
62   {"ignore-interrupts", no_argument, NULL, 'i'},
63   {"output-error", optional_argument, NULL, 'p'},
64   {GETOPT_HELP_OPTION_DECL},
65   {GETOPT_VERSION_OPTION_DECL},
66   {NULL, 0, NULL, 0}
67 };
68 
69 static char const *const output_error_args[] =
70 {
71   "warn", "warn-nopipe", "exit", "exit-nopipe", NULL
72 };
73 static enum output_error const output_error_types[] =
74 {
75   output_error_warn, output_error_warn_nopipe,
76   output_error_exit, output_error_exit_nopipe
77 };
78 ARGMATCH_VERIFY (output_error_args, output_error_types);
79 
80 void
usage(int status)81 usage (int status)
82 {
83   if (status != EXIT_SUCCESS)
84     emit_try_help ();
85   else
86     {
87       printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
88       fputs (_("\
89 Copy standard input to each FILE, and also to standard output.\n\
90 \n\
91   -a, --append              append to the given FILEs, do not overwrite\n\
92   -i, --ignore-interrupts   ignore interrupt signals\n\
93 "), stdout);
94       fputs (_("\
95   -p                        diagnose errors writing to non pipes\n\
96       --output-error[=MODE]   set behavior on write error.  See MODE below\n\
97 "), stdout);
98       fputs (HELP_OPTION_DESCRIPTION, stdout);
99       fputs (VERSION_OPTION_DESCRIPTION, stdout);
100       fputs (_("\
101 \n\
102 MODE determines behavior with write errors on the outputs:\n\
103   'warn'         diagnose errors writing to any output\n\
104   'warn-nopipe'  diagnose errors writing to any output not a pipe\n\
105   'exit'         exit on error writing to any output\n\
106   'exit-nopipe'  exit on error writing to any output not a pipe\n\
107 The default MODE for the -p option is 'warn-nopipe'.\n\
108 The default operation when --output-error is not specified, is to\n\
109 exit immediately on error writing to a pipe, and diagnose errors\n\
110 writing to non pipe outputs.\n\
111 "), stdout);
112       emit_ancillary_info (PROGRAM_NAME);
113     }
114   exit (status);
115 }
116 
117 int
main(int argc,char ** argv)118 main (int argc, char **argv)
119 {
120   bool ok;
121   int optc;
122 
123   initialize_main (&argc, &argv);
124   set_program_name (argv[0]);
125   setlocale (LC_ALL, "");
126   bindtextdomain (PACKAGE, LOCALEDIR);
127   textdomain (PACKAGE);
128 
129   atexit (close_stdout);
130 
131   append = false;
132   ignore_interrupts = false;
133 
134   while ((optc = getopt_long (argc, argv, "aip", long_options, NULL)) != -1)
135     {
136       switch (optc)
137         {
138         case 'a':
139           append = true;
140           break;
141 
142         case 'i':
143           ignore_interrupts = true;
144           break;
145 
146         case 'p':
147           if (optarg)
148             output_error = XARGMATCH ("--output-error", optarg,
149                                       output_error_args, output_error_types);
150           else
151             output_error = output_error_warn_nopipe;
152           break;
153 
154         case_GETOPT_HELP_CHAR;
155 
156         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
157 
158         default:
159           usage (EXIT_FAILURE);
160         }
161     }
162 
163   if (ignore_interrupts)
164     signal (SIGINT, SIG_IGN);
165 
166   if (output_error != output_error_sigpipe)
167     signal (SIGPIPE, SIG_IGN);
168 
169   /* Do *not* warn if tee is given no file arguments.
170      POSIX requires that it work when given no arguments.  */
171 
172   ok = tee_files (argc - optind, &argv[optind]);
173   if (close (STDIN_FILENO) != 0)
174     die (EXIT_FAILURE, errno, "%s", _("standard input"));
175 
176   return ok ? EXIT_SUCCESS : EXIT_FAILURE;
177 }
178 
179 /* Copy the standard input into each of the NFILES files in FILES
180    and into the standard output.  As a side effect, modify FILES[-1].
181    Return true if successful.  */
182 
183 static bool
tee_files(int nfiles,char ** files)184 tee_files (int nfiles, char **files)
185 {
186   size_t n_outputs = 0;
187   FILE **descriptors;
188   char buffer[BUFSIZ];
189   ssize_t bytes_read = 0;
190   int i;
191   bool ok = true;
192   char const *mode_string =
193     (O_BINARY
194      ? (append ? "ab" : "wb")
195      : (append ? "a" : "w"));
196 
197   xset_binary_mode (STDIN_FILENO, O_BINARY);
198   xset_binary_mode (STDOUT_FILENO, O_BINARY);
199   fadvise (stdin, FADVISE_SEQUENTIAL);
200 
201   /* Set up FILES[0 .. NFILES] and DESCRIPTORS[0 .. NFILES].
202      In both arrays, entry 0 corresponds to standard output.  */
203 
204   descriptors = xnmalloc (nfiles + 1, sizeof *descriptors);
205   files--;
206   descriptors[0] = stdout;
207   files[0] = bad_cast (_("standard output"));
208   setvbuf (stdout, NULL, _IONBF, 0);
209   n_outputs++;
210 
211   for (i = 1; i <= nfiles; i++)
212     {
213       /* Do not treat "-" specially - as mandated by POSIX.  */
214       descriptors[i] = fopen (files[i], mode_string);
215       if (descriptors[i] == NULL)
216         {
217           error (output_error == output_error_exit
218                  || output_error == output_error_exit_nopipe,
219                  errno, "%s", quotef (files[i]));
220           ok = false;
221         }
222       else
223         {
224           setvbuf (descriptors[i], NULL, _IONBF, 0);
225           n_outputs++;
226         }
227     }
228 
229   while (n_outputs)
230     {
231       bytes_read = read (0, buffer, sizeof buffer);
232       if (bytes_read < 0 && errno == EINTR)
233         continue;
234       if (bytes_read <= 0)
235         break;
236 
237       /* Write to all NFILES + 1 descriptors.
238          Standard output is the first one.  */
239       for (i = 0; i <= nfiles; i++)
240         if (descriptors[i]
241             && fwrite (buffer, bytes_read, 1, descriptors[i]) != 1)
242           {
243             int w_errno = errno;
244             bool fail = errno != EPIPE || (output_error == output_error_exit
245                                           || output_error == output_error_warn);
246             if (descriptors[i] == stdout)
247               clearerr (stdout); /* Avoid redundant close_stdout diagnostic.  */
248             if (fail)
249               {
250                 error (output_error == output_error_exit
251                        || output_error == output_error_exit_nopipe,
252                        w_errno, "%s", quotef (files[i]));
253               }
254             descriptors[i] = NULL;
255             if (fail)
256               ok = false;
257             n_outputs--;
258           }
259     }
260 
261   if (bytes_read == -1)
262     {
263       error (0, errno, _("read error"));
264       ok = false;
265     }
266 
267   /* Close the files, but not standard output.  */
268   for (i = 1; i <= nfiles; i++)
269     if (descriptors[i] && fclose (descriptors[i]) != 0)
270       {
271         error (0, errno, "%s", quotef (files[i]));
272         ok = false;
273       }
274 
275   free (descriptors);
276 
277   return ok;
278 }
279