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