1/* 2 * Copyright (c) 1980, 1987, 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#ifndef lint 31static const char copyright[] = 32"@(#) Copyright (c) 1980, 1987, 1991, 1993\n\ 33 The Regents of the University of California. All rights reserved.\n"; 34#endif /* not lint */ 35 36#if 0 37#ifndef lint 38static char sccsid[] = "@(#)wc.c 8.1 (Berkeley) 6/6/93"; 39#endif /* not lint */ 40#endif 41 42#include <sys/cdefs.h> 43__FBSDID("$FreeBSD$"); 44 45#include <sys/param.h> 46#include <sys/stat.h> 47 48#include <ctype.h> 49#include <err.h> 50#include <errno.h> 51#include <fcntl.h> 52#include <locale.h> 53#include <stdint.h> 54#include <stdio.h> 55#include <stdlib.h> 56#include <string.h> 57#include <unistd.h> 58#include <wchar.h> 59#include <wctype.h> 60 61static uintmax_t tlinect, twordct, tcharct, tlongline; 62static int doline, doword, dochar, domulti, dolongline; 63static volatile sig_atomic_t siginfo; 64 65static void show_cnt(const char *file, uintmax_t linect, uintmax_t wordct, 66 uintmax_t charct, uintmax_t llct); 67static int cnt(const char *); 68static void usage(void); 69 70static void 71siginfo_handler(int sig __unused) 72{ 73 74 siginfo = 1; 75} 76 77static void 78reset_siginfo(void) 79{ 80 81 signal(SIGINFO, SIG_DFL); 82 siginfo = 0; 83} 84 85int 86main(int argc, char *argv[]) 87{ 88 int ch, errors, total; 89 90 (void) setlocale(LC_CTYPE, ""); 91 92 while ((ch = getopt(argc, argv, "clmwL")) != -1) 93 switch((char)ch) { 94 case 'l': 95 doline = 1; 96 break; 97 case 'w': 98 doword = 1; 99 break; 100 case 'c': 101 dochar = 1; 102 domulti = 0; 103 break; 104 case 'L': 105 dolongline = 1; 106 break; 107 case 'm': 108 domulti = 1; 109 dochar = 0; 110 break; 111 case '?': 112 default: 113 usage(); 114 } 115 argv += optind; 116 argc -= optind; 117 118 (void)signal(SIGINFO, siginfo_handler); 119 120 /* Wc's flags are on by default. */ 121 if (doline + doword + dochar + domulti + dolongline == 0) 122 doline = doword = dochar = 1; 123 124 errors = 0; 125 total = 0; 126 if (!*argv) { 127 if (cnt((char *)NULL) != 0) 128 ++errors; 129 } else { 130 do { 131 if (cnt(*argv) != 0) 132 ++errors; 133 ++total; 134 } while(*++argv); 135 } 136 137 if (total > 1) 138 show_cnt("total", tlinect, twordct, tcharct, tlongline); 139 exit(errors == 0 ? 0 : 1); 140} 141 142static void 143show_cnt(const char *file, uintmax_t linect, uintmax_t wordct, 144 uintmax_t charct, uintmax_t llct) 145{ 146 FILE *out; 147 148 if (!siginfo) 149 out = stdout; 150 else { 151 out = stderr; 152 siginfo = 0; 153 } 154 155 if (doline) 156 (void)fprintf(out, " %7ju", linect); 157 if (doword) 158 (void)fprintf(out, " %7ju", wordct); 159 if (dochar || domulti) 160 (void)fprintf(out, " %7ju", charct); 161 if (dolongline) 162 (void)fprintf(out, " %7ju", llct); 163 if (file != NULL) 164 (void)fprintf(out, " %s\n", file); 165 else 166 (void)fprintf(out, "\n"); 167} 168 169static int 170cnt(const char *file) 171{ 172 struct stat sb; 173 uintmax_t linect, wordct, charct, llct, tmpll; 174 int fd, len, warned; 175 size_t clen; 176 short gotsp; 177 u_char *p; 178 u_char buf[MAXBSIZE]; 179 wchar_t wch; 180 mbstate_t mbs; 181 182 linect = wordct = charct = llct = tmpll = 0; 183 if (file == NULL) 184 fd = STDIN_FILENO; 185 else { 186 if ((fd = open(file, O_RDONLY, 0)) < 0) { 187 warn("%s: open", file); 188 return (1); 189 } 190 if (doword || (domulti && MB_CUR_MAX != 1)) 191 goto word; 192 /* 193 * Line counting is split out because it's a lot faster to get 194 * lines than to get words, since the word count requires some 195 * logic. 196 */ 197 if (doline) { 198 while ((len = read(fd, buf, MAXBSIZE))) { 199 if (len == -1) { 200 warn("%s: read", file); 201 (void)close(fd); 202 return (1); 203 } 204 if (siginfo) { 205 show_cnt(file, linect, wordct, charct, 206 llct); 207 } 208 charct += len; 209 for (p = buf; len--; ++p) 210 if (*p == '\n') { 211 if (tmpll > llct) 212 llct = tmpll; 213 tmpll = 0; 214 ++linect; 215 } else 216 tmpll++; 217 } 218 reset_siginfo(); 219 tlinect += linect; 220 if (dochar) 221 tcharct += charct; 222 if (dolongline) { 223 if (llct > tlongline) 224 tlongline = llct; 225 } 226 show_cnt(file, linect, wordct, charct, llct); 227 (void)close(fd); 228 return (0); 229 } 230 /* 231 * If all we need is the number of characters and it's a 232 * regular file, just stat the puppy. 233 */ 234 if (dochar || domulti) { 235 if (fstat(fd, &sb)) { 236 warn("%s: fstat", file); 237 (void)close(fd); 238 return (1); 239 } 240 if (S_ISREG(sb.st_mode)) { 241 reset_siginfo(); 242 charct = sb.st_size; 243 show_cnt(file, linect, wordct, charct, llct); 244 tcharct += charct; 245 (void)close(fd); 246 return (0); 247 } 248 } 249 } 250 251 /* Do it the hard way... */ 252word: gotsp = 1; 253 warned = 0; 254 memset(&mbs, 0, sizeof(mbs)); 255 while ((len = read(fd, buf, MAXBSIZE)) != 0) { 256 if (len == -1) { 257 warn("%s: read", file != NULL ? file : "stdin"); 258 (void)close(fd); 259 return (1); 260 } 261 p = buf; 262 while (len > 0) { 263 if (siginfo) 264 show_cnt(file, linect, wordct, charct, llct); 265 if (!domulti || MB_CUR_MAX == 1) { 266 clen = 1; 267 wch = (unsigned char)*p; 268 } else if ((clen = mbrtowc(&wch, p, len, &mbs)) == 269 (size_t)-1) { 270 if (!warned) { 271 errno = EILSEQ; 272 warn("%s", 273 file != NULL ? file : "stdin"); 274 warned = 1; 275 } 276 memset(&mbs, 0, sizeof(mbs)); 277 clen = 1; 278 wch = (unsigned char)*p; 279 } else if (clen == (size_t)-2) 280 break; 281 else if (clen == 0) 282 clen = 1; 283 charct++; 284 if (wch != L'\n') 285 tmpll++; 286 len -= clen; 287 p += clen; 288 if (wch == L'\n') { 289 if (tmpll > llct) 290 llct = tmpll; 291 tmpll = 0; 292 ++linect; 293 } 294 if (iswspace(wch)) 295 gotsp = 1; 296 else if (gotsp) { 297 gotsp = 0; 298 ++wordct; 299 } 300 } 301 } 302 reset_siginfo(); 303 if (domulti && MB_CUR_MAX > 1) 304 if (mbrtowc(NULL, NULL, 0, &mbs) == (size_t)-1 && !warned) 305 warn("%s", file != NULL ? file : "stdin"); 306 if (doline) 307 tlinect += linect; 308 if (doword) 309 twordct += wordct; 310 if (dochar || domulti) 311 tcharct += charct; 312 if (dolongline) { 313 if (llct > tlongline) 314 tlongline = llct; 315 } 316 show_cnt(file, linect, wordct, charct, llct); 317 (void)close(fd); 318 return (0); 319} 320 321static void 322usage(void) 323{ 324 (void)fprintf(stderr, "usage: wc [-Lclmw] [file ...]\n"); 325 exit(1); 326} 327