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