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