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 char buf[MAXBSIZE], *p; 218 struct stat sb; 219 mbstate_t mbs; 220 uintmax_t linect, wordct, charct, llct, tmpll; 221 ssize_t len; 222 size_t clen; 223 int fd; 224 wchar_t wch; 225 bool gotsp, warned; 226 227 linect = wordct = charct = llct = tmpll = 0; 228 if (file == NULL) { 229 fd = STDIN_FILENO; 230 file = stdin_filename; 231 } else if ((fd = fileargs_open(fa, file)) < 0) { 232 xo_warn("%s: open", file); 233 return (1); 234 } 235 if (doword || (domulti && MB_CUR_MAX != 1)) 236 goto word; 237 /* 238 * If all we need is the number of characters and it's a regular file, 239 * just stat it. 240 */ 241 if (doline == 0 && dolongline == 0) { 242 if (fstat(fd, &sb)) { 243 xo_warn("%s: fstat", file); 244 (void)close(fd); 245 return (1); 246 } 247 if (S_ISREG(sb.st_mode)) { 248 reset_siginfo(); 249 charct = sb.st_size; 250 show_cnt(file, linect, wordct, charct, llct); 251 tcharct += charct; 252 (void)close(fd); 253 return (0); 254 } 255 } 256 /* 257 * For files we can't stat, or if we need line counting, slurp the 258 * file. Line counting is split out because it's a lot faster to get 259 * lines than to get words, since the word count requires locale 260 * handling. 261 */ 262 while ((len = read(fd, buf, sizeof(buf)))) { 263 if (len < 0) { 264 xo_warn("%s: read", file); 265 (void)close(fd); 266 return (1); 267 } 268 if (siginfo) 269 show_cnt(file, linect, wordct, charct, llct); 270 charct += len; 271 if (doline || dolongline) { 272 for (p = buf; len > 0; --len, ++p) { 273 if (*p == '\n') { 274 if (tmpll > llct) 275 llct = tmpll; 276 tmpll = 0; 277 ++linect; 278 } else { 279 tmpll++; 280 } 281 } 282 } 283 } 284 reset_siginfo(); 285 if (doline) 286 tlinect += linect; 287 if (dochar) 288 tcharct += charct; 289 if (dolongline && llct > tlongline) 290 tlongline = llct; 291 show_cnt(file, linect, wordct, charct, llct); 292 (void)close(fd); 293 return (0); 294 295 /* Do it the hard way... */ 296 word: gotsp = true; 297 warned = false; 298 memset(&mbs, 0, sizeof(mbs)); 299 while ((len = read(fd, buf, sizeof(buf))) != 0) { 300 if (len < 0) { 301 xo_warn("%s: read", file); 302 (void)close(fd); 303 return (1); 304 } 305 p = buf; 306 while (len > 0) { 307 if (siginfo) 308 show_cnt(file, linect, wordct, charct, llct); 309 if (!domulti || MB_CUR_MAX == 1) { 310 clen = 1; 311 wch = (unsigned char)*p; 312 } else if ((clen = mbrtowc(&wch, p, len, &mbs)) == 0) { 313 clen = 1; 314 } else if (clen == (size_t)-1) { 315 if (!warned) { 316 errno = EILSEQ; 317 xo_warn("%s", file); 318 warned = true; 319 } 320 memset(&mbs, 0, sizeof(mbs)); 321 clen = 1; 322 wch = (unsigned char)*p; 323 } else if (clen == (size_t)-2) { 324 break; 325 } 326 charct++; 327 if (wch != L'\n') 328 tmpll++; 329 len -= clen; 330 p += clen; 331 if (wch == L'\n') { 332 if (tmpll > llct) 333 llct = tmpll; 334 tmpll = 0; 335 ++linect; 336 } 337 if (iswspace(wch)) { 338 gotsp = true; 339 } else if (gotsp) { 340 gotsp = false; 341 ++wordct; 342 } 343 } 344 } 345 reset_siginfo(); 346 if (domulti && MB_CUR_MAX > 1) { 347 if (mbrtowc(NULL, NULL, 0, &mbs) == (size_t)-1 && !warned) 348 xo_warn("%s", file); 349 } 350 if (doline) 351 tlinect += linect; 352 if (doword) 353 twordct += wordct; 354 if (dochar || domulti) 355 tcharct += charct; 356 if (dolongline && llct > tlongline) 357 tlongline = llct; 358 show_cnt(file, linect, wordct, charct, llct); 359 (void)close(fd); 360 return (0); 361 } 362 363 static void 364 usage(void) 365 { 366 xo_error("usage: wc [-Lclmw] [file ...]\n"); 367 exit(EXIT_FAILURE); 368 } 369