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