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