.
[coreutils.git] / src / wc.c
blob577c71dc7a4e68333a8e00d18276e17643521305
1 /* wc - print the number of bytes, words, and lines in files
2 Copyright (C) 85, 91, 95, 96, 1997 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 "error.h"
29 /* Size of atomic reads. */
30 #define BUFFER_SIZE (16 * 1024)
32 int safe_read ();
34 /* The name this program was run with. */
35 char *program_name;
37 /* Cumulative number of lines, words, and chars in all files so far.
38 max_line_length is the maximum over all files processed so far. */
39 static unsigned long total_lines, total_words, total_chars, max_line_length;
41 /* Which counts to print. */
42 static int print_lines, print_words, print_chars, print_linelength;
44 /* Nonzero if we have ever read the standard input. */
45 static int have_read_stdin;
47 /* The error code to return to the system. */
48 static int exit_status;
50 /* If nonzero, display usage information and exit. */
51 static int show_help;
53 /* If nonzero, print the version on standard output then exits. */
54 static int show_version;
56 static struct option const longopts[] =
58 {"bytes", no_argument, NULL, 'c'},
59 {"chars", no_argument, NULL, 'c'},
60 {"lines", no_argument, NULL, 'l'},
61 {"words", no_argument, NULL, 'w'},
62 {"max-line-length", no_argument, NULL, 'L'},
63 {"help", no_argument, &show_help, 1},
64 {"version", no_argument, &show_version, 1},
65 {NULL, 0, NULL, 0}
68 static void
69 usage (int status)
71 if (status != 0)
72 fprintf (stderr, _("Try `%s --help' for more information.\n"),
73 program_name);
74 else
76 printf (_("\
77 Usage: %s [OPTION]... [FILE]...\n\
78 "),
79 program_name);
80 printf (_("\
81 Print line, word, and byte counts for each FILE, and a total line if\n\
82 more than one FILE is specified. With no FILE, or when FILE is -,\n\
83 read standard input.\n\
84 -c, --bytes, --chars print the byte counts\n\
85 -l, --lines print the newline counts\n\
86 -L, --max-line-length print the length of the longest line\n\
87 -w, --words print the word counts\n\
88 --help display this help and exit\n\
89 --version output version information and exit\n\
90 "));
91 puts (_("\nReport bugs to <textutils-bugs@gnu.org>."));
93 exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
96 static void
97 write_counts (long unsigned int lines, long unsigned int words,
98 long unsigned int chars, long unsigned int linelength,
99 const char *file)
101 if (print_lines)
102 printf ("%7lu", lines);
103 if (print_words)
105 if (print_lines)
106 putchar (' ');
107 printf ("%7lu", words);
109 if (print_chars)
111 if (print_lines || print_words)
112 putchar (' ');
113 printf ("%7lu", chars);
115 if (print_linelength)
117 if (print_lines || print_words || print_chars)
118 putchar (' ');
119 printf ("%7lu", linelength);
121 if (*file)
122 printf (" %s", file);
123 putchar ('\n');
126 static void
127 wc (int fd, const char *file)
129 char buf[BUFFER_SIZE + 1];
130 register int bytes_read;
131 register int in_word = 0;
132 register unsigned long lines, words, chars, linelength;
134 lines = words = chars = linelength = 0;
136 /* When counting only bytes, save some line- and word-counting
137 overhead. If FD is a `regular' Unix file, using lseek is enough
138 to get its `size' in bytes. Otherwise, read blocks of BUFFER_SIZE
139 bytes at a time until EOF. Note that the `size' (number of bytes)
140 that wc reports is smaller than stats.st_size when the file is not
141 positioned at its beginning. That's why the lseek calls below are
142 necessary. For example the command
143 `(dd ibs=99k skip=1 count=0; ./wc -c) < /etc/group'
144 should make wc report `0' bytes. */
146 if (print_chars && !print_words && !print_lines && !print_linelength)
148 off_t current_pos, end_pos;
149 struct stat stats;
151 if (fstat (fd, &stats) == 0 && S_ISREG (stats.st_mode)
152 && (current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
153 && (end_pos = lseek (fd, (off_t) 0, SEEK_END)) != -1)
155 off_t diff;
156 /* Be careful here. The current position may actually be
157 beyond the end of the file. As in the example above. */
158 chars = (diff = end_pos - current_pos) < 0 ? 0 : diff;
160 else
162 while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
164 chars += bytes_read;
166 if (bytes_read < 0)
168 error (0, errno, "%s", file);
169 exit_status = 1;
173 else if (!print_words && !print_linelength)
175 /* Use a separate loop when counting only lines or lines and bytes --
176 but not words. */
177 while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
179 register char *p = buf;
181 while ((p = memchr (p, '\n', (buf + bytes_read) - p)))
183 ++p;
184 ++lines;
186 chars += bytes_read;
188 if (bytes_read < 0)
190 error (0, errno, "%s", file);
191 exit_status = 1;
194 else
196 register unsigned long linepos = 0;
198 while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
200 register char *p = buf;
202 chars += bytes_read;
205 switch (*p++)
207 case '\n':
208 lines++;
209 /* Fall through. */
210 case '\r':
211 case '\f':
212 if (linepos > linelength)
213 linelength = linepos;
214 linepos = 0;
215 goto word_separator;
216 case '\t':
217 linepos += 8 - (linepos % 8);
218 goto word_separator;
219 case ' ':
220 linepos++;
221 /* Fall through. */
222 case '\v':
223 word_separator:
224 if (in_word)
226 in_word = 0;
227 words++;
229 break;
230 default:
231 linepos++;
232 in_word = 1;
233 break;
236 while (--bytes_read);
238 if (bytes_read < 0)
240 error (0, errno, "%s", file);
241 exit_status = 1;
243 if (linepos > linelength)
244 linelength = linepos;
245 if (in_word)
246 words++;
249 write_counts (lines, words, chars, linelength, file);
250 total_lines += lines;
251 total_words += words;
252 total_chars += chars;
253 if (linelength > max_line_length)
254 max_line_length = linelength;
257 static void
258 wc_file (const char *file)
260 if (!strcmp (file, "-"))
262 have_read_stdin = 1;
263 wc (0, file);
265 else
267 int fd = open (file, O_RDONLY);
268 if (fd == -1)
270 error (0, errno, "%s", file);
271 exit_status = 1;
272 return;
274 wc (fd, file);
275 if (close (fd))
277 error (0, errno, "%s", file);
278 exit_status = 1;
284 main (int argc, char **argv)
286 int optc;
287 int nfiles;
289 program_name = argv[0];
290 setlocale (LC_ALL, "");
291 bindtextdomain (PACKAGE, LOCALEDIR);
292 textdomain (PACKAGE);
294 exit_status = 0;
295 print_lines = print_words = print_chars = print_linelength = 0;
296 total_lines = total_words = total_chars = max_line_length = 0;
298 while ((optc = getopt_long (argc, argv, "clLw", longopts, NULL)) != -1)
299 switch (optc)
301 case 0:
302 break;
304 case 'c':
305 print_chars = 1;
306 break;
308 case 'l':
309 print_lines = 1;
310 break;
312 case 'w':
313 print_words = 1;
314 break;
316 case 'L':
317 print_linelength = 1;
318 break;
320 default:
321 usage (1);
324 if (show_version)
326 printf ("wc (%s) %s\n", GNU_PACKAGE, VERSION);
327 exit (EXIT_SUCCESS);
330 if (show_help)
331 usage (0);
333 if (print_lines + print_words + print_chars + print_linelength == 0)
334 print_lines = print_words = print_chars = 1;
336 nfiles = argc - optind;
338 if (nfiles == 0)
340 have_read_stdin = 1;
341 wc (0, "");
343 else
345 for (; optind < argc; ++optind)
346 wc_file (argv[optind]);
348 if (nfiles > 1)
349 write_counts (total_lines, total_words, total_chars, max_line_length,
350 _("total"));
353 if (have_read_stdin && close (0))
354 error (EXIT_FAILURE, errno, "-");
356 exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);