11590Srgrimes/*
21590Srgrimes * Copyright (c) 1980, 1987, 1991, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
301590Srgrimes#ifndef lint
3191792Smikestatic const char copyright[] =
321590Srgrimes"@(#) Copyright (c) 1980, 1987, 1991, 1993\n\
331590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
3491481Smike#endif /* not lint */
351590Srgrimes
3691792Smike#if 0
371590Srgrimes#ifndef lint
3891481Smikestatic char sccsid[] = "@(#)wc.c	8.1 (Berkeley) 6/6/93";
3991481Smike#endif /* not lint */
4015233Sbde#endif
411590Srgrimes
4291481Smike#include <sys/cdefs.h>
4391481Smike__FBSDID("$FreeBSD$");
4491481Smike
451590Srgrimes#include <sys/param.h>
461590Srgrimes#include <sys/stat.h>
4715233Sbde
4815233Sbde#include <ctype.h>
4915233Sbde#include <err.h>
5098165Stjr#include <errno.h>
511590Srgrimes#include <fcntl.h>
5213380Sache#include <locale.h>
5391481Smike#include <stdint.h>
541590Srgrimes#include <stdio.h>
551590Srgrimes#include <stdlib.h>
561590Srgrimes#include <string.h>
5715233Sbde#include <unistd.h>
58128049Stjr#include <wchar.h>
59101670Stjr#include <wctype.h>
601590Srgrimes
61227201Sedstatic uintmax_t tlinect, twordct, tcharct, tlongline;
62227201Sedstatic int doline, doword, dochar, domulti, dolongline;
63208210Spjdstatic volatile sig_atomic_t siginfo;
641590Srgrimes
65208170Spjdstatic void	show_cnt(const char *file, uintmax_t linect, uintmax_t wordct,
66208170Spjd		    uintmax_t charct, uintmax_t llct);
6792922Simpstatic int	cnt(const char *);
6892922Simpstatic void	usage(void);
691590Srgrimes
70208170Spjdstatic void
71208170Spjdsiginfo_handler(int sig __unused)
72208170Spjd{
73208170Spjd
74208170Spjd	siginfo = 1;
75208170Spjd}
76208170Spjd
77282278Sbdrewerystatic void
78282278Sbdreweryreset_siginfo(void)
79282278Sbdrewery{
80282278Sbdrewery
81282278Sbdrewery	signal(SIGINFO, SIG_DFL);
82282278Sbdrewery	siginfo = 0;
83282278Sbdrewery}
84282278Sbdrewery
851590Srgrimesint
86139364Sjosefmain(int argc, char *argv[])
871590Srgrimes{
8849685Ssheldonh	int ch, errors, total;
891590Srgrimes
9013380Sache	(void) setlocale(LC_CTYPE, "");
9113380Sache
92185714Skeramida	while ((ch = getopt(argc, argv, "clmwL")) != -1)
931590Srgrimes		switch((char)ch) {
941590Srgrimes		case 'l':
951590Srgrimes			doline = 1;
961590Srgrimes			break;
971590Srgrimes		case 'w':
981590Srgrimes			doword = 1;
991590Srgrimes			break;
1001590Srgrimes		case 'c':
1011590Srgrimes			dochar = 1;
10298165Stjr			domulti = 0;
1031590Srgrimes			break;
104185714Skeramida		case 'L':
105185714Skeramida			dolongline = 1;
106185714Skeramida			break;
10798165Stjr		case 'm':
10898165Stjr			domulti = 1;
10998165Stjr			dochar = 0;
11098165Stjr			break;
1111590Srgrimes		case '?':
1121590Srgrimes		default:
1131590Srgrimes			usage();
1141590Srgrimes		}
1151590Srgrimes	argv += optind;
1161590Srgrimes	argc -= optind;
1171590Srgrimes
118208170Spjd	(void)signal(SIGINFO, siginfo_handler);
119208170Spjd
1201590Srgrimes	/* Wc's flags are on by default. */
121185714Skeramida	if (doline + doword + dochar + domulti + dolongline == 0)
1221590Srgrimes		doline = doword = dochar = 1;
1231590Srgrimes
12415233Sbde	errors = 0;
1251590Srgrimes	total = 0;
1261590Srgrimes	if (!*argv) {
12715233Sbde		if (cnt((char *)NULL) != 0)
12815233Sbde			++errors;
129208170Spjd	} else {
130208170Spjd		do {
131208170Spjd			if (cnt(*argv) != 0)
132208170Spjd				++errors;
133208170Spjd			++total;
134208170Spjd		} while(*++argv);
1351590Srgrimes	}
1361590Srgrimes
137208170Spjd	if (total > 1)
138208170Spjd		show_cnt("total", tlinect, twordct, tcharct, tlongline);
13915233Sbde	exit(errors == 0 ? 0 : 1);
1401590Srgrimes}
1411590Srgrimes
142208170Spjdstatic void
143208170Spjdshow_cnt(const char *file, uintmax_t linect, uintmax_t wordct,
144208170Spjd    uintmax_t charct, uintmax_t llct)
145208170Spjd{
146208170Spjd	FILE *out;
147208170Spjd
148208170Spjd	if (!siginfo)
149208170Spjd		out = stdout;
150208170Spjd	else {
151208170Spjd		out = stderr;
152208170Spjd		siginfo = 0;
153208170Spjd	}
154208170Spjd
155208170Spjd	if (doline)
156208170Spjd		(void)fprintf(out, " %7ju", linect);
157208170Spjd	if (doword)
158208170Spjd		(void)fprintf(out, " %7ju", wordct);
159208170Spjd	if (dochar || domulti)
160208170Spjd		(void)fprintf(out, " %7ju", charct);
161208170Spjd	if (dolongline)
162208170Spjd		(void)fprintf(out, " %7ju", llct);
163208170Spjd	if (file != NULL)
164208170Spjd		(void)fprintf(out, " %s\n", file);
165208170Spjd	else
166208170Spjd		(void)fprintf(out, "\n");
167208170Spjd}
168208170Spjd
16991481Smikestatic int
170139364Sjosefcnt(const char *file)
1711590Srgrimes{
17249685Ssheldonh	struct stat sb;
173185714Skeramida	uintmax_t linect, wordct, charct, llct, tmpll;
174128049Stjr	int fd, len, warned;
175128049Stjr	size_t clen;
17649461Ssheldonh	short gotsp;
17749685Ssheldonh	u_char *p;
17898291Stjr	u_char buf[MAXBSIZE];
17998165Stjr	wchar_t wch;
180128049Stjr	mbstate_t mbs;
1811590Srgrimes
182185714Skeramida	linect = wordct = charct = llct = tmpll = 0;
183208170Spjd	if (file == NULL)
18415233Sbde		fd = STDIN_FILENO;
185208170Spjd	else {
18615179Swosch		if ((fd = open(file, O_RDONLY, 0)) < 0) {
18715233Sbde			warn("%s: open", file);
18815233Sbde			return (1);
18915179Swosch		}
19098165Stjr		if (doword || (domulti && MB_CUR_MAX != 1))
1911590Srgrimes			goto word;
1921590Srgrimes		/*
1931590Srgrimes		 * Line counting is split out because it's a lot faster to get
1941590Srgrimes		 * lines than to get words, since the word count requires some
1951590Srgrimes		 * logic.
1961590Srgrimes		 */
1971590Srgrimes		if (doline) {
19828696Scharnier			while ((len = read(fd, buf, MAXBSIZE))) {
19915233Sbde				if (len == -1) {
20015233Sbde					warn("%s: read", file);
20115233Sbde					(void)close(fd);
20215233Sbde					return (1);
20315233Sbde				}
204208170Spjd				if (siginfo) {
205208170Spjd					show_cnt(file, linect, wordct, charct,
206208170Spjd					    llct);
207208170Spjd				}
2081590Srgrimes				charct += len;
2091590Srgrimes				for (p = buf; len--; ++p)
210185714Skeramida					if (*p == '\n') {
211185714Skeramida						if (tmpll > llct)
212185714Skeramida							llct = tmpll;
213185714Skeramida						tmpll = 0;
2141590Srgrimes						++linect;
215185714Skeramida					} else
216185714Skeramida						tmpll++;
2171590Srgrimes			}
218282278Sbdrewery			reset_siginfo();
2191590Srgrimes			tlinect += linect;
220208170Spjd			if (dochar)
2211590Srgrimes				tcharct += charct;
222185714Skeramida			if (dolongline) {
223185714Skeramida				if (llct > tlongline)
224185714Skeramida					tlongline = llct;
225185714Skeramida			}
226208170Spjd			show_cnt(file, linect, wordct, charct, llct);
2271590Srgrimes			(void)close(fd);
22815233Sbde			return (0);
2291590Srgrimes		}
2301590Srgrimes		/*
2311590Srgrimes		 * If all we need is the number of characters and it's a
23298245Stjr		 * regular file, just stat the puppy.
2331590Srgrimes		 */
23498165Stjr		if (dochar || domulti) {
23515233Sbde			if (fstat(fd, &sb)) {
23615233Sbde				warn("%s: fstat", file);
23715233Sbde				(void)close(fd);
23815233Sbde				return (1);
23915233Sbde			}
24098245Stjr			if (S_ISREG(sb.st_mode)) {
241282278Sbdrewery				reset_siginfo();
242208170Spjd				charct = sb.st_size;
243208170Spjd				show_cnt(file, linect, wordct, charct, llct);
244208170Spjd				tcharct += charct;
2451590Srgrimes				(void)close(fd);
24615233Sbde				return (0);
2471590Srgrimes			}
2481590Srgrimes		}
2491590Srgrimes	}
2501590Srgrimes
2511590Srgrimes	/* Do it the hard way... */
25298165Stjrword:	gotsp = 1;
25398165Stjr	warned = 0;
254128049Stjr	memset(&mbs, 0, sizeof(mbs));
255128049Stjr	while ((len = read(fd, buf, MAXBSIZE)) != 0) {
256128049Stjr		if (len == -1) {
257208170Spjd			warn("%s: read", file != NULL ? file : "stdin");
25815233Sbde			(void)close(fd);
25915233Sbde			return (1);
26015233Sbde		}
26198165Stjr		p = buf;
26298165Stjr		while (len > 0) {
263208170Spjd			if (siginfo)
264208170Spjd				show_cnt(file, linect, wordct, charct, llct);
26598165Stjr			if (!domulti || MB_CUR_MAX == 1) {
26698165Stjr				clen = 1;
26798165Stjr				wch = (unsigned char)*p;
268128049Stjr			} else if ((clen = mbrtowc(&wch, p, len, &mbs)) ==
269128049Stjr			    (size_t)-1) {
270128049Stjr				if (!warned) {
271128049Stjr					errno = EILSEQ;
272208170Spjd					warn("%s",
273208170Spjd					    file != NULL ? file : "stdin");
274128049Stjr					warned = 1;
27598165Stjr				}
276128049Stjr				memset(&mbs, 0, sizeof(mbs));
277128049Stjr				clen = 1;
278128049Stjr				wch = (unsigned char)*p;
279128049Stjr			} else if (clen == (size_t)-2)
280128049Stjr				break;
281128049Stjr			else if (clen == 0)
282128049Stjr				clen = 1;
28398165Stjr			charct++;
284185714Skeramida			if (wch != L'\n')
285185714Skeramida				tmpll++;
28698165Stjr			len -= clen;
28798165Stjr			p += clen;
288185714Skeramida			if (wch == L'\n') {
289185714Skeramida				if (tmpll > llct)
290185714Skeramida					llct = tmpll;
291185714Skeramida				tmpll = 0;
2921590Srgrimes				++linect;
293185714Skeramida			}
294101670Stjr			if (iswspace(wch))
2951590Srgrimes				gotsp = 1;
2961590Srgrimes			else if (gotsp) {
2971590Srgrimes				gotsp = 0;
2981590Srgrimes				++wordct;
2991590Srgrimes			}
3001590Srgrimes		}
3011590Srgrimes	}
302282278Sbdrewery	reset_siginfo();
303128049Stjr	if (domulti && MB_CUR_MAX > 1)
304128049Stjr		if (mbrtowc(NULL, NULL, 0, &mbs) == (size_t)-1 && !warned)
305208170Spjd			warn("%s", file != NULL ? file : "stdin");
306208170Spjd	if (doline)
3071590Srgrimes		tlinect += linect;
308208170Spjd	if (doword)
3091590Srgrimes		twordct += wordct;
310208170Spjd	if (dochar || domulti)
3111590Srgrimes		tcharct += charct;
312185714Skeramida	if (dolongline) {
313185714Skeramida		if (llct > tlongline)
314185714Skeramida			tlongline = llct;
315185714Skeramida	}
316208170Spjd	show_cnt(file, linect, wordct, charct, llct);
3171590Srgrimes	(void)close(fd);
31815233Sbde	return (0);
3191590Srgrimes}
3201590Srgrimes
32191481Smikestatic void
322201181Sedusage(void)
3231590Srgrimes{
324185714Skeramida	(void)fprintf(stderr, "usage: wc [-Lclmw] [file ...]\n");
3251590Srgrimes	exit(1);
3261590Srgrimes}
327