/* paste - merge lines of files                                                 This is the paste utility
   Copyright (C) 1997-2018 Free Software Foundation, Inc.                       
   Copyright (C) 1984 David M. Ihnat                                            
                                                                                
   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 David Ihnat.  */                                                  
                                                                                
/* The list of valid escape sequences has been expanded over the Unix           
   version, to include \b, \f, \r, and \v.                                      
                                                                                
   POSIX changes, bug fixes, long-named options, and cleanup                    
   by David MacKenzie <djm@gnu.ai.mit.edu>.                                     
                                                                                
   Options:                                                                     
   --serial                                                                     
   -s    Paste one file at a time rather than                                   
                                one line from each file.                        
   --delimiters=delim-list                                                      
   -d delim-list  Consecutively use the characters in                           
                                DELIM-LIST instead of tab to separate           
                                merged lines.  When DELIM-LIST is exhausted,    
                                start again at its beginning.                   
   A FILE of '-' means standard input.                                          
   If no FILEs are given, standard input is used. */                            
                                                                                
#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 "system.h"                                                             ...!includes auto-comment...
#include "die.h"                                                                ...!includes auto-comment...
#include "error.h"                                                              ...!includes auto-comment...
#include "fadvise.h"                                                            ...!includes auto-comment...
                                                                                
/* The official name of this program (e.g., no 'g' prefix).  */                 
#define PROGRAM_NAME "paste"                                                    Line 49
                                                                                
#define AUTHORS \                                                               Line 51
  proper_name ("David M. Ihnat"), \                                             Line 52
  proper_name ("David MacKenzie")                                               Line 53
                                                                                
/* Indicates that no delimiter should be added in the current position. */      
#define EMPTY_DELIM '\0'                                                        Line 56
                                                                                
/* If nonzero, we have read standard input at some point. */                    
static bool have_read_stdin;                                                    Line 59
                                                                                
/* If nonzero, merge subsequent lines of each file rather than                  
   corresponding lines from each file in parallel. */                           
static bool serial_merge;                                                       Line 63
                                                                                
/* The delimiters between lines of input files (used cyclically). */            
static char *delims;                                                            Line 66
                                                                                
/* A pointer to the character after the end of 'delims'. */                     
static char const *delim_end;                                                   Line 69
                                                                                
static unsigned char line_delim = '\n';                                         Line 71
                                                                                
static struct option const longopts[] =                                         Line 73
{                                                                               
  {"serial", no_argument, NULL, 's'},                                           Line 75
  {"delimiters", required_argument, NULL, 'd'},                                 Line 76
  {"zero-terminated", no_argument, NULL, 'z'},                                  Line 77
  {GETOPT_HELP_OPTION_DECL},                                                    Line 78
  {GETOPT_VERSION_OPTION_DECL},                                                 Line 79
  {NULL, 0, NULL, 0}                                                            Line 80
};                                                                              Block 1
                                                                                
/* Set globals delims and delim_end.  Copy STRPTR to DELIMS, converting         
   backslash representations of special characters in STRPTR to their actual    
   values. The set of possible backslash characters has been expanded beyond    
   that recognized by the Unix version.                                         
   Return 0 upon success.                                                       
   If the string ends in an odd number of backslashes, ignore the               
   final backslash and return nonzero.  */                                      
                                                                                
static int                                                                      Line 91
collapse_escapes (char const *strptr)                                           Line 92
{                                                                               
  char *strout = xstrdup (strptr);                                              Line 94
  bool backslash_at_end = false;                                                Line 95
                                                                                
  delims = strout;                                                              Line 97
                                                                                
  while (*strptr)                                                               Line 99
    {                                                                           
      if (*strptr != '\\') /* Is it an escape character? */                     Line 101
        *strout++ = *strptr++; /* No, just transfer it. */                      Line 102
      else                                                                      Line 103
        {                                                                       
          switch (*++strptr)                                                    Line 105
            {                                                                   
            case '0':                                                           Line 107
              *strout++ = EMPTY_DELIM;                                          Line 108
              break;                                                            Line 109
                                                                                
            case 'b':                                                           Line 111
              *strout++ = '\b';                                                 Line 112
              break;                                                            Line 113
                                                                                
            case 'f':                                                           Line 115
              *strout++ = '\f';                                                 Line 116
              break;                                                            Line 117
                                                                                
            case 'n':                                                           Line 119
              *strout++ = '\n';                                                 Line 120
              break;                                                            Line 121
                                                                                
            case 'r':                                                           Line 123
              *strout++ = '\r';                                                 Line 124
              break;                                                            Line 125
                                                                                
            case 't':                                                           Line 127
              *strout++ = '\t';                                                 Line 128
              break;                                                            Line 129
                                                                                
            case 'v':                                                           Line 131
              *strout++ = '\v';                                                 Line 132
              break;                                                            Line 133
                                                                                
            case '\\':                                                          Line 135
              *strout++ = '\\';                                                 Line 136
              break;                                                            Line 137
                                                                                
            case '\0':                                                          Line 139
              backslash_at_end = true;                                          Line 140
              goto done;                                                        Line 141
                                                                                
            default:                                                            Line 143
              *strout++ = *strptr;                                              Line 144
              break;                                                            Line 145
            }                                                                   
          strptr++;                                                             Line 147
        }                                                                       
    }                                                                           
                                                                                
 done:                                                                          Line 151
                                                                                
  delim_end = strout;                                                           Line 153
  return backslash_at_end ? 1 : 0;                                              Line 154
}                                                                               Block 2
                                                                                
/* Report a write error and exit.  */                                           
                                                                                
static void write_error (void) ATTRIBUTE_NORETURN;                              Line 159
static void                                                                     Line 160
write_error (void)                                                              Line 161
{                                                                               
  die (EXIT_FAILURE, errno, _("write error"));                                  Line 163
}                                                                               Block 3
                                                                                
/* Output a single byte, reporting any write errors.  */                        
                                                                                
static inline void                                                              Line 168
xputchar (char c)                                                               Line 169
{                                                                               
  if (putchar (c) < 0)                                                          Line 171
    write_error ();                                                             Line 172
}                                                                               Block 4
                                                                                
/* Perform column paste on the NFILES files named in FNAMPTR.                   
   Return true if successful, false if one or more files could not be           
   opened or read. */                                                           
                                                                                
static bool                                                                     Line 179
paste_parallel (size_t nfiles, char **fnamptr)                                  Line 180
{                                                                               
  bool ok = true;                                                               Line 182
  /* If all files are just ready to be closed, or will be on this               
     round, the string of delimiters must be preserved.                         
     delbuf[0] through delbuf[nfiles]                                           
     store the delimiters for closed files. */                                  
  char *delbuf = xmalloc (nfiles + 2);                                          Line 187
                                                                                
  /* Streams open to the files to process; NULL if the corresponding            
     stream is closed.  */                                                      
  FILE **fileptr = xnmalloc (nfiles + 1, sizeof *fileptr);                      Line 191
                                                                                
  /* Number of files still open to process.  */                                 
  size_t files_open;                                                            Line 194
                                                                                
  /* True if any fopen got fd == STDIN_FILENO.  */                              
  bool opened_stdin = false;                                                    Line 197
                                                                                
  /* Attempt to open all files.  This could be expanded to an infinite          
     number of files, but at the (considerable) expense of remembering          
     each file and its current offset, then opening/reading/closing.  */        
                                                                                
  for (files_open = 0; files_open < nfiles; ++files_open)                       Line 203
    {                                                                           
      if (STREQ (fnamptr[files_open], "-"))                                     Line 205
        {                                                                       
          have_read_stdin = true;                                               Line 207
          fileptr[files_open] = stdin;                                          Line 208
        }                                                                       
      else                                                                      Line 210
        {                                                                       
          fileptr[files_open] = fopen (fnamptr[files_open], "r");               Line 212...!syscalls auto-comment...
          if (fileptr[files_open] == NULL)                                      Line 213
            die (EXIT_FAILURE, errno, "%s", quotef (fnamptr[files_open]));      Line 214
          else if (fileno (fileptr[files_open]) == STDIN_FILENO)                Line 215
            opened_stdin = true;                                                Line 216
          fadvise (fileptr[files_open], FADVISE_SEQUENTIAL);                    Line 217...!syscalls auto-comment...
        }                                                                       
    }                                                                           
                                                                                
  if (opened_stdin && have_read_stdin)                                          Line 221
    die (EXIT_FAILURE, 0, _("standard input is closed"));                       Line 222
                                                                                
  /* Read a line from each file and output it to stdout separated by a          
     delimiter, until we go through the loop without successfully               
     reading from any of the files. */                                          
                                                                                
  while (files_open)                                                            Line 228
    {                                                                           
      /* Set up for the next line. */                                           
      bool somedone = false;                                                    Line 231
      char const *delimptr = delims;                                            Line 232
      size_t delims_saved = 0; /* Number of delims saved in 'delbuf'. */        Line 233
                                                                                
      for (size_t i = 0; i < nfiles && files_open; i++)                         Line 235
        {                                                                       
          int chr IF_LINT ( = 0); /* Input character. */                        Line 237
          int err IF_LINT ( = 0); /* Input errno value.  */                     Line 238
          bool sometodo = false; /* Input chars to process.  */                 Line 239
                                                                                
          if (fileptr[i])                                                       Line 241
            {                                                                   
              chr = getc (fileptr[i]);                                          Line 243
              err = errno;                                                      Line 244
              if (chr != EOF && delims_saved)                                   Line 245
                {                                                               
                  if (fwrite (delbuf, 1, delims_saved, stdout) != delims_saved) Line 247...!syscalls auto-comment...
                    write_error ();                                             Line 248
                  delims_saved = 0;                                             Line 249
                }                                                               
                                                                                
              while (chr != EOF)                                                Line 252
                {                                                               
                  sometodo = true;                                              Line 254
                  if (chr == line_delim)                                        Line 255
                    break;                                                      Line 256
                  xputchar (chr);                                               Line 257
                  chr = getc (fileptr[i]);                                      Line 258
                  err = errno;                                                  Line 259
                }                                                               
            }                                                                   
                                                                                
          if (! sometodo)                                                       Line 263
            {                                                                   
              /* EOF, read error, or closed file.                               
                 If an EOF or error, close the file.  */                        
              if (fileptr[i])                                                   Line 267
                {                                                               
                  if (ferror (fileptr[i]))                                      Line 269
                    {                                                           
                      error (0, err, "%s", quotef (fnamptr[i]));                Line 271
                      ok = false;                                               Line 272
                    }                                                           
                  if (fileptr[i] == stdin)                                      Line 274
                    clearerr (fileptr[i]); /* Also clear EOF. */                Line 275
                  else if (fclose (fileptr[i]) == EOF)                          Line 276...!syscalls auto-comment...
                    {                                                           
                      error (0, errno, "%s", quotef (fnamptr[i]));              Line 278
                      ok = false;                                               Line 279
                    }                                                           
                                                                                
                  fileptr[i] = NULL;                                            Line 282
                  files_open--;                                                 Line 283
                }                                                               
                                                                                
              if (i + 1 == nfiles)                                              Line 286
                {                                                               
                  /* End of this output line.                                   
                     Is this the end of the whole thing? */                     
                  if (somedone)                                                 Line 290
                    {                                                           
                      /* No.  Some files were not closed for this line. */      
                      if (delims_saved)                                         Line 293
                        {                                                       
                          if (fwrite (delbuf, 1, delims_saved, stdout)          Line 295...!syscalls auto-comment...
                              != delims_saved)                                  Line 296
                            write_error ();                                     Line 297
                          delims_saved = 0;                                     Line 298
                        }                                                       
                      xputchar (line_delim);                                    Line 300
                    }                                                           
                  continue; /* Next read of files, or exit. */                  Line 302
                }                                                               
              else                                                              Line 304
                {                                                               
                  /* Closed file; add delimiter to 'delbuf'. */                 
                  if (*delimptr != EMPTY_DELIM)                                 Line 307
                    delbuf[delims_saved++] = *delimptr;                         Line 308
                  if (++delimptr == delim_end)                                  Line 309
                    delimptr = delims;                                          Line 310
                }                                                               
            }                                                                   
          else                                                                  Line 313
            {                                                                   
              /* Some data read. */                                             
              somedone = true;                                                  Line 316
                                                                                
              /* Except for last file, replace last newline with delim. */      
              if (i + 1 != nfiles)                                              Line 319
                {                                                               
                  if (chr != line_delim && chr != EOF)                          Line 321
                    xputchar (chr);                                             Line 322
                  if (*delimptr != EMPTY_DELIM)                                 Line 323
                    xputchar (*delimptr);                                       Line 324
                  if (++delimptr == delim_end)                                  Line 325
                    delimptr = delims;                                          Line 326
                }                                                               
              else                                                              Line 328
                {                                                               
                  /* If the last line of the last file lacks a newline,         
                     print one anyhow.  POSIX requires this.  */                
                  char c = (chr == EOF ? line_delim : chr);                     Line 332
                  xputchar (c);                                                 Line 333
                }                                                               
            }                                                                   
        }                                                                       
    }                                                                           
  free (fileptr);                                                               Line 338
  free (delbuf);                                                                Line 339
  return ok;                                                                    Line 340
}                                                                               Block 5
                                                                                
/* Perform serial paste on the NFILES files named in FNAMPTR.                   
   Return true if no errors, false if one or more files could not be            
   opened or read. */                                                           
                                                                                
static bool                                                                     Line 347
paste_serial (size_t nfiles, char **fnamptr)                                    Line 348
{                                                                               
  bool ok = true; /* false if open or read errors occur. */                     Line 350
  int charnew, charold; /* Current and previous char read. */                   Line 351
  char const *delimptr; /* Current delimiter char. */                           Line 352
  FILE *fileptr; /* Open for reading current file. */                           Line 353
                                                                                
  for (; nfiles; nfiles--, fnamptr++)                                           Line 355
    {                                                                           
      int saved_errno;                                                          Line 357
      bool is_stdin = STREQ (*fnamptr, "-");                                    Line 358
      if (is_stdin)                                                             Line 359
        {                                                                       
          have_read_stdin = true;                                               Line 361
          fileptr = stdin;                                                      Line 362
        }                                                                       
      else                                                                      Line 364
        {                                                                       
          fileptr = fopen (*fnamptr, "r");                                      Line 366...!syscalls auto-comment...
          if (fileptr == NULL)                                                  Line 367
            {                                                                   
              error (0, errno, "%s", quotef (*fnamptr));                        Line 369
              ok = false;                                                       Line 370
              continue;                                                         Line 371
            }                                                                   
          fadvise (fileptr, FADVISE_SEQUENTIAL);                                Line 373...!syscalls auto-comment...
        }                                                                       
                                                                                
      delimptr = delims; /* Set up for delimiter string. */                     Line 376
                                                                                
      charold = getc (fileptr);                                                 Line 378
      saved_errno = errno;                                                      Line 379
      if (charold != EOF)                                                       Line 380
        {                                                                       
          /* 'charold' is set up.  Hit it!                                      
             Keep reading characters, stashing them in 'charnew';               
             output 'charold', converting to the appropriate delimiter          
             character if needed.  After the EOF, output 'charold'              
             if it's a newline; otherwise, output it and then a newline. */     
                                                                                
          while ((charnew = getc (fileptr)) != EOF)                             Line 388
            {                                                                   
              /* Process the old character. */                                  
              if (charold == line_delim)                                        Line 391
                {                                                               
                  if (*delimptr != EMPTY_DELIM)                                 Line 393
                    xputchar (*delimptr);                                       Line 394
                                                                                
                  if (++delimptr == delim_end)                                  Line 396
                    delimptr = delims;                                          Line 397
                }                                                               
              else                                                              Line 399
                xputchar (charold);                                             Line 400
                                                                                
              charold = charnew;                                                Line 402
            }                                                                   
          saved_errno = errno;                                                  Line 404
                                                                                
          /* Hit EOF.  Process that last character. */                          
          xputchar (charold);                                                   Line 407
        }                                                                       
                                                                                
      if (charold != line_delim)                                                Line 410
        xputchar (line_delim);                                                  Line 411
                                                                                
      if (ferror (fileptr))                                                     Line 413
        {                                                                       
          error (0, saved_errno, "%s", quotef (*fnamptr));                      Line 415
          ok = false;                                                           Line 416
        }                                                                       
      if (is_stdin)                                                             Line 418
        clearerr (fileptr); /* Also clear EOF. */                               Line 419
      else if (fclose (fileptr) == EOF)                                         Line 420...!syscalls auto-comment...
        {                                                                       
          error (0, errno, "%s", quotef (*fnamptr));                            Line 422
          ok = false;                                                           Line 423
        }                                                                       
    }                                                                           
  return ok;                                                                    Line 426
}                                                                               Block 6
                                                                                
void                                                                            Line 429
usage (int status)                                                              Line 430
{                                                                               
  if (status != EXIT_SUCCESS)                                                   Line 432
    emit_try_help ();                                                           ...!common auto-comment...
  else                                                                          Line 434
    {                                                                           
      printf (_("\                                                              Line 436
Usage: %s [OPTION]... [FILE]...\n\                                              Line 437
"),                                                                             Line 438
              program_name);                                                    Line 439
      fputs (_("\                                                               Line 440
Write lines consisting of the sequentially corresponding lines from\n\          Line 441
each FILE, separated by TABs, to standard output.\n\                            Line 442
"), stdout);                                                                    Line 443
                                                                                
      emit_stdin_note ();                                                       ...!common auto-comment...
      emit_mandatory_arg_note ();                                               ...!common auto-comment...
                                                                                
      fputs (_("\                                                               Line 448
  -d, --delimiters=LIST   reuse characters from LIST instead of TABs\n\         Line 449
  -s, --serial            paste one file at a time instead of in parallel\n\    Line 450
"), stdout);                                                                    Line 451
      fputs (_("\                                                               Line 452
  -z, --zero-terminated    line delimiter is NUL, not newline\n\                Line 453
"), stdout);                                                                    Line 454
      fputs (HELP_OPTION_DESCRIPTION, stdout);                                  Line 455
      fputs (VERSION_OPTION_DESCRIPTION, stdout);                               Line 456
      /* FIXME: add a couple of examples.  */                                   
      emit_ancillary_info (PROGRAM_NAME);                                       Line 458
    }                                                                           
  exit (status);                                                                Line 460
}                                                                               Block 7
                                                                                
int                                                                             
main (int argc, char **argv)                                                    Line 464
{                                                                               
  int optc;                                                                     Line 466
  char const *delim_arg = "\t";                                                 Line 467
                                                                                
  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)
                                                                                
  have_read_stdin = false;                                                      Line 477
  serial_merge = false;                                                         Line 478
                                                                                
  while ((optc = getopt_long (argc, argv, "d:sz", longopts, NULL)) != -1)       Line 480
    {                                                                           
      switch (optc)                                                             Line 482
        {                                                                       
        case 'd':                                                               Line 484
          /* Delimiter character(s). */                                         
          delim_arg = (optarg[0] == '\0' ? "\\0" : optarg);                     Line 486
          break;                                                                Line 487
                                                                                
        case 's':                                                               Line 489
          serial_merge = true;                                                  Line 490
          break;                                                                Line 491
                                                                                
        case 'z':                                                               Line 493
          line_delim = '\0';                                                    Line 494
          break;                                                                Line 495
                                                                                
        case_GETOPT_HELP_CHAR;                                                  Line 497
                                                                                
        case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);                       Line 499
                                                                                
        default:                                                                Line 501
          usage (EXIT_FAILURE);                                                 Line 502
        }                                                                       
    }                                                                           
                                                                                
  int nfiles = argc - optind;                                                   Line 506
  if (nfiles == 0)                                                              Line 507
    {                                                                           
      argv[optind] = bad_cast ("-");                                            Line 509
      nfiles++;                                                                 Line 510
    }                                                                           
                                                                                
  if (collapse_escapes (delim_arg))                                             Line 513
    {                                                                           
      /* Don't use the quote() quoting style, because that would double the     
         number of displayed backslashes, making the diagnostic look bogus.  */ 
      die (EXIT_FAILURE, 0,                                                     Line 517
           _("delimiter list ends with an unescaped backslash: %s"),            Line 518
           quotearg_n_style_colon (0, c_maybe_quoting_style, delim_arg));       Line 519
    }                                                                           
                                                                                
  bool ok = ((serial_merge ? paste_serial : paste_parallel)                     Line 522
             (nfiles, &argv[optind]));                                          Line 523
                                                                                
  free (delims);                                                                Line 525
                                                                                
  if (have_read_stdin && fclose (stdin) == EOF)                                 Line 527...!syscalls auto-comment...
    die (EXIT_FAILURE, errno, "-");                                             Line 528
  return ok ? EXIT_SUCCESS : EXIT_FAILURE;                                      Line 529
}                                                                               Block 8