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