1 /* fold.c - Line wrap input.
2 *
3 * Copyright 2023 Rob Landley <[email protected]>
4 *
5 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/fold.html
6
7 USE_FOLD(NEWTOY(fold, "bsw#<1=80", TOYFLAG_USR|TOYFLAG_BIN))
8
9 config FOLD
10 bool "fold"
11 default y
12 help
13 usage: fold [-bs] [-w WIDTH] [FILE...]
14
15 Break long lines by inserting newlines.
16
17 -b Count bytes instead of utf-8 unicode columns
18 -s Wrap at whitespace when possible
19 -w Break at WIDTH columns (default 80)
20 */
21
22 #define FOR_fold
23 #include "toys.h"
24
GLOBALS(long w;)25 GLOBALS(
26 long w;
27 )
28
29 // wcwidth utf8towc
30 void do_fold(int fd, char *name)
31 {
32 FILE *fp = fd ? fdopen(fd, "r") : stdin;
33 char *rr, *ss;
34 long ii, bb, ww, width, space;
35 unsigned cc;
36
37 // Note: not bothering to handle embedded NUL bytes, they truncate the line.
38
39 // Loop reading/printing lines
40 while ((ss = rr = xgetdelim(fp, '\n'))) for (ii = width = space = 0;;) {
41 // Parse next character's byte length and column width
42 bb = ww = 1;
43 if (ss[ii]<32) ww = FLAG(b);
44 if (FLAG(b)) cc = ss[ii];
45 else {
46 if ((bb = utf8towc(&cc, ss+ii, 4))>0 && (ww = wcwidth(cc))<0) ww = 0;
47 if (cc=='\t') ww = 8-(width&7);
48 }
49
50 // Did line end?
51 if (!cc || cc=='\r' || cc=='\n') {
52 if (cc) ii++;
53 if (ii) {
54 xwrite(1, ss, ii);
55 ss += ii;
56 ii = width = space = 0;
57 } else {
58 free(rr);
59
60 break;
61 }
62
63 // backspace?
64 } else if (!FLAG(b) && cc=='\b') {
65 if (width) width--;
66 ii++;
67
68 // Is it time to wrap?
69
70 } else if (width+ww>TT.w && ss[ii+bb]!='\b'
71 && (ii || !strchr("\r\n", ss[ii+bb])))
72 {
73 if (!ii) ii += bb;
74 if (!space) space = ii;
75
76 cc = ss[space];
77 ss[space] = '\n';
78 xwrite(1, ss, space+1);
79 ss += space;
80 *ss = cc;
81 ii = width = space = 0;
82
83 // move the cursor
84 } else {
85 ii += bb;
86 width += ww;
87 if (FLAG(s) && iswspace(cc)) space = ii;
88 }
89 }
90 if (fp != stdin) fclose(fp);
91 }
92
fold_main(void)93 void fold_main(void)
94 {
95 loopfiles(toys.optargs, do_fold);
96 loopfiles_rw(toys.optargs, O_RDONLY|WARN_ONLY, 0, do_fold);
97 }
98