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