1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
#include "gbos.h"
/* tr [-d] SET1 [SET2]: translate or delete characters, stdin to stdout.
*
* ls | tr a-z A-Z ranges work
* cat f | tr -d aeiou -d deletes instead of mapping
* echo a b | tr \\s \\n \\s \\t \\n name characters the shell cannot quote
*
* ASCII only (a 128-entry map on the stack), and like tr(1) a short SET2 is
* padded with its last character. */
#define SETMAX 64
/* The shell has no quoting, so a space or a tab cannot be typed as an
argument: "-d ' '" arrives as three tokens. Escapes are how you name them
here - \s space, \t tab, \n newline, \\ backslash. */
static char unesc1(const char *s, unsigned char *used) {
*used = 1;
if (s[0] != '\\' || !s[1]) return s[0];
*used = 2;
switch (s[1]) {
case 's': return ' ';
case 't': return '\t';
case 'n': return '\n';
case 'r': return '\r';
default: return s[1];
}
}
/* expand "a-z0-9_" into the characters it names; returns how many */
static unsigned char expand(const char *s, char *out, unsigned char max) {
unsigned char n = 0, i = 0;
char a, b;
while (s[i] && n < max) {
unsigned char ua, ub;
a = unesc1(s + i, &ua);
if (s[i + ua] == '-' && s[i + ua + 1]) { /* a range */
b = unesc1(s + i + ua + 1, &ub);
while (a <= b && n < max) out[n++] = a++;
i += ua + 1 + ub;
} else {
out[n++] = a;
i += ua;
}
}
return n;
}
void main(void) {
char *argv[8];
char s1[SETMAX], s2[SETMAX];
unsigned char map[128];
unsigned char argc = argv_parse(argv, 8);
unsigned char del = hasflag(argv, argc, 'd');
unsigned char i, n1 = 0, n2 = 0, k;
char *set1 = 0, *set2 = 0;
char c;
for (i = 0; i < argc; i++) {
/* a bare "-" is a SET, not a flag: `tr . -` maps dots to dashes */
if (argv[i][0] == '-' && argv[i][1]) continue;
if (!set1) set1 = argv[i];
else if (!set2) set2 = argv[i];
}
if (!set1 || (!del && !set2)) {
puts("usage: tr [-d] SET1 [SET2]");
nl();
sexit(2);
}
n1 = expand(set1, s1, SETMAX);
if (set2) n2 = expand(set2, s2, SETMAX);
for (i = 0; i < 128; i++) map[i] = i; /* identity, then patch */
for (k = 0; k < n1; k++) {
unsigned char from = (unsigned char)s1[k] & 127;
if (del) map[from] = 0; /* 0 = drop it */
else map[from] = (unsigned char)s2[k < n2 ? k : n2 - 1];
}
for (;;) {
c = readc();
if (c == EOF) break;
if ((unsigned char)c < 128) {
unsigned char o = map[(unsigned char)c];
if (o) putc((char)o);
} else putc(c);
}
}
|