1/* wc.c - Word count 2 * 3 * Copyright 2011 Rob Landley <rob@landley.net> 4 * 5 * See http://opengroup.org/onlinepubs/9699919799/utilities/wc.html 6 7USE_WC(NEWTOY(wc, "mcwl", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE)) 8 9config WC 10 bool "wc" 11 default y 12 help 13 usage: wc -lwcm [FILE...] 14 15 Count lines, words, and characters in input. 16 17 -l Show lines 18 -w Show words 19 -c Show bytes 20 -m Show characters 21 22 By default outputs lines, words, bytes, and filename for each 23 argument (or from stdin if none). Displays only either bytes 24 or characters. 25*/ 26 27#define FOR_wc 28#include "toys.h" 29 30GLOBALS( 31 unsigned long totals[4]; 32) 33 34static void show_lengths(unsigned long *lengths, char *name) 35{ 36 int i, space = 0, first = 1; 37 38 // POSIX says there should never be leading spaces, but accepts that 39 // traditional implementations use 7 spaces, unless only one file (or 40 // just stdin) is being counted, when there should be no leading spaces, 41 // *except* for the case where we're going to output multiple numbers. 42 // And, yes, folks have test scripts that rely on all this nonsense :-( 43 // Note: sufficiently modern versions of coreutils wc will use the smallest 44 // column width necessary to have all columns be equal width rather than 0. 45 if (!(!toys.optc && !(toys.optflags & (toys.optflags-1))) && toys.optc!=1) 46 space = 7; 47 48 for (i = 0; i<4; i++) { 49 if (toys.optflags&(1<<i)) { 50 printf(" %*ld"+first, space, lengths[i]); 51 first = 0; 52 } 53 TT.totals[i] += lengths[i]; 54 } 55 if (*toys.optargs) printf(" %s", name); 56 xputc('\n'); 57} 58 59static void do_wc(int fd, char *name) 60{ 61 int len = 0, clen = 1, space = 0; 62 unsigned long word = 0, lengths[] = {0,0,0,0}; 63 64 // Speed up common case: wc -c normalfile is file length. 65 if (toys.optflags == FLAG_c) { 66 struct stat st; 67 68 // On Linux, files in /proc often report their size as 0. 69 if (!fstat(fd, &st) && S_ISREG(st.st_mode) && st.st_size) { 70 lengths[2] = st.st_size; 71 goto show; 72 } 73 } 74 75 for (;;) { 76 int pos, done = 0, len2 = read(fd, toybuf+len, sizeof(toybuf)-len); 77 unsigned wchar; 78 79 if (len2<0) perror_msg_raw(name); 80 else len += len2; 81 if (len2<1) done++; 82 83 for (pos = 0; pos<len; pos++) { 84 if (toybuf[pos]=='\n') lengths[0]++; 85 lengths[2]++; 86 if (FLAG(m)) { 87 // If we've consumed next wide char 88 if (--clen<1) { 89 // next wide size, don't count invalid, fetch more data if necessary 90 clen = utf8towc(&wchar, toybuf+pos, len-pos); 91 if (clen == -1) continue; 92 if (clen == -2 && !done) break; 93 94 lengths[3]++; 95 space = iswspace(wchar); 96 } 97 } else space = isspace(toybuf[pos]); 98 99 if (space) word=0; 100 else { 101 if (!word) lengths[1]++; 102 word=1; 103 } 104 } 105 if (done) break; 106 if (pos != len) memmove(toybuf, toybuf+pos, len-pos); 107 len -= pos; 108 } 109 110show: 111 show_lengths(lengths, name); 112} 113 114void wc_main(void) 115{ 116 if (!toys.optflags) toys.optflags = FLAG_l|FLAG_w|FLAG_c; 117 loopfiles(toys.optargs, do_wc); 118 if (toys.optc>1) show_lengths(TT.totals, "total"); 119} 120