f92f124a3b24b189ce419273fa0234a0d026cb88
[platform/upstream/coreutils.git] / src / wc.c
1 /* wc - print the number of bytes, words, and lines in files
2    Copyright (C) 85, 91, 1995-2001 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Written by Paul Rubin, phr@ocf.berkeley.edu
19    and David MacKenzie, djm@gnu.ai.mit.edu. */
20 \f
21 #include <config.h>
22 #if HAVE_INTTYPES_H
23 # include <inttypes.h>
24 #endif
25
26 #include <stdio.h>
27 #include <getopt.h>
28 #include <sys/types.h>
29
30 /* Get mbstate_t, mbrtowc(), wcwidth().  */
31 #if HAVE_WCHAR_H
32 # include <wchar.h>
33 #endif
34
35 /* Get iswprint().  */
36 #if HAVE_WCTYPE_H
37 # include <wctype.h>
38 #endif
39 #if !defined iswprint && !HAVE_ISWPRINT
40 # define iswprint(wc) 1
41 #endif
42
43 /* Include this after wctype.h so that we `#undef' ISPRINT
44    (from Solaris's euc.h, from widec.h, from wctype.h) before
45    redefining and using it. */
46 #include "system.h"
47
48 #include "closeout.h"
49 #include "error.h"
50 #include "human.h"
51 #include "safe-read.h"
52
53 /* Some systems, like BeOS, have multibyte encodings but lack mbstate_t.  */
54 #if HAVE_MBRTOWC && defined mbstate_t
55 # define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0)
56 #endif
57
58 #ifndef HAVE_DECL_WCWIDTH
59 "this configure-time declaration test was not run"
60 #endif
61 #if !HAVE_DECL_WCWIDTH
62 extern int wcwidth ();
63 #endif
64
65 /* If wcwidth() doesn't exist, assume all printable characters have
66    width 1.  */
67 #if !defined wcwidth && !HAVE_WCWIDTH
68 # define wcwidth(wc) ((wc) == 0 ? 0 : iswprint (wc) ? 1 : -1)
69 #endif
70
71 /* The official name of this program (e.g., no `g' prefix).  */
72 #define PROGRAM_NAME "wc"
73
74 #define AUTHORS N_ ("Paul Rubin and David MacKenzie")
75
76 /* Size of atomic reads. */
77 #define BUFFER_SIZE (16 * 1024)
78
79 /* The name this program was run with. */
80 char *program_name;
81
82 /* Cumulative number of lines, words, chars and bytes in all files so far.
83    max_line_length is the maximum over all files processed so far.  */
84 static uintmax_t total_lines;
85 static uintmax_t total_words;
86 static uintmax_t total_chars;
87 static uintmax_t total_bytes;
88 static uintmax_t max_line_length;
89
90 /* Which counts to print. */
91 static int print_lines, print_words, print_chars, print_bytes;
92 static int print_linelength;
93
94 /* Nonzero if we have ever read the standard input. */
95 static int have_read_stdin;
96
97 /* The error code to return to the system. */
98 static int exit_status;
99
100 /* If nonzero, do not line up columns but instead separate numbers by
101    a single space as specified in Single Unix Specification and POSIX. */
102 static int posixly_correct;
103
104 static struct option const longopts[] =
105 {
106   {"bytes", no_argument, NULL, 'c'},
107   {"chars", no_argument, NULL, 'm'},
108   {"lines", no_argument, NULL, 'l'},
109   {"words", no_argument, NULL, 'w'},
110   {"max-line-length", no_argument, NULL, 'L'},
111   {GETOPT_HELP_OPTION_DECL},
112   {GETOPT_VERSION_OPTION_DECL},
113   {NULL, 0, NULL, 0}
114 };
115
116 void
117 usage (int status)
118 {
119   if (status != 0)
120     fprintf (stderr, _("Try `%s --help' for more information.\n"),
121              program_name);
122   else
123     {
124       printf (_("\
125 Usage: %s [OPTION]... [FILE]...\n\
126 "),
127               program_name);
128       fputs (_("\
129 Print newline, word, and byte counts for each FILE, and a total line if\n\
130 more than one FILE is specified.  With no FILE, or when FILE is -,\n\
131 read standard input.\n\
132   -c, --bytes            print the byte counts\n\
133   -m, --chars            print the character counts\n\
134   -l, --lines            print the newline counts\n\
135 "), stdout);
136       fputs (_("\
137   -L, --max-line-length  print the length of the longest line\n\
138   -w, --words            print the word counts\n\
139       --help             display this help and exit\n\
140       --version          output version information and exit\n\
141 "), stdout);
142       puts (_("\nReport bugs to <bug-textutils@gnu.org>."));
143     }
144   exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
145 }
146
147 static void
148 write_counts (uintmax_t lines,
149               uintmax_t words,
150               uintmax_t chars,
151               uintmax_t bytes,
152               uintmax_t linelength,
153               const char *file)
154 {
155   char buf[LONGEST_HUMAN_READABLE + 1];
156   char const *space = "";
157   char const *format_int = (posixly_correct ? "%s" : "%7s");
158   char const *format_sp_int = (posixly_correct ? "%s%s" : "%s%7s");
159
160   if (print_lines)
161     {
162       printf (format_int, human_readable (lines, buf, 1, 1));
163       space = " ";
164     }
165   if (print_words)
166     {
167       printf (format_sp_int, space, human_readable (words, buf, 1, 1));
168       space = " ";
169     }
170   if (print_chars)
171     {
172       printf (format_sp_int, space, human_readable (chars, buf, 1, 1));
173       space = " ";
174     }
175   if (print_bytes)
176     {
177       printf (format_sp_int, space, human_readable (bytes, buf, 1, 1));
178       space = " ";
179     }
180   if (print_linelength)
181     {
182       printf (format_sp_int, space, human_readable (linelength, buf, 1, 1));
183     }
184   if (*file)
185     printf (" %s", file);
186   putchar ('\n');
187 }
188
189 static void
190 wc (int fd, const char *file)
191 {
192   char buf[BUFFER_SIZE + 1];
193   ssize_t bytes_read;
194   uintmax_t lines, words, chars, bytes, linelength;
195   int count_bytes, count_chars, count_complicated;
196
197   lines = words = chars = bytes = linelength = 0;
198
199   /* If in the current locale, chars are equivalent to bytes, we prefer
200      counting bytes, because that's easier.  */
201 #if HAVE_MBRTOWC && (MB_LEN_MAX > 1)
202   if (MB_CUR_MAX > 1)
203     {
204       count_bytes = print_bytes;
205       count_chars = print_chars;
206     }
207   else
208 #endif
209     {
210       count_bytes = print_bytes + print_chars;
211       count_chars = 0;
212     }
213   count_complicated = print_words + print_linelength;
214
215   /* We need binary input, since `wc' relies on `lseek' and byte counts.  */
216   SET_BINARY (fd);
217
218   /* When counting only bytes, save some line- and word-counting
219      overhead.  If FD is a `regular' Unix file, using lseek is enough
220      to get its `size' in bytes.  Otherwise, read blocks of BUFFER_SIZE
221      bytes at a time until EOF.  Note that the `size' (number of bytes)
222      that wc reports is smaller than stats.st_size when the file is not
223      positioned at its beginning.  That's why the lseek calls below are
224      necessary.  For example the command
225      `(dd ibs=99k skip=1 count=0; ./wc -c) < /etc/group'
226      should make wc report `0' bytes.  */
227
228   if (count_bytes && !count_chars && !print_lines && !count_complicated)
229     {
230       off_t current_pos, end_pos;
231       struct stat stats;
232
233       if (fstat (fd, &stats) == 0 && S_ISREG (stats.st_mode)
234           && (current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
235           && (end_pos = lseek (fd, (off_t) 0, SEEK_END)) != -1)
236         {
237           off_t diff;
238           /* Be careful here.  The current position may actually be
239              beyond the end of the file.  As in the example above.  */
240           bytes = (diff = end_pos - current_pos) < 0 ? 0 : diff;
241         }
242       else
243         {
244           while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
245             {
246               bytes += bytes_read;
247             }
248           if (bytes_read < 0)
249             {
250               error (0, errno, "%s", file);
251               exit_status = 1;
252             }
253         }
254     }
255   else if (!count_chars && !count_complicated)
256     {
257       /* Use a separate loop when counting only lines or lines and bytes --
258          but not chars or words.  */
259       while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
260         {
261           register char *p = buf;
262
263           while ((p = memchr (p, '\n', (buf + bytes_read) - p)))
264             {
265               ++p;
266               ++lines;
267             }
268           bytes += bytes_read;
269         }
270       if (bytes_read < 0)
271         {
272           error (0, errno, "%s", file);
273           exit_status = 1;
274         }
275     }
276 #if HAVE_MBRTOWC && (MB_LEN_MAX > 1)
277 # define SUPPORT_OLD_MBRTOWC 1
278   else if (MB_CUR_MAX > 1)
279     {
280       int in_word = 0;
281       uintmax_t linepos = 0;
282       mbstate_t state;
283       uintmax_t last_error_line = 0;
284       int last_error_errno = 0;
285 # if SUPPORT_OLD_MBRTOWC
286       /* Back-up the state before each multibyte character conversion and
287          move the last incomplete character of the buffer to the front
288          of the buffer.  This is needed because we don't know whether
289          the `mbrtowc' function updates the state when it returns -2, -
290          this is the ISO C 99 and glibc-2.2 behaviour - or not - amended
291          ANSI C, glibc-2.1 and Solaris 2.7 behaviour.  We don't have an
292          autoconf test for this, yet.  */
293       int prev = 0; /* number of bytes carried over from previous round */
294 # else
295       const int prev = 0;
296 # endif
297
298       memset (&state, 0, sizeof (mbstate_t));
299       while ((bytes_read = safe_read (fd, buf + prev, BUFFER_SIZE - prev)) > 0)
300         {
301           const char *p;
302 # if SUPPORT_OLD_MBRTOWC
303           mbstate_t backup_state;
304 # endif
305
306           bytes += bytes_read;
307           p = buf;
308           bytes_read += prev;
309           do
310             {
311               wchar_t wide_char;
312               size_t n;
313
314 # if SUPPORT_OLD_MBRTOWC
315               backup_state = state;
316 # endif
317               n = mbrtowc (&wide_char, p, bytes_read, &state);
318               if (n == (size_t) -2)
319                 {
320 # if SUPPORT_OLD_MBRTOWC
321                   state = backup_state;
322 # endif
323                   break;
324                 }
325               if (n == (size_t) -1)
326                 {
327                   /* Signal repeated errors only once per line.  */
328                   if (!(lines + 1 == last_error_line
329                         && errno == last_error_errno))
330                     {
331                       char hr_buf[LONGEST_HUMAN_READABLE + 1];
332                       last_error_line = lines + 1;
333                       last_error_errno = errno;
334                       error (0, errno, "%s:%s", file,
335                              human_readable (lines + 1, hr_buf, 1, 1));
336                     }
337                   p++;
338                   bytes_read--;
339                 }
340               else
341                 {
342                   if (n == 0)
343                     {
344                       wide_char = 0;
345                       n = 1;
346                     }
347                   p += n;
348                   bytes_read -= n;
349                   chars++;
350                   switch (wide_char)
351                     {
352                     case '\n':
353                       lines++;
354                       /* Fall through. */
355                     case '\r':
356                     case '\f':
357                       if (linepos > linelength)
358                         linelength = linepos;
359                       linepos = 0;
360                       goto mb_word_separator;
361                     case '\t':
362                       linepos += 8 - (linepos % 8);
363                       goto mb_word_separator;
364                     case ' ':
365                       linepos++;
366                       /* Fall through. */
367                     case '\v':
368                     mb_word_separator:
369                       if (in_word)
370                         {
371                           in_word = 0;
372                           words++;
373                         }
374                       break;
375                     default:
376                       if (iswprint (wide_char))
377                         {
378                           int width = wcwidth (wide_char);
379                           if (width > 0)
380                             linepos += width;
381                           in_word = 1;
382                         }
383                       break;
384                     }
385                 }
386             }
387           while (bytes_read > 0);
388
389 # if SUPPORT_OLD_MBRTOWC
390           if (bytes_read > 0)
391             {
392               if (bytes_read == BUFFER_SIZE)
393                 {
394                   /* Encountered a very long redundant shift sequence.  */
395                   p++;
396                   bytes_read--;
397                 }
398               memmove (buf, p, bytes_read);
399             }
400           prev = bytes_read;
401 # endif
402         }
403       if (bytes_read < 0)
404         {
405           error (0, errno, "%s", file);
406           exit_status = 1;
407         }
408       if (linepos > linelength)
409         linelength = linepos;
410       if (in_word)
411         words++;
412     }
413 #endif
414   else
415     {
416       int in_word = 0;
417       uintmax_t linepos = 0;
418
419       while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
420         {
421           const char *p = buf;
422
423           bytes += bytes_read;
424           do
425             {
426               switch (*p++)
427                 {
428                 case '\n':
429                   lines++;
430                   /* Fall through. */
431                 case '\r':
432                 case '\f':
433                   if (linepos > linelength)
434                     linelength = linepos;
435                   linepos = 0;
436                   goto word_separator;
437                 case '\t':
438                   linepos += 8 - (linepos % 8);
439                   goto word_separator;
440                 case ' ':
441                   linepos++;
442                   /* Fall through. */
443                 case '\v':
444                 word_separator:
445                   if (in_word)
446                     {
447                       in_word = 0;
448                       words++;
449                     }
450                   break;
451                 default:
452                   if (ISPRINT ((unsigned char) p[-1]))
453                     {
454                       linepos++;
455                       in_word = 1;
456                     }
457                   break;
458                 }
459             }
460           while (--bytes_read);
461         }
462       if (bytes_read < 0)
463         {
464           error (0, errno, "%s", file);
465           exit_status = 1;
466         }
467       if (linepos > linelength)
468         linelength = linepos;
469       if (in_word)
470         words++;
471     }
472
473   if (count_chars < print_chars)
474     chars = bytes;
475
476   write_counts (lines, words, chars, bytes, linelength, file);
477   total_lines += lines;
478   total_words += words;
479   total_chars += chars;
480   total_bytes += bytes;
481   if (linelength > max_line_length)
482     max_line_length = linelength;
483 }
484
485 static void
486 wc_file (const char *file)
487 {
488   if (STREQ (file, "-"))
489     {
490       have_read_stdin = 1;
491       wc (0, file);
492     }
493   else
494     {
495       int fd = open (file, O_RDONLY);
496       if (fd == -1)
497         {
498           error (0, errno, "%s", file);
499           exit_status = 1;
500           return;
501         }
502       wc (fd, file);
503       if (close (fd))
504         {
505           error (0, errno, "%s", file);
506           exit_status = 1;
507         }
508     }
509 }
510
511 int
512 main (int argc, char **argv)
513 {
514   int optc;
515   int nfiles;
516
517   program_name = argv[0];
518   setlocale (LC_ALL, "");
519   bindtextdomain (PACKAGE, LOCALEDIR);
520   textdomain (PACKAGE);
521
522   atexit (close_stdout);
523
524   exit_status = 0;
525   posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
526   print_lines = print_words = print_chars = print_bytes = print_linelength = 0;
527   total_lines = total_words = total_chars = total_bytes = max_line_length = 0;
528
529   while ((optc = getopt_long (argc, argv, "clLmw", longopts, NULL)) != -1)
530     switch (optc)
531       {
532       case 0:
533         break;
534
535       case 'c':
536         print_bytes = 1;
537         break;
538
539       case 'm':
540         print_chars = 1;
541         break;
542
543       case 'l':
544         print_lines = 1;
545         break;
546
547       case 'w':
548         print_words = 1;
549         break;
550
551       case 'L':
552         print_linelength = 1;
553         break;
554
555       case_GETOPT_HELP_CHAR;
556
557       case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
558
559       default:
560         usage (1);
561       }
562
563   if (print_lines + print_words + print_chars + print_bytes + print_linelength
564       == 0)
565     print_lines = print_words = print_bytes = 1;
566
567   nfiles = argc - optind;
568
569   if (nfiles == 0)
570     {
571       have_read_stdin = 1;
572       wc (0, "");
573     }
574   else
575     {
576       for (; optind < argc; ++optind)
577         wc_file (argv[optind]);
578
579       if (nfiles > 1)
580         write_counts (total_lines, total_words, total_chars, total_bytes,
581                       max_line_length, _("total"));
582     }
583
584   if (have_read_stdin && close (0))
585     error (EXIT_FAILURE, errno, "-");
586
587   exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
588 }