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 329b50d902SRodney W. Grimes #ifndef lint 3306469209SMike Barcroft static const char copyright[] = 349b50d902SRodney W. Grimes "@(#) Copyright (c) 1980, 1987, 1991, 1993\n\ 359b50d902SRodney W. Grimes The Regents of the University of California. All rights reserved.\n"; 36a821e36eSMike Barcroft #endif /* not lint */ 379b50d902SRodney W. Grimes 3806469209SMike Barcroft #if 0 399b50d902SRodney W. Grimes #ifndef lint 40a821e36eSMike Barcroft static char sccsid[] = "@(#)wc.c 8.1 (Berkeley) 6/6/93"; 41a821e36eSMike Barcroft #endif /* not lint */ 422c51e5edSBruce Evans #endif 439b50d902SRodney W. Grimes 44a821e36eSMike Barcroft #include <sys/cdefs.h> 45a821e36eSMike Barcroft __FBSDID("$FreeBSD$"); 46a821e36eSMike Barcroft 479e4c5144SMariusz Zaborski #include <sys/capsicum.h> 489b50d902SRodney W. Grimes #include <sys/param.h> 499b50d902SRodney W. Grimes #include <sys/stat.h> 502c51e5edSBruce Evans 519e4c5144SMariusz Zaborski #include <capsicum_helpers.h> 522c51e5edSBruce Evans #include <ctype.h> 532c51e5edSBruce Evans #include <err.h> 54ebb42aeeSTim J. Robbins #include <errno.h> 559b50d902SRodney W. Grimes #include <fcntl.h> 56ae6fa8aeSAndrey A. Chernov #include <locale.h> 57a821e36eSMike Barcroft #include <stdint.h> 589b50d902SRodney W. Grimes #include <stdio.h> 599b50d902SRodney W. Grimes #include <stdlib.h> 609b50d902SRodney W. Grimes #include <string.h> 612c51e5edSBruce Evans #include <unistd.h> 62149a123bSTim J. Robbins #include <wchar.h> 63e58245f7STim J. Robbins #include <wctype.h> 646711c482SMarcel Moolenaar #include <libxo/xo.h> 659b50d902SRodney W. Grimes 669e4c5144SMariusz Zaborski #include <libcasper.h> 679e4c5144SMariusz Zaborski #include <casper/cap_fileargs.h> 689e4c5144SMariusz Zaborski 699e4c5144SMariusz Zaborski static fileargs_t *fa; 708df975a2SEd Schouten static uintmax_t tlinect, twordct, tcharct, tlongline; 718df975a2SEd Schouten static int doline, doword, dochar, domulti, dolongline; 72c9a96406SPawel Jakub Dawidek static volatile sig_atomic_t siginfo; 736711c482SMarcel Moolenaar static xo_handle_t *stderr_handle; 749b50d902SRodney W. Grimes 75f09bc093SPawel Jakub Dawidek static void show_cnt(const char *file, uintmax_t linect, uintmax_t wordct, 76f09bc093SPawel Jakub Dawidek uintmax_t charct, uintmax_t llct); 773f330d7dSWarner Losh static int cnt(const char *); 783f330d7dSWarner Losh static void usage(void); 799b50d902SRodney W. Grimes 80f09bc093SPawel Jakub Dawidek static void 81f09bc093SPawel Jakub Dawidek siginfo_handler(int sig __unused) 82f09bc093SPawel Jakub Dawidek { 83f09bc093SPawel Jakub Dawidek 84f09bc093SPawel Jakub Dawidek siginfo = 1; 85f09bc093SPawel Jakub Dawidek } 86f09bc093SPawel Jakub Dawidek 878ded906dSBryan Drewery static void 888ded906dSBryan Drewery reset_siginfo(void) 898ded906dSBryan Drewery { 908ded906dSBryan Drewery 918ded906dSBryan Drewery signal(SIGINFO, SIG_DFL); 928ded906dSBryan Drewery siginfo = 0; 938ded906dSBryan Drewery } 948ded906dSBryan Drewery 959b50d902SRodney W. Grimes int 96806abfccSJosef El-Rayes main(int argc, char *argv[]) 979b50d902SRodney W. Grimes { 98a0cf59e6SSheldon Hearn int ch, errors, total; 999e4c5144SMariusz Zaborski cap_rights_t rights; 1009b50d902SRodney W. Grimes 101ae6fa8aeSAndrey A. Chernov (void) setlocale(LC_CTYPE, ""); 102ae6fa8aeSAndrey A. Chernov 1036711c482SMarcel Moolenaar argc = xo_parse_args(argc, argv); 1046711c482SMarcel Moolenaar if (argc < 0) 1056711c482SMarcel Moolenaar return (argc); 1066711c482SMarcel Moolenaar 107f45dd010SGiorgos Keramidas while ((ch = getopt(argc, argv, "clmwL")) != -1) 1089b50d902SRodney W. Grimes switch((char)ch) { 1099b50d902SRodney W. Grimes case 'l': 1109b50d902SRodney W. Grimes doline = 1; 1119b50d902SRodney W. Grimes break; 1129b50d902SRodney W. Grimes case 'w': 1139b50d902SRodney W. Grimes doword = 1; 1149b50d902SRodney W. Grimes break; 1159b50d902SRodney W. Grimes case 'c': 1169b50d902SRodney W. Grimes dochar = 1; 117ebb42aeeSTim J. Robbins domulti = 0; 118ebb42aeeSTim J. Robbins break; 119f45dd010SGiorgos Keramidas case 'L': 120f45dd010SGiorgos Keramidas dolongline = 1; 121f45dd010SGiorgos Keramidas break; 122ebb42aeeSTim J. Robbins case 'm': 123ebb42aeeSTim J. Robbins domulti = 1; 124ebb42aeeSTim J. Robbins dochar = 0; 1259b50d902SRodney W. Grimes break; 1269b50d902SRodney W. Grimes case '?': 1279b50d902SRodney W. Grimes default: 1289b50d902SRodney W. Grimes usage(); 1299b50d902SRodney W. Grimes } 1309b50d902SRodney W. Grimes argv += optind; 1319b50d902SRodney W. Grimes argc -= optind; 1329b50d902SRodney W. Grimes 133f09bc093SPawel Jakub Dawidek (void)signal(SIGINFO, siginfo_handler); 134f09bc093SPawel Jakub Dawidek 1359e4c5144SMariusz Zaborski fa = fileargs_init(argc, argv, O_RDONLY, 0, 136*d76eef34SEd Maste cap_rights_init(&rights, CAP_READ, CAP_FSTAT), FA_OPEN); 1379e4c5144SMariusz Zaborski if (fa == NULL) { 1389e4c5144SMariusz Zaborski xo_warn("Unable to init casper"); 1399e4c5144SMariusz Zaborski exit(1); 1409e4c5144SMariusz Zaborski } 1419e4c5144SMariusz Zaborski 1429e4c5144SMariusz Zaborski caph_cache_catpages(); 1439e4c5144SMariusz Zaborski if (caph_limit_stdio() < 0) { 1449e4c5144SMariusz Zaborski xo_warn("Unable to limit stdio"); 1459e4c5144SMariusz Zaborski fileargs_free(fa); 1469e4c5144SMariusz Zaborski exit(1); 1479e4c5144SMariusz Zaborski } 1489e4c5144SMariusz Zaborski 149509e73d4SMariusz Zaborski if (caph_enter_casper() < 0) { 1509e4c5144SMariusz Zaborski xo_warn("Unable to enter capability mode"); 1519e4c5144SMariusz Zaborski fileargs_free(fa); 1529e4c5144SMariusz Zaborski exit(1); 1539e4c5144SMariusz Zaborski } 1549e4c5144SMariusz Zaborski 1559b50d902SRodney W. Grimes /* Wc's flags are on by default. */ 156f45dd010SGiorgos Keramidas if (doline + doword + dochar + domulti + dolongline == 0) 1579b50d902SRodney W. Grimes doline = doword = dochar = 1; 1589b50d902SRodney W. Grimes 1596711c482SMarcel Moolenaar stderr_handle = xo_create_to_file(stderr, XO_STYLE_TEXT, 0); 1606711c482SMarcel Moolenaar xo_open_container("wc"); 1616711c482SMarcel Moolenaar xo_open_list("file"); 1626711c482SMarcel Moolenaar 1632c51e5edSBruce Evans errors = 0; 1649b50d902SRodney W. Grimes total = 0; 1659b50d902SRodney W. Grimes if (!*argv) { 1666711c482SMarcel Moolenaar xo_open_instance("file"); 1672c51e5edSBruce Evans if (cnt((char *)NULL) != 0) 1682c51e5edSBruce Evans ++errors; 1696711c482SMarcel Moolenaar xo_close_instance("file"); 170f09bc093SPawel Jakub Dawidek } else { 171f09bc093SPawel Jakub Dawidek do { 1726711c482SMarcel Moolenaar xo_open_instance("file"); 1732c51e5edSBruce Evans if (cnt(*argv) != 0) 1742c51e5edSBruce Evans ++errors; 1756711c482SMarcel Moolenaar xo_close_instance("file"); 1769b50d902SRodney W. Grimes ++total; 1779b50d902SRodney W. Grimes } while(*++argv); 1789b50d902SRodney W. Grimes } 179f09bc093SPawel Jakub Dawidek 180399d3485SMarcel Moolenaar xo_close_list("file"); 181399d3485SMarcel Moolenaar 1826711c482SMarcel Moolenaar if (total > 1) { 1836711c482SMarcel Moolenaar xo_open_container("total"); 184f09bc093SPawel Jakub Dawidek show_cnt("total", tlinect, twordct, tcharct, tlongline); 1856711c482SMarcel Moolenaar xo_close_container("total"); 1866711c482SMarcel Moolenaar } 187399d3485SMarcel Moolenaar 1889e4c5144SMariusz Zaborski fileargs_free(fa); 1896711c482SMarcel Moolenaar xo_close_container("wc"); 1906711c482SMarcel Moolenaar xo_finish(); 1912c51e5edSBruce Evans exit(errors == 0 ? 0 : 1); 1929b50d902SRodney W. Grimes } 1939b50d902SRodney W. Grimes 194f09bc093SPawel Jakub Dawidek static void 195f09bc093SPawel Jakub Dawidek show_cnt(const char *file, uintmax_t linect, uintmax_t wordct, 196f09bc093SPawel Jakub Dawidek uintmax_t charct, uintmax_t llct) 197f09bc093SPawel Jakub Dawidek { 1986711c482SMarcel Moolenaar xo_handle_t *xop; 199f09bc093SPawel Jakub Dawidek 200f09bc093SPawel Jakub Dawidek if (!siginfo) 2016711c482SMarcel Moolenaar xop = NULL; 202f09bc093SPawel Jakub Dawidek else { 2036711c482SMarcel Moolenaar xop = stderr_handle; 204f09bc093SPawel Jakub Dawidek siginfo = 0; 205f09bc093SPawel Jakub Dawidek } 206f09bc093SPawel Jakub Dawidek 207f09bc093SPawel Jakub Dawidek if (doline) 2086711c482SMarcel Moolenaar xo_emit_h(xop, " {:lines/%7ju/%ju}", linect); 209f09bc093SPawel Jakub Dawidek if (doword) 2106711c482SMarcel Moolenaar xo_emit_h(xop, " {:words/%7ju/%ju}", wordct); 211f09bc093SPawel Jakub Dawidek if (dochar || domulti) 2126711c482SMarcel Moolenaar xo_emit_h(xop, " {:characters/%7ju/%ju}", charct); 213f09bc093SPawel Jakub Dawidek if (dolongline) 2146711c482SMarcel Moolenaar xo_emit_h(xop, " {:long-lines/%7ju/%ju}", llct); 215f09bc093SPawel Jakub Dawidek if (file != NULL) 216985c93f0SMarcel Moolenaar xo_emit_h(xop, " {:filename/%s}\n", file); 217f09bc093SPawel Jakub Dawidek else 2186711c482SMarcel Moolenaar xo_emit_h(xop, "\n"); 219f09bc093SPawel Jakub Dawidek } 220f09bc093SPawel Jakub Dawidek 221a821e36eSMike Barcroft static int 222806abfccSJosef El-Rayes cnt(const char *file) 2239b50d902SRodney W. Grimes { 2249b50d902SRodney W. Grimes struct stat sb; 225f45dd010SGiorgos Keramidas uintmax_t linect, wordct, charct, llct, tmpll; 226149a123bSTim J. Robbins int fd, len, warned; 227149a123bSTim J. Robbins size_t clen; 228a0cf59e6SSheldon Hearn short gotsp; 229a0cf59e6SSheldon Hearn u_char *p; 230abd0c85dSTim J. Robbins u_char buf[MAXBSIZE]; 231ebb42aeeSTim J. Robbins wchar_t wch; 232149a123bSTim J. Robbins mbstate_t mbs; 2339b50d902SRodney W. Grimes 234f45dd010SGiorgos Keramidas linect = wordct = charct = llct = tmpll = 0; 235f09bc093SPawel Jakub Dawidek if (file == NULL) 2362c51e5edSBruce Evans fd = STDIN_FILENO; 2379e4c5144SMariusz Zaborski else if ((fd = fileargs_open(fa, file)) < 0) { 2386711c482SMarcel Moolenaar xo_warn("%s: open", file); 2392c51e5edSBruce Evans return (1); 240a0d038a4SWolfram Schneider } 241ebb42aeeSTim J. Robbins if (doword || (domulti && MB_CUR_MAX != 1)) 2429b50d902SRodney W. Grimes goto word; 2439b50d902SRodney W. Grimes /* 2440dc7c9e6SConrad Meyer * If all we need is the number of characters and it's a regular file, 2450dc7c9e6SConrad Meyer * just stat it. 2469b50d902SRodney W. Grimes */ 24784b851c2SConrad Meyer if (doline == 0 && dolongline == 0) { 2480dc7c9e6SConrad Meyer if (fstat(fd, &sb)) { 2490dc7c9e6SConrad Meyer xo_warn("%s: fstat", file); 2500dc7c9e6SConrad Meyer (void)close(fd); 2510dc7c9e6SConrad Meyer return (1); 2520dc7c9e6SConrad Meyer } 2530dc7c9e6SConrad Meyer if (S_ISREG(sb.st_mode)) { 2540dc7c9e6SConrad Meyer reset_siginfo(); 2550dc7c9e6SConrad Meyer charct = sb.st_size; 2560dc7c9e6SConrad Meyer show_cnt(file, linect, wordct, charct, llct); 2570dc7c9e6SConrad Meyer tcharct += charct; 2580dc7c9e6SConrad Meyer (void)close(fd); 2590dc7c9e6SConrad Meyer return (0); 2600dc7c9e6SConrad Meyer } 2610dc7c9e6SConrad Meyer } 2620dc7c9e6SConrad Meyer /* 2630dc7c9e6SConrad Meyer * For files we can't stat, or if we need line counting, slurp the 2640dc7c9e6SConrad Meyer * file. Line counting is split out because it's a lot faster to get 2650dc7c9e6SConrad Meyer * lines than to get words, since the word count requires locale 2660dc7c9e6SConrad Meyer * handling. 2670dc7c9e6SConrad Meyer */ 2688c85cce7SPhilippe Charnier while ((len = read(fd, buf, MAXBSIZE))) { 2692c51e5edSBruce Evans if (len == -1) { 2706711c482SMarcel Moolenaar xo_warn("%s: read", file); 2712c51e5edSBruce Evans (void)close(fd); 2722c51e5edSBruce Evans return (1); 2732c51e5edSBruce Evans } 2740dc7c9e6SConrad Meyer if (siginfo) 2750dc7c9e6SConrad Meyer show_cnt(file, linect, wordct, charct, llct); 2769b50d902SRodney W. Grimes charct += len; 27784b851c2SConrad Meyer if (doline || dolongline) { 2789b50d902SRodney W. Grimes for (p = buf; len--; ++p) 279f45dd010SGiorgos Keramidas if (*p == '\n') { 280f45dd010SGiorgos Keramidas if (tmpll > llct) 281f45dd010SGiorgos Keramidas llct = tmpll; 282f45dd010SGiorgos Keramidas tmpll = 0; 2839b50d902SRodney W. Grimes ++linect; 284f45dd010SGiorgos Keramidas } else 285f45dd010SGiorgos Keramidas tmpll++; 2869b50d902SRodney W. Grimes } 287de143041SConrad Meyer } 2888ded906dSBryan Drewery reset_siginfo(); 289de143041SConrad Meyer if (doline) 2909b50d902SRodney W. Grimes tlinect += linect; 291f09bc093SPawel Jakub Dawidek if (dochar) 2929b50d902SRodney W. Grimes tcharct += charct; 29384b851c2SConrad Meyer if (dolongline && llct > tlongline) 294f45dd010SGiorgos Keramidas tlongline = llct; 295f09bc093SPawel Jakub Dawidek show_cnt(file, linect, wordct, charct, llct); 2969b50d902SRodney W. Grimes (void)close(fd); 2972c51e5edSBruce Evans return (0); 2989b50d902SRodney W. Grimes 2999b50d902SRodney W. Grimes /* Do it the hard way... */ 300ebb42aeeSTim J. Robbins word: gotsp = 1; 301ebb42aeeSTim J. Robbins warned = 0; 302149a123bSTim J. Robbins memset(&mbs, 0, sizeof(mbs)); 303149a123bSTim J. Robbins while ((len = read(fd, buf, MAXBSIZE)) != 0) { 304149a123bSTim J. Robbins if (len == -1) { 3056711c482SMarcel Moolenaar xo_warn("%s: read", file != NULL ? file : "stdin"); 3062c51e5edSBruce Evans (void)close(fd); 3072c51e5edSBruce Evans return (1); 3082c51e5edSBruce Evans } 309ebb42aeeSTim J. Robbins p = buf; 310ebb42aeeSTim J. Robbins while (len > 0) { 311f09bc093SPawel Jakub Dawidek if (siginfo) 312f09bc093SPawel Jakub Dawidek show_cnt(file, linect, wordct, charct, llct); 313ebb42aeeSTim J. Robbins if (!domulti || MB_CUR_MAX == 1) { 314ebb42aeeSTim J. Robbins clen = 1; 315ebb42aeeSTim J. Robbins wch = (unsigned char)*p; 316149a123bSTim J. Robbins } else if ((clen = mbrtowc(&wch, p, len, &mbs)) == 317149a123bSTim J. Robbins (size_t)-1) { 318ebb42aeeSTim J. Robbins if (!warned) { 319ebb42aeeSTim J. Robbins errno = EILSEQ; 3206711c482SMarcel Moolenaar xo_warn("%s", 321f09bc093SPawel Jakub Dawidek file != NULL ? file : "stdin"); 322ebb42aeeSTim J. Robbins warned = 1; 323ebb42aeeSTim J. Robbins } 324149a123bSTim J. Robbins memset(&mbs, 0, sizeof(mbs)); 325149a123bSTim J. Robbins clen = 1; 326149a123bSTim J. Robbins wch = (unsigned char)*p; 327149a123bSTim J. Robbins } else if (clen == (size_t)-2) 328ebb42aeeSTim J. Robbins break; 329149a123bSTim J. Robbins else if (clen == 0) 330149a123bSTim J. Robbins clen = 1; 331ebb42aeeSTim J. Robbins charct++; 332f45dd010SGiorgos Keramidas if (wch != L'\n') 333f45dd010SGiorgos Keramidas tmpll++; 334ebb42aeeSTim J. Robbins len -= clen; 335ebb42aeeSTim J. Robbins p += clen; 336f45dd010SGiorgos Keramidas if (wch == L'\n') { 337f45dd010SGiorgos Keramidas if (tmpll > llct) 338f45dd010SGiorgos Keramidas llct = tmpll; 339f45dd010SGiorgos Keramidas tmpll = 0; 3409b50d902SRodney W. Grimes ++linect; 341f45dd010SGiorgos Keramidas } 342e58245f7STim J. Robbins if (iswspace(wch)) 3439b50d902SRodney W. Grimes gotsp = 1; 3449b50d902SRodney W. Grimes else if (gotsp) { 3459b50d902SRodney W. Grimes gotsp = 0; 3469b50d902SRodney W. Grimes ++wordct; 3479b50d902SRodney W. Grimes } 3489b50d902SRodney W. Grimes } 3499b50d902SRodney W. Grimes } 3508ded906dSBryan Drewery reset_siginfo(); 351149a123bSTim J. Robbins if (domulti && MB_CUR_MAX > 1) 352149a123bSTim J. Robbins if (mbrtowc(NULL, NULL, 0, &mbs) == (size_t)-1 && !warned) 3536711c482SMarcel Moolenaar xo_warn("%s", file != NULL ? file : "stdin"); 354f09bc093SPawel Jakub Dawidek if (doline) 3559b50d902SRodney W. Grimes tlinect += linect; 356f09bc093SPawel Jakub Dawidek if (doword) 3579b50d902SRodney W. Grimes twordct += wordct; 358f09bc093SPawel Jakub Dawidek if (dochar || domulti) 3599b50d902SRodney W. Grimes tcharct += charct; 36084b851c2SConrad Meyer if (dolongline && llct > tlongline) 361f45dd010SGiorgos Keramidas tlongline = llct; 362f09bc093SPawel Jakub Dawidek show_cnt(file, linect, wordct, charct, llct); 3639b50d902SRodney W. Grimes (void)close(fd); 3642c51e5edSBruce Evans return (0); 3659b50d902SRodney W. Grimes } 3669b50d902SRodney W. Grimes 367a821e36eSMike Barcroft static void 3680970727fSEd Schouten usage(void) 3699b50d902SRodney W. Grimes { 3706711c482SMarcel Moolenaar xo_error("usage: wc [-Lclmw] [file ...]\n"); 3719b50d902SRodney W. Grimes exit(1); 3729b50d902SRodney W. Grimes } 373