wc.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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. USE_WC(NEWTOY(wc, "mcwl", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
  7. config WC
  8. bool "wc"
  9. default y
  10. help
  11. usage: wc -lwcm [FILE...]
  12. Count lines, words, and characters in input.
  13. -l Show lines
  14. -w Show words
  15. -c Show bytes
  16. -m Show characters
  17. By default outputs lines, words, bytes, and filename for each
  18. argument (or from stdin if none). Displays only either bytes
  19. or characters.
  20. */
  21. #define FOR_wc
  22. #include "toys.h"
  23. GLOBALS(
  24. unsigned long totals[4];
  25. )
  26. static void show_lengths(unsigned long *lengths, char *name)
  27. {
  28. int i, space = 0, first = 1;
  29. // POSIX says there should never be leading spaces, but accepts that
  30. // traditional implementations use 7 spaces, unless only one file (or
  31. // just stdin) is being counted, when there should be no leading spaces,
  32. // *except* for the case where we're going to output multiple numbers.
  33. // And, yes, folks have test scripts that rely on all this nonsense :-(
  34. // Note: sufficiently modern versions of coreutils wc will use the smallest
  35. // column width necessary to have all columns be equal width rather than 0.
  36. if (!(!toys.optc && !(toys.optflags & (toys.optflags-1))) && toys.optc!=1)
  37. space = 7;
  38. for (i = 0; i<4; i++) {
  39. if (toys.optflags&(1<<i)) {
  40. printf(" %*ld"+first, space, lengths[i]);
  41. first = 0;
  42. }
  43. TT.totals[i] += lengths[i];
  44. }
  45. if (*toys.optargs) printf(" %s", name);
  46. xputc('\n');
  47. }
  48. static void do_wc(int fd, char *name)
  49. {
  50. int len = 0, clen = 1, space = 0;
  51. unsigned long word = 0, lengths[] = {0,0,0,0};
  52. // Speed up common case: wc -c normalfile is file length.
  53. if (toys.optflags == FLAG_c) {
  54. struct stat st;
  55. // On Linux, files in /proc often report their size as 0.
  56. if (!fstat(fd, &st) && S_ISREG(st.st_mode) && st.st_size) {
  57. lengths[2] = st.st_size;
  58. goto show;
  59. }
  60. }
  61. for (;;) {
  62. int pos, done = 0, len2 = read(fd, toybuf+len, sizeof(toybuf)-len);
  63. unsigned wchar;
  64. if (len2<0) perror_msg_raw(name);
  65. else len += len2;
  66. if (len2<1) done++;
  67. for (pos = 0; pos<len; pos++) {
  68. if (toybuf[pos]=='\n') lengths[0]++;
  69. lengths[2]++;
  70. if (FLAG(m)) {
  71. // If we've consumed next wide char
  72. if (--clen<1) {
  73. // next wide size, don't count invalid, fetch more data if necessary
  74. clen = utf8towc(&wchar, toybuf+pos, len-pos);
  75. if (clen == -1) continue;
  76. if (clen == -2 && !done) break;
  77. lengths[3]++;
  78. space = iswspace(wchar);
  79. }
  80. } else space = isspace(toybuf[pos]);
  81. if (space) word=0;
  82. else {
  83. if (!word) lengths[1]++;
  84. word=1;
  85. }
  86. }
  87. if (done) break;
  88. if (pos != len) memmove(toybuf, toybuf+pos, len-pos);
  89. len -= pos;
  90. }
  91. show:
  92. show_lengths(lengths, name);
  93. }
  94. void wc_main(void)
  95. {
  96. if (!toys.optflags) toys.optflags = FLAG_l|FLAG_w|FLAG_c;
  97. loopfiles(toys.optargs, do_wc);
  98. if (toys.optc>1) show_lengths(TT.totals, "total");
  99. }