• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     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 
110 show:
111   show_lengths(lengths, name);
112 }
113 
wc_main(void)114 void 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