toybox/toys/posix/wc.c
<<
>>
Prefs
   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
  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
 111show:
 112  show_lengths(lengths, name);
 113}
 114
 115void 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