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