xref: /minix3/usr.bin/tr/tr.c (revision 84d9c625bfea59e274550651111ae9edfdc40fbd)
1 /*	$NetBSD: tr.c,v 1.20 2013/08/11 01:54:35 dholland Exp $	*/
2 
3 /*
4  * Copyright (c) 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1988, 1993\
35  The Regents of the University of California.  All rights reserved.");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)tr.c	8.2 (Berkeley) 5/4/95";
41 #endif
42 __RCSID("$NetBSD: tr.c,v 1.20 2013/08/11 01:54:35 dholland Exp $");
43 #endif /* not lint */
44 
45 #include <sys/types.h>
46 
47 #include <err.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 
53 #include "extern.h"
54 
55 static int string1[NCHARS], string2[NCHARS];
56 
57 static void setup(int *, const char *, int, int);
58 __dead static void usage(void);
59 
60 int
main(int argc,char ** argv)61 main(int argc, char **argv)
62 {
63 	int ch, ch2, lastch;
64 	int cflag, dflag, sflag, isstring2;
65 	STR *s1, *s2;
66 
67 	cflag = dflag = sflag = 0;
68 	while ((ch = getopt(argc, argv, "cds")) != -1)
69 		switch (ch) {
70 		case 'c':
71 			cflag = 1;
72 			break;
73 		case 'd':
74 			dflag = 1;
75 			break;
76 		case 's':
77 			sflag = 1;
78 			break;
79 		case '?':
80 		default:
81 			usage();
82 		}
83 	argc -= optind;
84 	argv += optind;
85 
86 	switch(argc) {
87 	case 0:
88 	default:
89 		usage();
90 		/* NOTREACHED */
91 	case 1:
92 		isstring2 = 0;
93 		break;
94 	case 2:
95 		isstring2 = 1;
96 		break;
97 	}
98 
99 	/*
100 	 * tr -ds [-c] string1 string2
101 	 * Delete all characters (or complemented characters) in string1.
102 	 * Squeeze all characters in string2.
103 	 */
104 	if (dflag && sflag) {
105 		if (!isstring2)
106 			usage();
107 
108 		setup(string1, argv[0], 1, cflag);
109 		setup(string2, argv[1], 2, 0);
110 
111 		for (lastch = OOBCH; (ch = getchar()) != EOF; )
112 			if (!string1[ch] && (!string2[ch] || lastch != ch)) {
113 				lastch = ch;
114 				(void)putchar(ch);
115 			}
116 		exit(0);
117 	}
118 
119 	/*
120 	 * tr -d [-c] string1
121 	 * Delete all characters (or complemented characters) in string1.
122 	 */
123 	if (dflag) {
124 		if (isstring2)
125 			usage();
126 
127 		setup(string1, argv[0], 1, cflag);
128 
129 		while ((ch = getchar()) != EOF)
130 			if (!string1[ch])
131 				(void)putchar(ch);
132 		exit(0);
133 	}
134 
135 	/*
136 	 * tr -s [-c] string1
137 	 * Squeeze all characters (or complemented characters) in string1.
138 	 */
139 	if (sflag && !isstring2) {
140 		setup(string1, argv[0], 1, cflag);
141 
142 		for (lastch = OOBCH; (ch = getchar()) != EOF;)
143 			if (!string1[ch] || lastch != ch) {
144 				lastch = ch;
145 				(void)putchar(ch);
146 			}
147 		exit(0);
148 	}
149 
150 	/*
151 	 * tr [-cs] string1 string2
152 	 * Replace all characters (or complemented characters) in string1 with
153 	 * the character in the same position in string2.  If the -s option is
154 	 * specified, squeeze all the characters in string2.
155 	 */
156 	if (!isstring2)
157 		usage();
158 
159 	/*
160 	 * The first and second strings need to be matched up. This
161 	 * means that if we are doing -c, we need to scan the first
162 	 * string in advance, complement it, and match *that* against
163 	 * the second string; otherwise we need to scan them together.
164 	 */
165 
166 	if (cflag) {
167 		/*
168 		 * Scan string 1 and complement it. After this,
169 		 * string1[] contains 0 for chars to leave alone and 1
170 		 * for chars to translate.
171 		 */
172 		setup(string1, argv[0], 1, cflag);
173 		s1 = NULL; /* for safety */
174 		/* we will use ch to iterate over string1, so start it */
175 		ch = -1;
176 	} else {
177 		/* Create the scanner for string 1. */
178 		s1 = str_create(1, argv[0]);
179 		for (ch = 0; ch < NCHARS; ch++) {
180 			string1[ch] = ch;
181 		}
182 	}
183 	/* Create the scanner for string 2. */
184 	s2 = str_create(2, argv[1]);
185 
186 	/* Read the first char of string 2 first to make sure there is one. */
187 	if (!next(s2, &ch2))
188 		errx(1, "empty string2");
189 
190 	/*
191 	 * Loop over the chars from string 1. After this loop string1[]
192 	 * is a mapping from input to output chars.
193 	 */
194 	while (1) {
195 		if (cflag) {
196 			/*
197 			 * Try each character in order. For characters we
198 			 * skip over because we aren't translating them,
199 			 * set the translation to the identity.
200 			 */
201 			ch++;
202 			while (ch < NCHARS && string1[ch] == 0) {
203 				if (string1[ch] == 0) {
204 					string1[ch] = ch;
205 				}
206 				ch++;
207 			}
208 			if (ch == NCHARS) {
209 				break;
210 			}
211 		}
212 		else {
213 			/* Get the next character from string 1. */
214 			if (!next(s1, &ch)) {
215 				break;
216 			}
217 		}
218 
219 		/* Set the translation to the character from string 2. */
220 		string1[ch] = ch2;
221 
222 		/* Note the characters to squeeze in string2[]. */
223 		if (sflag) {
224 			string2[ch2] = 1;
225 		}
226 
227 		/*
228 		 * Get the next character from string 2. If it runs
229 		 * out, this will keep returning the last character
230 		 * over and over again.
231 		 */
232 		(void)next(s2, &ch2);
233 	}
234 
235 	/*
236 	 * Now do it.
237 	 */
238 
239 	if (sflag)
240 		for (lastch = OOBCH; (ch = getchar()) != EOF;) {
241 			ch = string1[ch];
242 			if (!string2[ch] || lastch != ch) {
243 				lastch = ch;
244 				(void)putchar(ch);
245 			}
246 		}
247 	else
248 		while ((ch = getchar()) != EOF)
249 			(void)putchar(string1[ch]);
250 
251 	/* Clean up and exit. */
252 	if (s1 != NULL) {
253 		str_destroy(s1);
254 	}
255 	str_destroy(s2);
256 	exit (0);
257 }
258 
259 static void
setup(int * string,const char * arg,int whichstring,int cflag)260 setup(int *string, const char *arg, int whichstring, int cflag)
261 {
262 	int cnt, *p;
263 	int ch;
264 	STR *str;
265 
266 	str = str_create(whichstring, arg);
267 	while (next(str, &ch))
268 		string[ch] = 1;
269 	if (cflag)
270 		for (p = string, cnt = NCHARS; cnt--; ++p)
271 			*p = !*p;
272 	str_destroy(str);
273 }
274 
275 static void
usage(void)276 usage(void)
277 {
278 	(void)fprintf(stderr, "usage: tr [-cs] string1 string2\n");
279 	(void)fprintf(stderr, "       tr [-c] -d string1\n");
280 	(void)fprintf(stderr, "       tr [-c] -s string1\n");
281 	(void)fprintf(stderr, "       tr [-c] -ds string1 string2\n");
282 	exit(1);
283 }
284