1 | /* $OpenBSD: unvis.c,v 1.12 2005/08/08 08:05:34 espie 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 | |
34 | #include "compat.h" |
35 | |
36 | /* |
37 | * decode driven by state machine |
38 | */ |
39 | #define S_GROUND 0 /* haven't seen escape char */ |
40 | #define S_START 1 /* start decoding special sequence */ |
41 | #define S_META 2 /* metachar started (M) */ |
42 | #define S_META1 3 /* metachar more, regular char (-) */ |
43 | #define S_CTRL 4 /* control char started (^) */ |
44 | #define S_OCTAL2 5 /* octal digit 2 */ |
45 | #define S_OCTAL3 6 /* octal digit 3 */ |
46 | |
47 | #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') |
48 | |
49 | /* |
50 | * unvis - decode characters previously encoded by vis |
51 | */ |
52 | int |
53 | unvis(char *cp, char c, int *astate, int flag) |
54 | { |
55 | |
56 | if (flag & UNVIS_END) { |
57 | if (*astate == S_OCTAL2 || *astate == S_OCTAL3) { |
58 | *astate = S_GROUND; |
59 | return (UNVIS_VALID); |
60 | } |
61 | return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD); |
62 | } |
63 | |
64 | switch (*astate) { |
65 | |
66 | case S_GROUND: |
67 | *cp = 0; |
68 | if (c == '\\') { |
69 | *astate = S_START; |
70 | return (0); |
71 | } |
72 | *cp = c; |
73 | return (UNVIS_VALID); |
74 | |
75 | case S_START: |
76 | switch(c) { |
77 | case '\\': |
78 | *cp = c; |
79 | *astate = S_GROUND; |
80 | return (UNVIS_VALID); |
81 | case '0': case '1': case '2': case '3': |
82 | case '4': case '5': case '6': case '7': |
83 | *cp = (c - '0'); |
84 | *astate = S_OCTAL2; |
85 | return (0); |
86 | case 'M': |
87 | *cp = (char) 0200; |
88 | *astate = S_META; |
89 | return (0); |
90 | case '^': |
91 | *astate = S_CTRL; |
92 | return (0); |
93 | case 'n': |
94 | *cp = '\n'; |
95 | *astate = S_GROUND; |
96 | return (UNVIS_VALID); |
97 | case 'r': |
98 | *cp = '\r'; |
99 | *astate = S_GROUND; |
100 | return (UNVIS_VALID); |
101 | case 'b': |
102 | *cp = '\b'; |
103 | *astate = S_GROUND; |
104 | return (UNVIS_VALID); |
105 | case 'a': |
106 | *cp = '\007'; |
107 | *astate = S_GROUND; |
108 | return (UNVIS_VALID); |
109 | case 'v': |
110 | *cp = '\v'; |
111 | *astate = S_GROUND; |
112 | return (UNVIS_VALID); |
113 | case 't': |
114 | *cp = '\t'; |
115 | *astate = S_GROUND; |
116 | return (UNVIS_VALID); |
117 | case 'f': |
118 | *cp = '\f'; |
119 | *astate = S_GROUND; |
120 | return (UNVIS_VALID); |
121 | case 's': |
122 | *cp = ' '; |
123 | *astate = S_GROUND; |
124 | return (UNVIS_VALID); |
125 | case 'E': |
126 | *cp = '\033'; |
127 | *astate = S_GROUND; |
128 | return (UNVIS_VALID); |
129 | case '\n': |
130 | /* |
131 | * hidden newline |
132 | */ |
133 | *astate = S_GROUND; |
134 | return (UNVIS_NOCHAR); |
135 | case '$': |
136 | /* |
137 | * hidden marker |
138 | */ |
139 | *astate = S_GROUND; |
140 | return (UNVIS_NOCHAR); |
141 | } |
142 | *astate = S_GROUND; |
143 | return (UNVIS_SYNBAD); |
144 | |
145 | case S_META: |
146 | if (c == '-') |
147 | *astate = S_META1; |
148 | else if (c == '^') |
149 | *astate = S_CTRL; |
150 | else { |
151 | *astate = S_GROUND; |
152 | return (UNVIS_SYNBAD); |
153 | } |
154 | return (0); |
155 | |
156 | case S_META1: |
157 | *astate = S_GROUND; |
158 | *cp |= c; |
159 | return (UNVIS_VALID); |
160 | |
161 | case S_CTRL: |
162 | if (c == '?') |
163 | *cp |= 0177; |
164 | else |
165 | *cp |= c & 037; |
166 | *astate = S_GROUND; |
167 | return (UNVIS_VALID); |
168 | |
169 | case S_OCTAL2: /* second possible octal digit */ |
170 | if (isoctal(c)) { |
171 | /* |
172 | * yes - and maybe a third |
173 | */ |
174 | *cp = (*cp << 3) + (c - '0'); |
175 | *astate = S_OCTAL3; |
176 | return (0); |
177 | } |
178 | /* |
179 | * no - done with current sequence, push back passed char |
180 | */ |
181 | *astate = S_GROUND; |
182 | return (UNVIS_VALIDPUSH); |
183 | |
184 | case S_OCTAL3: /* third possible octal digit */ |
185 | *astate = S_GROUND; |
186 | if (isoctal(c)) { |
187 | *cp = (*cp << 3) + (c - '0'); |
188 | return (UNVIS_VALID); |
189 | } |
190 | /* |
191 | * we were done, push back passed char |
192 | */ |
193 | return (UNVIS_VALIDPUSH); |
194 | |
195 | default: |
196 | /* |
197 | * decoder in unknown state - (probably uninitialized) |
198 | */ |
199 | *astate = S_GROUND; |
200 | return (UNVIS_SYNBAD); |
201 | } |
202 | } |
203 | |
204 | /* |
205 | * strunvis - decode src into dst |
206 | * |
207 | * Number of chars decoded into dst is returned, -1 on error. |
208 | * Dst is null terminated. |
209 | */ |
210 | |
211 | int |
212 | strunvis(char *dst, const char *src) |
213 | { |
214 | char c; |
215 | char *start = dst; |
216 | int state = 0; |
217 | |
218 | while ((c = *src++)) { |
219 | again: |
220 | switch (unvis(dst, c, &state, 0)) { |
221 | case UNVIS_VALID: |
222 | dst++; |
223 | break; |
224 | case UNVIS_VALIDPUSH: |
225 | dst++; |
226 | goto again; |
227 | case 0: |
228 | case UNVIS_NOCHAR: |
229 | break; |
230 | default: |
231 | *dst = '\0'; |
232 | return (-1); |
233 | } |
234 | } |
235 | if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID) |
236 | dst++; |
237 | *dst = '\0'; |
238 | return (dst - start); |
239 | } |
240 | |
241 | ssize_t |
242 | strnunvis(char *dst, const char *src, size_t sz) |
243 | { |
244 | char c, p; |
245 | char *start = dst, *end = dst + sz - 1; |
246 | int state = 0; |
247 | |
248 | if (sz > 0) |
249 | *end = '\0'; |
250 | while ((c = *src++)) { |
251 | again: |
252 | switch (unvis(&p, c, &state, 0)) { |
253 | case UNVIS_VALID: |
254 | if (dst < end) |
255 | *dst = p; |
256 | dst++; |
257 | break; |
258 | case UNVIS_VALIDPUSH: |
259 | if (dst < end) |
260 | *dst = p; |
261 | dst++; |
262 | goto again; |
263 | case 0: |
264 | case UNVIS_NOCHAR: |
265 | break; |
266 | default: |
267 | if (dst <= end) |
268 | *dst = '\0'; |
269 | return (-1); |
270 | } |
271 | } |
272 | if (unvis(&p, c, &state, UNVIS_END) == UNVIS_VALID) { |
273 | if (dst < end) |
274 | *dst = p; |
275 | dst++; |
276 | } |
277 | if (dst <= end) |
278 | *dst = '\0'; |
279 | return (dst - start); |
280 | } |
281 | |
282 | |