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