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 = 7, first = 1;
37
38  for (i = 0; i<4; i++) if (toys.optflags == (1<<i)) space = 0;
39  for (i = 0; i<4; i++) {
40    if (toys.optflags&(1<<i)) {
41      printf(" %*ld"+first, space, lengths[i]);
42      first = 0;
43    }
44    TT.totals[i] += lengths[i];
45  }
46  if (*toys.optargs) printf(" %s", name);
47  xputc('\n');
48}
49
50static void do_wc(int fd, char *name)
51{
52  int len = 0, clen = 1, space = 0;
53  unsigned long word = 0, lengths[] = {0,0,0,0};
54
55  // Speed up common case: wc -c normalfile is file length.
56  if (toys.optflags == FLAG_c) {
57    struct stat st;
58
59    // On Linux, files in /proc often report their size as 0.
60    if (!fstat(fd, &st) && S_ISREG(st.st_mode) && st.st_size) {
61      lengths[2] = st.st_size;
62      goto show;
63    }
64  }
65
66  for (;;) {
67    int pos, done = 0, len2 = read(fd, toybuf+len, sizeof(toybuf)-len);
68
69    if (len2<0) perror_msg_raw(name);
70    else len += len2;
71    if (len2<1) done++;
72
73    for (pos = 0; pos<len; pos++) {
74      if (toybuf[pos]=='\n') lengths[0]++;
75      lengths[2]++;
76      if (toys.optflags&FLAG_m) {
77        // If we've consumed next wide char
78        if (--clen<1) {
79          wchar_t wchar;
80
81          // next wide size, don't count invalid, fetch more data if necessary
82          clen = utf8towc(&wchar, toybuf+pos, len-pos);
83          if (clen == -1) continue;
84          if (clen == -2 && !done) break;
85
86          lengths[3]++;
87          space = iswspace(wchar);
88        }
89      } else space = isspace(toybuf[pos]);
90
91      if (space) word=0;
92      else {
93        if (!word) lengths[1]++;
94        word=1;
95      }
96    }
97    if (done) break;
98    if (pos != len) memmove(toybuf, toybuf+pos, len-pos);
99    len -= pos;
100  }
101
102show:
103  show_lengths(lengths, name);
104}
105
106void wc_main(void)
107{
108  if (!toys.optflags) toys.optflags = FLAG_l|FLAG_w|FLAG_c;
109  loopfiles(toys.optargs, do_wc);
110  if (toys.optc>1) show_lengths(TT.totals, "total");
111}
112