1 /*	$OpenBSD: tr.c,v 1.13 2004/09/15 22:12:19 deraadt Exp $	*/
2 /*	$NetBSD: tr.c,v 1.5 1995/08/31 22:13:48 jtc Exp $	*/
3 
4 /*
5  * Copyright (c) 2007, 2008, 2009, 2014
6  *	Thorsten Glaser <tg@mirbsd.org>
7  * Copyright (c) 1988, 1993
8  *	The Regents of the University of California.  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/types.h>
36 
37 #include <unistd.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <stdbool.h>
41 #include <mbfun.h>
42 #include <wctype.h>
43 
44 #include "extern.h"
45 
46 __COPYRIGHT("@(#) Copyright (c) 1988, 1993\n\
47 	The Regents of the University of California.  All rights reserved.\n");
48 __SCCSID("@(#)tr.c	8.2 (Berkeley) 5/4/95");
49 __RCSID("$MirOS: src/usr.bin/tr/tr.c,v 1.7 2014/02/20 01:16:27 tg Exp $");
50 
51 static wchar_t string1[NCHARS], string2[NCHARS];
52 
53 STR s1 = {
54 	.which = STRING1,
55 	.state = NORMAL,
56 	.cnt = 0,
57 	.lastch = OOBCH,
58 	.equiv = { 0, OOBCH },
59 	.set = NULL,
60 	.use_wctrans = false,
61 	.str = NULL
62 };
63 STR s2 = {
64 	.which = STRING2,
65 	.state = NORMAL,
66 	.cnt = 0,
67 	.lastch = OOBCH,
68 	.equiv = { 0, OOBCH },
69 	.set = NULL,
70 	.use_wctrans = false,
71 	.str = NULL
72 };
73 
74 static void setup(wchar_t *, char *, STR *, bool);
75 static void usage(void) __dead;
76 
77 #define getwcf() __extension__({				\
78 	wint_t getwcf_c;					\
79 								\
80 	if ((getwcf_c = getwchar()) == WEOF && ferror(stdin) &&	\
81 	    errno == EILSEQ) {					\
82 		clearerr(stdin);				\
83 		getwcf_c = 0xFFFD;				\
84 	}							\
85 	(getwcf_c);						\
86 })
87 
88 int
main(int argc,char * argv[])89 main(int argc, char *argv[])
90 {
91 	wint_t ch;
92 	wchar_t cnt, lastch, *p;
93 	int ich;
94 	bool cflag, dflag, sflag, isstring2;
95 
96 #ifndef __MirBSD__
97 	setlocale(LC_ALL, "");
98 #endif
99 
100 	for (lastch = 0; lastch < NCHARS; ++lastch)
101 		string1[lastch] = lastch;
102 
103 	cflag = dflag = sflag = false;
104 	while ((ich = getopt(argc, argv, "cds")) != -1)
105 		switch (ich) {
106 		case 'c':
107 			cflag = true;
108 			break;
109 		case 'd':
110 			dflag = true;
111 			break;
112 		case 's':
113 			sflag = true;
114 			break;
115 		case '?':
116 		default:
117 			usage();
118 		}
119 	argc -= optind;
120 	argv += optind;
121 
122 	switch(argc) {
123 	case 0:
124 	default:
125 		usage();
126 		/* NOTREACHED */
127 	case 1:
128 		isstring2 = false;
129 		break;
130 	case 2:
131 		isstring2 = true;
132 		break;
133 	}
134 
135 	/*
136 	 * tr -ds [-c] string1 string2
137 	 * Delete all characters (or complemented characters) in string1.
138 	 * Squeeze all characters in string2.
139 	 */
140 	if (dflag && sflag) {
141 		if (!isstring2)
142 			usage();
143 
144 		setup(string1, argv[0], &s1, cflag);
145 		setup(string2, argv[1], &s2, false);
146 
147 		for (lastch = OOBCH; (ch = getwcf()) != WEOF; )
148 			if (!string1[ch] && (!string2[ch] || lastch != ch)) {
149 				lastch = ch;
150 				putwchar(ch);
151 			}
152 		exit(0);
153 	}
154 
155 	/*
156 	 * tr -d [-c] string1
157 	 * Delete all characters (or complemented characters) in string1.
158 	 */
159 	if (dflag) {
160 		if (isstring2)
161 			usage();
162 
163 		setup(string1, argv[0], &s1, cflag);
164 
165 		while ((ch = getwcf()) != WEOF)
166 			if (!string1[ch])
167 				putwchar(ch);
168 		exit(0);
169 	}
170 
171 	/*
172 	 * tr -s [-c] string1
173 	 * Squeeze all characters (or complemented characters) in string1.
174 	 */
175 	if (sflag && !isstring2) {
176 		setup(string1, argv[0], &s1, cflag);
177 
178 		for (lastch = OOBCH; (ch = getwcf()) != WEOF; )
179 			if (!string1[ch] || lastch != ch) {
180 				lastch = ch;
181 				putwchar(ch);
182 			}
183 		exit(0);
184 	}
185 
186 	/*
187 	 * tr [-cs] string1 string2
188 	 * Replace all characters (or complemented characters) in string1 with
189 	 * the character in the same position in string2.  If the -s option is
190 	 * specified, squeeze all the characters in string2.
191 	 */
192 	if (!isstring2)
193 		usage();
194 
195 	s1.str = ambstowcs(argv[0]);
196 	s2.str = ambstowcs(argv[1]);
197 	if ((strstr(argv[0], "[:lower:]") && strstr(argv[1], "[:upper:]")) ||
198 	    (strstr(argv[1], "[:lower:]") && strstr(argv[0], "[:upper:]"))) {
199 		s1.use_wctrans = true;
200 		s2.use_wctrans = true;
201 	}
202 
203 	if (cflag)
204 		for (cnt = NCHARS, p = string1; cnt--;)
205 			*p++ = OOBCH;
206 
207 	if (!next(&s2))
208 		errx(1, "empty string2");
209 
210 	/* If string2 runs out of characters, use the last one specified. */
211 	ch = s2.lastch;
212 	if (sflag)
213 		while (next(&s1)) {
214 			string1[s1.lastch] = ch = s2.lastch;
215 			string2[ch] = 1;
216 			(void)next(&s2);
217 		}
218 	else
219 		while (next(&s1)) {
220 			string1[s1.lastch] = ch = s2.lastch;
221 			(void)next(&s2);
222 		}
223 
224 	if (cflag)
225 		for (cnt = 0, p = string1; cnt < NCHARS; ++p, ++cnt)
226 			*p = *p == OOBCH ? ch : cnt;
227 
228 	if (sflag)
229 		for (lastch = OOBCH; (ch = getwcf()) != WEOF;) {
230 			ch = string1[ch];
231 			if (!string2[ch] || lastch != ch) {
232 				lastch = ch;
233 				putwchar(ch);
234 			}
235 		}
236 	else
237 		while ((ch = getwcf()) != WEOF)
238 			putwchar(string1[ch]);
239 	exit(0);
240 }
241 
242 static void
setup(wchar_t * string,char * arg,STR * str,bool cflag)243 setup(wchar_t *string, char *arg, STR *str, bool cflag)
244 {
245 	wchar_t cnt, *p;
246 
247 	str->str = ambstowcs(arg);
248 	bzero(string, NCHARS * sizeof (wchar_t));
249 	while (next(str))
250 		string[str->lastch] = 1;
251 	if (cflag)
252 		for (p = string, cnt = NCHARS; cnt--; ++p)
253 			*p = !*p;
254 }
255 
256 static void
usage(void)257 usage(void)
258 {
259 	fprintf(stderr, "usage:\ttr [-cs] string1 string2\n"
260 	    "\ttr [-c] -d string1\n"
261 	    "\ttr [-c] -s string1\n"
262 	    "\ttr [-c] -ds string1 string2\n");
263 	exit(1);
264 }
265