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