kernel - Fix SMAP/SMEP caught user mode access part 1/2.
[dragonfly.git] / usr.bin / wc / wc.c
1 /*
2  * Copyright (c) 1980, 1987, 1991, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#) Copyright (c) 1980, 1987, 1991, 1993 The Regents of the University of California.  All rights reserved.
30  * @(#)wc.c     8.1 (Berkeley) 6/6/93
31  * $FreeBSD: head/usr.bin/wc/wc.c 281617 2015-04-16 21:44:35Z bdrewery $
32  */
33
34 #include <sys/param.h>
35 #include <sys/stat.h>
36
37 #include <ctype.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <locale.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
50 static uintmax_t tlinect, twordct, tcharct, tlongline;
51 static int doline, doword, dochar, domulti, dolongline;
52 static volatile sig_atomic_t siginfo;
53
54 static void     show_cnt(const char *file, uintmax_t linect, uintmax_t wordct,
55                     uintmax_t charct, uintmax_t llct);
56 static int      cnt(const char *);
57 static void     usage(void);
58
59 static void
60 siginfo_handler(int sig __unused)
61 {
62
63         siginfo = 1;
64 }
65
66 static void
67 reset_siginfo(void)
68 {
69
70         signal(SIGINFO, SIG_DFL);
71         siginfo = 0;
72 }
73
74 int
75 main(int argc, char *argv[])
76 {
77         int ch, errors, total;
78
79         setlocale(LC_CTYPE, "");
80
81         while ((ch = getopt(argc, argv, "clmwL")) != -1) {
82                 switch (ch) {
83                 case 'l':
84                         doline = 1;
85                         break;
86                 case 'w':
87                         doword = 1;
88                         break;
89                 case 'c':
90                         dochar = 1;
91                         domulti = 0;
92                         break;
93                 case 'L':
94                         dolongline = 1;
95                         break;
96                 case 'm':
97                         domulti = 1;
98                         dochar = 0;
99                         break;
100                 case '?':
101                 default:
102                         usage();
103                 }
104         }
105         argv += optind;
106         argc -= optind;
107
108         signal(SIGINFO, siginfo_handler);
109
110         /* Wc's flags are on by default. */
111         if (doline + doword + dochar + domulti + dolongline == 0)
112                 doline = doword = dochar = 1;
113
114         errors = 0;
115         total = 0;
116         if (*argv == NULL) {
117                 if (cnt(NULL) != 0)
118                         ++errors;
119         } else {
120                 do {
121                         if (cnt(*argv) != 0)
122                                 ++errors;
123                         ++total;
124                 } while(*++argv);
125         }
126
127         if (total > 1)
128                 show_cnt("total", tlinect, twordct, tcharct, tlongline);
129         exit(errors == 0 ? 0 : 1);
130 }
131
132 static void
133 show_cnt(const char *file, uintmax_t linect, uintmax_t wordct,
134     uintmax_t charct, uintmax_t llct)
135 {
136         FILE *out;
137
138         if (!siginfo)
139                 out = stdout;
140         else {
141                 out = stderr;
142                 siginfo = 0;
143         }
144
145         if (doline)
146                 fprintf(out, " %7ju", linect);
147         if (doword)
148                 fprintf(out, " %7ju", wordct);
149         if (dochar || domulti)
150                 fprintf(out, " %7ju", charct);
151         if (dolongline)
152                 fprintf(out, " %7ju", llct);
153         if (file != NULL)
154                 fprintf(out, " %s\n", file);
155         else
156                 fprintf(out, "\n");
157 }
158
159 static int
160 cnt(const char *file)
161 {
162         struct stat sb;
163         uintmax_t linect, wordct, charct, llct, tmpll;
164         int fd, len, warned;
165         size_t clen;
166         short gotsp;
167         u_char *p;
168         u_char buf[MAXBSIZE];
169         wchar_t wch;
170         mbstate_t mbs;
171
172         linect = wordct = charct = llct = tmpll = 0;
173         if (file == NULL) {
174                 fd = STDIN_FILENO;
175         } else {
176                 if ((fd = open(file, O_RDONLY)) < 0) {
177                         warn("%s: open", file);
178                         return (1);
179                 }
180                 if (doword || (domulti && MB_CUR_MAX != 1))
181                         goto word;
182                 /*
183                  * Line counting is split out because it's a lot faster to get
184                  * lines than to get words, since the word count requires some
185                  * logic.
186                  */
187                 if (doline) {
188                         while ((len = read(fd, buf, MAXBSIZE))) {
189                                 if (len == -1) {
190                                         warn("%s: read", file);
191                                         close(fd);
192                                         return (1);
193                                 }
194                                 if (siginfo) {
195                                         show_cnt(file, linect, wordct, charct,
196                                             llct);
197                                 }
198                                 charct += len;
199                                 for (p = buf; len--; ++p) {
200                                         if (*p == '\n') {
201                                                 if (tmpll > llct)
202                                                         llct = tmpll;
203                                                 tmpll = 0;
204                                                 ++linect;
205                                         } else
206                                                 tmpll++;
207                                 }
208                         }
209                         reset_siginfo();
210                         tlinect += linect;
211                         if (dochar)
212                                 tcharct += charct;
213                         if (dolongline) {
214                                 if (llct > tlongline)
215                                         tlongline = llct;
216                         }
217                         show_cnt(file, linect, wordct, charct, llct);
218                         close(fd);
219                         return (0);
220                 }
221                 /*
222                  * If all we need is the number of characters and it's a
223                  * regular file, just stat the puppy.
224                  */
225                 if (dochar || domulti) {
226                         if (fstat(fd, &sb)) {
227                                 warn("%s: fstat", file);
228                                 close(fd);
229                                 return (1);
230                         }
231                         if (S_ISREG(sb.st_mode)) {
232                                 reset_siginfo();
233                                 charct = sb.st_size;
234                                 show_cnt(file, linect, wordct, charct, llct);
235                                 tcharct += charct;
236                                 close(fd);
237                                 return (0);
238                         }
239                 }
240         }
241
242         /* Do it the hard way... */
243 word:   gotsp = 1;
244         warned = 0;
245         memset(&mbs, 0, sizeof(mbs));
246         while ((len = read(fd, buf, MAXBSIZE)) != 0) {
247                 if (len == -1) {
248                         warn("%s: read", file != NULL ? file : "stdin");
249                         close(fd);
250                         return (1);
251                 }
252                 p = buf;
253                 while (len > 0) {
254                         if (siginfo)
255                                 show_cnt(file, linect, wordct, charct, llct);
256                         if (!domulti || MB_CUR_MAX == 1) {
257                                 clen = 1;
258                                 wch = (unsigned char)*p;
259                         } else if ((clen = mbrtowc(&wch, p, len, &mbs)) ==
260                             (size_t)-1) {
261                                 if (!warned) {
262                                         errno = EILSEQ;
263                                         warn("%s",
264                                             file != NULL ? file : "stdin");
265                                         warned = 1;
266                                 }
267                                 memset(&mbs, 0, sizeof(mbs));
268                                 clen = 1;
269                                 wch = (unsigned char)*p;
270                         } else if (clen == (size_t)-2)
271                                 break;
272                         else if (clen == 0)
273                                 clen = 1;
274                         charct++;
275                         if (wch != L'\n')
276                                 tmpll++;
277                         len -= clen;
278                         p += clen;
279                         if (wch == L'\n') {
280                                 if (tmpll > llct)
281                                         llct = tmpll;
282                                 tmpll = 0;
283                                 ++linect;
284                         }
285                         if (iswspace(wch))
286                                 gotsp = 1;
287                         else if (gotsp) {
288                                 gotsp = 0;
289                                 ++wordct;
290                         }
291                 }
292         }
293         reset_siginfo();
294         if (domulti && MB_CUR_MAX > 1)
295                 if (mbrtowc(NULL, NULL, 0, &mbs) == (size_t)-1 && !warned)
296                         warn("%s", file != NULL ? file : "stdin");
297         if (doline)
298                 tlinect += linect;
299         if (doword)
300                 twordct += wordct;
301         if (dochar || domulti)
302                 tcharct += charct;
303         if (dolongline) {
304                 if (llct > tlongline)
305                         tlongline = llct;
306         }
307         show_cnt(file, linect, wordct, charct, llct);
308         close(fd);
309         return (0);
310 }
311
312 static void
313 usage(void)
314 {
315         fprintf(stderr, "usage: wc [-Lclmw] [file ...]\n");
316         exit(1);
317 }