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 
7 USE_FOLD(NEWTOY(fold, "bsuw#<1", TOYFLAG_USR|TOYFLAG_BIN))
8 
9 config 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 
GLOBALS(int width;)27 GLOBALS(
28   int width;
29 )
30 
31 // wcwidth mbrtowc
32 void 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 
fold_main(void)94 void fold_main(void)
95 {
96   loopfiles(toys.optargs, do_fold);
97 }
98