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/capsicum.h> 48 #include <sys/param.h> 49 #include <sys/stat.h> 50 51 #include <capsicum_helpers.h> 52 #include <ctype.h> 53 #include <err.h> 54 #include <errno.h> 55 #include <fcntl.h> 56 #include <locale.h> 57 #include <stdint.h> 58 #include <stdio.h> 59 #include <stdlib.h> 60 #include <string.h> 61 #include <unistd.h> 62 #include <wchar.h> 63 #include <wctype.h> 64 #include <libxo/xo.h> 65 66 #include <libcasper.h> 67 #include <casper/cap_fileargs.h> 68 69 static fileargs_t *fa; 70 static uintmax_t tlinect, twordct, tcharct, tlongline; 71 static int doline, doword, dochar, domulti, dolongline; 72 static volatile sig_atomic_t siginfo; 73 static xo_handle_t *stderr_handle; 74 75 static void show_cnt(const char *file, uintmax_t linect, uintmax_t wordct, 76 uintmax_t charct, uintmax_t llct); 77 static int cnt(const char *); 78 static void usage(void); 79 80 static void 81 siginfo_handler(int sig __unused) 82 { 83 84 siginfo = 1; 85 } 86 87 static void 88 reset_siginfo(void) 89 { 90 91 signal(SIGINFO, SIG_DFL); 92 siginfo = 0; 93 } 94 95 int 96 main(int argc, char *argv[]) 97 { 98 int ch, errors, total; 99 cap_rights_t rights; 100 101 (void) setlocale(LC_CTYPE, ""); 102 103 argc = xo_parse_args(argc, argv); 104 if (argc < 0) 105 return (argc); 106 107 while ((ch = getopt(argc, argv, "clmwL")) != -1) 108 switch((char)ch) { 109 case 'l': 110 doline = 1; 111 break; 112 case 'w': 113 doword = 1; 114 break; 115 case 'c': 116 dochar = 1; 117 domulti = 0; 118 break; 119 case 'L': 120 dolongline = 1; 121 break; 122 case 'm': 123 domulti = 1; 124 dochar = 0; 125 break; 126 case '?': 127 default: 128 usage(); 129 } 130 argv += optind; 131 argc -= optind; 132 133 (void)signal(SIGINFO, siginfo_handler); 134 135 fa = fileargs_init(argc, argv, O_RDONLY, 0, 136 cap_rights_init(&rights, CAP_READ, CAP_FSTAT)); 137 if (fa == NULL) { 138 xo_warn("Unable to init casper"); 139 exit(1); 140 } 141 142 caph_cache_catpages(); 143 if (caph_limit_stdio() < 0) { 144 xo_warn("Unable to limit stdio"); 145 fileargs_free(fa); 146 exit(1); 147 } 148 149 if (caph_enter_casper() < 0) { 150 xo_warn("Unable to enter capability mode"); 151 fileargs_free(fa); 152 exit(1); 153 } 154 155 /* Wc's flags are on by default. */ 156 if (doline + doword + dochar + domulti + dolongline == 0) 157 doline = doword = dochar = 1; 158 159 stderr_handle = xo_create_to_file(stderr, XO_STYLE_TEXT, 0); 160 xo_open_container("wc"); 161 xo_open_list("file"); 162 163 errors = 0; 164 total = 0; 165 if (!*argv) { 166 xo_open_instance("file"); 167 if (cnt((char *)NULL) != 0) 168 ++errors; 169 xo_close_instance("file"); 170 } else { 171 do { 172 xo_open_instance("file"); 173 if (cnt(*argv) != 0) 174 ++errors; 175 xo_close_instance("file"); 176 ++total; 177 } while(*++argv); 178 } 179 180 xo_close_list("file"); 181 182 if (total > 1) { 183 xo_open_container("total"); 184 show_cnt("total", tlinect, twordct, tcharct, tlongline); 185 xo_close_container("total"); 186 } 187 188 fileargs_free(fa); 189 xo_close_container("wc"); 190 xo_finish(); 191 exit(errors == 0 ? 0 : 1); 192 } 193 194 static void 195 show_cnt(const char *file, uintmax_t linect, uintmax_t wordct, 196 uintmax_t charct, uintmax_t llct) 197 { 198 xo_handle_t *xop; 199 200 if (!siginfo) 201 xop = NULL; 202 else { 203 xop = stderr_handle; 204 siginfo = 0; 205 } 206 207 if (doline) 208 xo_emit_h(xop, " {:lines/%7ju/%ju}", linect); 209 if (doword) 210 xo_emit_h(xop, " {:words/%7ju/%ju}", wordct); 211 if (dochar || domulti) 212 xo_emit_h(xop, " {:characters/%7ju/%ju}", charct); 213 if (dolongline) 214 xo_emit_h(xop, " {:long-lines/%7ju/%ju}", llct); 215 if (file != NULL) 216 xo_emit_h(xop, " {:filename/%s}\n", file); 217 else 218 xo_emit_h(xop, "\n"); 219 } 220 221 static int 222 cnt(const char *file) 223 { 224 struct stat sb; 225 uintmax_t linect, wordct, charct, llct, tmpll; 226 int fd, len, warned; 227 size_t clen; 228 short gotsp; 229 u_char *p; 230 u_char buf[MAXBSIZE]; 231 wchar_t wch; 232 mbstate_t mbs; 233 234 linect = wordct = charct = llct = tmpll = 0; 235 if (file == NULL) 236 fd = STDIN_FILENO; 237 else if ((fd = fileargs_open(fa, file)) < 0) { 238 xo_warn("%s: open", file); 239 return (1); 240 } 241 if (doword || (domulti && MB_CUR_MAX != 1)) 242 goto word; 243 /* 244 * If all we need is the number of characters and it's a regular file, 245 * just stat it. 246 */ 247 if (doline == 0 && dolongline == 0) { 248 if (fstat(fd, &sb)) { 249 xo_warn("%s: fstat", file); 250 (void)close(fd); 251 return (1); 252 } 253 if (S_ISREG(sb.st_mode)) { 254 reset_siginfo(); 255 charct = sb.st_size; 256 show_cnt(file, linect, wordct, charct, llct); 257 tcharct += charct; 258 (void)close(fd); 259 return (0); 260 } 261 } 262 /* 263 * For files we can't stat, or if we need line counting, slurp the 264 * file. Line counting is split out because it's a lot faster to get 265 * lines than to get words, since the word count requires locale 266 * handling. 267 */ 268 while ((len = read(fd, buf, MAXBSIZE))) { 269 if (len == -1) { 270 xo_warn("%s: read", file); 271 (void)close(fd); 272 return (1); 273 } 274 if (siginfo) 275 show_cnt(file, linect, wordct, charct, llct); 276 charct += len; 277 if (doline || dolongline) { 278 for (p = buf; len--; ++p) 279 if (*p == '\n') { 280 if (tmpll > llct) 281 llct = tmpll; 282 tmpll = 0; 283 ++linect; 284 } else 285 tmpll++; 286 } 287 } 288 reset_siginfo(); 289 if (doline) 290 tlinect += linect; 291 if (dochar) 292 tcharct += charct; 293 if (dolongline && llct > tlongline) 294 tlongline = llct; 295 show_cnt(file, linect, wordct, charct, llct); 296 (void)close(fd); 297 return (0); 298 299 /* Do it the hard way... */ 300 word: gotsp = 1; 301 warned = 0; 302 memset(&mbs, 0, sizeof(mbs)); 303 while ((len = read(fd, buf, MAXBSIZE)) != 0) { 304 if (len == -1) { 305 xo_warn("%s: read", file != NULL ? file : "stdin"); 306 (void)close(fd); 307 return (1); 308 } 309 p = buf; 310 while (len > 0) { 311 if (siginfo) 312 show_cnt(file, linect, wordct, charct, llct); 313 if (!domulti || MB_CUR_MAX == 1) { 314 clen = 1; 315 wch = (unsigned char)*p; 316 } else if ((clen = mbrtowc(&wch, p, len, &mbs)) == 317 (size_t)-1) { 318 if (!warned) { 319 errno = EILSEQ; 320 xo_warn("%s", 321 file != NULL ? file : "stdin"); 322 warned = 1; 323 } 324 memset(&mbs, 0, sizeof(mbs)); 325 clen = 1; 326 wch = (unsigned char)*p; 327 } else if (clen == (size_t)-2) 328 break; 329 else if (clen == 0) 330 clen = 1; 331 charct++; 332 if (wch != L'\n') 333 tmpll++; 334 len -= clen; 335 p += clen; 336 if (wch == L'\n') { 337 if (tmpll > llct) 338 llct = tmpll; 339 tmpll = 0; 340 ++linect; 341 } 342 if (iswspace(wch)) 343 gotsp = 1; 344 else if (gotsp) { 345 gotsp = 0; 346 ++wordct; 347 } 348 } 349 } 350 reset_siginfo(); 351 if (domulti && MB_CUR_MAX > 1) 352 if (mbrtowc(NULL, NULL, 0, &mbs) == (size_t)-1 && !warned) 353 xo_warn("%s", file != NULL ? file : "stdin"); 354 if (doline) 355 tlinect += linect; 356 if (doword) 357 twordct += wordct; 358 if (dochar || domulti) 359 tcharct += charct; 360 if (dolongline && llct > tlongline) 361 tlongline = llct; 362 show_cnt(file, linect, wordct, charct, llct); 363 (void)close(fd); 364 return (0); 365 } 366 367 static void 368 usage(void) 369 { 370 xo_error("usage: wc [-Lclmw] [file ...]\n"); 371 exit(1); 372 } 373