xref: /plan9-contrib/sys/src/cmd/wc.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 /*
2  * wc -- count things in utf-encoded text files
3  * Bugs:
4  *	The only white space characters recognized are ' ', '\t' and '\n', even though
5  *	ISO 10646 has many more blanks scattered through it.
6  *	Should count characters that cannot occur in any rune (hex f0-ff) separately.
7  *	Should count non-canonical runes (e.g. hex c1,80 instead of hex 40).
8  */
9 #include <u.h>
10 #include <libc.h>
11 #define	NBUF	(8*1024)
12 long nline, tnline, pline;
13 long nword, tnword, pword;
14 long nrune, tnrune, prune;
15 long nbadr, tnbadr, pbadr;
16 long nchar, tnchar, pchar;
17 void count(int, char *);
18 void report(long, long, long, long, long, char *);
19 void
20 main(int argc, char *argv[])
21 {
22 	char *status="";
23 	int i, f;
24 	ARGBEGIN {
25 	case 'l': pline++; break;
26 	case 'w': pword++; break;
27 	case 'r': prune++; break;
28 	case 'b': pbadr++; break;
29 	case 'c': pchar++; break;
30 	default:
31 		fprint(2, "Usage: %s [-lwrbc] [file ...]\n", argv0);
32 		exits("usage");
33 	} ARGEND
34 	if(pline+pword+prune+pbadr+pchar == 0)
35 		pline=pword=pchar=1;
36 	if(argc==0)
37 		count(0, 0);
38 	else{
39 		for(i=0;i<argc;i++){
40 			f=open(argv[i], OREAD);
41 			if(f<0){
42 				perror(argv[i]);
43 				status="can't open";
44 			}
45 			else{
46 				count(f, argv[i]);
47 				tnline+=nline;
48 				tnword+=nword;
49 				tnrune+=nrune;
50 				tnbadr+=nbadr;
51 				tnchar+=nchar;
52 				close(f);
53 			}
54 		}
55 		if(argc>1)
56 			report(tnline, tnword, tnrune, tnbadr, tnchar, "total");
57 	}
58 	exits(status);
59 }
60 void
61 report(long nline, long nword, long nrune, long nbadr, long nchar, char *fname)
62 {
63 	char line[1024], word[128];
64 	line[0] = '\0';
65 	if(pline){
66 		sprint(word, " %7ld", nline);
67 		strcat(line, word);
68 	}
69 	if(pword){
70 		sprint(word, " %7ld", nword);
71 		strcat(line, word);
72 	}
73 	if(prune){
74 		sprint(word, " %7ld", nrune);
75 		strcat(line, word);
76 	}
77 	if(pbadr){
78 		sprint(word, " %7ld", nbadr);
79 		strcat(line, word);
80 	}
81 	if(pchar){
82 		sprint(word, " %7ld", nchar);
83 		strcat(line, word);
84 	}
85 	if(fname){
86 		sprint(word, " %s",   fname);
87 		strcat(line, word);
88 	}
89 	print("%s\n", line+1);
90 }
91 /*
92  * How it works.  Start in statesp.  Each time we read a character,
93  * increment various counts, and do state transitions according to the
94  * following table.  If we're not in statesp or statewd when done, the
95  * file ends with a partial rune.
96  *        |                character
97  *  state |09,20| 0a  |00-7f|80-bf|c0-df|e0-ef|f0-ff
98  * -------+-----+-----+-----+-----+-----+-----+-----
99  * statesp|ASP  |ASPN |AWDW |AWDWX|AC2W |AC3W |AWDWX
100  * statewd|ASP  |ASPN |AWD  |AWDX |AC2  |AC3  |AWDX
101  * statec2|ASPX |ASPNX|AWDX |AWDR |AC2X |AC3X |AWDX
102  * statec3|ASPX |ASPNX|AWDX |AC2R |AC2X |AC3X |AWDX
103  */
104 enum{			/* actions */
105 	AC2,		/* enter statec2 */
106 	AC2R,		/* enter statec2, don't count a rune */
107 	AC2W,		/* enter statec2, count a word */
108 	AC2X,		/* enter statec2, count a bad rune */
109 	AC3,		/* enter statec3 */
110 	AC3W,		/* enter statec3, count a word */
111 	AC3X,		/* enter statec3, count a bad rune */
112 	ASP,		/* enter statesp */
113 	ASPN,		/* enter statesp, count a newline */
114 	ASPNX,		/* enter statesp, count a newline, count a bad rune */
115 	ASPX,		/* enter statesp, count a bad rune */
116 	AWD,		/* enter statewd */
117 	AWDR,		/* enter statewd, don't count a rune */
118 	AWDW,		/* enter statewd, count a word */
119 	AWDWX,		/* enter statewd, count a word, count a bad rune */
120 	AWDX,		/* enter statewd, count a bad rune */
121 };
122 uchar statesp[256]={	/* looking for the start of a word */
123 AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 00-07 */
124 AWDW, ASP,  ASPN, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 08-0f */
125 AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 10-17 */
126 AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 18-1f */
127 ASP,  AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 20-27 */
128 AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 28-2f */
129 AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 30-37 */
130 AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 38-3f */
131 AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 40-47 */
132 AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 48-4f */
133 AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 50-57 */
134 AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 58-5f */
135 AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 60-67 */
136 AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 68-6f */
137 AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 70-77 */
138 AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 78-7f */
139 AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* 80-87 */
140 AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* 88-8f */
141 AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* 90-97 */
142 AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* 98-9f */
143 AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* a0-a7 */
144 AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* a8-af */
145 AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* b0-b7 */
146 AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* b8-bf */
147 AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W,	/* c0-c7 */
148 AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W,	/* c8-cf */
149 AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W,	/* d0-d7 */
150 AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W,	/* d8-df */
151 AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W,	/* e0-e7 */
152 AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W,	/* e8-ef */
153 AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* f0-f7 */
154 AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* f8-ff */
155 };
156 uchar statewd[256]={	/* looking for the next character in a word */
157 AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 00-07 */
158 AWD,  ASP,  ASPN, AWD,  AWD,  AWD,  AWD,  AWD,	/* 08-0f */
159 AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 10-17 */
160 AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 18-1f */
161 ASP,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 20-27 */
162 AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 28-2f */
163 AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 30-37 */
164 AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 38-3f */
165 AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 40-47 */
166 AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 48-4f */
167 AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 50-57 */
168 AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 58-5f */
169 AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 60-67 */
170 AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 68-6f */
171 AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 70-77 */
172 AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 78-7f */
173 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 80-87 */
174 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 88-8f */
175 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 90-97 */
176 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 98-9f */
177 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* a0-a7 */
178 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* a8-af */
179 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* b0-b7 */
180 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* b8-bf */
181 AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,	/* c0-c7 */
182 AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,	/* c8-cf */
183 AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,	/* d0-d7 */
184 AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,	/* d8-df */
185 AC3,  AC3,  AC3,  AC3,  AC3,  AC3,  AC3,  AC3,	/* e0-e7 */
186 AC3,  AC3,  AC3,  AC3,  AC3,  AC3,  AC3,  AC3,	/* e8-ef */
187 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* f0-f7 */
188 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* f8-ff */
189 };
190 uchar statec2[256]={	/* looking for 10xxxxxx to complete a rune */
191 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 00-07 */
192 AWDX, ASPX, ASPNX,AWDX, AWDX, AWDX, AWDX, AWDX,	/* 08-0f */
193 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 10-17 */
194 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 18-1f */
195 ASPX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 20-27 */
196 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 28-2f */
197 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 30-37 */
198 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 38-3f */
199 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 40-47 */
200 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 48-4f */
201 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 50-57 */
202 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 58-5f */
203 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 60-67 */
204 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 68-6f */
205 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 70-77 */
206 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 78-7f */
207 AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,	/* 80-87 */
208 AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,	/* 88-8f */
209 AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,	/* 90-97 */
210 AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,	/* 98-9f */
211 AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,	/* a0-a7 */
212 AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,	/* a8-af */
213 AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,	/* b0-b7 */
214 AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,	/* b8-bf */
215 AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,	/* c0-c7 */
216 AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,	/* c8-cf */
217 AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,	/* d0-d7 */
218 AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,	/* d8-df */
219 AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X,	/* e0-e7 */
220 AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X,	/* e8-ef */
221 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* f0-f7 */
222 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* f8-ff */
223 };
224 uchar statec3[256]={	/* looking for 10xxxxxx,10xxxxxx to complete a rune */
225 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 00-07 */
226 AWDX, ASPX, ASPNX,AWDX, AWDX, AWDX, AWDX, AWDX,	/* 08-0f */
227 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 10-17 */
228 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 18-1f */
229 ASPX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 20-27 */
230 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 28-2f */
231 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 30-37 */
232 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 38-3f */
233 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 40-47 */
234 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 48-4f */
235 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 50-57 */
236 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 58-5f */
237 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 60-67 */
238 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 68-6f */
239 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 70-77 */
240 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 78-7f */
241 AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,	/* 80-87 */
242 AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,	/* 88-8f */
243 AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,	/* 90-97 */
244 AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,	/* 98-9f */
245 AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,	/* a0-a7 */
246 AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,	/* a8-af */
247 AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,	/* b0-b7 */
248 AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,	/* b8-bf */
249 AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,	/* c0-c7 */
250 AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,	/* c8-cf */
251 AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,	/* d0-d7 */
252 AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,	/* d8-df */
253 AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X,	/* e0-e7 */
254 AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X,	/* e8-ef */
255 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* f0-f7 */
256 AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* f8-ff */
257 };
258 void
259 count(int f, char *name)
260 {
261 	int n;
262 	uchar buf[NBUF];
263 	uchar *bufp, *ebuf;
264 	uchar *state=statesp;
265 	nline=nword=nrune=nbadr=nchar=0;
266 	for(;;){
267 		n=read(f, buf, NBUF);
268 		if(n<=0)
269 			break;
270 		nchar+=n;
271 		nrune+=n;	/* might be too large, gets decreased later */
272 		bufp=buf;
273 		ebuf=buf+n;
274 		do{
275 			switch(state[*bufp]){
276 			case AC2:   state=statec2;                   break;
277 			case AC2R:  state=statec2; --nrune;          break;
278 			case AC2W:  state=statec2; nword++;          break;
279 			case AC2X:  state=statec2;          nbadr++; break;
280 			case AC3:   state=statec3;                   break;
281 			case AC3W:  state=statec3; nword++;          break;
282 			case AC3X:  state=statec3;          nbadr++; break;
283 			case ASP:   state=statesp;                   break;
284 			case ASPN:  state=statesp; nline++;          break;
285 			case ASPNX: state=statesp; nline++; nbadr++; break;
286 			case ASPX:  state=statesp;          nbadr++; break;
287 			case AWD:   state=statewd;                   break;
288 			case AWDR:  state=statewd; --nrune;          break;
289 			case AWDW:  state=statewd; nword++;          break;
290 			case AWDWX: state=statewd; nword++; nbadr++; break;
291 			case AWDX:  state=statewd;          nbadr++; break;
292 			}
293 		}while(++bufp!=ebuf);
294 	}
295 	if(state!=statesp && state!=statewd)
296 		nbadr++;
297 	if(n<0)
298 		perror(name);
299 	report(nline, nword, nrune, nbadr, nchar, name);
300 }
301