/* wc - print the number of lines, words, and bytes in files                    This is the wc utility
   Copyright (C) 1985-2018 Free Software Foundation, Inc.                       
                                                                                
   This program is free software: you can redistribute it and/or modify         
   it under the terms of the GNU General Public License as published by         
   the Free Software Foundation, either version 3 of the License, or            
   (at your option) any later version.                                          
                                                                                
   This program is distributed in the hope that it will be useful,              
   but WITHOUT ANY WARRANTY; without even the implied warranty of               
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                
   GNU General Public License for more details.                                 
                                                                                
   You should have received a copy of the GNU General Public License            
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */   The GNUv3 license
                                                                                
/* Written by Paul Rubin, phr@ocf.berkeley.edu                                  
   and David MacKenzie, djm@gnu.ai.mit.edu. */                                  
                                                                                
#include <config.h>                                                             Provides system specific information
                                                                                
#include <stdio.h>                                                              Provides standard I/O capability
#include <assert.h>                                                             ...!includes auto-comment...
#include <getopt.h>                                                             ...!includes auto-comment...
#include <sys/types.h>                                                          Provides system data types
#include <wchar.h>                                                              ...!includes auto-comment...
#include <wctype.h>                                                             ...!includes auto-comment...
                                                                                
#include "system.h"                                                             ...!includes auto-comment...
#include "argv-iter.h"                                                          ...!includes auto-comment...
#include "die.h"                                                                ...!includes auto-comment...
#include "error.h"                                                              ...!includes auto-comment...
#include "fadvise.h"                                                            ...!includes auto-comment...
#include "mbchar.h"                                                             ...!includes auto-comment...
#include "physmem.h"                                                            ...!includes auto-comment...
#include "readtokens0.h"                                                        ...!includes auto-comment...
#include "safe-read.h"                                                          ...!includes auto-comment...
#include "stat-size.h"                                                          ...!includes auto-comment...
#include "xbinary-io.h"                                                         ...!includes auto-comment...
                                                                                
#if !defined iswspace && !HAVE_ISWSPACE                                         Line 41
# define iswspace(wc) \                                                         Line 42
    ((wc) == to_uchar (wc) && isspace (to_uchar (wc)))                          Line 43
#endif                                                                          Line 44
                                                                                
/* The official name of this program (e.g., no 'g' prefix).  */                 
#define PROGRAM_NAME "wc"                                                       Line 47
                                                                                
#define AUTHORS \                                                               Line 49
  proper_name ("Paul Rubin"), \                                                 Line 50
  proper_name ("David MacKenzie")                                               Line 51
                                                                                
/* Size of atomic reads. */                                                     
#define BUFFER_SIZE (16 * 1024)                                                 Line 54
                                                                                
/* Cumulative number of lines, words, chars and bytes in all files so far.      
   max_line_length is the maximum over all files processed so far.  */          
static uintmax_t total_lines;                                                   Line 58
static uintmax_t total_words;                                                   Line 59
static uintmax_t total_chars;                                                   Line 60
static uintmax_t total_bytes;                                                   Line 61
static uintmax_t max_line_length;                                               Line 62
                                                                                
/* Which counts to print. */                                                    
static bool print_lines, print_words, print_chars, print_bytes;                 Line 65
static bool print_linelength;                                                   Line 66
                                                                                
/* The print width of each count.  */                                           
static int number_width;                                                        Line 69
                                                                                
/* True if we have ever read the standard input. */                             
static bool have_read_stdin;                                                    Line 72
                                                                                
/* Used to determine if file size can be determined without reading.  */        
static size_t page_size;                                                        Line 75
                                                                                
/* The result of calling fstat or stat on a file descriptor or file.  */        
struct fstatus                                                                  Line 78
{                                                                               
  /* If positive, fstat or stat has not been called yet.  Otherwise,            
     this is the value returned from fstat or stat.  */                         
  int failed;                                                                   Line 82
                                                                                
  /* If FAILED is zero, this is the file's status.  */                          
  struct stat st;                                                               Line 85
};                                                                              
                                                                                
/* For long options that have no equivalent short option, use a                 
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */      
enum                                                                            Line 90
{                                                                               
  FILES0_FROM_OPTION = CHAR_MAX + 1                                             Line 92
};                                                                              Block 2
                                                                                
static struct option const longopts[] =                                         Line 95
{                                                                               
  {"bytes", no_argument, NULL, 'c'},                                            Line 97
  {"chars", no_argument, NULL, 'm'},                                            Line 98
  {"lines", no_argument, NULL, 'l'},                                            Line 99
  {"words", no_argument, NULL, 'w'},                                            Line 100
  {"files0-from", required_argument, NULL, FILES0_FROM_OPTION},                 Line 101
  {"max-line-length", no_argument, NULL, 'L'},                                  Line 102
  {GETOPT_HELP_OPTION_DECL},                                                    Line 103
  {GETOPT_VERSION_OPTION_DECL},                                                 Line 104
  {NULL, 0, NULL, 0}                                                            Line 105
};                                                                              Block 3
                                                                                
void                                                                            Line 108
usage (int status)                                                              Line 109
{                                                                               
  if (status != EXIT_SUCCESS)                                                   Line 111
    emit_try_help ();                                                           ...!common auto-comment...
  else                                                                          Line 113
    {                                                                           
      printf (_("\                                                              Line 115
Usage: %s [OPTION]... [FILE]...\n\                                              Line 116
  or:  %s [OPTION]... --files0-from=F\n\                                        Line 117
"),                                                                             Line 118
              program_name, program_name);                                      Line 119
      fputs (_("\                                                               Line 120
Print newline, word, and byte counts for each FILE, and a total line if\n\      Line 121
more than one FILE is specified.  A word is a non-zero-length sequence of\n\    Line 122
characters delimited by white space.\n\                                         Line 123
"), stdout);                                                                    Line 124
                                                                                
      emit_stdin_note ();                                                       ...!common auto-comment...
                                                                                
      fputs (_("\                                                               Line 128
\n\                                                                             
The options below may be used to select which counts are printed, always in\n\  Line 130
the following order: newline, word, character, byte, maximum line length.\n\    Line 131
  -c, --bytes            print the byte counts\n\                               Line 132
  -m, --chars            print the character counts\n\                          Line 133
  -l, --lines            print the newline counts\n\                            Line 134
"), stdout);                                                                    Line 135
      fputs (_("\                                                               Line 136
      --files0-from=F    read input from the files specified by\n\              Line 137
                           NUL-terminated names in file F;\n\                   Line 138
                           If F is - then read names from standard input\n\     Line 139
  -L, --max-line-length  print the maximum display width\n\                     Line 140
  -w, --words            print the word counts\n\                               Line 141
"), stdout);                                                                    Line 142
      fputs (HELP_OPTION_DESCRIPTION, stdout);                                  Line 143
      fputs (VERSION_OPTION_DESCRIPTION, stdout);                               Line 144
      emit_ancillary_info (PROGRAM_NAME);                                       Line 145
    }                                                                           
  exit (status);                                                                Line 147
}                                                                               Block 4
                                                                                
/* FILE is the name of the file (or NULL for standard input)                    
   associated with the specified counters.  */                                  
static void                                                                     Line 152
write_counts (uintmax_t lines,                                                  Line 153
              uintmax_t words,                                                  Line 154
              uintmax_t chars,                                                  Line 155
              uintmax_t bytes,                                                  Line 156
              uintmax_t linelength,                                             Line 157
              const char *file)                                                 Line 158
{                                                                               
  static char const format_sp_int[] = " %*s";                                   Line 160
  char const *format_int = format_sp_int + 1;                                   Line 161
  char buf[INT_BUFSIZE_BOUND (uintmax_t)];                                      Line 162
                                                                                
  if (print_lines)                                                              Line 164
    {                                                                           
      printf (format_int, number_width, umaxtostr (lines, buf));                Line 166
      format_int = format_sp_int;                                               Line 167
    }                                                                           
  if (print_words)                                                              Line 169
    {                                                                           
      printf (format_int, number_width, umaxtostr (words, buf));                Line 171
      format_int = format_sp_int;                                               Line 172
    }                                                                           
  if (print_chars)                                                              Line 174
    {                                                                           
      printf (format_int, number_width, umaxtostr (chars, buf));                Line 176
      format_int = format_sp_int;                                               Line 177
    }                                                                           
  if (print_bytes)                                                              Line 179
    {                                                                           
      printf (format_int, number_width, umaxtostr (bytes, buf));                Line 181
      format_int = format_sp_int;                                               Line 182
    }                                                                           
  if (print_linelength)                                                         Line 184
    {                                                                           
      printf (format_int, number_width, umaxtostr (linelength, buf));           Line 186
    }                                                                           
  if (file)                                                                     Line 188
    printf (" %s", strchr (file, '\n') ? quotef (file) : file);                 Line 189
  putchar ('\n');                                                               Line 190
}                                                                               Block 5
                                                                                
/* Count words.  FILE_X is the name of the file (or NULL for standard           
   input) that is open on descriptor FD.  *FSTATUS is its status.               
   CURRENT_POS is the current file offset if known, negative if unknown.        
   Return true if successful.  */                                               
static bool                                                                     Line 197
wc (int fd, char const *file_x, struct fstatus *fstatus, off_t current_pos)     Line 198
{                                                                               
  bool ok = true;                                                               Line 200
  char buf[BUFFER_SIZE + 1];                                                    Line 201
  size_t bytes_read;                                                            Line 202
  uintmax_t lines, words, chars, bytes, linelength;                             Line 203
  bool count_bytes, count_chars, count_complicated;                             Line 204
  char const *file = file_x ? file_x : _("standard input");                     Line 205
                                                                                
  lines = words = chars = bytes = linelength = 0;                               Line 207
                                                                                
  /* If in the current locale, chars are equivalent to bytes, we prefer         
     counting bytes, because that's easier.  */                                 
#if MB_LEN_MAX > 1                                                              Line 211
  if (MB_CUR_MAX > 1)                                                           Line 212
    {                                                                           
      count_bytes = print_bytes;                                                Line 214
      count_chars = print_chars;                                                Line 215
    }                                                                           
  else                                                                          Line 217
#endif                                                                          Line 218
    {                                                                           
      count_bytes = print_bytes || print_chars;                                 Line 220
      count_chars = false;                                                      Line 221
    }                                                                           
  count_complicated = print_words || print_linelength;                          Line 223
                                                                                
  /* Advise the kernel of our access pattern only if we will read().  */        
  if (!count_bytes || count_chars || print_lines || count_complicated)          Line 226
    fdadvise (fd, 0, 0, FADVISE_SEQUENTIAL);                                    Line 227
                                                                                
  /* When counting only bytes, save some line- and word-counting                
     overhead.  If FD is a 'regular' Unix file, using lseek is enough           
     to get its 'size' in bytes.  Otherwise, read blocks of BUFFER_SIZE         
     bytes at a time until EOF.  Note that the 'size' (number of bytes)         
     that wc reports is smaller than stats.st_size when the file is not         
     positioned at its beginning.  That's why the lseek calls below are         
     necessary.  For example the command                                        
     '(dd ibs=99k skip=1 count=0; ./wc -c) < /etc/group'                        
     should make wc report '0' bytes.  */                                       
                                                                                
  if (count_bytes && !count_chars && !print_lines && !count_complicated)        Line 239
    {                                                                           
      bool skip_read = false;                                                   Line 241
                                                                                
      if (0 < fstatus->failed)                                                  Line 243
        fstatus->failed = fstat (fd, &fstatus->st);                             Line 244...!syscalls auto-comment......!syscalls auto-comment...
                                                                                
      /* For sized files, seek to one st_blksize before EOF rather than to EOF. 
         This works better for files in proc-like file systems where            
         the size is only approximate.  */                                      
      if (! fstatus->failed && usable_st_size (&fstatus->st)                    Line 249
          && 0 <= fstatus->st.st_size)                                          Line 250
        {                                                                       
          size_t end_pos = fstatus->st.st_size;                                 Line 252
          if (current_pos < 0)                                                  Line 253
            current_pos = lseek (fd, 0, SEEK_CUR);                              Line 254
                                                                                
          if (end_pos % page_size)                                              Line 256
            {                                                                   
              /* We only need special handling of /proc and /sys files etc.     
                 when they're a multiple of PAGE_SIZE.  In the common case      
                 for files with st_size not a multiple of PAGE_SIZE,            
                 it's more efficient and accurate to use st_size.               
                                                                                
                 Be careful here.  The current position may actually be         
                 beyond the end of the file.  As in the example above.  */      
                                                                                
              bytes = end_pos < current_pos ? 0 : end_pos - current_pos;        Line 266
              skip_read = true;                                                 Line 267
            }                                                                   
          else                                                                  Line 269
            {                                                                   
              off_t hi_pos = end_pos - end_pos % (ST_BLKSIZE (fstatus->st) + 1);Line 271
              if (0 <= current_pos && current_pos < hi_pos                      Line 272
                  && 0 <= lseek (fd, hi_pos, SEEK_CUR))                         Line 273
                bytes = hi_pos - current_pos;                                   Line 274
            }                                                                   
        }                                                                       
                                                                                
      if (! skip_read)                                                          Line 278
        {                                                                       
          fdadvise (fd, 0, 0, FADVISE_SEQUENTIAL);                              Line 280
          while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)           Line 281...!syscalls auto-comment...
            {                                                                   
              if (bytes_read == SAFE_READ_ERROR)                                Line 283
                {                                                               
                  error (0, errno, "%s", quotef (file));                        Line 285
                  ok = false;                                                   Line 286
                  break;                                                        Line 287
                }                                                               
              bytes += bytes_read;                                              Line 289
            }                                                                   
        }                                                                       
    }                                                                           
  else if (!count_chars && !count_complicated)                                  Line 293
    {                                                                           
      /* Use a separate loop when counting only lines or lines and bytes --     
         but not chars or words.  */                                            
      bool long_lines = false;                                                  Line 297
      while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)               Line 298...!syscalls auto-comment...
        {                                                                       
          if (bytes_read == SAFE_READ_ERROR)                                    Line 300
            {                                                                   
              error (0, errno, "%s", quotef (file));                            Line 302
              ok = false;                                                       Line 303
              break;                                                            Line 304
            }                                                                   
                                                                                
          bytes += bytes_read;                                                  Line 307
                                                                                
          char *p = buf;                                                        Line 309
          char *end = p + bytes_read;                                           Line 310
          uintmax_t plines = lines;                                             Line 311
                                                                                
          if (! long_lines)                                                     Line 313
            {                                                                   
              /* Avoid function call overhead for shorter lines.  */            
              while (p != end)                                                  Line 316
                lines += *p++ == '\n';                                          Line 317
            }                                                                   
          else                                                                  Line 319
            {                                                                   
              /* memchr is more efficient with longer lines.  */                
              while ((p = memchr (p, '\n', end - p)))                           Line 322
                {                                                               
                  ++p;                                                          Line 324
                  ++lines;                                                      Line 325
                }                                                               
            }                                                                   
                                                                                
          /* If the average line length in the block is >= 15, then use         
             memchr for the next block, where system specific optimizations     
             may outweigh function call overhead.                               
             FIXME: This line length was determined in 2015, on both            
             x86_64 and ppc64, but it's worth re-evaluating in future with      
             newer compilers, CPUs, or memchr() implementations etc.  */        
          if (lines - plines <= bytes_read / 15)                                Line 335
            long_lines = true;                                                  Line 336
          else                                                                  Line 337
            long_lines = false;                                                 Line 338
        }                                                                       
    }                                                                           
#if MB_LEN_MAX > 1                                                              Line 341
# define SUPPORT_OLD_MBRTOWC 1                                                  Line 342
  else if (MB_CUR_MAX > 1)                                                      Line 343
    {                                                                           
      bool in_word = false;                                                     Line 345
      uintmax_t linepos = 0;                                                    Line 346
      mbstate_t state = { 0, };                                                 Line 347
      bool in_shift = false;                                                    Line 348
# if SUPPORT_OLD_MBRTOWC                                                        Line 349
      /* Back-up the state before each multibyte character conversion and       
         move the last incomplete character of the buffer to the front          
         of the buffer.  This is needed because we don't know whether           
         the 'mbrtowc' function updates the state when it returns -2, --        
         this is the ISO C 99 and glibc-2.2 behaviour - or not - amended        
         ANSI C, glibc-2.1 and Solaris 5.7 behaviour.  We don't have an         
         autoconf test for this, yet.  */                                       
      size_t prev = 0; /* number of bytes carried over from previous round */   Line 357
# else                                                                          Line 358
      const size_t prev = 0;                                                    Line 359
# endif                                                                         Line 360
                                                                                
      while ((bytes_read = safe_read (fd, buf + prev, BUFFER_SIZE - prev)) > 0) Line 362...!syscalls auto-comment...
        {                                                                       
          const char *p;                                                        Line 364
# if SUPPORT_OLD_MBRTOWC                                                        Line 365
          mbstate_t backup_state;                                               Line 366
# endif                                                                         Line 367
          if (bytes_read == SAFE_READ_ERROR)                                    Line 368
            {                                                                   
              error (0, errno, "%s", quotef (file));                            Line 370
              ok = false;                                                       Line 371
              break;                                                            Line 372
            }                                                                   
                                                                                
          bytes += bytes_read;                                                  Line 375
          p = buf;                                                              Line 376
          bytes_read += prev;                                                   Line 377
          do                                                                    
            {                                                                   
              wchar_t wide_char;                                                Line 380
              size_t n;                                                         Line 381
              bool wide = true;                                                 Line 382
                                                                                
              if (!in_shift && is_basic (*p))                                   Line 384
                {                                                               
                  /* Handle most ASCII characters quickly, without calling      
                     mbrtowc().  */                                             
                  n = 1;                                                        Line 388
                  wide_char = *p;                                               Line 389
                  wide = false;                                                 Line 390
                }                                                               
              else                                                              Line 392
                {                                                               
                  in_shift = true;                                              Line 394
# if SUPPORT_OLD_MBRTOWC                                                        Line 395
                  backup_state = state;                                         Line 396
# endif                                                                         Line 397
                  n = mbrtowc (&wide_char, p, bytes_read, &state);              Line 398
                  if (n == (size_t) -2)                                         Line 399
                    {                                                           
# if SUPPORT_OLD_MBRTOWC                                                        Line 401
                      state = backup_state;                                     Line 402
# endif                                                                         Line 403
                      break;                                                    Line 404
                    }                                                           
                  if (n == (size_t) -1)                                         Line 406
                    {                                                           
                      /* Remember that we read a byte, but don't complain       
                         about the error.  Because of the decoding error,       
                         this is a considered to be byte but not a              
                         character (that is, chars is not incremented).  */     
                      p++;                                                      Line 412
                      bytes_read--;                                             Line 413
                      continue;                                                 Line 414
                    }                                                           
                  if (mbsinit (&state))                                         Line 416
                    in_shift = false;                                           Line 417
                  if (n == 0)                                                   Line 418
                    {                                                           
                      wide_char = 0;                                            Line 420
                      n = 1;                                                    Line 421
                    }                                                           
                }                                                               
                                                                                
              switch (wide_char)                                                Line 425
                {                                                               
                case '\n':                                                      Line 427
                  lines++;                                                      Line 428
                  FALLTHROUGH;                                                  Line 429
                case '\r':                                                      Line 430
                case '\f':                                                      Line 431
                  if (linepos > linelength)                                     Line 432
                    linelength = linepos;                                       Line 433
                  linepos = 0;                                                  Line 434
                  goto mb_word_separator;                                       Line 435
                case '\t':                                                      Line 436
                  linepos += 8 - (linepos % 8);                                 Line 437
                  goto mb_word_separator;                                       Line 438
                case ' ':                                                       Line 439
                  linepos++;                                                    Line 440
                  FALLTHROUGH;                                                  Line 441
                case '\v':                                                      Line 442
                mb_word_separator:                                              Line 443
                  words += in_word;                                             Line 444
                  in_word = false;                                              Line 445
                  break;                                                        Line 446
                default:                                                        Line 447
                  if (wide && iswprint (wide_char))                             Line 448
                    {                                                           
                      /* wcwidth can be expensive on OSX for example,           
                         so avoid if uneeded.  */                               
                      if (print_linelength)                                     Line 452
                        {                                                       
                          int width = wcwidth (wide_char);                      Line 454
                          if (width > 0)                                        Line 455
                            linepos += width;                                   Line 456
                        }                                                       
                      if (iswspace (wide_char))                                 Line 458
                        goto mb_word_separator;                                 Line 459
                      in_word = true;                                           Line 460
                    }                                                           
                  else if (!wide && isprint (to_uchar (*p)))                    Line 462
                    {                                                           
                      linepos++;                                                Line 464
                      if (isspace (to_uchar (*p)))                              Line 465
                        goto mb_word_separator;                                 Line 466
                      in_word = true;                                           Line 467
                    }                                                           
                  break;                                                        Line 469
                }                                                               
                                                                                
              p += n;                                                           Line 472
              bytes_read -= n;                                                  Line 473
              chars++;                                                          Line 474
            }                                                                   
          while (bytes_read > 0);                                               Line 476
                                                                                
# if SUPPORT_OLD_MBRTOWC                                                        Line 478
          if (bytes_read > 0)                                                   Line 479
            {                                                                   
              if (bytes_read == BUFFER_SIZE)                                    Line 481
                {                                                               
                  /* Encountered a very long redundant shift sequence.  */      
                  p++;                                                          Line 484
                  bytes_read--;                                                 Line 485
                }                                                               
              memmove (buf, p, bytes_read);                                     Line 487
            }                                                                   
          prev = bytes_read;                                                    Line 489
# endif                                                                         Line 490
        }                                                                       
      if (linepos > linelength)                                                 Line 492
        linelength = linepos;                                                   Line 493
      words += in_word;                                                         Line 494
    }                                                                           
#endif                                                                          Line 496
  else                                                                          Line 497
    {                                                                           
      bool in_word = false;                                                     Line 499
      uintmax_t linepos = 0;                                                    Line 500
                                                                                
      while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)               Line 502...!syscalls auto-comment...
        {                                                                       
          const char *p = buf;                                                  Line 504
          if (bytes_read == SAFE_READ_ERROR)                                    Line 505
            {                                                                   
              error (0, errno, "%s", quotef (file));                            Line 507
              ok = false;                                                       Line 508
              break;                                                            Line 509
            }                                                                   
                                                                                
          bytes += bytes_read;                                                  Line 512
          do                                                                    
            {                                                                   
              switch (*p++)                                                     Line 515
                {                                                               
                case '\n':                                                      Line 517
                  lines++;                                                      Line 518
                  FALLTHROUGH;                                                  Line 519
                case '\r':                                                      Line 520
                case '\f':                                                      Line 521
                  if (linepos > linelength)                                     Line 522
                    linelength = linepos;                                       Line 523
                  linepos = 0;                                                  Line 524
                  goto word_separator;                                          Line 525
                case '\t':                                                      Line 526
                  linepos += 8 - (linepos % 8);                                 Line 527
                  goto word_separator;                                          Line 528
                case ' ':                                                       Line 529
                  linepos++;                                                    Line 530
                  FALLTHROUGH;                                                  Line 531
                case '\v':                                                      Line 532
                word_separator:                                                 Line 533
                  words += in_word;                                             Line 534
                  in_word = false;                                              Line 535
                  break;                                                        Line 536
                default:                                                        Line 537
                  if (isprint (to_uchar (p[-1])))                               Line 538
                    {                                                           
                      linepos++;                                                Line 540
                      if (isspace (to_uchar (p[-1])))                           Line 541
                        goto word_separator;                                    Line 542
                      in_word = true;                                           Line 543
                    }                                                           
                  break;                                                        Line 545
                }                                                               
            }                                                                   
          while (--bytes_read);                                                 Line 548
        }                                                                       
      if (linepos > linelength)                                                 Line 550
        linelength = linepos;                                                   Line 551
      words += in_word;                                                         Line 552
    }                                                                           
                                                                                
  if (count_chars < print_chars)                                                Line 555
    chars = bytes;                                                              Line 556
                                                                                
  write_counts (lines, words, chars, bytes, linelength, file_x);                Line 558
  total_lines += lines;                                                         Line 559
  total_words += words;                                                         Line 560
  total_chars += chars;                                                         Line 561
  total_bytes += bytes;                                                         Line 562
  if (linelength > max_line_length)                                             Line 563
    max_line_length = linelength;                                               Line 564
                                                                                
  return ok;                                                                    Line 566
}                                                                               Block 6
                                                                                
static bool                                                                     Line 569
wc_file (char const *file, struct fstatus *fstatus)                             Line 570
{                                                                               
  if (! file || STREQ (file, "-"))                                              Line 572
    {                                                                           
      have_read_stdin = true;                                                   Line 574
      xset_binary_mode (STDIN_FILENO, O_BINARY);                                Line 575
      return wc (STDIN_FILENO, file, fstatus, -1);                              Line 576
    }                                                                           
  else                                                                          Line 578
    {                                                                           
      int fd = open (file, O_RDONLY | O_BINARY);                                Line 580...!syscalls auto-comment...
      if (fd == -1)                                                             Line 581
        {                                                                       
          error (0, errno, "%s", quotef (file));                                Line 583
          return false;                                                         Line 584
        }                                                                       
      else                                                                      Line 586
        {                                                                       
          bool ok = wc (fd, file, fstatus, 0);                                  Line 588
          if (close (fd) != 0)                                                  Line 589...!syscalls auto-comment...
            {                                                                   
              error (0, errno, "%s", quotef (file));                            Line 591
              return false;                                                     Line 592
            }                                                                   
          return ok;                                                            Line 594
        }                                                                       
    }                                                                           
}                                                                               Block 7
                                                                                
/* Return the file status for the NFILES files addressed by FILE.               
   Optimize the case where only one number is printed, for just one             
   file; in that case we can use a print width of 1, so we don't need           
   to stat the file.  Handle the case of (nfiles == 0) in the same way;         
   that happens when we don't know how long the list of file names will be.  */ 
                                                                                
static struct fstatus *                                                         Line 605
get_input_fstatus (size_t nfiles, char *const *file)                            Line 606
{                                                                               
  struct fstatus *fstatus = xnmalloc (nfiles ? nfiles : 1, sizeof *fstatus);    Line 608
                                                                                
  if (nfiles == 0                                                               Line 610
      || (nfiles == 1                                                           Line 611
          && ((print_lines + print_words + print_chars                          Line 612
               + print_bytes + print_linelength)                                Line 613
              == 1)))                                                           Line 614
    fstatus[0].failed = 1;                                                      Line 615
  else                                                                          Line 616
    {                                                                           
      for (size_t i = 0; i < nfiles; i++)                                       Line 618
        fstatus[i].failed = (! file[i] || STREQ (file[i], "-")                  Line 619
                             ? fstat (STDIN_FILENO, &fstatus[i].st)             Line 620...!syscalls auto-comment......!syscalls auto-comment...
                             : stat (file[i], &fstatus[i].st));                 Line 621...!syscalls auto-comment...
    }                                                                           
                                                                                
  return fstatus;                                                               Line 624
}                                                                               Block 8
                                                                                
/* Return a print width suitable for the NFILES files whose status is           
   recorded in FSTATUS.  Optimize the same special case that                    
   get_input_fstatus optimizes.  */                                             
                                                                                
static int _GL_ATTRIBUTE_PURE                                                   Line 631
compute_number_width (size_t nfiles, struct fstatus const *fstatus)             Line 632
{                                                                               
  int width = 1;                                                                Line 634
                                                                                
  if (0 < nfiles && fstatus[0].failed <= 0)                                     Line 636
    {                                                                           
      int minimum_width = 1;                                                    Line 638
      uintmax_t regular_total = 0;                                              Line 639
                                                                                
      for (size_t i = 0; i < nfiles; i++)                                       Line 641
        if (! fstatus[i].failed)                                                Line 642
          {                                                                     
            if (S_ISREG (fstatus[i].st.st_mode))                                Line 644
              regular_total += fstatus[i].st.st_size;                           Line 645
            else                                                                Line 646
              minimum_width = 7;                                                Line 647
          }                                                                     
                                                                                
      for (; 10 <= regular_total; regular_total /= 10)                          Line 650
        width++;                                                                Line 651
      if (width < minimum_width)                                                Line 652
        width = minimum_width;                                                  Line 653
    }                                                                           
                                                                                
  return width;                                                                 Line 656
}                                                                               Block 9
                                                                                
                                                                                
int                                                                             
main (int argc, char **argv)                                                    Line 661
{                                                                               
  bool ok;                                                                      Line 663
  int optc;                                                                     Line 664
  size_t nfiles;                                                                Line 665
  char **files;                                                                 Line 666
  char *files_from = NULL;                                                      Line 667
  struct fstatus *fstatus;                                                      Line 668
  struct Tokens tok;                                                            Line 669
                                                                                
  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)
                                                                                
  page_size = getpagesize ();                                                   Line 679
  /* Line buffer stdout to ensure lines are written atomically and immediately  
     so that processes running in parallel do not intersperse their output.  */ 
  setvbuf (stdout, NULL, _IOLBF, 0);                                            Line 682
                                                                                
  print_lines = print_words = print_chars = print_bytes = false;                Line 684
  print_linelength = false;                                                     Line 685
  total_lines = total_words = total_chars = total_bytes = max_line_length = 0;  Line 686
                                                                                
  while ((optc = getopt_long (argc, argv, "clLmw", longopts, NULL)) != -1)      Line 688
    switch (optc)                                                               Line 689
      {                                                                         
      case 'c':                                                                 Line 691
        print_bytes = true;                                                     Line 692
        break;                                                                  Line 693
                                                                                
      case 'm':                                                                 Line 695
        print_chars = true;                                                     Line 696
        break;                                                                  Line 697
                                                                                
      case 'l':                                                                 Line 699
        print_lines = true;                                                     Line 700
        break;                                                                  Line 701
                                                                                
      case 'w':                                                                 Line 703
        print_words = true;                                                     Line 704
        break;                                                                  Line 705
                                                                                
      case 'L':                                                                 Line 707
        print_linelength = true;                                                Line 708
        break;                                                                  Line 709
                                                                                
      case FILES0_FROM_OPTION:                                                  Line 711
        files_from = optarg;                                                    Line 712
        break;                                                                  Line 713
                                                                                
      case_GETOPT_HELP_CHAR;                                                    Line 715
                                                                                
      case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);                         Line 717
                                                                                
      default:                                                                  Line 719
        usage (EXIT_FAILURE);                                                   Line 720
      }                                                                         
                                                                                
  if (! (print_lines || print_words || print_chars || print_bytes               Line 723
         || print_linelength))                                                  Line 724
    print_lines = print_words = print_bytes = true;                             Line 725
                                                                                
  bool read_tokens = false;                                                     Line 727
  struct argv_iterator *ai;                                                     Line 728
  if (files_from)                                                               Line 729
    {                                                                           
      FILE *stream;                                                             Line 731
                                                                                
      /* When using --files0-from=F, you may not specify any files              
         on the command-line.  */                                               
      if (optind < argc)                                                        Line 735
        {                                                                       
          error (0, 0, _("extra operand %s"), quoteaf (argv[optind]));          Line 737
          fprintf (stderr, "%s\n",                                              Line 738
                   _("file operands cannot be combined with --files0-from"));   Line 739
          usage (EXIT_FAILURE);                                                 Line 740
        }                                                                       
                                                                                
      if (STREQ (files_from, "-"))                                              Line 743
        stream = stdin;                                                         Line 744
      else                                                                      Line 745
        {                                                                       
          stream = fopen (files_from, "r");                                     Line 747...!syscalls auto-comment...
          if (stream == NULL)                                                   Line 748
            die (EXIT_FAILURE, errno, _("cannot open %s for reading"),          Line 749
                 quoteaf (files_from));                                         Line 750
        }                                                                       
                                                                                
      /* Read the file list into RAM if we can detect its size and that         
         size is reasonable.  Otherwise, we'll read a name at a time.  */       
      struct stat st;                                                           Line 755
      if (fstat (fileno (stream), &st) == 0                                     Line 756...!syscalls auto-comment......!syscalls auto-comment...
          && S_ISREG (st.st_mode)                                               Line 757
          && st.st_size <= MIN (10 * 1024 * 1024, physmem_available () / 2))    Line 758
        {                                                                       
          read_tokens = true;                                                   Line 760
          readtokens0_init (&tok);                                              Line 761
          if (! readtokens0 (stream, &tok) || fclose (stream) != 0)             Line 762...!syscalls auto-comment...
            die (EXIT_FAILURE, 0, _("cannot read file names from %s"),          Line 763
                 quoteaf (files_from));                                         Line 764
          files = tok.tok;                                                      Line 765
          nfiles = tok.n_tok;                                                   Line 766
          ai = argv_iter_init_argv (files);                                     Line 767
        }                                                                       
      else                                                                      Line 769
        {                                                                       
          files = NULL;                                                         Line 771
          nfiles = 0;                                                           Line 772
          ai = argv_iter_init_stream (stream);                                  Line 773
        }                                                                       
    }                                                                           
  else                                                                          Line 776
    {                                                                           
      static char *stdin_only[] = { NULL };                                     Line 778
      files = (optind < argc ? argv + optind : stdin_only);                     Line 779
      nfiles = (optind < argc ? argc - optind : 1);                             Line 780
      ai = argv_iter_init_argv (files);                                         Line 781
    }                                                                           
                                                                                
  if (!ai)                                                                      Line 784
    xalloc_die ();                                                              ...!common auto-comment...
                                                                                
  fstatus = get_input_fstatus (nfiles, files);                                  Line 787
  number_width = compute_number_width (nfiles, fstatus);                        Line 788
                                                                                
  ok = true;                                                                    Line 790
  for (int i = 0; /* */; i++)                                                   Line 791
    {                                                                           
      bool skip_file = false;                                                   Line 793
      enum argv_iter_err ai_err;                                                Line 794
      char *file_name = argv_iter (ai, &ai_err);                                Line 795
      if (!file_name)                                                           Line 796
        {                                                                       
          switch (ai_err)                                                       Line 798
            {                                                                   
            case AI_ERR_EOF:                                                    Line 800
              goto argv_iter_done;                                              Line 801
            case AI_ERR_READ:                                                   Line 802
              error (0, errno, _("%s: read error"),                             Line 803
                     quotef (files_from));                                      Line 804
              ok = false;                                                       Line 805
              goto argv_iter_done;                                              Line 806
            case AI_ERR_MEM:                                                    Line 807
              xalloc_die ();                                                    ...!common auto-comment...
            default:                                                            Line 809
              assert (!"unexpected error code from argv_iter");                 Line 810
            }                                                                   
        }                                                                       
      if (files_from && STREQ (files_from, "-") && STREQ (file_name, "-"))      Line 813
        {                                                                       
          /* Give a better diagnostic in an unusual case:                       
             printf - | wc --files0-from=- */                                   
          error (0, 0, _("when reading file names from stdin, "                 Line 817
                         "no file name of %s allowed"),                         Line 818
                 quoteaf (file_name));                                          Line 819
          skip_file = true;                                                     Line 820
        }                                                                       
                                                                                
      if (!file_name[0])                                                        Line 823
        {                                                                       
          /* Diagnose a zero-length file name.  When it's one                   
             among many, knowing the record number may help.                    
             FIXME: currently print the record number only with                 
             --files0-from=FILE.  Maybe do it for argv, too?  */                
          if (files_from == NULL)                                               Line 829
            error (0, 0, "%s", _("invalid zero-length file name"));             Line 830
          else                                                                  Line 831
            {                                                                   
              /* Using the standard 'filename:line-number:' prefix here is      
                 not totally appropriate, since NUL is the separator, not NL,   
                 but it might be better than nothing.  */                       
              unsigned long int file_number = argv_iter_n_args (ai);            Line 836
              error (0, 0, "%s:%lu: %s", quotef (files_from),                   Line 837
                     file_number, _("invalid zero-length file name"));          Line 838
            }                                                                   
          skip_file = true;                                                     Line 840
        }                                                                       
                                                                                
      if (skip_file)                                                            Line 843
        ok = false;                                                             Line 844
      else                                                                      Line 845
        ok &= wc_file (file_name, &fstatus[nfiles ? i : 0]);                    Line 846
                                                                                
      if (! nfiles)                                                             Line 848
        fstatus[0].failed = 1;                                                  Line 849
    }                                                                           
 argv_iter_done:                                                                Line 851
                                                                                
  /* No arguments on the command line is fine.  That means read from stdin.     
     However, no arguments on the --files0-from input stream is an error        
     means don't read anything.  */                                             
  if (ok && !files_from && argv_iter_n_args (ai) == 0)                          Line 856
    ok &= wc_file (NULL, &fstatus[0]);                                          Line 857
                                                                                
  if (read_tokens)                                                              Line 859
    readtokens0_free (&tok);                                                    Line 860
                                                                                
  if (1 < argv_iter_n_args (ai))                                                Line 862
    write_counts (total_lines, total_words, total_chars, total_bytes,           Line 863
                  max_line_length, _("total"));                                 Line 864
                                                                                
  argv_iter_free (ai);                                                          Line 866
                                                                                
  free (fstatus);                                                               Line 868
                                                                                
  if (have_read_stdin && close (STDIN_FILENO) != 0)                             Line 870...!syscalls auto-comment...
    die (EXIT_FAILURE, errno, "-");                                             Line 871
                                                                                
  return ok ? EXIT_SUCCESS : EXIT_FAILURE;                                      Line 873
}                                                                               Block 10