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 30 #ifndef lint 31 static const char copyright[] = 32 "@(#) Copyright (c) 1980, 1987, 1991, 1993\n\ 33 The Regents of the University of California. All rights reserved.\n"; 34 #endif /* not lint */ 35 36 #if 0 37 #ifndef lint 38 static char sccsid[] = "@(#)wc.c 8.1 (Berkeley) 6/6/93"; 39 #endif /* not lint */ 40 #endif 41 42 #include <sys/cdefs.h> 43 __FBSDID("$FreeBSD$"); 44 45 #include <sys/param.h> 46 #include <sys/stat.h> 47 48 #include <ctype.h> 49 #include <err.h> 50 #include <errno.h> 51 #include <fcntl.h> 52 #include <locale.h> 53 #include <stdint.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 #include <wchar.h> 59 #include <wctype.h> 60 #include <libxo/xo.h> 61 62 static uintmax_t tlinect, twordct, tcharct, tlongline; 63 static int doline, doword, dochar, domulti, dolongline; 64 static volatile sig_atomic_t siginfo; 65 static xo_handle_t *stderr_handle; 66 67 static void show_cnt(const char *file, uintmax_t linect, uintmax_t wordct, 68 uintmax_t charct, uintmax_t llct); 69 static int cnt(const char *); 70 static void usage(void); 71 72 static void 73 siginfo_handler(int sig __unused) 74 { 75 76 siginfo = 1; 77 } 78 79 static void 80 reset_siginfo(void) 81 { 82 83 signal(SIGINFO, SIG_DFL); 84 siginfo = 0; 85 } 86 87 int 88 main(int argc, char *argv[]) 89 { 90 int ch, errors, total; 91 92 (void) setlocale(LC_CTYPE, ""); 93 94 argc = xo_parse_args(argc, argv); 95 if (argc < 0) 96 return (argc); 97 98 while ((ch = getopt(argc, argv, "clmwL")) != -1) 99 switch((char)ch) { 100 case 'l': 101 doline = 1; 102 break; 103 case 'w': 104 doword = 1; 105 break; 106 case 'c': 107 dochar = 1; 108 domulti = 0; 109 break; 110 case 'L': 111 dolongline = 1; 112 break; 113 case 'm': 114 domulti = 1; 115 dochar = 0; 116 break; 117 case '?': 118 default: 119 usage(); 120 } 121 argv += optind; 122 argc -= optind; 123 124 (void)signal(SIGINFO, siginfo_handler); 125 126 /* Wc's flags are on by default. */ 127 if (doline + doword + dochar + domulti + dolongline == 0) 128 doline = doword = dochar = 1; 129 130 stderr_handle = xo_create_to_file(stderr, XO_STYLE_TEXT, 0); 131 xo_open_container("wc"); 132 xo_open_list("file"); 133 134 errors = 0; 135 total = 0; 136 if (!*argv) { 137 xo_open_instance("file"); 138 if (cnt((char *)NULL) != 0) 139 ++errors; 140 xo_close_instance("file"); 141 } else { 142 do { 143 xo_open_instance("file"); 144 if (cnt(*argv) != 0) 145 ++errors; 146 xo_close_instance("file"); 147 ++total; 148 } while(*++argv); 149 } 150 151 xo_close_list("file"); 152 153 if (total > 1) { 154 xo_open_container("total"); 155 show_cnt("total", tlinect, twordct, tcharct, tlongline); 156 xo_close_container("total"); 157 } 158 159 xo_close_container("wc"); 160 xo_finish(); 161 exit(errors == 0 ? 0 : 1); 162 } 163 164 static void 165 show_cnt(const char *file, uintmax_t linect, uintmax_t wordct, 166 uintmax_t charct, uintmax_t llct) 167 { 168 xo_handle_t *xop; 169 170 if (!siginfo) 171 xop = NULL; 172 else { 173 xop = stderr_handle; 174 siginfo = 0; 175 } 176 177 if (doline) 178 xo_emit_h(xop, " {:lines/%7ju/%ju}", linect); 179 if (doword) 180 xo_emit_h(xop, " {:words/%7ju/%ju}", wordct); 181 if (dochar || domulti) 182 xo_emit_h(xop, " {:characters/%7ju/%ju}", charct); 183 if (dolongline) 184 xo_emit_h(xop, " {:long-lines/%7ju/%ju}", llct); 185 if (file != NULL) 186 xo_emit_h(xop, " {:filename/%s}\n", file); 187 else 188 xo_emit_h(xop, "\n"); 189 } 190 191 static int 192 cnt(const char *file) 193 { 194 struct stat sb; 195 uintmax_t linect, wordct, charct, llct, tmpll; 196 int fd, len, warned; 197 size_t clen; 198 short gotsp; 199 u_char *p; 200 u_char buf[MAXBSIZE]; 201 wchar_t wch; 202 mbstate_t mbs; 203 204 linect = wordct = charct = llct = tmpll = 0; 205 if (file == NULL) 206 fd = STDIN_FILENO; 207 else { 208 if ((fd = open(file, O_RDONLY, 0)) < 0) { 209 xo_warn("%s: open", file); 210 return (1); 211 } 212 if (doword || (domulti && MB_CUR_MAX != 1)) 213 goto word; 214 /* 215 * Line counting is split out because it's a lot faster to get 216 * lines than to get words, since the word count requires some 217 * logic. 218 */ 219 if (doline) { 220 while ((len = read(fd, buf, MAXBSIZE))) { 221 if (len == -1) { 222 xo_warn("%s: read", file); 223 (void)close(fd); 224 return (1); 225 } 226 if (siginfo) { 227 show_cnt(file, linect, wordct, charct, 228 llct); 229 } 230 charct += len; 231 for (p = buf; len--; ++p) 232 if (*p == '\n') { 233 if (tmpll > llct) 234 llct = tmpll; 235 tmpll = 0; 236 ++linect; 237 } else 238 tmpll++; 239 } 240 reset_siginfo(); 241 tlinect += linect; 242 if (dochar) 243 tcharct += charct; 244 if (dolongline) { 245 if (llct > tlongline) 246 tlongline = llct; 247 } 248 show_cnt(file, linect, wordct, charct, llct); 249 (void)close(fd); 250 return (0); 251 } 252 /* 253 * If all we need is the number of characters and it's a 254 * regular file, just stat the puppy. 255 */ 256 if (dochar || domulti) { 257 if (fstat(fd, &sb)) { 258 xo_warn("%s: fstat", file); 259 (void)close(fd); 260 return (1); 261 } 262 if (S_ISREG(sb.st_mode)) { 263 reset_siginfo(); 264 charct = sb.st_size; 265 show_cnt(file, linect, wordct, charct, llct); 266 tcharct += charct; 267 (void)close(fd); 268 return (0); 269 } 270 } 271 } 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 } 349