fold.c 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. /* fold.c - fold text
  2. *
  3. * Copyright 2014 Samuel Holland <samuel@sholland.net>
  4. *
  5. * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/fold.html
  6. USE_FOLD(NEWTOY(fold, "bsuw#<1", TOYFLAG_USR|TOYFLAG_BIN))
  7. config FOLD
  8. bool "fold"
  9. default n
  10. help
  11. usage: fold [-bsu] [-w WIDTH] [FILE...]
  12. Folds (wraps) or unfolds ascii text by adding or removing newlines.
  13. Default line width is 80 columns for folding and infinite for unfolding.
  14. -b Fold based on bytes instead of columns
  15. -s Fold/unfold at whitespace boundaries if possible
  16. -u Unfold text (and refold if -w is given)
  17. -w Set lines to WIDTH columns or bytes
  18. */
  19. #define FOR_fold
  20. #include "toys.h"
  21. GLOBALS(
  22. int width;
  23. )
  24. // wcwidth mbrtowc
  25. void do_fold(int fd, char *name)
  26. {
  27. int bufsz, len = 0, maxlen;
  28. if (toys.optflags & FLAG_w) maxlen = TT.width;
  29. else if (toys.optflags & FLAG_u) maxlen = 0;
  30. else maxlen = 80;
  31. while ((bufsz = read(fd, toybuf, sizeof(toybuf))) > 0) {
  32. char *buf = toybuf;
  33. int pos = 0, split = -1;
  34. while (pos < bufsz) {
  35. switch (buf[pos]) {
  36. case '\n':
  37. // print everything but the \n, then move on to the next buffer
  38. if ((toys.optflags & FLAG_u) && buf[pos-1] != '\n'
  39. && buf[pos+1] != '\n') {
  40. xwrite(1, buf, pos);
  41. bufsz -= pos + 1;
  42. buf += pos + 1;
  43. pos = 0;
  44. split = -1;
  45. // reset len, FLAG_b or not; just print multiple lines at once
  46. } else len = 0;
  47. break;
  48. case '\b':
  49. // len cannot be negative; not allowed to wrap after backspace
  50. if (toys.optflags & FLAG_b) len++;
  51. else if (len > 0) len--;
  52. break;
  53. case '\r':
  54. // not allowed to wrap after carriage return
  55. if (toys.optflags & FLAG_b) len++;
  56. else len = 0;
  57. break;
  58. case '\t':
  59. // round to 8, but we add one after falling through
  60. // (because of whitespace, but it also takes care of FLAG_b)
  61. if (!(toys.optflags & FLAG_b)) len = (len & ~7) + 7;
  62. case ' ':
  63. split = pos;
  64. default:
  65. len++;
  66. }
  67. // we don't want to double up \n; not allowed to wrap before \b
  68. if (maxlen > 0 && len >= maxlen && buf[pos+1] != '\n' && buf[pos+1] != '\b') {
  69. if (!(toys.optflags & FLAG_s) || split < 0) split = pos;
  70. xwrite(1, buf, split + 1);
  71. xputc('\n');
  72. bufsz -= split + 1;
  73. buf += split + 1;
  74. len = pos = 0;
  75. split = -1;
  76. } else pos++;
  77. }
  78. xwrite(1, buf, bufsz);
  79. }
  80. xputc('\n');
  81. }
  82. void fold_main(void)
  83. {
  84. loopfiles(toys.optargs, do_fold);
  85. }