Don't include version.h.
[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., 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   -l, --lines            print the newline counts\n\
83   -w, --words            print the word counts\n\
84   -c, --bytes, --chars   print the byte counts\n\
85       --help             display this help and exit\n\
86       --version          output version information and exit\n\
87 "));
88     }
89   exit (status);
90 }
91
92 static void
93 write_counts (long unsigned int lines, long unsigned int words,
94               long unsigned int chars, const char *file)
95 {
96   if (print_lines)
97     printf ("%7lu", lines);
98   if (print_words)
99     {
100       if (print_lines)
101         putchar (' ');
102       printf ("%7lu", words);
103     }
104   if (print_chars)
105     {
106       if (print_lines || print_words)
107         putchar (' ');
108       printf ("%7lu", chars);
109     }
110   if (*file)
111     printf (" %s", file);
112   putchar ('\n');
113 }
114
115 static void
116 wc (int fd, const char *file)
117 {
118   char buf[BUFFER_SIZE + 1];
119   register int bytes_read;
120   register int in_word = 0;
121   register unsigned long lines, words, chars;
122
123   lines = words = chars = 0;
124
125   /* When counting only bytes, save some line- and word-counting
126      overhead.  If FD is a `regular' Unix file, using lseek is enough
127      to get its `size' in bytes.  Otherwise, read blocks of BUFFER_SIZE
128      bytes at a time until EOF.  Note that the `size' (number of bytes)
129      that wc reports is smaller than stats.st_size when the file is not
130      positioned at its beginning.  That's why the lseek calls below are
131      necessary.  For example the command
132      `(dd ibs=99k skip=1 count=0; ./wc -c) < /etc/group'
133      should make wc report `0' bytes.  */
134
135   if (print_chars && !print_words && !print_lines)
136     {
137       off_t current_pos, end_pos;
138       struct stat stats;
139
140       if (fstat (fd, &stats) == 0 && S_ISREG (stats.st_mode)
141           && (current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
142           && (end_pos = lseek (fd, (off_t) 0, SEEK_END)) != -1)
143         {
144           off_t diff;
145           /* Be careful here.  The current position may actually be
146              beyond the end of the file.  As in the example above.  */
147           chars = (diff = end_pos - current_pos) < 0 ? 0 : diff;
148         }
149       else
150         {
151           while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
152             {
153               chars += bytes_read;
154             }
155           if (bytes_read < 0)
156             {
157               error (0, errno, "%s", file);
158               exit_status = 1;
159             }
160         }
161     }
162   else if (!print_words)
163     {
164       /* Use a separate loop when counting only lines or lines and bytes --
165          but not words.  */
166       while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
167         {
168           register char *p = buf;
169
170           while ((p = memchr (p, '\n', (buf + bytes_read) - p)))
171             {
172               ++p;
173               ++lines;
174             }
175           chars += bytes_read;
176         }
177       if (bytes_read < 0)
178         {
179           error (0, errno, "%s", file);
180           exit_status = 1;
181         }
182     }
183   else
184     {
185       while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
186         {
187           register char *p = buf;
188
189           chars += bytes_read;
190           do
191             {
192               switch (*p++)
193                 {
194                 case '\n':
195                   lines++;
196                   /* Fall through. */
197                 case '\r':
198                 case '\f':
199                 case '\t':
200                 case '\v':
201                 case ' ':
202                   if (in_word)
203                     {
204                       in_word = 0;
205                       words++;
206                     }
207                   break;
208                 default:
209                   in_word = 1;
210                   break;
211                 }
212             }
213           while (--bytes_read);
214         }
215       if (bytes_read < 0)
216         {
217           error (0, errno, "%s", file);
218           exit_status = 1;
219         }
220       if (in_word)
221         words++;
222     }
223
224   write_counts (lines, words, chars, file);
225   total_lines += lines;
226   total_words += words;
227   total_chars += chars;
228 }
229
230 static void
231 wc_file (const char *file)
232 {
233   if (!strcmp (file, "-"))
234     {
235       have_read_stdin = 1;
236       wc (0, file);
237     }
238   else
239     {
240       int fd = open (file, O_RDONLY);
241       if (fd == -1)
242         {
243           error (0, errno, "%s", file);
244           exit_status = 1;
245           return;
246         }
247       wc (fd, file);
248       if (close (fd))
249         {
250           error (0, errno, "%s", file);
251           exit_status = 1;
252         }
253     }
254 }
255
256 void
257 main (int argc, char **argv)
258 {
259   int optc;
260   int nfiles;
261
262   program_name = argv[0];
263   setlocale (LC_ALL, "");
264   bindtextdomain (PACKAGE, LOCALEDIR);
265   textdomain (PACKAGE);
266
267   exit_status = 0;
268   print_lines = print_words = print_chars = 0;
269   total_lines = total_words = total_chars = 0;
270
271   while ((optc = getopt_long (argc, argv, "clw", longopts, (int *) 0)) != EOF)
272     switch (optc)
273       {
274       case 0:
275         break;
276
277       case 'c':
278         print_chars = 1;
279         break;
280
281       case 'l':
282         print_lines = 1;
283         break;
284
285       case 'w':
286         print_words = 1;
287         break;
288
289       default:
290         usage (1);
291       }
292
293   if (show_version)
294     {
295       printf ("wc - %s\n", PACKAGE_VERSION);
296       exit (0);
297     }
298
299   if (show_help)
300     usage (0);
301
302   if (print_lines + print_words + print_chars == 0)
303     print_lines = print_words = print_chars = 1;
304
305   nfiles = argc - optind;
306
307   if (nfiles == 0)
308     {
309       have_read_stdin = 1;
310       wc (0, "");
311     }
312   else
313     {
314       for (; optind < argc; ++optind)
315         wc_file (argv[optind]);
316
317       if (nfiles > 1)
318         write_counts (total_lines, total_words, total_chars, _("total"));
319     }
320
321   if (have_read_stdin && close (0))
322     error (1, errno, "-");
323
324   exit (exit_status);
325 }