1 /*	$OpenBSD: wc.c,v 1.10 2005/04/11 07:04:47 deraadt 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. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __COPYRIGHT("@(#) Copyright (c) 1980, 1987, 1991, 1993\n\
34 	The Regents of the University of California.  All rights reserved.\n");
35 __SCCSID("@(#)wc.c	8.2 (Berkeley) 5/2/95");
36 __RCSID("$MirOS: src/usr.bin/wc/wc.c,v 1.4 2009/05/31 15:28:18 tg Exp $");
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <locale.h>
42 #include <ctype.h>
43 #include <err.h>
44 #include <sys/param.h>
45 #include <sys/stat.h>
46 #include <sys/file.h>
47 #include <unistd.h>
48 
49 int64_t	tlinect, twordct, tcharct;
50 int	doline, doword, dochar;
51 int 	rval;
52 extern char *__progname;
53 
54 void	print_counts(int64_t, int64_t, int64_t, const char *);
55 void	cnt(char *);
56 
57 int
main(int argc,char * argv[])58 main(int argc, char *argv[])
59 {
60 	int ch;
61 
62 #ifndef __MirBSD__
63 	setlocale(LC_ALL, "");
64 #endif
65 
66 	while ((ch = getopt(argc, argv, "lwcm")) != -1)
67 		switch((char)ch) {
68 		case 'l':
69 			doline = 1;
70 			break;
71 		case 'w':
72 			doword = 1;
73 			break;
74 		case 'c':
75 		case 'm':
76 			dochar = 1;
77 			break;
78 		case '?':
79 		default:
80 			(void)fprintf(stderr,
81 			    "usage: %s [-c | -m] [-lw] [file ...]\n",
82 			    __progname);
83 			exit(1);
84 		}
85 	argv += optind;
86 	argc -= optind;
87 
88 	/*
89 	 * wc is unusual in that its flags are on by default, so,
90 	 * if you don't get any arguments, you have to turn them
91 	 * all on.
92 	 */
93 	if (!doline && !doword && !dochar)
94 		doline = doword = dochar = 1;
95 
96 	if (!*argv) {
97 		cnt((char *)NULL);
98 	} else {
99 		int dototal = (argc > 1);
100 
101 		do {
102 			cnt(*argv);
103 		} while(*++argv);
104 
105 		if (dototal)
106 			print_counts(tlinect, twordct, tcharct, "total");
107 	}
108 
109 	exit(rval);
110 }
111 
112 void
cnt(char * file)113 cnt(char *file)
114 {
115 	u_char *C;
116 	short gotsp;
117 	int len;
118 	int64_t linect, wordct, charct;
119 	struct stat sbuf;
120 	int fd;
121 	u_char buf[MAXBSIZE];
122 
123 	linect = wordct = charct = 0;
124 	if (file) {
125 		if ((fd = open(file, O_RDONLY, 0)) < 0) {
126 			warn("%s", file);
127 			rval = 1;
128 			return;
129 		}
130 	} else  {
131 		fd = STDIN_FILENO;
132 	}
133 
134 	if (!doword) {
135 		/*
136 		 * Line counting is split out because it's a lot
137 		 * faster to get lines than to get words, since
138 		 * the word count requires some logic.
139 		 */
140 		if (doline) {
141 			while ((len = read(fd, buf, MAXBSIZE)) > 0) {
142 				charct += len;
143 				for (C = buf; len--; ++C)
144 					if (*C == '\n')
145 						++linect;
146 			}
147 			if (len == -1) {
148 				warn("%s", file);
149 				rval = 1;
150 			}
151 		}
152 		/*
153 		 * If all we need is the number of characters and
154 		 * it's a directory or a regular or linked file, just
155 		 * stat the puppy.  We avoid testing for it not being
156 		 * a special device in case someone adds a new type
157 		 * of inode.
158 		 */
159 		else if (dochar) {
160 			mode_t ifmt;
161 
162 			if (fstat(fd, &sbuf)) {
163 				warn("%s", file);
164 				rval = 1;
165 			} else {
166 				ifmt = sbuf.st_mode & S_IFMT;
167 				if (ifmt == S_IFREG || ifmt == S_IFLNK
168 				    || ifmt == S_IFDIR) {
169 					charct = sbuf.st_size;
170 				} else {
171 					while ((len = read(fd, buf, MAXBSIZE)) > 0)
172 						charct += len;
173 					if (len == -1) {
174 						warn("%s", file);
175 						rval = 1;
176 					}
177 				}
178 			}
179 		}
180 	} else {
181 		/* Do it the hard way... */
182 		gotsp = 1;
183 		while ((len = read(fd, buf, MAXBSIZE)) > 0) {
184 			/*
185 			 * This loses in the presence of multi-byte characters.
186 			 * To do it right would require a function to return a
187 			 * character while knowing how many bytes it consumed.
188 			 */
189 			charct += len;
190 			for (C = buf; len--; ++C) {
191 				if (isspace (*C)) {
192 					gotsp = 1;
193 					if (*C == '\n')
194 						++linect;
195 				} else {
196 					/*
197 					 * This line implements the POSIX
198 					 * spec, i.e. a word is a "maximal
199 					 * string of characters delimited by
200 					 * whitespace."  Notice nothing was
201 					 * said about a character being
202 					 * printing or non-printing.
203 					 */
204 					if (gotsp) {
205 						gotsp = 0;
206 						++wordct;
207 					}
208 				}
209 			}
210 		}
211 		if (len == -1) {
212 			warn("%s", file);
213 			rval = 1;
214 		}
215 	}
216 
217 	print_counts(linect, wordct, charct, file);
218 
219 	/*
220 	 * Don't bother checking doline, doword, or dochar -- speeds
221 	 * up the common case
222 	 */
223 	tlinect += linect;
224 	twordct += wordct;
225 	tcharct += charct;
226 
227 	if (close(fd) != 0) {
228 		warn("%s", file);
229 		rval = 1;
230 	}
231 }
232 
233 void
print_counts(int64_t lines,int64_t words,int64_t chars,const char * name)234 print_counts(int64_t lines, int64_t words, int64_t chars, const char *name)
235 {
236 
237 	if (doline)
238 		(void)printf(" %7lld", (long long)lines);
239 	if (doword)
240 		(void)printf(" %7lld", (long long)words);
241 	if (dochar)
242 		(void)printf(" %7lld", (long long)chars);
243 
244 	if (name)
245 		printf(" %s\n", name);
246 	else
247 		printf("\n");
248 }
249