|
|
1.1 root 1: /*
2: * Tr translates characters from the standard input to the standard output.
3: * It usage is as follows:
4: * tr [-c] [-d] [-s] [string1] [string2]
5: * In the two strings, in addition to normal characters, one can include
6: * `x-y' all characters between `x' and `y'
7: * \r ascii carriage return
8: * \n ascii line feed
9: * \b ascii back space
10: * \t ascii horizontal tab
11: * \f ascii form feed
12: * \d, \dd or \ddd
13: * character with ascii code d (or dd or ddd) in octal
14: * \x `x' for any `x' not listed above
15: * In the absence of any options, tr simply converts any character appearing
16: * in string1 to the character in the same position in string2. If string2
17: * is shorter then string1, then it is extended by replicating the last
18: * character.
19: * The `-c' option simply compliments string1 with respect to the character
20: * set. The resulting set of characters is used in increasing order.
21: * The `-d' option causes characters in string1 to be deleted rather than
22: * being translated.
23: * The `-s' option causes characters in string2 (or string1 if there is no
24: * string2 and the -d option is not specified) to be compressed on output.
25: * This means that multiple occurances of the same character are compressed
26: * to one occurance.
27: */
28: #include <stdio.h>
29: #include <sys/mdata.h>
30:
31:
32: #define bool char /* boolean type */
33: #define not ! /* logical negation operator */
34: #define and && /* logical conjunction */
35: #define or || /* logical disjunction */
36: #define TRUE (0 == 0)
37: #define FALSE (not TRUE)
38: #define EOS '\0' /* end-of-string char */
39: #define CSIZE (1 << NBCHAR) /* character set size */
40: #define MAXDIG ((NBCHAR+2) / 3) /* max digits in character constant */
41:
42:
43: /*
44: * List is used to hold a character list from which characters are begin
45: * extrated. It allows expansion of character ranges, backslash-protected
46: * characters and character ranges.
47: */
48: typedef struct list {
49: unsigned char *l_next, /* next character from string */
50: *l_start; /* start of string */
51: bool l_inr; /* iff we are in a-b range */
52: int l_rnext, /* next to give if inrange */
53: l_rhi; /* maximum to give if inrange */
54: } list;
55:
56:
57: bool cflag, /* compliment string 1 */
58: dflag, /* delete chars in string 1 */
59: sflag, /* remove duplicates in string 2 */
60: delete[CSIZE], /* set of chars to delete on input */
61: squeeze[CSIZE]; /* set of chars to squeeze on output */
62: unsigned char map[CSIZE]; /* map to apply to characters */
63:
64: int nextchar(); /* get next char of list */
65: void die(), /* print error message and exit */
66: usage(), /* print usage message and exit */
67: scan(), /* actually do copy */
68: startlist(), /* start list from string */
69: makeset(), /* make set of chars in list */
70: maketrans(); /* make transformation table */
71: unsigned char *cstr(); /* compliment character list */
72: char *alloc(), /* unfailable malloc */
73: *ralloc(); /* unfailable realloc */
74:
75:
76: int
77: main(argc, argv)
78: int argc;
79: register char *argv[];
80: {
81: register unsigned char *str1,
82: *str2;
83:
84: for (str1=*++argv; str1 != NULL && *str1 == '-'; str1=*++argv)
85: while (*++str1 != EOS)
86: switch (*str1) {
87: case 'c':
88: cflag = TRUE;
89: break;
90: case 'd':
91: dflag = TRUE;
92: break;
93: case 's':
94: sflag = TRUE;
95: break;
96: default:
97: usage();
98: }
99: if (str1 == NULL)
100: usage();
101: if (cflag)
102: str1 = cstr(str1);
103: str2 = *++argv;
104: if (str2 != NULL && *++argv != NULL)
105: usage();
106: if (dflag)
107: if (sflag) { /* -d and -s */
108: if (str2 == NULL)
109: usage();
110: makeset(str1, delete);
111: makeset(str2, squeeze);
112: maketrans(str1, str2, map);
113: } else { /* -d and no -s */
114: if (str2 != NULL)
115: usage();
116: makeset(str1, delete);
117: maketrans("", "", map);
118: }
119: else
120: if (sflag)
121: if (str2 == NULL) { /* -s, no -d and one string */
122: maketrans("", "", map);
123: makeset(str1, squeeze);
124: } else { /* -s, no -d and two strings */
125: maketrans(str1, str2, map);
126: makeset(str2, squeeze);
127: }
128: else { /* no -s, no -d */
129: if (str2 == NULL)
130: usage();
131: maketrans(str1, str2, map);
132: }
133: scan();
134: return (0);
135: }
136:
137:
138: /*
139: * Die simply sends an error message to stderr and exits.
140: */
141: void
142: die(str)
143: char *str;
144: {
145: fprintf(stderr, "%r\n", &str);
146: exit(1);
147: }
148:
149:
150: /*
151: * Usage gives a usage error message and exits.
152: */
153: void
154: usage()
155: {
156: die("usage: tr [-cds] [from_list [to_list]]");
157: }
158:
159:
160: /*
161: * Scan does the acutal copying. It deletes any input characters in
162: * the set `delete'. It then transforms input characters to output
163: * characters useing the mapping `map'. Finally, it changes multiple
164: * occurances of output characters in the set `squeeze' to single
165: * occurances.
166: */
167: void
168: scan()
169: {
170: register int ch,
171: lastch;
172:
173: lastch = EOF;
174: while ((ch=getchar()) != EOF) {
175: if (delete[ch])
176: continue;
177: ch = map[ch];
178: if (ch == lastch && squeeze[ch])
179: continue;
180: putchar(ch);
181: lastch = ch;
182: }
183: }
184:
185:
186: /*
187: * Makeset sets the array of bools `set' (indexed by chars) such that
188: * the i'th entry is TRUE iff character i appears in the character
189: * list `str'.
190: */
191: void
192: makeset(str, set)
193: unsigned char *str;
194: register bool *set;
195: {
196: register bool *rp;
197: register int n;
198: list cl;
199:
200: for (rp=set, n=CSIZE; --n >= 0;)
201: *rp++ = FALSE;
202: startlist(&cl, str);
203: while ((n=nextchar(&cl)) != EOF)
204: set[n] = TRUE;
205: return;
206: }
207:
208:
209: /*
210: * Maketrans sets the array of chars `map' (indexed by chars) to
211: * the mapping which converts all characters in the character list
212: * `str1' to the corresponding character in the character list `str2'.
213: * If `str2' is short, the last character is duplicated.
214: */
215: void
216: maketrans(str1, str2, map)
217: unsigned char *str1,
218: *str2,
219: *map;
220: {
221: register unsigned char *rp;
222: register int n,
223: m;
224: list l1,
225: l2;
226: bool extra;
227:
228: startlist(&l1, str1);
229: startlist(&l2, str2);
230: for (rp=map, n=0; n < CSIZE; ++n)
231: *rp++ = n;
232: rp = map;
233: n = nextchar(&l2);
234: if (n == EOF) {
235: if (nextchar(&l1) != EOF)
236: die("Second string empty");
237: return;
238: }
239: for (extra=FALSE; (m=nextchar(&l1)) != EOF;) {
240: rp[m] = n;
241: if (not extra) {
242: m = nextchar(&l2);
243: if (m != EOF)
244: n = m;
245: else
246: extra = TRUE;
247: }
248: }
249: if (not extra && nextchar(&l2) != EOF)
250: die("Extra characters in second string");
251: return;
252: }
253:
254:
255: /*
256: * Cstr returns a string which is the compliment of the string `str'.
257: */
258: unsigned char *
259: cstr(str)
260: unsigned char *str;
261: {
262: register unsigned char *rp;
263: register bool *sp;
264: register int n;
265: bool set[CSIZE];
266: unsigned char *res;
267:
268: makeset(str, set);
269: res = rp = (unsigned char *)alloc(3 + 2 + CSIZE + 1);
270: sp = set;
271: if (not *sp++) { /* handle EOS specially */
272: *rp++ = '\\';
273: *rp++ = '0';
274: *rp++ = '0';
275: *rp++ = '0';
276: }
277: for (n=0; ++n < CSIZE;)
278: if (not *sp++) {
279: if (n == '\\' || n == '-')
280: *rp++ = '\\';
281: *rp++ = n;
282: }
283: *rp++ = EOS;
284: return ((unsigned char *)ralloc(res, rp - res));
285: }
286:
287:
288: /*
289: * Startlist sets the list pointed to by `lp' to the string `str'.
290: */
291: void
292: startlist(lp, str)
293: register list *lp;
294: unsigned char *str;
295: {
296: lp->l_next = lp->l_start = str;
297: lp->l_inr = FALSE;
298: }
299:
300:
301: /*
302: * Nextchar returns the next character from the list pointed to by
303: * `lp'. This includes backslash protection and character ranges.
304: * When there are no more characters, it returns EOF.
305: */
306: int
307: nextchar(lp)
308: register list *lp;
309: {
310: register int res;
311: int getprot();
312:
313: if (lp->l_inr) {
314: res = lp->l_rnext;
315: lp->l_inr = ++lp->l_rnext <= lp->l_rhi;
316: return (res);
317: }
318: res = *lp->l_next++;
319: if (res == EOS)
320: return (EOF);
321: if (res == '\\')
322: res = getprot(lp);
323: if (*lp->l_next != '-')
324: return (res);
325: ++lp->l_next;
326: lp->l_rnext = res;
327: res = *lp->l_next++;
328: if (res == EOS)
329: die("Unexpected end of character list in `%s'", lp->l_start);
330: if (res == '\\')
331: res = getprot(lp);
332: if (lp->l_rnext > res)
333: die("Bad character range in `%s'", lp->l_start);
334: lp->l_rhi = res;
335: res = lp->l_rnext;
336: lp->l_inr = ++lp->l_rnext <= lp->l_rhi;
337: return (res);
338: }
339:
340:
341: /*
342: * Getprot is used to get a backslash protected character from the
343: * character list pointed to by `lp'.
344: */
345: int
346: getprot(lp)
347: register list *lp;
348: {
349: register unsigned char ch;
350: register unsigned n;
351: int m;
352:
353: ch = *lp->l_next++;
354: switch (ch) {
355: case EOS:
356: die("Unexpected end of character list in `%s'", lp->l_start);
357: case 'r':
358: return ('\r');
359: case 'b':
360: return ('\b');
361: case 't':
362: return ('\t');
363: case 'n':
364: return ('\n');
365: case 'f':
366: return ('\f');
367: default:
368: break;
369: }
370: if ('0' > ch || ch > '7')
371: return (ch);
372: n = ch - '0';
373: ch = *lp->l_next;
374: for (m=MAXDIG; --m > 0 && '0' <= ch && ch <= '7'; ch=*++lp->l_next)
375: n = n*8 + ch-'0';
376: if (n >= CSIZE)
377: die("Illegal character constant in `%s'", lp->l_start);
378: return (n);
379: }
380:
381:
382: /*
383: * Alloc is simply an interface to malloc which does not return if
384: * there is no space.
385: */
386: char *
387: alloc(len)
388: unsigned len;
389: {
390: register char *res;
391: extern char *malloc();
392:
393: res = malloc(len);
394: if (res == NULL)
395: die("Out of space");
396: return (res);
397: }
398:
399:
400: /*
401: * Ralloc is simply an interface to realloc which does not return if
402: * there is no space.
403: */
404: char *
405: ralloc(cp, len)
406: char *cp;
407: unsigned len;
408: {
409: register char *res;
410: extern char *realloc();
411:
412: res = realloc(cp, len);
413: if (res == NULL)
414: die("Out of space");
415: return (res);
416: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.