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