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