1 /* @(#)count.c 1.31 21/08/20 Copyright 1986-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)count.c 1.31 21/08/20 Copyright 1986-2021 J. Schilling";
6 #endif
7 /*
8 * count words, lines, and/or chars in files
9 *
10 * Copyright (c) 1986-2021 J. Schilling
11 */
12 /*
13 * The contents of this file are subject to the terms of the
14 * Common Development and Distribution License, Version 1.0 only
15 * (the "License"). You may not use this file except in compliance
16 * with the License.
17 *
18 * See the file CDDL.Schily.txt in this distribution for details.
19 * A copy of the CDDL is also available via the Internet at
20 * http://www.opensource.org/licenses/cddl1.txt
21 *
22 * When distributing Covered Code, include this CDDL HEADER in each
23 * file and include the License file CDDL.Schily.txt from this distribution.
24 */
25
26 #include <schily/stdio.h>
27 #include <schily/stdlib.h>
28 #include <schily/unistd.h> /* Include sys/types.h to make off_t available */
29 #include <schily/utypes.h>
30 #include <schily/standard.h>
31 #define GT_COMERR /* #define comerr gtcomerr */
32 #define GT_ERROR /* #define error gterror */
33 #include <schily/schily.h>
34 #include <schily/nlsdefs.h>
35 #include <schily/limits.h> /* for MB_LEN_MAX */
36 #include <schily/ctype.h> /* For isprint() */
37 #include <schily/wchar.h> /* wchar_t */
38 #include <schily/wctype.h> /* For iswprint() */
39
40 #define iswhite(c) (c == ' ' || c == '\t' || c == '\n')
41
42 #define TABSTOP 8
43 int tabstop = TABSTOP;
44 /*
45 * Make it long long as sums may be always more than 2 GB
46 */
47 Llong tchars = (Llong)0;
48 Llong tmchars = (Llong)0;
49 Llong twords = (Llong)0;
50 Llong tlines = (Llong)0;
51 Llong tllen = (Llong)0;
52 char flags[] = "lines,l,words,w,chars,c,mchars,m,C,llen,ll,stat,s,total,t,tab#,help,version";
53 int cflg = 0;
54 int mflg = 0;
55 int wflg = 0;
56 int lflg = 0;
57 int llflg = 0;
58 int sflg = 0;
59 int head = 0;
60 int totflg = 0;
61 int nfiles = 0;
62 int help = 0;
63 int prversion = 0;
64 char *filename = 0; /* current file name */
65
66 LOCAL void usage __PR((int exitcode));
67 EXPORT int main __PR((int ac, char **av));
68 LOCAL void count __PR((FILE * f));
69 LOCAL void phead __PR((void));
70 LOCAL void p __PR((Llong val));
71 LOCAL int statfile __PR((FILE * f));
72
73 LOCAL void
usage(exitcode)74 usage(exitcode)
75 int exitcode;
76 {
77 error("Usage: count [options] file1...filen\n");
78 error("Options:\n");
79 error(" -lines Count lines\n");
80 error(" -words Count words\n");
81 error(" -chars Count characters based on bytes\n");
82 error(" -mchars Count multi byte characters\n");
83 error(" -llen Count max linelen\n");
84 error(" -stat Stat file for character count\n");
85 error(" tab=# Set tabsize to # (default %d)\n", TABSTOP);
86 error(" -total Print only grand total\n");
87 error(" -help Print this help.\n");
88 error(" -version Print version number.\n");
89 exit(exitcode);
90 }
91
92 EXPORT int
main(ac,av)93 main(ac, av)
94 int ac;
95 char *av[];
96 {
97 int cac;
98 char * const *cav;
99 FILE *f;
100 #if defined(USE_NLS)
101 char *dir;
102 #endif
103
104 save_args(ac, av);
105
106 (void) setlocale(LC_ALL, "");
107
108 #if defined(USE_NLS)
109 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
110 #define TEXT_DOMAIN "count" /* Use this only if it weren't */
111 #endif
112 dir = searchfileinpath("share/locale", F_OK,
113 SIP_ANY_FILE|SIP_NO_PATH, NULL);
114 if (dir)
115 (void) bindtextdomain(TEXT_DOMAIN, dir);
116 else
117 #if !defined(INS_BASE)
118 #define INS_BASE "/usr"
119 #endif
120 #ifdef PROTOTYPES
121 (void) bindtextdomain(TEXT_DOMAIN, INS_BASE "/share/locale");
122 #else
123 (void) bindtextdomain(TEXT_DOMAIN, "/usr/share/locale");
124 #endif
125 (void) textdomain(TEXT_DOMAIN);
126 #endif
127
128 cac = --ac;
129 cav = ++av;
130
131 if (getallargs(&cac, &cav, flags,
132 &lflg, &lflg,
133 &wflg, &wflg,
134 &cflg, &cflg,
135 &mflg, &mflg, &mflg,
136 &llflg, &llflg,
137 &sflg, &sflg,
138 &totflg, &totflg, &tabstop,
139 &help, &prversion) < 0) {
140 errmsgno(EX_BAD, "Bad Option: '%s'.\n", cav[0]);
141 usage(EX_BAD);
142 }
143 if (help)
144 usage(0);
145 if (prversion) {
146 gtprintf(
147 "Count release %s %s (%s-%s-%s) Copyright (C) 1986-2021 %s\n",
148 "1.31", "2021/08/20",
149 HOST_CPU, HOST_VENDOR, HOST_OS,
150 _("J�rg Schilling"));
151 exit(0);
152 }
153 if (!(lflg || wflg || cflg || mflg || llflg)) {
154 lflg++;
155 wflg++;
156 cflg++;
157 llflg++;
158 }
159 if (sflg) {
160 lflg = 0;
161 wflg = 0;
162 cflg++;
163 mflg = 0;
164 llflg = 0;
165 }
166 #if MB_LEN_MAX == 1
167 if (mflg && !cflg)
168 cflg++;
169 mflg = 0;
170 #endif
171 cac = ac;
172 cav = av;
173 if (getfiles(&cac, &cav, flags) == 0) {
174 filename = "stdin";
175 count(stdin);
176 } else do {
177 filename = cav[0];
178 if (streql(filename, "-")) {
179 filename = "stdin";
180 count(stdin);
181 } else {
182 if ((f = fileopen(filename, "r")) == NULL)
183 errmsg("Can't open '%s'.\n", filename);
184 else
185 count(f);
186 }
187 cav++;
188 cac--;
189 } while (getfiles(&cac, &cav, flags) > 0);
190
191 if (nfiles > 1 || totflg) {
192 if (!head)
193 phead();
194 if (lflg)
195 p(tlines);
196 if (wflg)
197 p(twords);
198 if (cflg)
199 p(tchars);
200 if (mflg)
201 p(tmchars);
202 if (llflg)
203 p(tllen);
204 printf(_(" total\n"));
205 }
206 exit(0);
207 return (0); /* Keep lint happy */
208 }
209
210 LOCAL void
count(f)211 count(f)
212 register FILE *f;
213 {
214 register wint_t c;
215 register BOOL inword = FALSE;
216 register off_t hpos = (off_t)0;
217 register off_t chars = (off_t)0;
218 register off_t words = (off_t)0;
219 register off_t lines = (off_t)0;
220 register off_t llen = (off_t)0;
221 register off_t mchars = (off_t)0;
222 #if MB_LEN_MAX > 1
223 char mb[MB_LEN_MAX+1];
224 register size_t nmb;
225 int mlen;
226 wchar_t wc;
227 #endif
228
229 file_raise(f, FALSE);
230
231 #ifdef HAVE_SETVBUF
232 setvbuf(f, NULL, _IOFBF, 32*1024);
233 #endif
234
235 if (sflg && statfile(f))
236 return;
237
238 if (mflg) {
239 #if MB_LEN_MAX > 1
240 nmb = 0;
241 if (wflg == 0 && llflg == 0) {
242 while ((c = getc(f)) != EOF) {
243 mb[nmb++] = c;
244 if ((mlen = mbtowc(&wc, mb, nmb)) < 0) {
245 (void) mbtowc(NULL, NULL, 0);
246 if (nmb < MB_LEN_MAX)
247 continue;
248 wc = mb[0] & 0xFF;
249 chars++;
250 mchars++;
251 mb[nmb] = '\0';
252 nmb -= 1;
253 ovstrcpy(mb, &mb[1]);
254 continue;
255 } else {
256 if (mlen == 0)
257 mlen++;
258 chars += mlen;
259 mchars++;
260 if (nmb > mlen) {
261 mb[nmb] = '\0';
262 nmb -= mlen;
263 ovstrcpy(mb, &mb[mlen]);
264 } else {
265 nmb = 0;
266 }
267 }
268 if (c == '\n') {
269 lines++;
270 }
271 }
272 } else while ((c = getc(f)) != EOF) {
273 mb[nmb++] = c;
274 if ((mlen = mbtowc(&wc, mb, nmb)) < 0) {
275 (void) mbtowc(NULL, NULL, 0);
276 if (nmb < MB_LEN_MAX)
277 continue;
278 wc = mb[0] & 0xFF;
279 chars++;
280 mchars++;
281 mb[nmb] = '\0';
282 nmb -= 1;
283 ovstrcpy(mb, &mb[1]);
284 continue;
285 } else {
286 if (mlen == 0)
287 mlen++;
288 chars += mlen;
289 mchars++;
290 if (nmb > mlen) {
291 mb[nmb] = '\0';
292 nmb -= mlen;
293 ovstrcpy(mb, &mb[mlen]);
294 } else {
295 nmb = 0;
296 }
297 }
298
299 if (iswhite(wc)) {
300 if (wc == '\n') {
301 if (hpos > llen)
302 llen = hpos;
303 hpos = 0;
304 lines++;
305 } else if (wc == '\t') {
306 hpos = (hpos / tabstop) * tabstop + tabstop;
307 } else {
308 hpos++;
309 }
310 if (inword)
311 words++;
312 inword = FALSE;
313 } else {
314 hpos++;
315 inword = TRUE;
316 }
317 }
318 chars += nmb;
319 mchars += nmb;
320 #endif
321 } else
322 if (wflg == 0 && llflg == 0) {
323 while ((c = getc(f)) != EOF) {
324 if (c == '\n') {
325 lines++;
326 }
327 chars++;
328 }
329 } else while ((c = getc(f)) != EOF) {
330 chars++;
331 if (iswhite(c)) {
332 if (c == '\n') {
333 if (hpos > llen)
334 llen = hpos;
335 hpos = 0;
336 lines++;
337 } else if (c == '\t') {
338 hpos = (hpos / tabstop) * tabstop + tabstop;
339 } else {
340 hpos++;
341 }
342 if (inword)
343 words++;
344 inword = FALSE;
345 } else {
346 hpos++;
347 inword = TRUE;
348 }
349 }
350 if (c == EOF && ferror(f))
351 errmsg("I/O error on '%s'.\n", filename);
352
353 if (hpos > llen)
354 llen = hpos;
355 if (f != stdin)
356 fclose(f);
357 if (!totflg) {
358 if (!head)
359 phead();
360 if (lflg)
361 p((Llong)lines);
362 if (wflg)
363 p((Llong)words);
364 if (cflg)
365 p((Llong)chars);
366 if (mflg)
367 p((Llong)mchars);
368 if (llflg)
369 p((Llong)llen);
370 printf(" %s\n", filename);
371 }
372 tchars += chars;
373 tmchars += mchars;
374 twords += words;
375 tlines += lines;
376 if (llen > tllen)
377 tllen = llen;
378 chars = (off_t)0;
379 mchars = (off_t)0;
380 lines = 0;
381 words = 0;
382 nfiles++;
383 }
384
385 LOCAL void
phead()386 phead()
387 {
388 head++;
389 if (lflg)
390 printf("%8s", _("lines"));
391 if (wflg)
392 printf("%8s", _("words"));
393 if (cflg)
394 printf("%8s", _("chars"));
395 if (mflg)
396 printf("%8s", _("mchars"));
397 if (llflg)
398 printf("%8s", _("linelen"));
399 printf("\n");
400 }
401
402 LOCAL void
p(val)403 p(val)
404 Llong val;
405 {
406 if (sizeof (val) > sizeof (long))
407 printf(" %7lld", val);
408 else
409 printf(" %7ld", (long)val);
410 }
411
412 #include <schily/stat.h>
413
414 LOCAL int
statfile(f)415 statfile(f)
416 FILE *f;
417 {
418 struct stat sb;
419
420 if (fstat(fileno(f), &sb) < 0) {
421 errmsg("Can't stat '%s'.\n", filename);
422 return (0);
423 }
424 if ((sb.st_mode & S_IFMT) != S_IFREG)
425 return (0);
426
427 if (f != stdin)
428 fclose(f);
429
430 if (!totflg) {
431 if (!head)
432 phead();
433 p(sb.st_size);
434 printf(" %s\n", filename);
435 }
436 tchars += sb.st_size;
437 nfiles++;
438 return (1);
439 }
440