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