wc.c revision 1.16 1 /* $NetBSD: wc.c,v 1.16 1999/02/14 18:03:18 mjacob Exp $ */
2
3 /*
4 * Copyright (c) 1980, 1987, 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1980, 1987, 1991, 1993\n\
39 The Regents of the University of California. All rights reserved.\n");
40 #endif /* not lint */
41
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)wc.c 8.2 (Berkeley) 5/2/95";
45 #else
46 __RCSID("$NetBSD: wc.c,v 1.16 1999/02/14 18:03:18 mjacob Exp $");
47 #endif
48 #endif /* not lint */
49
50 /* wc line, word and char count */
51
52 #include <sys/param.h>
53 #include <sys/stat.h>
54 #include <sys/types.h>
55
56 #include <fcntl.h>
57 #include <unistd.h>
58 #include <errno.h>
59 #include <stdio.h>
60
61 #include <stdlib.h>
62 #include <string.h>
63 #include <locale.h>
64 #include <ctype.h>
65 #include <errno.h>
66 #include <sys/param.h>
67 #include <sys/stat.h>
68 #include <sys/file.h>
69 #include <unistd.h>
70 #include <err.h>
71
72 static u_int64_t tlinect, twordct, tcharct;
73 static int doline, doword, dochar;
74 static int rval = 0;
75 static char *qfmt;
76
77 static void cnt __P((char *));
78 static void print_counts __P((u_int64_t, u_int64_t, u_int64_t, char *));
79 static void usage __P((void));
80 int main __P((int, char *[]));
81
82 int
83 main(argc, argv)
84 int argc;
85 char *argv[];
86 {
87 int ch;
88
89 setlocale(LC_ALL, "");
90 if (sizeof (u_int64_t) > 4)
91 qfmt = " %7u";
92 else
93 qfmt = " %7qu";
94
95 while ((ch = getopt(argc, argv, "lwcm")) != -1)
96 switch((char)ch) {
97 case 'l':
98 doline = 1;
99 break;
100 case 'w':
101 doword = 1;
102 break;
103 case 'c':
104 case 'm':
105 dochar = 1;
106 break;
107 case '?':
108 default:
109 usage();
110 }
111 argv += optind;
112 argc -= optind;
113
114 /* Wc's flags are on by default. */
115 if (doline + doword + dochar == 0)
116 doline = doword = dochar = 1;
117
118 if (!*argv) {
119 cnt(NULL);
120 } else {
121 int dototal = (argc > 1);
122
123 do {
124 cnt(*argv);
125 } while(*++argv);
126
127 if (dototal) {
128 print_counts(tlinect, twordct, tcharct, "total");
129 }
130 }
131
132 exit(rval);
133 }
134
135 static void
136 cnt(file)
137 char *file;
138 {
139 u_char *C;
140 short gotsp;
141 int len;
142 u_int64_t linect, wordct, charct;
143 struct stat sb;
144 int fd;
145 u_char buf[MAXBSIZE];
146
147 linect = wordct = charct = 0;
148 if (file) {
149 if ((fd = open(file, O_RDONLY, 0)) < 0) {
150 warn("%s", file);
151 rval = 1;
152 return;
153 }
154 } else {
155 fd = STDIN_FILENO;
156 }
157
158 if (!doword) {
159 /*
160 * line counting is split out because it's a lot
161 * faster to get lines than to get words, since
162 * the word count requires some logic.
163 */
164 if (doline) {
165 while ((len = read(fd, buf, MAXBSIZE)) > 0) {
166 charct += len;
167 for (C = buf; len--; ++C)
168 if (*C == '\n')
169 ++linect;
170 }
171 if (len == -1) {
172 warn ("%s", file);
173 rval = 1;
174 }
175 }
176
177 /*
178 * if all we need is the number of characters and
179 * it's a directory or a regular or linked file, just
180 * stat the puppy. We avoid testing for it not being
181 * a special device in case someone adds a new type
182 * of inode.
183 */
184 else if (dochar) {
185 if (fstat(fd, &sb)) {
186 warn("%s", file);
187 rval = 1;
188 } else {
189 if (S_ISREG(sb.st_mode) ||
190 S_ISLNK(sb.st_mode) ||
191 S_ISDIR(sb.st_mode)) {
192 charct = sb.st_size;
193 } else {
194 while ((len = read(fd, buf, MAXBSIZE)) > 0)
195 charct += len;
196 if (len == -1) {
197 warn ("%s", file);
198 rval = 1;
199 }
200 }
201 }
202 }
203 }
204 else
205 {
206 /* do it the hard way... */
207 gotsp = 1;
208 while ((len = read(fd, buf, MAXBSIZE)) > 0) {
209 charct += len;
210 for (C = buf; len--; ++C) {
211 if (isspace(*C)) {
212 gotsp = 1;
213 if (*C == '\n') {
214 ++linect;
215 }
216 } else {
217 /*
218 * This line implements the POSIX
219 * spec, i.e. a word is a "maximal
220 * string of characters delimited by
221 * whitespace." Notice nothing was
222 * said about a character being
223 * printing or non-printing.
224 */
225 if (gotsp) {
226 gotsp = 0;
227 ++wordct;
228 }
229 }
230 }
231 }
232 if (len == -1) {
233 warn("%s", file);
234 rval = 1;
235 }
236 }
237
238 print_counts(linect, wordct, charct, file ? file : "");
239
240 /* don't bother checkint doline, doword, or dochar --- speeds
241 up the common case */
242 tlinect += linect;
243 twordct += wordct;
244 tcharct += charct;
245
246 if (close(fd)) {
247 warn ("%s", file);
248 rval = 1;
249 }
250 }
251
252 static void
253 print_counts(lines, words, chars, name)
254 u_int64_t lines;
255 u_int64_t words;
256 u_int64_t chars;
257 char *name;
258 {
259
260 if (doline)
261 printf(qfmt, lines);
262 if (doword)
263 printf(qfmt, words);
264 if (dochar)
265 printf(qfmt, chars);
266
267 printf(" %s\n", name);
268 }
269
270 static void
271 usage()
272 {
273 (void)fprintf(stderr, "usage: wc [-clw] [files]\n");
274 exit(1);
275 }
276