/* tac - concatenate and print files in reverse                                 This is the tac utility
   Copyright (C) 1988-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
                                                                                
/* Written by Jay Lepreau (lepreau@cs.utah.edu).                                
   GNU enhancements by David MacKenzie (djm@gnu.ai.mit.edu). */                 
                                                                                
/* Copy each FILE, or the standard input if none are given or when a            
   FILE name of "-" is encountered, to the standard output with the             
   order of the records reversed.  The records are separated by                 
   instances of a string, or a newline if none is given.  By default, the       
   separator string is attached to the end of the record that it                
   follows in the file.                                                         
                                                                                
   Options:                                                                     
   -b, --before   The separator is attached to the beginning                    
                                of the record that it precedes in the file.     
   -r, --regex   The separator is a regular expression.                         
   -s, --separator=separator Use SEPARATOR as the record separator.             
                                                                                
   To reverse a file byte by byte, use (in bash, ksh, or sh):                   
tac -r -s '.\|                                                                  
' file */                                                                       
                                                                                
#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 <regex.h>                                                              ...!includes auto-comment...
                                                                                
#include "die.h"                                                                ...!includes auto-comment...
#include "error.h"                                                              ...!includes auto-comment...
#include "filenamecat.h"                                                        ...!includes auto-comment...
#include "safe-read.h"                                                          ...!includes auto-comment...
#include "stdlib--.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 "tac"                                                      Line 54
                                                                                
#define AUTHORS \                                                               Line 56
  proper_name ("Jay Lepreau"), \                                                Line 57
  proper_name ("David MacKenzie")                                               Line 58
                                                                                
#if defined __MSDOS__ || defined _WIN32                                         Line 60
/* Define this to non-zero on systems for which the regular mechanism           
   (of unlinking an open file and expecting to be able to write, seek           
   back to the beginning, then reread it) doesn't work.  E.g., on Windows       
   and DOS systems.  */                                                         
# define DONT_UNLINK_WHILE_OPEN 1                                               Line 65
#endif                                                                          Line 66
                                                                                
                                                                                
#ifndef DEFAULT_TMPDIR                                                          Line 69
# define DEFAULT_TMPDIR "/tmp"                                                  Line 70
#endif                                                                          Line 71
                                                                                
/* The number of bytes per atomic read. */                                      
#define INITIAL_READSIZE 8192                                                   Line 74
                                                                                
/* The number of bytes per atomic write. */                                     
#define WRITESIZE 8192                                                          Line 77
                                                                                
/* The string that separates the records of the file. */                        
static char const *separator;                                                   Line 80
                                                                                
/* True if we have ever read standard input.  */                                
static bool have_read_stdin = false;                                            Line 83
                                                                                
/* If true, print 'separator' along with the record preceding it                
   in the file; otherwise with the record following it. */                      
static bool separator_ends_record;                                              Line 87
                                                                                
/* 0 if 'separator' is to be matched as a regular expression;                   
   otherwise, the length of 'separator', used as a sentinel to                  
   stop the search. */                                                          
static size_t sentinel_length;                                                  Line 92
                                                                                
/* The length of a match with 'separator'.  If 'sentinel_length' is 0,          
   'match_length' is computed every time a match succeeds;                      
   otherwise, it is simply the length of 'separator'. */                        
static size_t match_length;                                                     Line 97
                                                                                
/* The input buffer. */                                                         
static char *G_buffer;                                                          Line 100
                                                                                
/* The number of bytes to read at once into 'buffer'. */                        
static size_t read_size;                                                        Line 103
                                                                                
/* The size of 'buffer'.  This is read_size * 2 + sentinel_length + 2.          
   The extra 2 bytes allow 'past_end' to have a value beyond the                
   end of 'G_buffer' and 'match_start' to run off the front of 'G_buffer'. */   
static size_t G_buffer_size;                                                    Line 108
                                                                                
/* The compiled regular expression representing 'separator'. */                 
static struct re_pattern_buffer compiled_separator;                             Line 111
static char compiled_separator_fastmap[UCHAR_MAX + 1];                          Line 112
static struct re_registers regs;                                                Line 113
                                                                                
static struct option const longopts[] =                                         Line 115
{                                                                               
  {"before", no_argument, NULL, 'b'},                                           Line 117
  {"regex", no_argument, NULL, 'r'},                                            Line 118
  {"separator", required_argument, NULL, 's'},                                  Line 119
  {GETOPT_HELP_OPTION_DECL},                                                    Line 120
  {GETOPT_VERSION_OPTION_DECL},                                                 Line 121
  {NULL, 0, NULL, 0}                                                            Line 122
};                                                                              Block 1
                                                                                
void                                                                            Line 125
usage (int status)                                                              Line 126
{                                                                               
  if (status != EXIT_SUCCESS)                                                   Line 128
    emit_try_help ();                                                           ...!common auto-comment...
  else                                                                          Line 130
    {                                                                           
      printf (_("\                                                              Line 132
Usage: %s [OPTION]... [FILE]...\n\                                              Line 133
"),                                                                             Line 134
              program_name);                                                    Line 135
      fputs (_("\                                                               Line 136
Write each FILE to standard output, last line first.\n\                         Line 137
"), stdout);                                                                    Line 138
                                                                                
      emit_stdin_note ();                                                       ...!common auto-comment...
      emit_mandatory_arg_note ();                                               ...!common auto-comment...
                                                                                
      fputs (_("\                                                               Line 143
  -b, --before             attach the separator before instead of after\n\      Line 144
  -r, --regex              interpret the separator as a regular expression\n\   Line 145
  -s, --separator=STRING   use STRING as the separator instead of newline\n\    Line 146
"), stdout);                                                                    Line 147
      fputs (HELP_OPTION_DESCRIPTION, stdout);                                  Line 148
      fputs (VERSION_OPTION_DESCRIPTION, stdout);                               Line 149
      emit_ancillary_info (PROGRAM_NAME);                                       Line 150
    }                                                                           
  exit (status);                                                                Line 152
}                                                                               Block 2
                                                                                
/* Print the characters from START to PAST_END - 1.                             
   If START is NULL, just flush the buffer. */                                  
                                                                                
static void                                                                     Line 158
output (const char *start, const char *past_end)                                Line 159
{                                                                               
  static char buffer[WRITESIZE];                                                Line 161
  static size_t bytes_in_buffer = 0;                                            Line 162
  size_t bytes_to_add = past_end - start;                                       Line 163
  size_t bytes_available = WRITESIZE - bytes_in_buffer;                         Line 164
                                                                                
  if (start == 0)                                                               Line 166
    {                                                                           
      fwrite (buffer, 1, bytes_in_buffer, stdout);                              Line 168...!syscalls auto-comment...
      bytes_in_buffer = 0;                                                      Line 169
      return;                                                                   Line 170
    }                                                                           
                                                                                
  /* Write out as many full buffers as possible. */                             
  while (bytes_to_add >= bytes_available)                                       Line 174
    {                                                                           
      memcpy (buffer + bytes_in_buffer, start, bytes_available);                Line 176
      bytes_to_add -= bytes_available;                                          Line 177
      start += bytes_available;                                                 Line 178
      fwrite (buffer, 1, WRITESIZE, stdout);                                    Line 179...!syscalls auto-comment...
      bytes_in_buffer = 0;                                                      Line 180
      bytes_available = WRITESIZE;                                              Line 181
    }                                                                           
                                                                                
  memcpy (buffer + bytes_in_buffer, start, bytes_to_add);                       Line 184
  bytes_in_buffer += bytes_to_add;                                              Line 185
}                                                                               Block 3
                                                                                
/* Print in reverse the file open on descriptor FD for reading FILE.            
   The file is already positioned at FILE_POS, which should be near its end.    
   Return true if successful.  */                                               
                                                                                
static bool                                                                     Line 192
tac_seekable (int input_fd, const char *file, off_t file_pos)                   Line 193
{                                                                               
  /* Pointer to the location in 'G_buffer' where the search for                 
     the next separator will begin. */                                          
  char *match_start;                                                            Line 197
                                                                                
  /* Pointer to one past the rightmost character in 'G_buffer' that             
     has not been printed yet. */                                               
  char *past_end;                                                               Line 201
                                                                                
  /* Length of the record growing in 'G_buffer'. */                             
  size_t saved_record_size;                                                     Line 204
                                                                                
  /* True if 'output' has not been called yet for any file.                     
     Only used when the separator is attached to the preceding record. */       
  bool first_time = true;                                                       Line 208
  char first_char = *separator; /* Speed optimization, non-regexp. */           Line 209
  char const *separator1 = separator + 1; /* Speed optimization, non-regexp. */ Line 210
  size_t match_length1 = match_length - 1; /* Speed optimization, non-regexp. */Line 211
                                                                                
  /* Arrange for the first read to lop off enough to leave the rest of the      
     file a multiple of 'read_size'.  Since 'read_size' can change, this may    
     not always hold during the program run, but since it usually will, leave   
     it here for i/o efficiency (page/sector boundaries and all that).          
     Note: the efficiency gain has not been verified. */                        
  size_t remainder = file_pos % read_size;                                      Line 218
  if (remainder != 0)                                                           Line 219
    {                                                                           
      file_pos -= remainder;                                                    Line 221
      if (lseek (input_fd, file_pos, SEEK_SET) < 0)                             Line 222
        error (0, errno, _("%s: seek failed"), quotef (file));                  Line 223
    }                                                                           
                                                                                
  /* Scan backward, looking for end of file.  This caters to proc-like          
     file systems where the file size is just an estimate.  */                  
  while ((saved_record_size = safe_read (input_fd, G_buffer, read_size)) == 0   Line 228...!syscalls auto-comment...
         && file_pos != 0)                                                      Line 229
    {                                                                           
      off_t rsize = read_size;                                                  Line 231
      if (lseek (input_fd, -rsize, SEEK_CUR) < 0)                               Line 232
        error (0, errno, _("%s: seek failed"), quotef (file));                  Line 233
      file_pos -= read_size;                                                    Line 234
    }                                                                           
                                                                                
  /* Now scan forward, looking for end of file.  */                             
  while (saved_record_size == read_size)                                        Line 238
    {                                                                           
      size_t nread = safe_read (input_fd, G_buffer, read_size);                 Line 240...!syscalls auto-comment...
      if (nread == 0)                                                           Line 241
        break;                                                                  Line 242
      saved_record_size = nread;                                                Line 243
      if (saved_record_size == SAFE_READ_ERROR)                                 Line 244
        break;                                                                  Line 245
      file_pos += nread;                                                        Line 246
    }                                                                           
                                                                                
  if (saved_record_size == SAFE_READ_ERROR)                                     Line 249
    {                                                                           
      error (0, errno, _("%s: read error"), quotef (file));                     Line 251
      return false;                                                             Line 252
    }                                                                           
                                                                                
  match_start = past_end = G_buffer + saved_record_size;                        Line 255
  /* For non-regexp search, move past impossible positions for a match. */      
  if (sentinel_length)                                                          Line 257
    match_start -= match_length1;                                               Line 258
                                                                                
  while (true)                                                                  Line 260
    {                                                                           
      /* Search backward from 'match_start' - 1 to 'G_buffer' for a match       
         with 'separator'; for speed, use strncmp if 'separator' contains no    
         metacharacters.                                                        
         If the match succeeds, set 'match_start' to point to the start of      
         the match and 'match_length' to the length of the match.               
         Otherwise, make 'match_start' < 'G_buffer'. */                         
      if (sentinel_length == 0)                                                 Line 268
        {                                                                       
          size_t i = match_start - G_buffer;                                    Line 270
          regoff_t ri = i;                                                      Line 271
          regoff_t range = 1 - ri;                                              Line 272
          regoff_t ret;                                                         Line 273
                                                                                
          if (1 < range)                                                        Line 275
            die (EXIT_FAILURE, 0, _("record too large"));                       Line 276
                                                                                
          if (range == 1                                                        Line 278
              || ((ret = re_search (&compiled_separator, G_buffer,              Line 279
                                    i, i - 1, range, ®s))                    Line 280
                  == -1))                                                       Line 281
            match_start = G_buffer - 1;                                         Line 282
          else if (ret == -2)                                                   Line 283
            {                                                                   
              die (EXIT_FAILURE, 0,                                             Line 285
                   _("error in regular expression search"));                    Line 286
            }                                                                   
          else                                                                  Line 288
            {                                                                   
              match_start = G_buffer + regs.start[0];                           Line 290
              match_length = regs.end[0] - regs.start[0];                       Line 291
            }                                                                   
        }                                                                       
      else                                                                      Line 294
        {                                                                       
          /* 'match_length' is constant for non-regexp boundaries. */           
          while (*--match_start != first_char                                   Line 297
                 || (match_length1 && !STREQ_LEN (match_start + 1, separator1,  Line 298
                                                  match_length1)))              Line 299
            /* Do nothing. */ ;                                                 
        }                                                                       
                                                                                
      /* Check whether we backed off the front of 'G_buffer' without finding    
         a match for 'separator'. */                                            
      if (match_start < G_buffer)                                               Line 305
        {                                                                       
          if (file_pos == 0)                                                    Line 307
            {                                                                   
              /* Hit the beginning of the file; print the remaining record. */  
              output (G_buffer, past_end);                                      Line 310
              return true;                                                      Line 311
            }                                                                   
                                                                                
          saved_record_size = past_end - G_buffer;                              Line 314
          if (saved_record_size > read_size)                                    Line 315
            {                                                                   
              /* 'G_buffer_size' is about twice 'read_size', so since           
                 we want to read in another 'read_size' bytes before            
                 the data already in 'G_buffer', we need to increase            
                 'G_buffer_size'. */                                            
              char *newbuffer;                                                  Line 321
              size_t offset = sentinel_length ? sentinel_length : 1;            Line 322
              size_t old_G_buffer_size = G_buffer_size;                         Line 323
                                                                                
              read_size *= 2;                                                   Line 325
              G_buffer_size = read_size * 2 + sentinel_length + 2;              Line 326
              if (G_buffer_size < old_G_buffer_size)                            Line 327
                xalloc_die ();                                                  ...!common auto-comment...
              newbuffer = xrealloc (G_buffer - offset, G_buffer_size);          Line 329
              newbuffer += offset;                                              Line 330
              G_buffer = newbuffer;                                             Line 331
            }                                                                   
                                                                                
          /* Back up to the start of the next bufferfull of the file.  */       
          if (file_pos >= read_size)                                            Line 335
            file_pos -= read_size;                                              Line 336
          else                                                                  Line 337
            {                                                                   
              read_size = file_pos;                                             Line 339
              file_pos = 0;                                                     Line 340
            }                                                                   
          if (lseek (input_fd, file_pos, SEEK_SET) < 0)                         Line 342
            error (0, errno, _("%s: seek failed"), quotef (file));              Line 343
                                                                                
          /* Shift the pending record data right to make room for the new.      
             The source and destination regions probably overlap.  */           
          memmove (G_buffer + read_size, G_buffer, saved_record_size);          Line 347
          past_end = G_buffer + read_size + saved_record_size;                  Line 348
          /* For non-regexp searches, avoid unnecessary scanning. */            
          if (sentinel_length)                                                  Line 350
            match_start = G_buffer + read_size;                                 Line 351
          else                                                                  Line 352
            match_start = past_end;                                             Line 353
                                                                                
          if (safe_read (input_fd, G_buffer, read_size) != read_size)           Line 355...!syscalls auto-comment...
            {                                                                   
              error (0, errno, _("%s: read error"), quotef (file));             Line 357
              return false;                                                     Line 358
            }                                                                   
        }                                                                       
      else                                                                      Line 361
        {                                                                       
          /* Found a match of 'separator'. */                                   
          if (separator_ends_record)                                            Line 364
            {                                                                   
              char *match_end = match_start + match_length;                     Line 366
                                                                                
              /* If this match of 'separator' isn't at the end of the           
                 file, print the record. */                                     
              if (!first_time || match_end != past_end)                         Line 370
                output (match_end, past_end);                                   Line 371
              past_end = match_end;                                             Line 372
              first_time = false;                                               Line 373
            }                                                                   
          else                                                                  Line 375
            {                                                                   
              output (match_start, past_end);                                   Line 377
              past_end = match_start;                                           Line 378
            }                                                                   
                                                                                
          /* For non-regex matching, we can back up.  */                        
          if (sentinel_length > 0)                                              Line 382
            match_start -= match_length - 1;                                    Line 383
        }                                                                       
    }                                                                           
}                                                                               
                                                                                
#if DONT_UNLINK_WHILE_OPEN                                                      Line 388
                                                                                
/* FIXME-someday: remove all of this DONT_UNLINK_WHILE_OPEN junk.               
   Using atexit like this is wrong, since it can fail                           
   when called e.g. 32 or more times.                                           
   But this isn't a big deal, since the code is used only on WOE/DOS            
   systems, and few people invoke tac on that many nonseekable files.  */       
                                                                                
static const char *file_to_remove;                                              Line 396
static FILE *fp_to_close;                                                       Line 397
                                                                                
static void                                                                     Line 399
unlink_tempfile (void)                                                          Line 400
{                                                                               
  fclose (fp_to_close);                                                         Line 402...!syscalls auto-comment...
  unlink (file_to_remove);                                                      Line 403...!syscalls auto-comment......!syscalls auto-comment...
}                                                                               Block 5
                                                                                
static void                                                                     Line 406
record_or_unlink_tempfile (char const *fn, FILE *fp)                            Line 407
{                                                                               
  if (!file_to_remove)                                                          Line 409
    {                                                                           
      file_to_remove = fn;                                                      Line 411
      fp_to_close = fp;                                                         Line 412
      atexit (unlink_tempfile);                                                 Close stdout on exit (see gnulib)
    }                                                                           
}                                                                               Block 6
                                                                                
#else                                                                           Line 417
                                                                                
static void                                                                     Line 419
record_or_unlink_tempfile (char const *fn, FILE *fp _GL_UNUSED)                 Line 420
{                                                                               
  unlink (fn);                                                                  Line 422...!syscalls auto-comment......!syscalls auto-comment...
}                                                                               Block 7
                                                                                
#endif                                                                          Line 425
                                                                                
/* A wrapper around mkstemp that gives us both an open stream pointer,          
   FP, and the corresponding FILE_NAME.  Always return the same FP/name         
   pair, rewinding/truncating it upon each reuse.  */                           
static bool                                                                     Line 430
temp_stream (FILE **fp, char **file_name)                                       Line 431
{                                                                               
  static char *tempfile = NULL;                                                 Line 433
  static FILE *tmp_fp;                                                          Line 434
  if (tempfile == NULL)                                                         Line 435
    {                                                                           
      char const *t = getenv ("TMPDIR");                                        Line 437
      char const *tempdir = t ? t : DEFAULT_TMPDIR;                             Line 438
      tempfile = mfile_name_concat (tempdir, "tacXXXXXX", NULL);                Line 439
      if (tempdir == NULL)                                                      Line 440
        {                                                                       
          error (0, 0, _("memory exhausted"));                                  Line 442
          return false;                                                         Line 443
        }                                                                       
                                                                                
      /* FIXME: there's a small window between a successful mkstemp call        
         and the unlink that's performed by record_or_unlink_tempfile.          
         If we're interrupted in that interval, this code fails to remove       
         the temporary file.  On systems that define DONT_UNLINK_WHILE_OPEN,    
         the window is much larger -- it extends to the atexit-called           
         unlink_tempfile.                                                       
         FIXME: clean up upon fatal signal.  Don't block them, in case          
         $TMPFILE is a remote file system.  */                                  
                                                                                
      int fd = mkstemp (tempfile);                                              Line 455
      if (fd < 0)                                                               Line 456
        {                                                                       
          error (0, errno, _("failed to create temporary file in %s"),          Line 458
                 quoteaf (tempdir));                                            Line 459
          goto Reset;                                                           Line 460
        }                                                                       
                                                                                
      tmp_fp = fdopen (fd, (O_BINARY ? "w+b" : "w+"));                          Line 463...!syscalls auto-comment...
      if (! tmp_fp)                                                             Line 464
        {                                                                       
          error (0, errno, _("failed to open %s for writing"),                  Line 466
                 quoteaf (tempfile));                                           Line 467
          close (fd);                                                           Line 468...!syscalls auto-comment...
          unlink (tempfile);                                                    Line 469...!syscalls auto-comment......!syscalls auto-comment...
        Reset:                                                                  Line 470
          free (tempfile);                                                      Line 471
          tempfile = NULL;                                                      Line 472
          return false;                                                         Line 473
        }                                                                       
                                                                                
      record_or_unlink_tempfile (tempfile, tmp_fp);                             Line 476
    }                                                                           
  else                                                                          Line 478
    {                                                                           
      clearerr (tmp_fp);                                                        Line 480
      if (fseeko (tmp_fp, 0, SEEK_SET) < 0                                      Line 481
          || ftruncate (fileno (tmp_fp), 0) < 0)                                Line 482...!syscalls auto-comment...
        {                                                                       
          error (0, errno, _("failed to rewind stream for %s"),                 Line 484
                 quoteaf (tempfile));                                           Line 485
          return false;                                                         Line 486
        }                                                                       
    }                                                                           
                                                                                
  *fp = tmp_fp;                                                                 Line 490
  *file_name = tempfile;                                                        Line 491
  return true;                                                                  Line 492
}                                                                               Block 8
                                                                                
/* Copy from file descriptor INPUT_FD (corresponding to the named FILE) to      
   a temporary file, and set *G_TMP and *G_TEMPFILE to the resulting stream     
   and file name.  Return the number of bytes copied, or -1 on error.  */       
                                                                                
static off_t                                                                    Line 499
copy_to_temp (FILE **g_tmp, char **g_tempfile, int input_fd, char const *file)  Line 500
{                                                                               
  FILE *fp;                                                                     Line 502
  char *file_name;                                                              Line 503
  uintmax_t bytes_copied = 0;                                                   Line 504
  if (!temp_stream (&fp, &file_name))                                           Line 505
    return -1;                                                                  Line 506
                                                                                
  while (1)                                                                     Line 508
    {                                                                           
      size_t bytes_read = safe_read (input_fd, G_buffer, read_size);            Line 510...!syscalls auto-comment...
      if (bytes_read == 0)                                                      Line 511
        break;                                                                  Line 512
      if (bytes_read == SAFE_READ_ERROR)                                        Line 513
        {                                                                       
          error (0, errno, _("%s: read error"), quotef (file));                 Line 515
          return -1;                                                            Line 516
        }                                                                       
                                                                                
      if (fwrite (G_buffer, 1, bytes_read, fp) != bytes_read)                   Line 519...!syscalls auto-comment...
        {                                                                       
          error (0, errno, _("%s: write error"), quotef (file_name));           Line 521
          return -1;                                                            Line 522
        }                                                                       
                                                                                
      /* Implicitly <= OFF_T_MAX due to preceding fwrite(),                     
         but unsigned type used to avoid compiler warnings                      
         not aware of this fact.  */                                            
      bytes_copied += bytes_read;                                               Line 528
    }                                                                           
                                                                                
  if (fflush (fp) != 0)                                                         Line 531
    {                                                                           
      error (0, errno, _("%s: write error"), quotef (file_name));               Line 533
      return -1;                                                                Line 534
    }                                                                           
                                                                                
  *g_tmp = fp;                                                                  Line 537
  *g_tempfile = file_name;                                                      Line 538
  return bytes_copied;                                                          Line 539
}                                                                               Block 9
                                                                                
/* Copy INPUT_FD to a temporary, then tac that file.                            
   Return true if successful.  */                                               
                                                                                
static bool                                                                     Line 545
tac_nonseekable (int input_fd, const char *file)                                Line 546
{                                                                               
  FILE *tmp_stream;                                                             Line 548
  char *tmp_file;                                                               Line 549
  off_t bytes_copied = copy_to_temp (&tmp_stream, &tmp_file, input_fd, file);   Line 550
  if (bytes_copied < 0)                                                         Line 551
    return false;                                                               Line 552
                                                                                
  bool ok = tac_seekable (fileno (tmp_stream), tmp_file, bytes_copied);         Line 554
  return ok;                                                                    Line 555
}                                                                               Block 10
                                                                                
/* Print FILE in reverse, copying it to a temporary                             
   file first if it is not seekable.                                            
   Return true if successful.  */                                               
                                                                                
static bool                                                                     Line 562
tac_file (const char *filename)                                                 Line 563
{                                                                               
  bool ok;                                                                      Line 565
  off_t file_size;                                                              Line 566
  int fd;                                                                       Line 567
  bool is_stdin = STREQ (filename, "-");                                        Line 568
                                                                                
  if (is_stdin)                                                                 Line 570
    {                                                                           
      have_read_stdin = true;                                                   Line 572
      fd = STDIN_FILENO;                                                        Line 573
      filename = _("standard input");                                           Line 574
      xset_binary_mode (STDIN_FILENO, O_BINARY);                                Line 575
    }                                                                           
  else                                                                          Line 577
    {                                                                           
      fd = open (filename, O_RDONLY | O_BINARY);                                Line 579...!syscalls auto-comment...
      if (fd < 0)                                                               Line 580
        {                                                                       
          error (0, errno, _("failed to open %s for reading"),                  Line 582
                 quoteaf (filename));                                           Line 583
          return false;                                                         Line 584
        }                                                                       
    }                                                                           
                                                                                
  file_size = lseek (fd, 0, SEEK_END);                                          Line 588
                                                                                
  ok = (file_size < 0 || isatty (fd)                                            Line 590
        ? tac_nonseekable (fd, filename)                                        Line 591
        : tac_seekable (fd, filename, file_size));                              Line 592
                                                                                
  if (!is_stdin && close (fd) != 0)                                             Line 594...!syscalls auto-comment...
    {                                                                           
      error (0, errno, _("%s: read error"), quotef (filename));                 Line 596
      ok = false;                                                               Line 597
    }                                                                           
  return ok;                                                                    Line 599
}                                                                               Block 11
                                                                                
int                                                                             
main (int argc, char **argv)                                                    Line 603
{                                                                               
  const char *error_message; /* Return value from re_compile_pattern. */        Line 605
  int optc;                                                                     Line 606
  bool ok;                                                                      Line 607
  size_t half_buffer_size;                                                      Line 608
                                                                                
  /* Initializer for file_list if no file-arguments                             
     were specified on the command line.  */                                    
  static char const *const default_file_list[] = {"-", NULL};                   Line 612
  char const *const *file;                                                      Line 613
                                                                                
  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)
                                                                                
  separator = "\n";                                                             Line 623
  sentinel_length = 1;                                                          Line 624
  separator_ends_record = true;                                                 Line 625
                                                                                
  while ((optc = getopt_long (argc, argv, "brs:", longopts, NULL)) != -1)       Line 627
    {                                                                           
      switch (optc)                                                             Line 629
        {                                                                       
        case 'b':                                                               Line 631
          separator_ends_record = false;                                        Line 632
          break;                                                                Line 633
        case 'r':                                                               Line 634
          sentinel_length = 0;                                                  Line 635
          break;                                                                Line 636
        case 's':                                                               Line 637
          separator = optarg;                                                   Line 638
          break;                                                                Line 639
        case_GETOPT_HELP_CHAR;                                                  Line 640
        case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);                       Line 641
        default:                                                                Line 642
          usage (EXIT_FAILURE);                                                 Line 643
        }                                                                       
    }                                                                           
                                                                                
  if (sentinel_length == 0)                                                     Line 647
    {                                                                           
      if (*separator == 0)                                                      Line 649
        die (EXIT_FAILURE, 0, _("separator cannot be empty"));                  Line 650
                                                                                
      compiled_separator.buffer = NULL;                                         Line 652
      compiled_separator.allocated = 0;                                         Line 653
      compiled_separator.fastmap = compiled_separator_fastmap;                  Line 654
      compiled_separator.translate = NULL;                                      Line 655
      error_message = re_compile_pattern (separator, strlen (separator),        Line 656
                                          &compiled_separator);                 Line 657
      if (error_message)                                                        Line 658
        die (EXIT_FAILURE, 0, "%s", (error_message));                           Line 659
    }                                                                           
  else                                                                          Line 661
    match_length = sentinel_length = *separator ? strlen (separator) : 1;       Line 662
                                                                                
  read_size = INITIAL_READSIZE;                                                 Line 664
  while (sentinel_length >= read_size / 2)                                      Line 665
    {                                                                           
      if (SIZE_MAX / 2 < read_size)                                             Line 667
        xalloc_die ();                                                          ...!common auto-comment...
      read_size *= 2;                                                           Line 669
    }                                                                           
  half_buffer_size = read_size + sentinel_length + 1;                           Line 671
  G_buffer_size = 2 * half_buffer_size;                                         Line 672
  if (! (read_size < half_buffer_size && half_buffer_size < G_buffer_size))     Line 673
    xalloc_die ();                                                              ...!common auto-comment...
  G_buffer = xmalloc (G_buffer_size);                                           Line 675
  if (sentinel_length)                                                          Line 676
    {                                                                           
      memcpy (G_buffer, separator, sentinel_length + 1);                        Line 678
      G_buffer += sentinel_length;                                              Line 679
    }                                                                           
  else                                                                          Line 681
    {                                                                           
      ++G_buffer;                                                               Line 683
    }                                                                           
                                                                                
  file = (optind < argc                                                         Line 686
          ? (char const *const *) &argv[optind]                                 Line 687
          : default_file_list);                                                 Line 688
                                                                                
  xset_binary_mode (STDOUT_FILENO, O_BINARY);                                   Line 690
                                                                                
  {                                                                             
    ok = true;                                                                  Line 693
    for (size_t i = 0; file[i]; ++i)                                            Line 694
      ok &= tac_file (file[i]);                                                 Line 695
  }                                                                             
                                                                                
  /* Flush the output buffer. */                                                
  output ((char *) NULL, (char *) NULL);                                        Line 699
                                                                                
  if (have_read_stdin && close (STDIN_FILENO) < 0)                              Line 701...!syscalls auto-comment...
    {                                                                           
      error (0, errno, "-");                                                    Line 703
      ok = false;                                                               Line 704
    }                                                                           
                                                                                
#ifdef lint                                                                     Line 707
  size_t offset = sentinel_length ? sentinel_length : 1;                        Line 708
  free (G_buffer - offset);                                                     Line 709
#endif                                                                          Line 710
                                                                                
  return ok ? EXIT_SUCCESS : EXIT_FAILURE;                                      Line 712
}                                                                               Block 12