1/*	$NetBSD: tr.c,v 1.19 2013/08/11 00:49:15 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
40static char sccsid[] = "@(#)tr.c	8.2 (Berkeley) 5/4/95";
41#endif
42__RCSID("$NetBSD: tr.c,v 1.19 2013/08/11 00:49:15 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
55static int string1[NCHARS], string2[NCHARS];
56
57static void setup(int *, const char *, int, int);
58__dead static void usage(void);
59
60int
61main(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
259static void
260setup(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
275static void
276usage(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