unexpand.c - sbase - suckless unix tools
(HTM) git clone git://git.suckless.org/sbase
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
unexpand.c (3318B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include <stdint.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 #include "utf.h"
7 #include "util.h"
8
9 static int aflag = 0;
10 static size_t *tablist = NULL;
11 static size_t tablistlen = 8;
12
13 static size_t
14 parselist(const char *s)
15 {
16 size_t i;
17 char *p, *tmp;
18
19 tmp = estrdup(s);
20 for (i = 0; (p = strsep(&tmp, " ,")); i++) {
21 if (*p == '\0')
22 eprintf("empty field in tablist\n");
23 tablist = ereallocarray(tablist, i + 1, sizeof(*tablist));
24 tablist[i] = estrtonum(p, 1, MIN(LLONG_MAX, SIZE_MAX));
25 if (i > 0 && tablist[i - 1] >= tablist[i])
26 eprintf("tablist must be ascending\n");
27 }
28 tablist = ereallocarray(tablist, i + 1, sizeof(*tablist));
29
30 return i;
31 }
32
33 static void
34 unexpandspan(size_t last, size_t col)
35 {
36 size_t off, i, j;
37 Rune r;
38
39 if (tablistlen == 1) {
40 i = 0;
41 off = last % tablist[i];
42
43 if ((col - last) + off >= tablist[i] && last < col)
44 last -= off;
45
46 r = '\t';
47 for (; last + tablist[i] <= col; last += tablist[i])
48 efputrune(&r, stdout, "<stdout>");
49 r = ' ';
50 for (; last < col; last++)
51 efputrune(&r, stdout, "<stdout>");
52 } else {
53 for (i = 0; i < tablistlen; i++)
54 if (col < tablist[i])
55 break;
56 for (j = 0; j < tablistlen; j++)
57 if (last < tablist[j])
58 break;
59 r = '\t';
60 for (; j < i; j++) {
61 efputrune(&r, stdout, "<stdout>");
62 last = tablist[j];
63 }
64 r = ' ';
65 for (; last < col; last++)
66 efputrune(&r, stdout, "<stdout>");
67 }
68 }
69
70 static void
71 unexpand(const char *file, FILE *fp)
72 {
73 Rune r;
74 size_t last = 0, col = 0, i;
75 int bol = 1;
76
77 while (efgetrune(&r, fp, file)) {
78 switch (r) {
79 case ' ':
80 if (!bol && !aflag)
81 last++;
82 col++;
83 break;
84 case '\t':
85 if (tablistlen == 1) {
86 if (!bol && !aflag)
87 last += tablist[0] - col % tablist[0];
88 col += tablist[0] - col % tablist[0];
89 } else {
90 for (i = 0; i < tablistlen; i++)
91 if (col < tablist[i])
92 break;
93 if (!bol && !aflag)
94 last = tablist[i];
95 col = tablist[i];
96 }
97 break;
98 case '\b':
99 if (bol || aflag)
100 unexpandspan(last, col);
101 col -= (col > 0);
102 last = col;
103 bol = 0;
104 break;
105 case '\n':
106 if (bol || aflag)
107 unexpandspan(last, col);
108 last = col = 0;
109 bol = 1;
110 break;
111 default:
112 if (bol || aflag)
113 unexpandspan(last, col);
114 last = ++col;
115 bol = 0;
116 break;
117 }
118 if ((r != ' ' && r != '\t') || (!aflag && !bol))
119 efputrune(&r, stdout, "<stdout>");
120 }
121 if (last < col && (bol || aflag))
122 unexpandspan(last, col);
123 }
124
125 static void
126 usage(void)
127 {
128 eprintf("usage: %s [-a] [-t tablist] [file ...]\n", argv0);
129 }
130
131 int
132 main(int argc, char *argv[])
133 {
134 FILE *fp;
135 int ret = 0;
136 char *tl = "8";
137
138 ARGBEGIN {
139 case 't':
140 tl = EARGF(usage());
141 if (!*tl)
142 eprintf("tablist cannot be empty\n");
143 /* Fallthrough: -t implies -a */
144 case 'a':
145 aflag = 1;
146 break;
147 default:
148 usage();
149 } ARGEND
150
151 tablistlen = parselist(tl);
152
153 if (!argc) {
154 unexpand("<stdin>", stdin);
155 } else {
156 for (; *argv; argc--, argv++) {
157 if (!strcmp(*argv, "-")) {
158 *argv = "<stdin>";
159 fp = stdin;
160 } else if (!(fp = fopen(*argv, "r"))) {
161 weprintf("fopen %s:", *argv);
162 ret = 1;
163 continue;
164 }
165 unexpand(*argv, fp);
166 if (fp != stdin && fshut(fp, *argv))
167 ret = 1;
168 }
169 }
170
171 ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
172
173 return ret;
174 }