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