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