/* 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