xref: /original-bsd/usr.bin/tr/tr.c (revision 6f738a42)
1 /*
2  * Copyright (c) 1988 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1988 The Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)tr.c	4.3 (Berkeley) 09/03/88";
26 #endif /* not lint */
27 
28 #include <sys/types.h>
29 #include <stdio.h>
30 #include <ctype.h>
31 
32 #define	NCHARS	256				/* size of u_char */
33 #define	OOBCH	257				/* out of band value */
34 
35 typedef struct {
36 	char *str;
37 	int lastch, endrange;
38 	enum { NORM, INRANGE, EOS } state;
39 } STR;
40 
41 main(argc, argv)
42 	int argc;
43 	char **argv;
44 {
45 	extern int optind;
46 	STR s1, s2;
47 	register int ch, indx, lastch;
48 	int cflag, dflag, sflag;
49 	u_char *tp, tab[NCHARS], squeeze[NCHARS];
50 
51 	cflag = dflag = sflag = 0;
52 	while ((ch = getopt(argc, argv, "cds")) != EOF)
53 		switch((char)ch) {
54 		case 'c':
55 			cflag = 1;
56 			break;
57 		case 'd':
58 			dflag = 1;
59 			break;
60 		case 's':
61 			sflag = 1;
62 			break;
63 		case '?':
64 		default:
65 			fprintf(stderr,
66 			    "usage: tr [-cds] [string1 [string2]]\n");
67 			exit(1);
68 		}
69 	argc -= optind;
70 	argv += optind;
71 
72 	/*
73 	 * the original tr was amazingly tolerant of the command line.
74 	 * Neither -c or -s have any effect unless there are two strings.
75 	 * Extra arguments are silently ignored.  Bag this noise, they
76 	 * should all be errors.
77 	 */
78 	if (argc < 2 && !dflag) {
79 		while ((ch = getchar()) != EOF)
80 			putchar(ch);
81 		exit(0);
82 	}
83 
84 	bzero(tab, NCHARS);
85 	if (sflag) {
86 		s1.str = argv[1];
87 		s1.state = NORM;
88 		s1.lastch = OOBCH;
89 		while (next(&s1))
90 			squeeze[s1.lastch] = 1;
91 	}
92 	if (dflag) {
93 		s1.str = argv[0];
94 		s1.state = NORM;
95 		s1.lastch = OOBCH;
96 		while (next(&s1))
97 			tab[s1.lastch] = 1;
98 		if (cflag)
99 			for (tp = tab, indx = 0; indx < NCHARS; ++tp, ++indx)
100 				*tp = !*tp;
101 		if (sflag)
102 			for (lastch = OOBCH; (ch = getchar()) != EOF;) {
103 				if (tab[ch] || (squeeze[ch] && lastch == ch))
104 					continue;
105 				lastch = ch;
106 				putchar(ch);
107 			}
108 		else
109 			while ((ch = getchar()) != EOF)
110 				if (!tab[ch])
111 					putchar(ch);
112 	} else {
113 		s1.str = argv[0];
114 		s2.str = argv[1];
115 		s1.state = s2.state = NORM;
116 		s1.lastch = s2.lastch = OOBCH;
117 		if (cflag) {
118 			/*
119 			 * if cflag is set, tr just pretends it only got one
120 			 * character in string2.  As reasonable as anything
121 			 * else.  Should really be an error.
122 			 */
123 			while (next(&s2));
124 			lastch = s2.lastch;
125 			for (tp = tab, indx = 0; indx < NCHARS; ++tp, ++indx)
126 				*tp = lastch;
127 			while (next(&s1))
128 				tab[s1.lastch] = s1.lastch;
129 		} else {
130 			for (tp = tab, indx = 0; indx < NCHARS; ++tp, ++indx)
131 				*tp = indx;
132 			while (next(&s1)) {
133 				(void)next(&s2);
134 				tab[s1.lastch] = s2.lastch;
135 			}
136 		}
137 		if (sflag)
138 			for (lastch = OOBCH; (ch = getchar()) != EOF;) {
139 				ch = tab[ch];
140 				if (squeeze[ch] && lastch == ch)
141 					continue;
142 				lastch = ch;
143 				putchar(ch);
144 			}
145 		else
146 			while ((ch = getchar()) != EOF)
147 				putchar((int)tab[ch]);
148 	}
149 	exit(0);
150 }
151 
152 next(s)
153 	STR *s;
154 {
155 	register int ch;
156 	int cnt, val;
157 
158 	if (s->state == EOS)
159 		return(0);
160 	if (s->state == INRANGE) {
161 		if (++s->lastch == s->endrange)
162 			s->state = NORM;
163 		return(1);
164 	}
165 	if (!(ch = *s->str++)) {
166 		s->state = EOS;
167 		return(0);
168 	}
169 	if (ch == '\\') {			/* escape; \nnn is octal # */
170 		for (val = cnt = 0; isascii(ch = *s->str) && isdigit(ch)
171 		    && cnt++ < 3; ++s->str)
172 			val = val * 8 + ch - '0';
173 		s->lastch = cnt ? val : ch;
174 		return(1);
175 	}
176 	if (ch == '-') {			/* ranges */
177 		if (s->lastch == OOBCH) {	/* "-a" */
178 			s->lastch = '-';
179 			return(1);
180 		}
181 		s->endrange = ch = *s->str;
182 		if (!ch) {			/* "a-" */
183 			s->lastch = '-';
184 			return(1);
185 		}
186 		if (s->lastch > ch) { 		/* "z-a" */
187 			s->lastch = '-';
188 			return(1);
189 		}
190 		++s->str;
191 		if (s->lastch == ch)		/* "a-a" */
192 			return(next(s));
193 		s->state = INRANGE;		/* "a-z" */
194 		++s->lastch;
195 		return(1);
196 	}
197 	s->lastch = ch;
198 	return(1);
199 }
200