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