Compare getopt_long return value against -1, not EOF. Use NULL, not '(int *) 0'...
[platform/upstream/coreutils.git] / src / wc.c
1 /* wc - print the number of bytes, words, and lines in files
2    Copyright (C) 85, 91, 95, 1996 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
16    Foundation, 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
23 #include <stdio.h>
24 #include <getopt.h>
25 #include <sys/types.h>
26 #include "system.h"
27 #include "error.h"
28
29 /* Size of atomic reads. */
30 #define BUFFER_SIZE (16 * 1024)
31
32 int safe_read ();
33
34 /* The name this program was run with. */
35 char *program_name;
36
37 /* Cumulative number of lines, words, and chars in all files so far. */
38 static unsigned long total_lines, total_words, total_chars;
39
40 /* Which counts to print. */
41 static int print_lines, print_words, print_chars;
42
43 /* Nonzero if we have ever read the standard input. */
44 static int have_read_stdin;
45
46 /* The error code to return to the system. */
47 static int exit_status;
48
49 /* If nonzero, display usage information and exit.  */
50 static int show_help;
51
52 /* If nonzero, print the version on standard output then exits.  */
53 static int show_version;
54
55 static struct option const longopts[] =
56 {
57   {"bytes", no_argument, NULL, 'c'},
58   {"chars", no_argument, NULL, 'c'},
59   {"lines", no_argument, NULL, 'l'},
60   {"words", no_argument, NULL, 'w'},
61   {"help", no_argument, &show_help, 1},
62   {"version", no_argument, &show_version, 1},
63   {NULL, 0, NULL, 0}
64 };
65
66 static void
67 usage (int status)
68 {
69   if (status != 0)
70     fprintf (stderr, _("Try `%s --help' for more information.\n"),
71              program_name);
72   else
73     {
74       printf (_("\
75 Usage: %s [OPTION]... [FILE]...\n\
76 "),
77               program_name);
78       printf (_("\
79 Print line, word, and byte counts for each FILE, and a total line if\n\
80 more than one FILE is specified.  With no FILE, or when FILE is -,\n\
81 read standard input.\n\
82   -c, --bytes, --chars   print the byte counts\n\
83   -l, --lines            print the newline counts\n\
84   -w, --words            print the word counts\n\
85       --help             display this help and exit\n\
86       --version          output version information and exit\n\
87 "));
88       puts (_("\nReport bugs to textutils-bugs@gnu.ai.mit.edu"));
89     }
90   exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
91 }
92
93 static void
94 write_counts (long unsigned int lines, long unsigned int words,
95               long unsigned int chars, const char *file)
96 {
97   if (print_lines)
98     printf ("%7lu", lines);
99   if (print_words)
100     {
101       if (print_lines)
102         putchar (' ');
103       printf ("%7lu", words);
104     }
105   if (print_chars)
106     {
107       if (print_lines || print_words)
108         putchar (' ');
109       printf ("%7lu", chars);
110     }
111   if (*file)
112     printf (" %s", file);
113   putchar ('\n');
114 }
115
116 static void
117 wc (int fd, const char *file)
118 {
119   char buf[BUFFER_SIZE + 1];
120   register int bytes_read;
121   register int in_word = 0;
122   register unsigned long lines, words, chars;
123
124   lines = words = chars = 0;
125
126   /* When counting only bytes, save some line- and word-counting
127      overhead.  If FD is a `regular' Unix file, using lseek is enough
128      to get its `size' in bytes.  Otherwise, read blocks of BUFFER_SIZE
129      bytes at a time until EOF.  Note that the `size' (number of bytes)
130      that wc reports is smaller than stats.st_size when the file is not
131      positioned at its beginning.  That's why the lseek calls below are
132      necessary.  For example the command
133      `(dd ibs=99k skip=1 count=0; ./wc -c) < /etc/group'
134      should make wc report `0' bytes.  */
135
136   if (print_chars && !print_words && !print_lines)
137     {
138       off_t current_pos, end_pos;
139       struct stat stats;
140
141       if (fstat (fd, &stats) == 0 && S_ISREG (stats.st_mode)
142           && (current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
143           && (end_pos = lseek (fd, (off_t) 0, SEEK_END)) != -1)
144         {
145           off_t diff;
146           /* Be careful here.  The current position may actually be
147              beyond the end of the file.  As in the example above.  */
148           chars = (diff = end_pos - current_pos) < 0 ? 0 : diff;
149         }
150       else
151         {
152           while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
153             {
154               chars += bytes_read;
155             }
156           if (bytes_read < 0)
157             {
158               error (0, errno, "%s", file);
159               exit_status = 1;
160             }
161         }
162     }
163   else if (!print_words)
164     {
165       /* Use a separate loop when counting only lines or lines and bytes --
166          but not words.  */
167       while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
168         {
169           register char *p = buf;
170
171           while ((p = memchr (p, '\n', (buf + bytes_read) - p)))
172             {
173               ++p;
174               ++lines;
175             }
176           chars += bytes_read;
177         }
178       if (bytes_read < 0)
179         {
180           error (0, errno, "%s", file);
181           exit_status = 1;
182         }
183     }
184   else
185     {
186       while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
187         {
188           register char *p = buf;
189
190           chars += bytes_read;
191           do
192             {
193               switch (*p++)
194                 {
195                 case '\n':
196                   lines++;
197                   /* Fall through. */
198                 case '\r':
199                 case '\f':
200                 case '\t':
201                 case '\v':
202                 case ' ':
203                   if (in_word)
204                     {
205                       in_word = 0;
206                       words++;
207                     }
208                   break;
209                 default:
210                   in_word = 1;
211                   break;
212                 }
213             }
214           while (--bytes_read);
215         }
216       if (bytes_read < 0)
217         {
218           error (0, errno, "%s", file);
219           exit_status = 1;
220         }
221       if (in_word)
222         words++;
223     }
224
225   write_counts (lines, words, chars, file);
226   total_lines += lines;
227   total_words += words;
228   total_chars += chars;
229 }
230
231 static void
232 wc_file (const char *file)
233 {
234   if (!strcmp (file, "-"))
235     {
236       have_read_stdin = 1;
237       wc (0, file);
238     }
239   else
240     {
241       int fd = open (file, O_RDONLY);
242       if (fd == -1)
243         {
244           error (0, errno, "%s", file);
245           exit_status = 1;
246           return;
247         }
248       wc (fd, file);
249       if (close (fd))
250         {
251           error (0, errno, "%s", file);
252           exit_status = 1;
253         }
254     }
255 }
256
257 int
258 main (int argc, char **argv)
259 {
260   int optc;
261   int nfiles;
262
263   program_name = argv[0];
264   setlocale (LC_ALL, "");
265   bindtextdomain (PACKAGE, LOCALEDIR);
266   textdomain (PACKAGE);
267
268   exit_status = 0;
269   print_lines = print_words = print_chars = 0;
270   total_lines = total_words = total_chars = 0;
271
272   while ((optc = getopt_long (argc, argv, "clw", longopts, NULL)) != -1)
273     switch (optc)
274       {
275       case 0:
276         break;
277
278       case 'c':
279         print_chars = 1;
280         break;
281
282       case 'l':
283         print_lines = 1;
284         break;
285
286       case 'w':
287         print_words = 1;
288         break;
289
290       default:
291         usage (1);
292       }
293
294   if (show_version)
295     {
296       printf ("wc (%s) %s\n", GNU_PACKAGE, VERSION);
297       exit (EXIT_SUCCESS);
298     }
299
300   if (show_help)
301     usage (0);
302
303   if (print_lines + print_words + print_chars == 0)
304     print_lines = print_words = print_chars = 1;
305
306   nfiles = argc - optind;
307
308   if (nfiles == 0)
309     {
310       have_read_stdin = 1;
311       wc (0, "");
312     }
313   else
314     {
315       for (; optind < argc; ++optind)
316         wc_file (argv[optind]);
317
318       if (nfiles > 1)
319         write_counts (total_lines, total_words, total_chars, _("total"));
320     }
321
322   if (have_read_stdin && close (0))
323     error (EXIT_FAILURE, errno, "-");
324
325   exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
326 }