xref: /netbsd-src/usr.bin/wc/wc.c (revision f5d3fbbc6ff4a77159fb268d247bd94cb7d7e332)
1 /*	$NetBSD: wc.c,v 1.12 1997/10/19 19:33:37 mycroft 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 #ifndef lint
37 static char copyright[] =
38 "@(#) 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 static char rcsid[] = "$NetBSD: wc.c,v 1.12 1997/10/19 19:33:37 mycroft 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 
55 #include <fcntl.h>
56 #include <unistd.h>
57 #include <errno.h>
58 #include <stdio.h>
59 
60 #include <stdlib.h>
61 #include <string.h>
62 #include <locale.h>
63 #include <ctype.h>
64 #include <errno.h>
65 #include <sys/param.h>
66 #include <sys/stat.h>
67 #include <sys/file.h>
68 #include <unistd.h>
69 #include <err.h>
70 
71 static ulong	tlinect, twordct, tcharct;
72 static int	doline, doword, dochar;
73 static int 	rval = 0;
74 
75 static void	cnt __P((char *));
76 static void	print_counts __P((long, long, long, char *));
77 static void	usage __P((void));
78 
79 int
80 main(argc, argv)
81 	int argc;
82 	char *argv[];
83 {
84 	register int ch;
85 
86 	setlocale(LC_ALL, "");
87 
88 	while ((ch = getopt(argc, argv, "lwcm")) != -1)
89 		switch((char)ch) {
90 		case 'l':
91 			doline = 1;
92 			break;
93 		case 'w':
94 			doword = 1;
95 			break;
96 		case 'c':
97 		case 'm':
98 			dochar = 1;
99 			break;
100 		case '?':
101 		default:
102 			usage();
103 		}
104 	argv += optind;
105 	argc -= optind;
106 
107 	/* Wc's flags are on by default. */
108 	if (doline + doword + dochar == 0)
109 		doline = doword = dochar = 1;
110 
111 	if (!*argv) {
112 		cnt(NULL);
113 	} else {
114 		int dototal = (argc > 1);
115 
116 		do {
117 			cnt(*argv);
118 		} while(*++argv);
119 
120 		if (dototal) {
121 			print_counts(tlinect, twordct, tcharct, "total");
122 		}
123 	}
124 
125 	exit(rval);
126 }
127 
128 static void
129 cnt(file)
130 	char *file;
131 {
132 	register u_char *C;
133 	register short gotsp;
134 	register int ch, len;
135 	register u_long linect, wordct, charct;
136 	struct stat sb;
137 	int fd;
138 	u_char buf[MAXBSIZE];
139 
140 	linect = wordct = charct = 0;
141 	if (file) {
142 		if ((fd = open(file, O_RDONLY, 0)) < 0) {
143 			warn("%s", file);
144 			rval = 1;
145 			return;
146 		}
147 	} else  {
148 		fd = STDIN_FILENO;
149 	}
150 
151 	if (!doword) {
152 		/*
153 		 * line counting is split out because it's a lot
154 		 * faster to get lines than to get words, since
155 		 * the word count requires some logic.
156 		 */
157 		if (doline) {
158 			while ((len = read(fd, buf, MAXBSIZE)) > 0) {
159 				charct += len;
160 				for (C = buf; len--; ++C)
161 					if (*C == '\n')
162 						++linect;
163 			}
164 			if (len == -1) {
165 				warn ("%s", file);
166 				rval = 1;
167 			}
168 		}
169 
170 		/*
171 		 * if all we need is the number of characters and
172 		 * it's a directory or a regular or linked file, just
173 		 * stat the puppy.  We avoid testing for it not being
174 		 * a special device in case someone adds a new type
175 		 * of inode.
176 		 */
177 		else if (dochar) {
178 			if (fstat(fd, &sb)) {
179 				warn("%s", file);
180 				rval = 1;
181 			} else {
182 				if (S_ISREG(sb.st_mode) ||
183 				    S_ISLNK(sb.st_mode) ||
184 				    S_ISDIR(sb.st_mode)) {
185 					charct = sb.st_size;
186 				} else {
187 					while ((len = read(fd, buf, MAXBSIZE)) > 0)
188 						charct += len;
189 					if (len == -1) {
190 						warn ("%s", file);
191 						rval = 1;
192 					}
193 				}
194 			}
195 		}
196 	}
197 	else
198 	{
199 		/* do it the hard way... */
200 		gotsp = 1;
201 		while ((len = read(fd, buf, MAXBSIZE)) > 0) {
202 			charct += len;
203 			for (C = buf; len--; ++C) {
204 				if (isspace(*C)) {
205 					gotsp = 1;
206 					if (*C == '\n') {
207 						++linect;
208 					}
209 				} else {
210 					/*
211 					 * This line implements the POSIX
212 					 * spec, i.e. a word is a "maximal
213 					 * string of characters delimited by
214 					 * whitespace."  Notice nothing was
215 					 * said about a character being
216 					 * printing or non-printing.
217 					 */
218 					if (gotsp) {
219 						gotsp = 0;
220 						++wordct;
221 					}
222 				}
223 			}
224 		}
225 		if (len == -1) {
226 			warn("%s", file);
227 			rval = 1;
228 		}
229 	}
230 
231 	print_counts(linect, wordct, charct, file ? file : "");
232 
233 	/* don't bother checkint doline, doword, or dochar --- speeds
234            up the common case */
235 	tlinect += linect;
236 	twordct += wordct;
237 	tcharct += charct;
238 
239 	if (close(fd)) {
240 		warn ("%s", file);
241 		rval = 1;
242 	}
243 }
244 
245 static void
246 print_counts(lines, words, chars, name)
247 	long lines;
248 	long words;
249 	long chars;
250 	char *name;
251 {
252 
253 	if (doline)
254 		printf(" %7ld", lines);
255 	if (doword)
256 		printf(" %7ld", words);
257 	if (dochar)
258 		printf(" %7ld", chars);
259 
260 	printf(" %s\n", name);
261 }
262 
263 static void
264 usage()
265 {
266 	(void)fprintf(stderr, "usage: wc [-clw] [files]\n");
267 	exit(1);
268 }
269