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