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