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 if (total > 1) { 144 xo_open_container("total"); 145 show_cnt("total", tlinect, twordct, tcharct, tlongline); 146 xo_close_container("total"); 147 } 148 xo_close_list("file"); 149 xo_close_container("wc"); 150 xo_finish(); 151 exit(errors == 0 ? 0 : 1); 152 } 153 154 static void 155 show_cnt(const char *file, uintmax_t linect, uintmax_t wordct, 156 uintmax_t charct, uintmax_t llct) 157 { 158 xo_handle_t *xop; 159 160 if (!siginfo) 161 xop = NULL; 162 else { 163 xop = stderr_handle; 164 siginfo = 0; 165 } 166 167 if (doline) 168 xo_emit_h(xop, " {:lines/%7ju/%ju}", linect); 169 if (doword) 170 xo_emit_h(xop, " {:words/%7ju/%ju}", wordct); 171 if (dochar || domulti) 172 xo_emit_h(xop, " {:characters/%7ju/%ju}", charct); 173 if (dolongline) 174 xo_emit_h(xop, " {:long-lines/%7ju/%ju}", llct); 175 if (file != NULL) 176 xo_emit_h(xop, " {:filename/%s}\n", file); 177 else 178 xo_emit_h(xop, "\n"); 179 } 180 181 static int 182 cnt(const char *file) 183 { 184 struct stat sb; 185 uintmax_t linect, wordct, charct, llct, tmpll; 186 int fd, len, warned; 187 size_t clen; 188 short gotsp; 189 u_char *p; 190 u_char buf[MAXBSIZE]; 191 wchar_t wch; 192 mbstate_t mbs; 193 194 linect = wordct = charct = llct = tmpll = 0; 195 if (file == NULL) 196 fd = STDIN_FILENO; 197 else { 198 if ((fd = open(file, O_RDONLY, 0)) < 0) { 199 xo_warn("%s: open", file); 200 return (1); 201 } 202 if (doword || (domulti && MB_CUR_MAX != 1)) 203 goto word; 204 /* 205 * Line counting is split out because it's a lot faster to get 206 * lines than to get words, since the word count requires some 207 * logic. 208 */ 209 if (doline) { 210 while ((len = read(fd, buf, MAXBSIZE))) { 211 if (len == -1) { 212 xo_warn("%s: read", file); 213 (void)close(fd); 214 return (1); 215 } 216 if (siginfo) { 217 show_cnt(file, linect, wordct, charct, 218 llct); 219 } 220 charct += len; 221 for (p = buf; len--; ++p) 222 if (*p == '\n') { 223 if (tmpll > llct) 224 llct = tmpll; 225 tmpll = 0; 226 ++linect; 227 } else 228 tmpll++; 229 } 230 tlinect += linect; 231 if (dochar) 232 tcharct += charct; 233 if (dolongline) { 234 if (llct > tlongline) 235 tlongline = llct; 236 } 237 show_cnt(file, linect, wordct, charct, llct); 238 (void)close(fd); 239 return (0); 240 } 241 /* 242 * If all we need is the number of characters and it's a 243 * regular file, just stat the puppy. 244 */ 245 if (dochar || domulti) { 246 if (fstat(fd, &sb)) { 247 xo_warn("%s: fstat", file); 248 (void)close(fd); 249 return (1); 250 } 251 if (S_ISREG(sb.st_mode)) { 252 charct = sb.st_size; 253 show_cnt(file, linect, wordct, charct, llct); 254 tcharct += charct; 255 (void)close(fd); 256 return (0); 257 } 258 } 259 } 260 261 /* Do it the hard way... */ 262 word: gotsp = 1; 263 warned = 0; 264 memset(&mbs, 0, sizeof(mbs)); 265 while ((len = read(fd, buf, MAXBSIZE)) != 0) { 266 if (len == -1) { 267 xo_warn("%s: read", file != NULL ? file : "stdin"); 268 (void)close(fd); 269 return (1); 270 } 271 p = buf; 272 while (len > 0) { 273 if (siginfo) 274 show_cnt(file, linect, wordct, charct, llct); 275 if (!domulti || MB_CUR_MAX == 1) { 276 clen = 1; 277 wch = (unsigned char)*p; 278 } else if ((clen = mbrtowc(&wch, p, len, &mbs)) == 279 (size_t)-1) { 280 if (!warned) { 281 errno = EILSEQ; 282 xo_warn("%s", 283 file != NULL ? file : "stdin"); 284 warned = 1; 285 } 286 memset(&mbs, 0, sizeof(mbs)); 287 clen = 1; 288 wch = (unsigned char)*p; 289 } else if (clen == (size_t)-2) 290 break; 291 else if (clen == 0) 292 clen = 1; 293 charct++; 294 if (wch != L'\n') 295 tmpll++; 296 len -= clen; 297 p += clen; 298 if (wch == L'\n') { 299 if (tmpll > llct) 300 llct = tmpll; 301 tmpll = 0; 302 ++linect; 303 } 304 if (iswspace(wch)) 305 gotsp = 1; 306 else if (gotsp) { 307 gotsp = 0; 308 ++wordct; 309 } 310 } 311 } 312 if (domulti && MB_CUR_MAX > 1) 313 if (mbrtowc(NULL, NULL, 0, &mbs) == (size_t)-1 && !warned) 314 xo_warn("%s", file != NULL ? file : "stdin"); 315 if (doline) 316 tlinect += linect; 317 if (doword) 318 twordct += wordct; 319 if (dochar || domulti) 320 tcharct += charct; 321 if (dolongline) { 322 if (llct > tlongline) 323 tlongline = llct; 324 } 325 show_cnt(file, linect, wordct, charct, llct); 326 (void)close(fd); 327 return (0); 328 } 329 330 static void 331 usage(void) 332 { 333 xo_error("usage: wc [-Lclmw] [file ...]\n"); 334 exit(1); 335 } 336