1/* See LICENSE file for copyright and license details. */
2
3#include <stdlib.h>
4
5#include "utf.h"
6#include "util.h"
7
8static int cflag = 0;
9static int dflag = 0;
10static int sflag = 0;
11
12struct range {
13 Rune start;
14 Rune end;
15 size_t quant;
16};
17
18static struct {
19 char *name;
20 int (*check)(Rune);
21} classes[] = {
22 {"alnum", isalnumrune},
23 {"alpha", isalpharune},
24 {"blank", isblankrune},
25 {"cntrl", iscntrlrune},
26 {"digit", isdigitrune},
27 {"graph", isgraphrune},
28 {"lower", islowerrune},
29 {"print", isprintrune},
30 {"punct", ispunctrune},
31 {"space", isspacerune},
32 {"upper", isupperrune},
33 {"xdigit", isxdigitrune},
34};
35
36#define ISLOWERBIT 1U << 6
37#define ISUPPERBIT 1U << 10
38
39static struct range *set1 = NULL;
40static size_t set1ranges = 0;
41static unsigned set1checks = 0;
42static struct range *set2 = NULL;
43static size_t set2ranges = 0;
44static unsigned set2checks = 0;
45
46static int
47check(Rune rune, unsigned checks)
48{
49 size_t i;
50
51 for (i = 0; checks && i < LEN(classes); i++, checks >>= 1)
52 if (checks & 1 && classes[i].check(rune))
53 return 1;
54
55 return 0;
56}
57
58static size_t
59rangelen(struct range r)
60{
61 return (r.end - r.start + 1) * r.quant;
62}
63
64static size_t
65setlen(struct range *set, size_t setranges)
66{
67 size_t len = 0, i;
68
69 for (i = 0; i < setranges; i++)
70 len += rangelen(set[i]);
71
72 return len;
73}
74
75static int
76rstrmatch(Rune *r, char *s, size_t n)
77{
78 size_t i;
79
80 for (i = 0; i < n; i++)
81 if (r[i] != s[i])
82 return 0;
83 return 1;
84}
85
86static size_t
87makeset(char *str, struct range **set, unsigned *checks)
88{
89 Rune *rstr;
90 size_t len, i, j, m, n;
91 size_t q, setranges = 0;
92 int factor, base;
93
94 /* rstr defines at most len ranges */
95 unescape(str);
96 rstr = ereallocarray(NULL, utflen(str) + 1, sizeof(*rstr));
97 len = utftorunestr(str, rstr);
98 *set = ereallocarray(NULL, len, sizeof(**set));
99
100 for (i = 0; i < len; i++) {
101 if (rstr[i] == '[') {
102 j = i;
103 nextbrack:
104 if (j >= len)
105 goto literal;
106 for (m = j; m < len; m++)
107 if (rstr[m] == ']') {
108 j = m;
109 break;
110 }
111 if (j == i)
112 goto literal;
113
114 /* CLASSES [=EQUIV=] (skip) */
115 if (j - i > 3 && rstr[i + 1] == '=' && rstr[m - 1] == '=') {
116 if (j - i != 4)
117 goto literal;
118 (*set)[setranges].start = rstr[i + 2];
119 (*set)[setranges].end = rstr[i + 2];
120 (*set)[setranges].quant = 1;
121 setranges++;
122 i = j;
123 continue;
124 }
125
126 /* CLASSES [:CLASS:] */
127 if (j - i > 3 && rstr[i + 1] == ':' && rstr[m - 1] == ':') {
128 for (n = 0; n < LEN(classes); n++) {
129 if (rstrmatch(rstr + i + 2, classes[n].name, j - i - 3)) {
130 *checks |= 1 << n;
131 i = j;
132 break;
133 }
134 }
135 if (n < LEN(classes))
136 continue;
137 eprintf("Invalid character class.\n");
138 }
139
140 /* REPEAT [_*n] (only allowed in set2) */
141 if (j - i > 2 && rstr[i + 2] == '*') {
142 /* check if right side of '*' is a number */
143 q = 0;
144 factor = 1;
145 base = (rstr[i + 3] == '0') ? 8 : 10;
146 for (n = j - 1; n > i + 2; n--) {
147 if (rstr[n] < '0' || rstr[n] > '9') {
148 n = 0;
149 break;
150 }
151 q += (rstr[n] - '0') * factor;
152 factor *= base;
153 }
154 if (n == 0) {
155 j = m + 1;
156 goto nextbrack;
157 }
158 (*set)[setranges].start = rstr[i + 1];
159 (*set)[setranges].end = rstr[i + 1];
160 (*set)[setranges].quant = q ? q : setlen(set1, MAX(set1ranges, 1));
161 setranges++;
162 i = j;
163 continue;
164 }
165
166 j = m + 1;
167 goto nextbrack;
168 }
169 literal:
170 /* RANGES [_-__-_], _-__-_ */
171 /* LITERALS _______ */
172 (*set)[setranges].start = rstr[i];
173
174 if (i < len - 2 && rstr[i + 1] == '-' && rstr[i + 2] >= rstr[i])
175 i += 2;
176 (*set)[setranges].end = rstr[i];
177 (*set)[setranges].quant = 1;
178 setranges++;
179 }
180
181 free(rstr);
182 return setranges;
183}
184
185static void
186usage(void)
187{
188 eprintf("usage: %s [-cCds] set1 [set2]\n", argv0);
189}
190
191// ?man tr: translate characters
192// ?man arguments: set1 [set2]
193// ?man translate, squeeze, or delete characters from standard input
194int
195main(int argc, char *argv[])
196{
197 Rune r, lastrune = 0;
198 size_t off1, off2, i, m;
199 int ret = 0;
200
201 ARGBEGIN
202 {
203 // ?man -c: print count or perform stdout action
204 case 'c':
205 // ?man -C: specify option flag
206 case 'C':
207 cflag = 1;
208 break;
209 // ?man -d: specify directory
210 case 'd':
211 dflag = 1;
212 break;
213 // ?man -s: silent mode or print summary
214 case 's':
215 sflag = 1;
216 break;
217 default:
218 usage();
219 }
220 ARGEND
221
222 if (!argc || argc > 2 || (dflag == sflag && argc != 2) || (dflag && argc != 1))
223 usage();
224
225 set1ranges = makeset(argv[0], &set1, &set1checks);
226 if (argc == 2) {
227 set2ranges = makeset(argv[1], &set2, &set2checks);
228 /* sanity checks as we are translating */
229 if (!set2ranges && !set2checks)
230 eprintf("cannot map to an empty set.\n");
231 if (set2checks && set2checks != ISLOWERBIT && set2checks != ISUPPERBIT) {
232 eprintf("can only map to 'lower' and 'upper' class.\n");
233 }
234 }
235read:
236 if (!efgetrune(&r, stdin, "<stdin>")) {
237 ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
238 return ret;
239 }
240 if (argc == 1 && sflag)
241 goto write;
242 for (i = 0, off1 = 0; i < set1ranges; off1 += rangelen(set1[i]), i++) {
243 if (set1[i].start <= r && r <= set1[i].end) {
244 if (dflag) {
245 if (cflag)
246 goto write;
247 else
248 goto read;
249 }
250 if (cflag)
251 goto write;
252
253 /* map r to set2 */
254 if (set2checks) {
255 if (set2checks == ISLOWERBIT)
256 r = tolowerrune(r);
257 else
258 r = toupperrune(r);
259 } else {
260 off1 += r - set1[i].start;
261 if (off1 > setlen(set2, set2ranges) - 1) {
262 r = set2[set2ranges - 1].end;
263 goto write;
264 }
265 for (m = 0, off2 = 0; m < set2ranges; m++) {
266 if (off2 + rangelen(set2[m]) > off1) {
267 m++;
268 break;
269 }
270 off2 += rangelen(set2[m]);
271 }
272 m--;
273 r = set2[m].start + (off1 - off2) / set2[m].quant;
274 }
275 goto write;
276 }
277 }
278 if (check(r, set1checks)) {
279 if (cflag)
280 goto write;
281 if (dflag)
282 goto read;
283 if (set2checks) {
284 if (set2checks == ISLOWERBIT)
285 r = tolowerrune(r);
286 else
287 r = toupperrune(r);
288 } else {
289 r = set2[set2ranges - 1].end;
290 }
291 goto write;
292 }
293 if (!dflag && cflag) {
294 if (set2checks) {
295 if (set2checks == ISLOWERBIT)
296 r = tolowerrune(r);
297 else
298 r = toupperrune(r);
299 } else {
300 r = set2[set2ranges - 1].end;
301 }
302 goto write;
303 }
304 if (dflag && cflag)
305 goto read;
306write:
307 if (argc == 1 && sflag && r == lastrune) {
308 if (check(r, set1checks))
309 goto read;
310 for (i = 0; i < set1ranges; i++) {
311 if (set1[i].start <= r && r <= set1[i].end)
312 goto read;
313 }
314 }
315 if (argc == 2 && sflag && r == lastrune) {
316 if (set2checks && check(r, set2checks))
317 goto read;
318 for (i = 0; i < set2ranges; i++) {
319 if (set2[i].start <= r && r <= set2[i].end)
320 goto read;
321 }
322 }
323 efputrune(&r, stdout, "<stdout>");
324 lastrune = r;
325 goto read;
326}