18a16b7a1SPedro F. Giffuni /*- 28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 38a16b7a1SPedro F. Giffuni * 49b50d902SRodney W. Grimes * Copyright (c) 1980, 1987, 1991, 1993 59b50d902SRodney W. Grimes * The Regents of the University of California. All rights reserved. 69b50d902SRodney W. Grimes * 79b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 89b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions 99b50d902SRodney W. Grimes * are met: 109b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 119b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 129b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 139b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 149b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution. 15fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 169b50d902SRodney W. Grimes * may be used to endorse or promote products derived from this software 179b50d902SRodney W. Grimes * without specific prior written permission. 189b50d902SRodney W. Grimes * 199b50d902SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 209b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 219b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 229b50d902SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 239b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 249b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 259b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 269b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 279b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 289b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 299b50d902SRodney W. Grimes * SUCH DAMAGE. 309b50d902SRodney W. Grimes */ 319b50d902SRodney W. Grimes 329e4c5144SMariusz Zaborski #include <sys/capsicum.h> 339b50d902SRodney W. Grimes #include <sys/param.h> 349b50d902SRodney W. Grimes #include <sys/stat.h> 352c51e5edSBruce Evans 369e4c5144SMariusz Zaborski #include <capsicum_helpers.h> 372c51e5edSBruce Evans #include <ctype.h> 38ebb42aeeSTim J. Robbins #include <errno.h> 399b50d902SRodney W. Grimes #include <fcntl.h> 40ae6fa8aeSAndrey A. Chernov #include <locale.h> 41dad64f0eSDag-Erling Smørgrav #include <stdbool.h> 42a821e36eSMike Barcroft #include <stdint.h> 439b50d902SRodney W. Grimes #include <stdio.h> 449b50d902SRodney W. Grimes #include <stdlib.h> 459b50d902SRodney W. Grimes #include <string.h> 462c51e5edSBruce Evans #include <unistd.h> 47149a123bSTim J. Robbins #include <wchar.h> 48e58245f7STim J. Robbins #include <wctype.h> 496711c482SMarcel Moolenaar #include <libxo/xo.h> 509b50d902SRodney W. Grimes 519e4c5144SMariusz Zaborski #include <libcasper.h> 529e4c5144SMariusz Zaborski #include <casper/cap_fileargs.h> 539e4c5144SMariusz Zaborski 54dad64f0eSDag-Erling Smørgrav static const char *stdin_filename = "stdin"; 55dad64f0eSDag-Erling Smørgrav 569e4c5144SMariusz Zaborski static fileargs_t *fa; 578df975a2SEd Schouten static uintmax_t tlinect, twordct, tcharct, tlongline; 58dad64f0eSDag-Erling Smørgrav static bool doline, doword, dochar, domulti, dolongline; 59c9a96406SPawel Jakub Dawidek static volatile sig_atomic_t siginfo; 606711c482SMarcel Moolenaar static xo_handle_t *stderr_handle; 619b50d902SRodney W. Grimes 62f09bc093SPawel Jakub Dawidek static void show_cnt(const char *file, uintmax_t linect, uintmax_t wordct, 63f09bc093SPawel Jakub Dawidek uintmax_t charct, uintmax_t llct); 643f330d7dSWarner Losh static int cnt(const char *); 653f330d7dSWarner Losh static void usage(void); 669b50d902SRodney W. Grimes 67f09bc093SPawel Jakub Dawidek static void 68f09bc093SPawel Jakub Dawidek siginfo_handler(int sig __unused) 69f09bc093SPawel Jakub Dawidek { 70f09bc093SPawel Jakub Dawidek 71f09bc093SPawel Jakub Dawidek siginfo = 1; 72f09bc093SPawel Jakub Dawidek } 73f09bc093SPawel Jakub Dawidek 748ded906dSBryan Drewery static void 758ded906dSBryan Drewery reset_siginfo(void) 768ded906dSBryan Drewery { 778ded906dSBryan Drewery 788ded906dSBryan Drewery signal(SIGINFO, SIG_DFL); 798ded906dSBryan Drewery siginfo = 0; 808ded906dSBryan Drewery } 818ded906dSBryan Drewery 829b50d902SRodney W. Grimes int 83806abfccSJosef El-Rayes main(int argc, char *argv[]) 849b50d902SRodney W. Grimes { 85a0cf59e6SSheldon Hearn int ch, errors, total; 869e4c5144SMariusz Zaborski cap_rights_t rights; 879b50d902SRodney W. Grimes 88ae6fa8aeSAndrey A. Chernov (void) setlocale(LC_CTYPE, ""); 89ae6fa8aeSAndrey A. Chernov 906711c482SMarcel Moolenaar argc = xo_parse_args(argc, argv); 916711c482SMarcel Moolenaar if (argc < 0) 92dad64f0eSDag-Erling Smørgrav exit(EXIT_FAILURE); 936711c482SMarcel Moolenaar 94f45dd010SGiorgos Keramidas while ((ch = getopt(argc, argv, "clmwL")) != -1) 959b50d902SRodney W. Grimes switch((char)ch) { 969b50d902SRodney W. Grimes case 'l': 97dad64f0eSDag-Erling Smørgrav doline = true; 989b50d902SRodney W. Grimes break; 999b50d902SRodney W. Grimes case 'w': 100dad64f0eSDag-Erling Smørgrav doword = true; 1019b50d902SRodney W. Grimes break; 1029b50d902SRodney W. Grimes case 'c': 103dad64f0eSDag-Erling Smørgrav dochar = true; 104dad64f0eSDag-Erling Smørgrav domulti = false; 105ebb42aeeSTim J. Robbins break; 106f45dd010SGiorgos Keramidas case 'L': 107dad64f0eSDag-Erling Smørgrav dolongline = true; 108f45dd010SGiorgos Keramidas break; 109ebb42aeeSTim J. Robbins case 'm': 110dad64f0eSDag-Erling Smørgrav domulti = true; 111dad64f0eSDag-Erling Smørgrav dochar = false; 1129b50d902SRodney W. Grimes break; 1139b50d902SRodney W. Grimes case '?': 1149b50d902SRodney W. Grimes default: 1159b50d902SRodney W. Grimes usage(); 1169b50d902SRodney W. Grimes } 1179b50d902SRodney W. Grimes argv += optind; 1189b50d902SRodney W. Grimes argc -= optind; 1199b50d902SRodney W. Grimes 120f09bc093SPawel Jakub Dawidek (void)signal(SIGINFO, siginfo_handler); 121f09bc093SPawel Jakub Dawidek 1229e4c5144SMariusz Zaborski fa = fileargs_init(argc, argv, O_RDONLY, 0, 123d76eef34SEd Maste cap_rights_init(&rights, CAP_READ, CAP_FSTAT), FA_OPEN); 124dad64f0eSDag-Erling Smørgrav if (fa == NULL) 125dad64f0eSDag-Erling Smørgrav xo_err(EXIT_FAILURE, "Unable to initialize casper"); 1269e4c5144SMariusz Zaborski caph_cache_catpages(); 127dad64f0eSDag-Erling Smørgrav if (caph_limit_stdio() < 0) 128dad64f0eSDag-Erling Smørgrav xo_err(EXIT_FAILURE, "Unable to limit stdio"); 129dad64f0eSDag-Erling Smørgrav if (caph_enter_casper() < 0) 130dad64f0eSDag-Erling Smørgrav xo_err(EXIT_FAILURE, "Unable to enter capability mode"); 1319e4c5144SMariusz Zaborski 1329b50d902SRodney W. Grimes /* Wc's flags are on by default. */ 133dad64f0eSDag-Erling Smørgrav if (!(doline || doword || dochar || domulti || dolongline)) 134dad64f0eSDag-Erling Smørgrav doline = doword = dochar = true; 1359b50d902SRodney W. Grimes 1366711c482SMarcel Moolenaar stderr_handle = xo_create_to_file(stderr, XO_STYLE_TEXT, 0); 1376711c482SMarcel Moolenaar xo_open_container("wc"); 1386711c482SMarcel Moolenaar xo_open_list("file"); 1396711c482SMarcel Moolenaar 1402c51e5edSBruce Evans errors = 0; 1419b50d902SRodney W. Grimes total = 0; 142dad64f0eSDag-Erling Smørgrav if (argc == 0) { 1436711c482SMarcel Moolenaar xo_open_instance("file"); 144dad64f0eSDag-Erling Smørgrav if (cnt(NULL) != 0) 1452c51e5edSBruce Evans ++errors; 1466711c482SMarcel Moolenaar xo_close_instance("file"); 147f09bc093SPawel Jakub Dawidek } else { 148dad64f0eSDag-Erling Smørgrav while (argc--) { 1496711c482SMarcel Moolenaar xo_open_instance("file"); 150dad64f0eSDag-Erling Smørgrav if (cnt(*argv++) != 0) 1512c51e5edSBruce Evans ++errors; 1526711c482SMarcel Moolenaar xo_close_instance("file"); 1539b50d902SRodney W. Grimes ++total; 154dad64f0eSDag-Erling Smørgrav } 1559b50d902SRodney W. Grimes } 156f09bc093SPawel Jakub Dawidek 157399d3485SMarcel Moolenaar xo_close_list("file"); 158399d3485SMarcel Moolenaar 1596711c482SMarcel Moolenaar if (total > 1) { 1606711c482SMarcel Moolenaar xo_open_container("total"); 161f09bc093SPawel Jakub Dawidek show_cnt("total", tlinect, twordct, tcharct, tlongline); 1626711c482SMarcel Moolenaar xo_close_container("total"); 1636711c482SMarcel Moolenaar } 164399d3485SMarcel Moolenaar 1659e4c5144SMariusz Zaborski fileargs_free(fa); 1666711c482SMarcel Moolenaar xo_close_container("wc"); 167dad64f0eSDag-Erling Smørgrav if (xo_finish() < 0) 168dad64f0eSDag-Erling Smørgrav xo_err(EXIT_FAILURE, "stdout"); 169dad64f0eSDag-Erling Smørgrav exit(errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 1709b50d902SRodney W. Grimes } 1719b50d902SRodney W. Grimes 172f09bc093SPawel Jakub Dawidek static void 173f09bc093SPawel Jakub Dawidek show_cnt(const char *file, uintmax_t linect, uintmax_t wordct, 174f09bc093SPawel Jakub Dawidek uintmax_t charct, uintmax_t llct) 175f09bc093SPawel Jakub Dawidek { 1766711c482SMarcel Moolenaar xo_handle_t *xop; 177f09bc093SPawel Jakub Dawidek 178f09bc093SPawel Jakub Dawidek if (!siginfo) 1796711c482SMarcel Moolenaar xop = NULL; 180f09bc093SPawel Jakub Dawidek else { 1816711c482SMarcel Moolenaar xop = stderr_handle; 182f09bc093SPawel Jakub Dawidek siginfo = 0; 183f09bc093SPawel Jakub Dawidek } 184f09bc093SPawel Jakub Dawidek 185f09bc093SPawel Jakub Dawidek if (doline) 1866711c482SMarcel Moolenaar xo_emit_h(xop, " {:lines/%7ju/%ju}", linect); 187f09bc093SPawel Jakub Dawidek if (doword) 1886711c482SMarcel Moolenaar xo_emit_h(xop, " {:words/%7ju/%ju}", wordct); 189f09bc093SPawel Jakub Dawidek if (dochar || domulti) 1906711c482SMarcel Moolenaar xo_emit_h(xop, " {:characters/%7ju/%ju}", charct); 191f09bc093SPawel Jakub Dawidek if (dolongline) 1926711c482SMarcel Moolenaar xo_emit_h(xop, " {:long-lines/%7ju/%ju}", llct); 193dad64f0eSDag-Erling Smørgrav if (file != stdin_filename) 194985c93f0SMarcel Moolenaar xo_emit_h(xop, " {:filename/%s}\n", file); 195f09bc093SPawel Jakub Dawidek else 1966711c482SMarcel Moolenaar xo_emit_h(xop, "\n"); 197f09bc093SPawel Jakub Dawidek } 198f09bc093SPawel Jakub Dawidek 199a821e36eSMike Barcroft static int 200806abfccSJosef El-Rayes cnt(const char *file) 2019b50d902SRodney W. Grimes { 2025016d112SDag-Erling Smørgrav static char buf[MAXBSIZE]; 2039b50d902SRodney W. Grimes struct stat sb; 204149a123bSTim J. Robbins mbstate_t mbs; 2055016d112SDag-Erling Smørgrav const char *p; 206dad64f0eSDag-Erling Smørgrav uintmax_t linect, wordct, charct, llct, tmpll; 207dad64f0eSDag-Erling Smørgrav ssize_t len; 208dad64f0eSDag-Erling Smørgrav size_t clen; 209dad64f0eSDag-Erling Smørgrav int fd; 210dad64f0eSDag-Erling Smørgrav wchar_t wch; 211dad64f0eSDag-Erling Smørgrav bool gotsp, warned; 2129b50d902SRodney W. Grimes 213f45dd010SGiorgos Keramidas linect = wordct = charct = llct = tmpll = 0; 214dad64f0eSDag-Erling Smørgrav if (file == NULL) { 2152c51e5edSBruce Evans fd = STDIN_FILENO; 216dad64f0eSDag-Erling Smørgrav file = stdin_filename; 217dad64f0eSDag-Erling Smørgrav } else if ((fd = fileargs_open(fa, file)) < 0) { 2186711c482SMarcel Moolenaar xo_warn("%s: open", file); 2192c51e5edSBruce Evans return (1); 220a0d038a4SWolfram Schneider } 221ebb42aeeSTim J. Robbins if (doword || (domulti && MB_CUR_MAX != 1)) 2229b50d902SRodney W. Grimes goto word; 2239b50d902SRodney W. Grimes /* 2240dc7c9e6SConrad Meyer * If all we need is the number of characters and it's a regular file, 2250dc7c9e6SConrad Meyer * just stat it. 2269b50d902SRodney W. Grimes */ 22784b851c2SConrad Meyer if (doline == 0 && dolongline == 0) { 2280dc7c9e6SConrad Meyer if (fstat(fd, &sb)) { 229dad64f0eSDag-Erling Smørgrav xo_warn("%s: fstat", file); 2300dc7c9e6SConrad Meyer (void)close(fd); 2310dc7c9e6SConrad Meyer return (1); 2320dc7c9e6SConrad Meyer } 233*8e05c237SRicardo Branco /* pseudo-filesystems advertize a zero size */ 234*8e05c237SRicardo Branco if (S_ISREG(sb.st_mode) && sb.st_size > 0) { 2350dc7c9e6SConrad Meyer reset_siginfo(); 2360dc7c9e6SConrad Meyer charct = sb.st_size; 2370dc7c9e6SConrad Meyer show_cnt(file, linect, wordct, charct, llct); 2380dc7c9e6SConrad Meyer tcharct += charct; 2390dc7c9e6SConrad Meyer (void)close(fd); 2400dc7c9e6SConrad Meyer return (0); 2410dc7c9e6SConrad Meyer } 2420dc7c9e6SConrad Meyer } 2430dc7c9e6SConrad Meyer /* 2440dc7c9e6SConrad Meyer * For files we can't stat, or if we need line counting, slurp the 2450dc7c9e6SConrad Meyer * file. Line counting is split out because it's a lot faster to get 2460dc7c9e6SConrad Meyer * lines than to get words, since the word count requires locale 2470dc7c9e6SConrad Meyer * handling. 2480dc7c9e6SConrad Meyer */ 2495016d112SDag-Erling Smørgrav while ((len = read(fd, buf, sizeof(buf))) != 0) { 250dad64f0eSDag-Erling Smørgrav if (len < 0) { 251dad64f0eSDag-Erling Smørgrav xo_warn("%s: read", file); 2522c51e5edSBruce Evans (void)close(fd); 2532c51e5edSBruce Evans return (1); 2542c51e5edSBruce Evans } 2550dc7c9e6SConrad Meyer if (siginfo) 2560dc7c9e6SConrad Meyer show_cnt(file, linect, wordct, charct, llct); 2579b50d902SRodney W. Grimes charct += len; 25884b851c2SConrad Meyer if (doline || dolongline) { 259dad64f0eSDag-Erling Smørgrav for (p = buf; len > 0; --len, ++p) { 260f45dd010SGiorgos Keramidas if (*p == '\n') { 261f45dd010SGiorgos Keramidas if (tmpll > llct) 262f45dd010SGiorgos Keramidas llct = tmpll; 263f45dd010SGiorgos Keramidas tmpll = 0; 2649b50d902SRodney W. Grimes ++linect; 265dad64f0eSDag-Erling Smørgrav } else { 266f45dd010SGiorgos Keramidas tmpll++; 2679b50d902SRodney W. Grimes } 268de143041SConrad Meyer } 269dad64f0eSDag-Erling Smørgrav } 270dad64f0eSDag-Erling Smørgrav } 2718ded906dSBryan Drewery reset_siginfo(); 272de143041SConrad Meyer if (doline) 2739b50d902SRodney W. Grimes tlinect += linect; 274f09bc093SPawel Jakub Dawidek if (dochar) 2759b50d902SRodney W. Grimes tcharct += charct; 27684b851c2SConrad Meyer if (dolongline && llct > tlongline) 277f45dd010SGiorgos Keramidas tlongline = llct; 278f09bc093SPawel Jakub Dawidek show_cnt(file, linect, wordct, charct, llct); 2799b50d902SRodney W. Grimes (void)close(fd); 2802c51e5edSBruce Evans return (0); 2819b50d902SRodney W. Grimes 2829b50d902SRodney W. Grimes /* Do it the hard way... */ 283dad64f0eSDag-Erling Smørgrav word: gotsp = true; 284dad64f0eSDag-Erling Smørgrav warned = false; 285149a123bSTim J. Robbins memset(&mbs, 0, sizeof(mbs)); 286dad64f0eSDag-Erling Smørgrav while ((len = read(fd, buf, sizeof(buf))) != 0) { 287dad64f0eSDag-Erling Smørgrav if (len < 0) { 288dad64f0eSDag-Erling Smørgrav xo_warn("%s: read", file); 2892c51e5edSBruce Evans (void)close(fd); 2902c51e5edSBruce Evans return (1); 2912c51e5edSBruce Evans } 292ebb42aeeSTim J. Robbins p = buf; 293ebb42aeeSTim J. Robbins while (len > 0) { 294f09bc093SPawel Jakub Dawidek if (siginfo) 295f09bc093SPawel Jakub Dawidek show_cnt(file, linect, wordct, charct, llct); 296ebb42aeeSTim J. Robbins if (!domulti || MB_CUR_MAX == 1) { 297ebb42aeeSTim J. Robbins clen = 1; 298ebb42aeeSTim J. Robbins wch = (unsigned char)*p; 299dad64f0eSDag-Erling Smørgrav } else if ((clen = mbrtowc(&wch, p, len, &mbs)) == 0) { 300dad64f0eSDag-Erling Smørgrav clen = 1; 301dad64f0eSDag-Erling Smørgrav } else if (clen == (size_t)-1) { 302ebb42aeeSTim J. Robbins if (!warned) { 303ebb42aeeSTim J. Robbins errno = EILSEQ; 304dad64f0eSDag-Erling Smørgrav xo_warn("%s", file); 305dad64f0eSDag-Erling Smørgrav warned = true; 306ebb42aeeSTim J. Robbins } 307149a123bSTim J. Robbins memset(&mbs, 0, sizeof(mbs)); 308149a123bSTim J. Robbins clen = 1; 309149a123bSTim J. Robbins wch = (unsigned char)*p; 310dad64f0eSDag-Erling Smørgrav } else if (clen == (size_t)-2) { 311ebb42aeeSTim J. Robbins break; 312dad64f0eSDag-Erling Smørgrav } 313ebb42aeeSTim J. Robbins charct++; 314f45dd010SGiorgos Keramidas if (wch != L'\n') 315f45dd010SGiorgos Keramidas tmpll++; 316ebb42aeeSTim J. Robbins len -= clen; 317ebb42aeeSTim J. Robbins p += clen; 318f45dd010SGiorgos Keramidas if (wch == L'\n') { 319f45dd010SGiorgos Keramidas if (tmpll > llct) 320f45dd010SGiorgos Keramidas llct = tmpll; 321f45dd010SGiorgos Keramidas tmpll = 0; 3229b50d902SRodney W. Grimes ++linect; 323f45dd010SGiorgos Keramidas } 324dad64f0eSDag-Erling Smørgrav if (iswspace(wch)) { 325dad64f0eSDag-Erling Smørgrav gotsp = true; 326dad64f0eSDag-Erling Smørgrav } else if (gotsp) { 327dad64f0eSDag-Erling Smørgrav gotsp = false; 3289b50d902SRodney W. Grimes ++wordct; 3299b50d902SRodney W. Grimes } 3309b50d902SRodney W. Grimes } 3319b50d902SRodney W. Grimes } 3328ded906dSBryan Drewery reset_siginfo(); 333dad64f0eSDag-Erling Smørgrav if (domulti && MB_CUR_MAX > 1) { 334149a123bSTim J. Robbins if (mbrtowc(NULL, NULL, 0, &mbs) == (size_t)-1 && !warned) 335dad64f0eSDag-Erling Smørgrav xo_warn("%s", file); 336dad64f0eSDag-Erling Smørgrav } 337f09bc093SPawel Jakub Dawidek if (doline) 3389b50d902SRodney W. Grimes tlinect += linect; 339f09bc093SPawel Jakub Dawidek if (doword) 3409b50d902SRodney W. Grimes twordct += wordct; 341f09bc093SPawel Jakub Dawidek if (dochar || domulti) 3429b50d902SRodney W. Grimes tcharct += charct; 34384b851c2SConrad Meyer if (dolongline && llct > tlongline) 344f45dd010SGiorgos Keramidas tlongline = llct; 345f09bc093SPawel Jakub Dawidek show_cnt(file, linect, wordct, charct, llct); 3469b50d902SRodney W. Grimes (void)close(fd); 3472c51e5edSBruce Evans return (0); 3489b50d902SRodney W. Grimes } 3499b50d902SRodney W. Grimes 350a821e36eSMike Barcroft static void 3510970727fSEd Schouten usage(void) 3529b50d902SRodney W. Grimes { 3536711c482SMarcel Moolenaar xo_error("usage: wc [-Lclmw] [file ...]\n"); 354dad64f0eSDag-Erling Smørgrav exit(EXIT_FAILURE); 3559b50d902SRodney W. Grimes } 356