.
[coreutils.git] / src / wc.c
blob8c812ca38ccf8cbc831b6e84a68324ced74eb002
1 /* wc - print the number of bytes, words, and lines in files
2 Copyright (C) 1985, 1991, 1995 Free Software Foundation, Inc.
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.
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.
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. */
18 /* Written by Paul Rubin, phr@ocf.berkeley.edu
19 and David MacKenzie, djm@gnu.ai.mit.edu. */
21 #include <config.h>
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"
30 /* Size of atomic reads. */
31 #define BUFFER_SIZE (16 * 1024)
33 int safe_read ();
35 static void wc ();
36 static void wc_file ();
37 static void write_counts ();
39 /* The name this program was run with. */
40 char *program_name;
42 /* Cumulative number of lines, words, and chars in all files so far. */
43 static unsigned long total_lines, total_words, total_chars;
45 /* Which counts to print. */
46 static int print_lines, print_words, print_chars;
48 /* Nonzero if we have ever read the standard input. */
49 static int have_read_stdin;
51 /* The error code to return to the system. */
52 static int exit_status;
54 /* If non-zero, display usage information and exit. */
55 static int show_help;
57 /* If non-zero, print the version on standard output then exits. */
58 static int show_version;
60 static struct option const longopts[] =
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}
71 static void
72 usage (status)
73 int status;
75 if (status != 0)
76 fprintf (stderr, "Try `%s --help' for more information.\n",
77 program_name);
78 else
80 printf ("\
81 Usage: %s [OPTION]... [FILE]...\n\
83 program_name);
84 printf ("\
85 Print line, word, and byte counts for each FILE, and a total line if\n\
86 more than one FILE is specified. With no FILE, or when FILE is -,\n\
87 read standard input.\n\
88 -l, --lines print the newline counts\n\
89 -w, --words print the word counts\n\
90 -c, --bytes, --chars print the byte counts\n\
91 --help display this help and exit\n\
92 --version output version information and exit\n\
93 ");
95 exit (status);
98 void
99 main (argc, argv)
100 int argc;
101 char **argv;
103 int optc;
104 int nfiles;
106 program_name = argv[0];
107 exit_status = 0;
108 print_lines = print_words = print_chars = 0;
109 total_lines = total_words = total_chars = 0;
111 while ((optc = getopt_long (argc, argv, "clw", longopts, (int *) 0)) != EOF)
112 switch (optc)
114 case 0:
115 break;
117 case 'c':
118 print_chars = 1;
119 break;
121 case 'l':
122 print_lines = 1;
123 break;
125 case 'w':
126 print_words = 1;
127 break;
129 default:
130 usage (1);
133 if (show_version)
135 printf ("wc - %s\n", version_string);
136 exit (0);
139 if (show_help)
140 usage (0);
142 if (print_lines + print_words + print_chars == 0)
143 print_lines = print_words = print_chars = 1;
145 nfiles = argc - optind;
147 if (nfiles == 0)
149 have_read_stdin = 1;
150 wc (0, "");
152 else
154 for (; optind < argc; ++optind)
155 wc_file (argv[optind]);
157 if (nfiles > 1)
158 write_counts (total_lines, total_words, total_chars, "total");
161 if (have_read_stdin && close (0))
162 error (1, errno, "-");
164 exit (exit_status);
167 static void
168 wc_file (file)
169 char *file;
171 if (!strcmp (file, "-"))
173 have_read_stdin = 1;
174 wc (0, file);
176 else
178 int fd = open (file, O_RDONLY);
179 if (fd == -1)
181 error (0, errno, "%s", file);
182 exit_status = 1;
183 return;
185 wc (fd, file);
186 if (close (fd))
188 error (0, errno, "%s", file);
189 exit_status = 1;
194 static void
195 wc (fd, file)
196 int fd;
197 char *file;
199 char buf[BUFFER_SIZE + 1];
200 register int bytes_read;
201 register int in_word = 0;
202 register unsigned long lines, words, chars;
204 lines = words = chars = 0;
206 /* When counting only bytes, save some line- and word-counting
207 overhead. If FD is a `regular' Unix file, using lseek is enough
208 to get its `size' in bytes. Otherwise, read blocks of BUFFER_SIZE
209 bytes at a time until EOF. Note that the `size' (number of bytes)
210 that wc reports is smaller than stats.st_size when the file is not
211 positioned at its beginning. That's why the lseek calls below are
212 necessary. For example the command
213 `(dd ibs=99k skip=1 count=0; ./wc -c) < /etc/group'
214 should make wc report `0' bytes. */
216 if (print_chars && !print_words && !print_lines)
218 off_t current_pos, end_pos;
219 struct stat stats;
221 if (fstat (fd, &stats) == 0 && S_ISREG (stats.st_mode)
222 && (current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
223 && (end_pos = lseek (fd, (off_t) 0, SEEK_END)) != -1)
225 off_t diff;
226 /* Be careful here. The current position may actually be
227 beyond the end of the file. As in the example above. */
228 chars = (diff = end_pos - current_pos) < 0 ? 0 : diff;
230 else
232 while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
234 chars += bytes_read;
236 if (bytes_read < 0)
238 error (0, errno, "%s", file);
239 exit_status = 1;
243 else if (!print_words)
245 /* Use a separate loop when counting only lines or lines and bytes --
246 but not words. */
247 while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
249 register char *p = buf;
251 while ((p = memchr (p, '\n', (buf + bytes_read) - p)))
253 ++p;
254 ++lines;
256 chars += bytes_read;
258 if (bytes_read < 0)
260 error (0, errno, "%s", file);
261 exit_status = 1;
264 else
266 while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
268 register char *p = buf;
270 chars += bytes_read;
273 switch (*p++)
275 case '\n':
276 lines++;
277 /* Fall through. */
278 case '\r':
279 case '\f':
280 case '\t':
281 case '\v':
282 case ' ':
283 if (in_word)
285 in_word = 0;
286 words++;
288 break;
289 default:
290 in_word = 1;
291 break;
294 while (--bytes_read);
296 if (bytes_read < 0)
298 error (0, errno, "%s", file);
299 exit_status = 1;
301 if (in_word)
302 words++;
305 write_counts (lines, words, chars, file);
306 total_lines += lines;
307 total_words += words;
308 total_chars += chars;
311 static void
312 write_counts (lines, words, chars, file)
313 unsigned long lines, words, chars;
314 char *file;
316 if (print_lines)
317 printf ("%7lu", lines);
318 if (print_words)
320 if (print_lines)
321 putchar (' ');
322 printf ("%7lu", words);
324 if (print_chars)
326 if (print_lines || print_words)
327 putchar (' ');
328 printf ("%7lu", chars);
330 if (*file)
331 printf (" %s", file);
332 putchar ('\n');