forget difference between big and small commands - obsolete with vm.
[minix.git] / commands / simple / tr.c
blob3f013418aef5ce99ec2c3108ea44625971fd1153
1 /* tr - translate characters Author: Michiel Huisjes */
2 /* Usage: tr [-cds] [string1 [string2]]
3 * c: take complement of string1
4 * d: delete input characters coded string1
5 * s: squeeze multiple output characters of string2 into one character
6 */
8 #define BUFFER_SIZE 1024
9 #define ASCII 0377
11 typedef char BOOL;
12 #define TRUE 1
13 #define FALSE 0
15 #define NIL_PTR ((char *) 0)
17 BOOL com_fl, del_fl, sq_fl;
19 unsigned char output[BUFFER_SIZE], input[BUFFER_SIZE];
20 unsigned char vector[ASCII + 1];
21 BOOL invec[ASCII + 1], outvec[ASCII + 1];
23 short in_index, out_index;
25 #include <sys/types.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <unistd.h>
30 _PROTOTYPE(int main, (int argc, char **argv));
31 _PROTOTYPE(void convert, (void));
32 _PROTOTYPE(void map, (unsigned char *string1, unsigned char *string2));
33 _PROTOTYPE(void expand, (char *arg, unsigned char *buffer));
34 _PROTOTYPE(void complement, (unsigned char *buffer));
36 int main(argc, argv)
37 int argc;
38 char *argv[];
40 register unsigned char *ptr;
41 int index = 1;
42 short i;
44 if (argc > 1 && argv[index][0] == '-') {
45 for (ptr = (unsigned char *) &argv[index][1]; *ptr; ptr++) {
46 switch (*ptr) {
47 case 'c': com_fl = TRUE; break;
48 case 'd': del_fl = TRUE; break;
49 case 's': sq_fl = TRUE; break;
50 default:
51 write(2,"Usage: tr [-cds] [string1 [string2]].\n", 38);
52 exit(1);
55 index++;
57 for (i = 0; i <= ASCII; i++) {
58 vector[i] = i;
59 invec[i] = outvec[i] = FALSE;
62 if (argv[index] != NIL_PTR) {
63 expand(argv[index++], input);
64 if (com_fl) complement(input);
65 if (argv[index] != NIL_PTR) expand(argv[index], output);
66 if (argv[index] != NIL_PTR) map(input, output);
67 for (ptr = input; *ptr; ptr++) invec[*ptr] = TRUE;
68 for (ptr = output; *ptr; ptr++) outvec[*ptr] = TRUE;
70 convert();
71 return(0);
74 void convert()
76 short read_chars = 0;
77 short c, coded;
78 short last = -1;
80 for (;;) {
81 if (in_index == read_chars) {
82 if ((read_chars = read(0, (char *)input, BUFFER_SIZE)) <= 0) {
83 if (write(1, (char *)output, out_index) != out_index)
84 write(2, "Bad write\n", 10);
85 exit(0);
87 in_index = 0;
89 c = input[in_index++];
90 coded = vector[c];
91 if (del_fl && invec[c]) continue;
92 if (sq_fl && last == coded && outvec[coded]) continue;
93 output[out_index++] = last = coded;
94 if (out_index == BUFFER_SIZE) {
95 if (write(1, (char *)output, out_index) != out_index) {
96 write(2, "Bad write\n", 10);
97 exit(1);
99 out_index = 0;
103 /* NOTREACHED */
106 void map(string1, string2)
107 register unsigned char *string1, *string2;
109 unsigned char last;
111 while (*string1) {
112 if (*string2 == '\0')
113 vector[*string1] = last;
114 else
115 vector[*string1] = last = *string2++;
116 string1++;
120 static int starts_with(const char *s1, const char *s2)
122 while (*s1 && *s1 == *s2)
124 s1++;
125 s2++;
127 return *s1 == 0;
131 * character classes from
132 * http://www.opengroup.org/onlinepubs/009695399/utilities/tr.html
133 * missing: blank, punct, cntrl, graph, print, space
135 static struct
137 const char *keyword;
138 char first;
139 char last;
140 } expand_keywords[] = {
141 { "[:alnum:]", 'A', 'Z' },
142 { "[:alnum:]", 'a', 'z' },
143 { "[:alnum:]", '0', '9' },
144 { "[:alpha:]", 'A', 'Z' },
145 { "[:alpha:]", 'a', 'z' },
146 { "[:digit:]", '0', '9' },
147 { "[:lower:]", 'a', 'z' },
148 { "[:upper:]", 'A', 'Z' },
149 { "[:xdigit:]", '0', '9' },
150 { "[:xdigit:]", 'A', 'F' },
151 { "[:xdigit:]", 'a', 'f' }
154 #define LENGTH(a) ((sizeof((a))) / (sizeof((a)[0])))
156 void expand(arg, buffer)
157 register char *arg;
158 register unsigned char *buffer;
160 int i, ac, keyword_index;
162 while (*arg) {
163 if (*arg == '\\') {
164 arg++;
165 i = ac = 0;
166 if (*arg >= '0' && *arg <= '7') {
167 do {
168 ac = (ac << 3) + *arg++ - '0';
169 i++;
170 } while (i < 4 && *arg >= '0' && *arg <= '7');
171 *buffer++ = ac;
172 } else if (*arg != '\0')
173 *buffer++ = *arg++;
174 } else if (*arg == '[') {
175 /* does one of the keywords match? */
176 keyword_index = -1;
177 for (i = 0; i < LENGTH(expand_keywords); i++)
178 if (starts_with(expand_keywords[i].keyword, arg))
180 /* we have a match, remember and expand */
181 keyword_index = i;
182 ac = expand_keywords[i].first;
183 while (ac <= expand_keywords[i].last)
184 *buffer++ = ac++;
187 /* skip keyword if found, otherwise expand range */
188 if (keyword_index >= 0)
189 arg += strlen(expand_keywords[keyword_index].keyword);
190 else
192 /* expand range */
193 arg++;
194 i = *arg++;
195 if (*arg++ != '-') {
196 *buffer++ = '[';
197 arg -= 2;
198 continue;
200 ac = *arg++;
201 while (i <= ac) *buffer++ = i++;
202 arg++; /* Skip ']' */
204 } else
205 *buffer++ = *arg++;
209 void complement(buffer)
210 unsigned char *buffer;
212 register unsigned char *ptr;
213 register short i, index;
214 unsigned char conv[ASCII + 2];
216 index = 0;
217 for (i = 1; i <= ASCII; i++) {
218 for (ptr = buffer; *ptr; ptr++)
219 if (*ptr == i) break;
220 if (*ptr == '\0') conv[index++] = i & ASCII;
222 conv[index] = '\0';
223 strcpy((char *)buffer, (char *)conv);