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