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
7 USE_WC(NEWTOY(wc, "mcwl", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
8
9 config 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
GLOBALS(unsigned long totals[4];)30 GLOBALS(
31 unsigned long totals[4];
32 )
33
34 static 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
do_wc(int fd,char * name)59 static 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
78 if (len2<0) perror_msg_raw(name);
79 else len += len2;
80 if (len2<1) done++;
81
82 for (pos = 0; pos<len; pos++) {
83 if (toybuf[pos]=='\n') lengths[0]++;
84 lengths[2]++;
85 if (FLAG(m)) {
86 // If we've consumed next wide char
87 if (--clen<1) {
88 wchar_t wchar;
89
90 // next wide size, don't count invalid, fetch more data if necessary
91 clen = utf8towc(&wchar, toybuf+pos, len-pos);
92 if (clen == -1) continue;
93 if (clen == -2 && !done) break;
94
95 lengths[3]++;
96 space = iswspace(wchar);
97 }
98 } else space = isspace(toybuf[pos]);
99
100 if (space) word=0;
101 else {
102 if (!word) lengths[1]++;
103 word=1;
104 }
105 }
106 if (done) break;
107 if (pos != len) memmove(toybuf, toybuf+pos, len-pos);
108 len -= pos;
109 }
110
111 show:
112 show_lengths(lengths, name);
113 }
114
wc_main(void)115 void wc_main(void)
116 {
117 if (!toys.optflags) toys.optflags = FLAG_l|FLAG_w|FLAG_c;
118 loopfiles(toys.optargs, do_wc);
119 if (toys.optc>1) show_lengths(TT.totals, "total");
120 }
121