Factor out some common strings to make translation easier.
[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 "), stdout);
140       fputs (_("\
141       --help             display this help and exit\n\
142       --version          output version information and exit\n\
143 "), stdout);
144       puts (_("\nReport bugs to <bug-textutils@gnu.org>."));
145     }
146   exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
147 }
148
149 static void
150 write_counts (uintmax_t lines,
151               uintmax_t words,
152               uintmax_t chars,
153               uintmax_t bytes,
154               uintmax_t linelength,
155               const char *file)
156 {
157   char buf[LONGEST_HUMAN_READABLE + 1];
158   char const *space = "";
159   char const *format_int = (posixly_correct ? "%s" : "%7s");
160   char const *format_sp_int = (posixly_correct ? "%s%s" : "%s%7s");
161
162   if (print_lines)
163     {
164       printf (format_int, human_readable (lines, buf, 1, 1));
165       space = " ";
166     }
167   if (print_words)
168     {
169       printf (format_sp_int, space, human_readable (words, buf, 1, 1));
170       space = " ";
171     }
172   if (print_chars)
173     {
174       printf (format_sp_int, space, human_readable (chars, buf, 1, 1));
175       space = " ";
176     }
177   if (print_bytes)
178     {
179       printf (format_sp_int, space, human_readable (bytes, buf, 1, 1));
180       space = " ";
181     }
182   if (print_linelength)
183     {
184       printf (format_sp_int, space, human_readable (linelength, buf, 1, 1));
185     }
186   if (*file)
187     printf (" %s", file);
188   putchar ('\n');
189 }
190
191 static void
192 wc (int fd, const char *file)
193 {
194   char buf[BUFFER_SIZE + 1];
195   ssize_t bytes_read;
196   uintmax_t lines, words, chars, bytes, linelength;
197   int count_bytes, count_chars, count_complicated;
198
199   lines = words = chars = bytes = linelength = 0;
200
201   /* If in the current locale, chars are equivalent to bytes, we prefer
202      counting bytes, because that's easier.  */
203 #if HAVE_MBRTOWC && (MB_LEN_MAX > 1)
204   if (MB_CUR_MAX > 1)
205     {
206       count_bytes = print_bytes;
207       count_chars = print_chars;
208     }
209   else
210 #endif
211     {
212       count_bytes = print_bytes + print_chars;
213       count_chars = 0;
214     }
215   count_complicated = print_words + print_linelength;
216
217   /* We need binary input, since `wc' relies on `lseek' and byte counts.  */
218   SET_BINARY (fd);
219
220   /* When counting only bytes, save some line- and word-counting
221      overhead.  If FD is a `regular' Unix file, using lseek is enough
222      to get its `size' in bytes.  Otherwise, read blocks of BUFFER_SIZE
223      bytes at a time until EOF.  Note that the `size' (number of bytes)
224      that wc reports is smaller than stats.st_size when the file is not
225      positioned at its beginning.  That's why the lseek calls below are
226      necessary.  For example the command
227      `(dd ibs=99k skip=1 count=0; ./wc -c) < /etc/group'
228      should make wc report `0' bytes.  */
229
230   if (count_bytes && !count_chars && !print_lines && !count_complicated)
231     {
232       off_t current_pos, end_pos;
233       struct stat stats;
234
235       if (fstat (fd, &stats) == 0 && S_ISREG (stats.st_mode)
236           && (current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
237           && (end_pos = lseek (fd, (off_t) 0, SEEK_END)) != -1)
238         {
239           off_t diff;
240           /* Be careful here.  The current position may actually be
241              beyond the end of the file.  As in the example above.  */
242           bytes = (diff = end_pos - current_pos) < 0 ? 0 : diff;
243         }
244       else
245         {
246           while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
247             {
248               bytes += bytes_read;
249             }
250           if (bytes_read < 0)
251             {
252               error (0, errno, "%s", file);
253               exit_status = 1;
254             }
255         }
256     }
257   else if (!count_chars && !count_complicated)
258     {
259       /* Use a separate loop when counting only lines or lines and bytes --
260          but not chars or words.  */
261       while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
262         {
263           register char *p = buf;
264
265           while ((p = memchr (p, '\n', (buf + bytes_read) - p)))
266             {
267               ++p;
268               ++lines;
269             }
270           bytes += bytes_read;
271         }
272       if (bytes_read < 0)
273         {
274           error (0, errno, "%s", file);
275           exit_status = 1;
276         }
277     }
278 #if HAVE_MBRTOWC && (MB_LEN_MAX > 1)
279 # define SUPPORT_OLD_MBRTOWC 1
280   else if (MB_CUR_MAX > 1)
281     {
282       int in_word = 0;
283       uintmax_t linepos = 0;
284       mbstate_t state;
285       uintmax_t last_error_line = 0;
286       int last_error_errno = 0;
287 # if SUPPORT_OLD_MBRTOWC
288       /* Back-up the state before each multibyte character conversion and
289          move the last incomplete character of the buffer to the front
290          of the buffer.  This is needed because we don't know whether
291          the `mbrtowc' function updates the state when it returns -2, -
292          this is the ISO C 99 and glibc-2.2 behaviour - or not - amended
293          ANSI C, glibc-2.1 and Solaris 2.7 behaviour.  We don't have an
294          autoconf test for this, yet.  */
295       int prev = 0; /* number of bytes carried over from previous round */
296 # else
297       const int prev = 0;
298 # endif
299
300       memset (&state, 0, sizeof (mbstate_t));
301       while ((bytes_read = safe_read (fd, buf + prev, BUFFER_SIZE - prev)) > 0)
302         {
303           const char *p;
304 # if SUPPORT_OLD_MBRTOWC
305           mbstate_t backup_state;
306 # endif
307
308           bytes += bytes_read;
309           p = buf;
310           bytes_read += prev;
311           do
312             {
313               wchar_t wide_char;
314               size_t n;
315
316 # if SUPPORT_OLD_MBRTOWC
317               backup_state = state;
318 # endif
319               n = mbrtowc (&wide_char, p, bytes_read, &state);
320               if (n == (size_t) -2)
321                 {
322 # if SUPPORT_OLD_MBRTOWC
323                   state = backup_state;
324 # endif
325                   break;
326                 }
327               if (n == (size_t) -1)
328                 {
329                   /* Signal repeated errors only once per line.  */
330                   if (!(lines + 1 == last_error_line
331                         && errno == last_error_errno))
332                     {
333                       char hr_buf[LONGEST_HUMAN_READABLE + 1];
334                       last_error_line = lines + 1;
335                       last_error_errno = errno;
336                       error (0, errno, "%s:%s", file,
337                              human_readable (lines + 1, hr_buf, 1, 1));
338                     }
339                   p++;
340                   bytes_read--;
341                 }
342               else
343                 {
344                   if (n == 0)
345                     {
346                       wide_char = 0;
347                       n = 1;
348                     }
349                   p += n;
350                   bytes_read -= n;
351                   chars++;
352                   switch (wide_char)
353                     {
354                     case '\n':
355                       lines++;
356                       /* Fall through. */
357                     case '\r':
358                     case '\f':
359                       if (linepos > linelength)
360                         linelength = linepos;
361                       linepos = 0;
362                       goto mb_word_separator;
363                     case '\t':
364                       linepos += 8 - (linepos % 8);
365                       goto mb_word_separator;
366                     case ' ':
367                       linepos++;
368                       /* Fall through. */
369                     case '\v':
370                     mb_word_separator:
371                       if (in_word)
372                         {
373                           in_word = 0;
374                           words++;
375                         }
376                       break;
377                     default:
378                       if (iswprint (wide_char))
379                         {
380                           int width = wcwidth (wide_char);
381                           if (width > 0)
382                             linepos += width;
383                           in_word = 1;
384                         }
385                       break;
386                     }
387                 }
388             }
389           while (bytes_read > 0);
390
391 # if SUPPORT_OLD_MBRTOWC
392           if (bytes_read > 0)
393             {
394               if (bytes_read == BUFFER_SIZE)
395                 {
396                   /* Encountered a very long redundant shift sequence.  */
397                   p++;
398                   bytes_read--;
399                 }
400               memmove (buf, p, bytes_read);
401             }
402           prev = bytes_read;
403 # endif
404         }
405       if (bytes_read < 0)
406         {
407           error (0, errno, "%s", file);
408           exit_status = 1;
409         }
410       if (linepos > linelength)
411         linelength = linepos;
412       if (in_word)
413         words++;
414     }
415 #endif
416   else
417     {
418       int in_word = 0;
419       uintmax_t linepos = 0;
420
421       while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
422         {
423           const char *p = buf;
424
425           bytes += bytes_read;
426           do
427             {
428               switch (*p++)
429                 {
430                 case '\n':
431                   lines++;
432                   /* Fall through. */
433                 case '\r':
434                 case '\f':
435                   if (linepos > linelength)
436                     linelength = linepos;
437                   linepos = 0;
438                   goto word_separator;
439                 case '\t':
440                   linepos += 8 - (linepos % 8);
441                   goto word_separator;
442                 case ' ':
443                   linepos++;
444                   /* Fall through. */
445                 case '\v':
446                 word_separator:
447                   if (in_word)
448                     {
449                       in_word = 0;
450                       words++;
451                     }
452                   break;
453                 default:
454                   if (ISPRINT ((unsigned char) p[-1]))
455                     {
456                       linepos++;
457                       in_word = 1;
458                     }
459                   break;
460                 }
461             }
462           while (--bytes_read);
463         }
464       if (bytes_read < 0)
465         {
466           error (0, errno, "%s", file);
467           exit_status = 1;
468         }
469       if (linepos > linelength)
470         linelength = linepos;
471       if (in_word)
472         words++;
473     }
474
475   if (count_chars < print_chars)
476     chars = bytes;
477
478   write_counts (lines, words, chars, bytes, linelength, file);
479   total_lines += lines;
480   total_words += words;
481   total_chars += chars;
482   total_bytes += bytes;
483   if (linelength > max_line_length)
484     max_line_length = linelength;
485 }
486
487 static void
488 wc_file (const char *file)
489 {
490   if (STREQ (file, "-"))
491     {
492       have_read_stdin = 1;
493       wc (0, file);
494     }
495   else
496     {
497       int fd = open (file, O_RDONLY);
498       if (fd == -1)
499         {
500           error (0, errno, "%s", file);
501           exit_status = 1;
502           return;
503         }
504       wc (fd, file);
505       if (close (fd))
506         {
507           error (0, errno, "%s", file);
508           exit_status = 1;
509         }
510     }
511 }
512
513 int
514 main (int argc, char **argv)
515 {
516   int optc;
517   int nfiles;
518
519   program_name = argv[0];
520   setlocale (LC_ALL, "");
521   bindtextdomain (PACKAGE, LOCALEDIR);
522   textdomain (PACKAGE);
523
524   atexit (close_stdout);
525
526   exit_status = 0;
527   posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
528   print_lines = print_words = print_chars = print_bytes = print_linelength = 0;
529   total_lines = total_words = total_chars = total_bytes = max_line_length = 0;
530
531   while ((optc = getopt_long (argc, argv, "clLmw", longopts, NULL)) != -1)
532     switch (optc)
533       {
534       case 0:
535         break;
536
537       case 'c':
538         print_bytes = 1;
539         break;
540
541       case 'm':
542         print_chars = 1;
543         break;
544
545       case 'l':
546         print_lines = 1;
547         break;
548
549       case 'w':
550         print_words = 1;
551         break;
552
553       case 'L':
554         print_linelength = 1;
555         break;
556
557       case_GETOPT_HELP_CHAR;
558
559       case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
560
561       default:
562         usage (1);
563       }
564
565   if (print_lines + print_words + print_chars + print_bytes + print_linelength
566       == 0)
567     print_lines = print_words = print_bytes = 1;
568
569   nfiles = argc - optind;
570
571   if (nfiles == 0)
572     {
573       have_read_stdin = 1;
574       wc (0, "");
575     }
576   else
577     {
578       for (; optind < argc; ++optind)
579         wc_file (argv[optind]);
580
581       if (nfiles > 1)
582         write_counts (total_lines, total_words, total_chars, total_bytes,
583                       max_line_length, _("total"));
584     }
585
586   if (have_read_stdin && close (0))
587     error (EXIT_FAILURE, errno, "-");
588
589   exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
590 }