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