#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); } }