1/*	$NetBSD: strunvis.c,v 1.5 2005/06/01 11:48:49 lukem Exp $	*/
2/*	from	NetBSD: unvis.c,v 1.27 2005/05/16 11:42:04 lukem Exp	*/
3
4/*-
5 * Copyright (c) 1989, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include "tnftp.h"
34
35/*
36 * decode driven by state machine
37 */
38#define	S_GROUND	0	/* haven't seen escape char */
39#define	S_START		1	/* start decoding special sequence */
40#define	S_META		2	/* metachar started (M) */
41#define	S_META1		3	/* metachar more, regular char (-) */
42#define	S_CTRL		4	/* control char started (^) */
43#define	S_OCTAL2	5	/* octal digit 2 */
44#define	S_OCTAL3	6	/* octal digit 3 */
45#define	S_HEX1		7	/* hex digit */
46#define	S_HEX2		8	/* hex digit 2 */
47
48#define	isoctal(c)	(((unsigned char)(c)) >= '0' && ((unsigned char)(c)) <= '7')
49#define	xtod(c)		(isdigit(c) ? (c - '0') : ((tolower(c) - 'a') + 10))
50
51/*
52 * unvis - decode characters previously encoded by vis
53 */
54int
55unvis(char *cp, int c, int *astate, int flag)
56{
57	unsigned char uc = (unsigned char)c;
58
59	if (flag & UNVIS_END) {
60		if (*astate == S_OCTAL2 || *astate == S_OCTAL3
61		    || *astate == S_HEX2) {
62			*astate = S_GROUND;
63			return (UNVIS_VALID);
64		}
65		return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD);
66	}
67
68	switch (*astate) {
69
70	case S_GROUND:
71		*cp = 0;
72		if (c == '\\') {
73			*astate = S_START;
74			return (0);
75		}
76		if ((flag & VIS_HTTPSTYLE) && c == '%') {
77			*astate = S_HEX1;
78			return (0);
79		}
80		*cp = c;
81		return (UNVIS_VALID);
82
83	case S_START:
84		switch(c) {
85		case '\\':
86			*cp = c;
87			*astate = S_GROUND;
88			return (UNVIS_VALID);
89		case '0': case '1': case '2': case '3':
90		case '4': case '5': case '6': case '7':
91			*cp = (c - '0');
92			*astate = S_OCTAL2;
93			return (0);
94		case 'M':
95			*cp = (char)0200;
96			*astate = S_META;
97			return (0);
98		case '^':
99			*astate = S_CTRL;
100			return (0);
101		case 'n':
102			*cp = '\n';
103			*astate = S_GROUND;
104			return (UNVIS_VALID);
105		case 'r':
106			*cp = '\r';
107			*astate = S_GROUND;
108			return (UNVIS_VALID);
109		case 'b':
110			*cp = '\b';
111			*astate = S_GROUND;
112			return (UNVIS_VALID);
113		case 'a':
114			*cp = '\007';
115			*astate = S_GROUND;
116			return (UNVIS_VALID);
117		case 'v':
118			*cp = '\v';
119			*astate = S_GROUND;
120			return (UNVIS_VALID);
121		case 't':
122			*cp = '\t';
123			*astate = S_GROUND;
124			return (UNVIS_VALID);
125		case 'f':
126			*cp = '\f';
127			*astate = S_GROUND;
128			return (UNVIS_VALID);
129		case 's':
130			*cp = ' ';
131			*astate = S_GROUND;
132			return (UNVIS_VALID);
133		case 'E':
134			*cp = '\033';
135			*astate = S_GROUND;
136			return (UNVIS_VALID);
137		case '\n':
138			/*
139			 * hidden newline
140			 */
141			*astate = S_GROUND;
142			return (UNVIS_NOCHAR);
143		case '$':
144			/*
145			 * hidden marker
146			 */
147			*astate = S_GROUND;
148			return (UNVIS_NOCHAR);
149		}
150		*astate = S_GROUND;
151		return (UNVIS_SYNBAD);
152
153	case S_META:
154		if (c == '-')
155			*astate = S_META1;
156		else if (c == '^')
157			*astate = S_CTRL;
158		else {
159			*astate = S_GROUND;
160			return (UNVIS_SYNBAD);
161		}
162		return (0);
163
164	case S_META1:
165		*astate = S_GROUND;
166		*cp |= c;
167		return (UNVIS_VALID);
168
169	case S_CTRL:
170		if (c == '?')
171			*cp |= 0177;
172		else
173			*cp |= c & 037;
174		*astate = S_GROUND;
175		return (UNVIS_VALID);
176
177	case S_OCTAL2:	/* second possible octal digit */
178		if (isoctal(uc)) {
179			/*
180			 * yes - and maybe a third
181			 */
182			*cp = (*cp << 3) + (c - '0');
183			*astate = S_OCTAL3;
184			return (0);
185		}
186		/*
187		 * no - done with current sequence, push back passed char
188		 */
189		*astate = S_GROUND;
190		return (UNVIS_VALIDPUSH);
191
192	case S_OCTAL3:	/* third possible octal digit */
193		*astate = S_GROUND;
194		if (isoctal(uc)) {
195			*cp = (*cp << 3) + (c - '0');
196			return (UNVIS_VALID);
197		}
198		/*
199		 * we were done, push back passed char
200		 */
201		return (UNVIS_VALIDPUSH);
202
203	case S_HEX1:
204		if (isxdigit(uc)) {
205			*cp = xtod(uc);
206			*astate = S_HEX2;
207			return (0);
208		}
209		/*
210		 * no - done with current sequence, push back passed char
211		 */
212		*astate = S_GROUND;
213		return (UNVIS_VALIDPUSH);
214
215	case S_HEX2:
216		*astate = S_GROUND;
217		if (isxdigit(uc)) {
218			*cp = xtod(uc) | (*cp << 4);
219			return (UNVIS_VALID);
220		}
221		return (UNVIS_VALIDPUSH);
222
223	default:
224		/*
225		 * decoder in unknown state - (probably uninitialized)
226		 */
227		*astate = S_GROUND;
228		return (UNVIS_SYNBAD);
229	}
230}
231
232/*
233 * strunvis - decode src into dst
234 *
235 *	Number of chars decoded into dst is returned, -1 on error.
236 *	Dst is null terminated.
237 */
238
239int
240strunvisx(char *dst, const char *src, int flag)
241{
242	char c;
243	char *start = dst;
244	int state = 0;
245
246	while ((c = *src++) != '\0') {
247 again:
248		switch (unvis(dst, c, &state, flag)) {
249		case UNVIS_VALID:
250			dst++;
251			break;
252		case UNVIS_VALIDPUSH:
253			dst++;
254			goto again;
255		case 0:
256		case UNVIS_NOCHAR:
257			break;
258		default:
259			return (-1);
260		}
261	}
262	if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID)
263		dst++;
264	*dst = '\0';
265	return (dst - start);
266}
267
268int
269strunvis(char *dst, const char *src)
270{
271	return strunvisx(dst, src, 0);
272}
273