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