Add dhcpcd_encode which encodes raw data back into a safe string.
[dhcpcd-ui] / src / libdhcpcd / unvis.c
1 /*      $NetBSD: unvis.c,v 1.44 2014/09/26 15:43:36 roy Exp $   */
2
3 /*-
4  * Copyright (c) 1989, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 /*
33  * HEAVILY trimmed down for use only in dhcpcd.
34  * Please use the source in NetBSD for a fuller working copy.
35  */
36
37 #include <sys/types.h>
38
39 #include <assert.h>
40 #include <ctype.h>
41 #include <stdint.h>
42 #include <stdio.h>
43 #include <errno.h>
44
45 #define IN_LIBDHCPCD
46 #include "dhcpcd.h"
47
48 #ifndef __arraycount
49 #define __arraycount(__x)       (sizeof(__x) / sizeof(__x[0]))
50 #endif
51
52 /*
53  * decode driven by state machine
54  */
55 #define S_GROUND        0       /* haven't seen escape char */
56 #define S_START         1       /* start decoding special sequence */
57 #define S_META          2       /* metachar started (M) */
58 #define S_META1         3       /* metachar more, regular char (-) */
59 #define S_CTRL          4       /* control char started (^) */
60 #define S_OCTAL2        5       /* octal digit 2 */
61 #define S_OCTAL3        6       /* octal digit 3 */
62 #define S_HEX           7       /* mandatory hex digit */
63 #define S_HEX1          8       /* http hex digit */
64 #define S_HEX2          9       /* http hex digit 2 */
65
66 #define isoctal(c)      (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
67 #define xtod(c)         (isdigit(c) ? (c - '0') : ((tolower(c) - 'a') + 10))
68
69 #define _VIS_END        0x0800  /* for unvis */
70 #define UNVIS_END       _VIS_END
71
72 /*
73  * unvis return codes
74  */
75 #define UNVIS_VALID      1      /* character valid */
76 #define UNVIS_VALIDPUSH  2      /* character valid, push back passed char */
77 #define UNVIS_NOCHAR     3      /* valid sequence, no character produced */
78 #define UNVIS_SYNBAD    -1      /* unrecognized escape sequence */
79 #define UNVIS_ERROR     -2      /* decoder in unknown state (unrecoverable) */
80
81 /*
82  * unvis - decode characters previously encoded by vis
83  */
84 static int
85 unvis(char *cp, int c, int *astate, int flag)
86 {
87         unsigned char uc = (unsigned char)c;
88         unsigned char st;
89
90 /*
91  * Bottom 8 bits of astate hold the state machine state.
92  * Top 8 bits hold the current character in the http 1866 nv string decoding
93  */
94 #define GS(a)           ((a) & 0xff)
95 #define SS(a, b)        (((uint32_t)(a) << 24) | (b))
96 #define GI(a)           ((uint32_t)(a) >> 24)
97
98         st = GS(*astate);
99
100         if (flag & UNVIS_END) {
101                 switch (st) {
102                 case S_OCTAL2:
103                 case S_OCTAL3:
104                 case S_HEX2:
105                         *astate = SS(0, S_GROUND);
106                         return UNVIS_VALID;
107                 case S_GROUND:
108                         return UNVIS_NOCHAR;
109                 default:
110                         return UNVIS_SYNBAD;
111                 }
112         }
113
114         switch (st) {
115
116         case S_GROUND:
117                 *cp = 0;
118                 if (c == '\\') {
119                         *astate = SS(0, S_START);
120                         return UNVIS_NOCHAR;
121                 }
122                 *cp = (char)c;
123                 return UNVIS_VALID;
124
125         case S_START:
126                 switch(c) {
127                 case '\\':
128                         *cp = (char)c;
129                         *astate = SS(0, S_GROUND);
130                         return UNVIS_VALID;
131                 case '0': case '1': case '2': case '3':
132                 case '4': case '5': case '6': case '7':
133                         *cp = (char)(c - '0');
134                         *astate = SS(0, S_OCTAL2);
135                         return UNVIS_NOCHAR;
136                 case 'M':
137                         *cp = (char)0200;
138                         *astate = SS(0, S_META);
139                         return UNVIS_NOCHAR;
140                 case '^':
141                         *astate = SS(0, S_CTRL);
142                         return UNVIS_NOCHAR;
143                 case 'n':
144                         *cp = '\n';
145                         *astate = SS(0, S_GROUND);
146                         return UNVIS_VALID;
147                 case 'r':
148                         *cp = '\r';
149                         *astate = SS(0, S_GROUND);
150                         return UNVIS_VALID;
151                 case 'b':
152                         *cp = '\b';
153                         *astate = SS(0, S_GROUND);
154                         return UNVIS_VALID;
155                 case 'a':
156                         *cp = '\007';
157                         *astate = SS(0, S_GROUND);
158                         return UNVIS_VALID;
159                 case 'v':
160                         *cp = '\v';
161                         *astate = SS(0, S_GROUND);
162                         return UNVIS_VALID;
163                 case 't':
164                         *cp = '\t';
165                         *astate = SS(0, S_GROUND);
166                         return UNVIS_VALID;
167                 case 'f':
168                         *cp = '\f';
169                         *astate = SS(0, S_GROUND);
170                         return UNVIS_VALID;
171                 case 's':
172                         *cp = ' ';
173                         *astate = SS(0, S_GROUND);
174                         return UNVIS_VALID;
175                 case 'E':
176                         *cp = '\033';
177                         *astate = SS(0, S_GROUND);
178                         return UNVIS_VALID;
179                 case 'x':
180                         *astate = SS(0, S_HEX);
181                         return UNVIS_NOCHAR;
182                 case '\n':
183                         /*
184                          * hidden newline
185                          */
186                         *astate = SS(0, S_GROUND);
187                         return UNVIS_NOCHAR;
188                 case '$':
189                         /*
190                          * hidden marker
191                          */
192                         *astate = SS(0, S_GROUND);
193                         return UNVIS_NOCHAR;
194                 default:
195                         if (isgraph(c)) {
196                                 *cp = (char)c;
197                                 *astate = SS(0, S_GROUND);
198                                 return UNVIS_VALID;
199                         }
200                 }
201                 goto bad;
202
203         case S_META:
204                 if (c == '-')
205                         *astate = SS(0, S_META1);
206                 else if (c == '^')
207                         *astate = SS(0, S_CTRL);
208                 else 
209                         goto bad;
210                 return UNVIS_NOCHAR;
211
212         case S_META1:
213                 *astate = SS(0, S_GROUND);
214                 *cp |= c;
215                 return UNVIS_VALID;
216
217         case S_CTRL:
218                 if (c == '?')
219                         *cp |= 0177;
220                 else
221                         *cp |= c & 037;
222                 *astate = SS(0, S_GROUND);
223                 return UNVIS_VALID;
224
225         case S_OCTAL2:  /* second possible octal digit */
226                 if (isoctal(uc)) {
227                         /*
228                          * yes - and maybe a third
229                          */
230                         *cp = (char)((*cp << 3) + (c - '0'));
231                         *astate = SS(0, S_OCTAL3);
232                         return UNVIS_NOCHAR;
233                 }
234                 /*
235                  * no - done with current sequence, push back passed char
236                  */
237                 *astate = SS(0, S_GROUND);
238                 return UNVIS_VALIDPUSH;
239
240         case S_OCTAL3:  /* third possible octal digit */
241                 *astate = SS(0, S_GROUND);
242                 if (isoctal(uc)) {
243                         *cp = (char)((*cp << 3) + (c - '0'));
244                         return UNVIS_VALID;
245                 }
246                 /*
247                  * we were done, push back passed char
248                  */
249                 return UNVIS_VALIDPUSH;
250
251         case S_HEX:
252                 if (!isxdigit(uc))
253                         goto bad;
254                 /*FALLTHROUGH*/
255         case S_HEX1:
256                 if (isxdigit(uc)) {
257                         *cp = (char)xtod(uc);
258                         *astate = SS(0, S_HEX2);
259                         return UNVIS_NOCHAR;
260                 }
261                 /*
262                  * no - done with current sequence, push back passed char
263                  */
264                 *astate = SS(0, S_GROUND);
265                 return UNVIS_VALIDPUSH;
266
267         case S_HEX2:
268                 *astate = S_GROUND;
269                 if (isxdigit(uc)) {
270                         *cp = (char)(xtod(uc) | (*cp << 4));
271                         return UNVIS_VALID;
272                 }
273                 return UNVIS_VALIDPUSH;
274
275         default:
276         bad:
277                 /*
278                  * decoder in unknown state - (probably uninitialized)
279                  */
280                 *astate = SS(0, S_GROUND);
281                 return UNVIS_SYNBAD;
282         }
283 }
284
285 /*
286  * strnunvisx - decode src into dst
287  *
288  *      Number of chars decoded into dst is returned, -1 on error.
289  *      Dst is null terminated.
290  */
291
292 int
293 dhcpcd_strnunvis(char *dst, size_t dlen, const char *src)
294 {
295         int c;
296         char t = '\0', *start = dst;
297         int state = 0;
298
299 #define CHECKSPACE() \
300         do { \
301                 if (dlen-- == 0) { \
302                         errno = ENOSPC; \
303                         return -1; \
304                 } \
305         } while (/*CONSTCOND*/0)
306
307         while ((c = *src++) != '\0') {
308  again:
309                 switch (unvis(&t, c, &state, 0)) {
310                 case UNVIS_VALID:
311                         CHECKSPACE();
312                         *dst++ = t;
313                         break;
314                 case UNVIS_VALIDPUSH:
315                         CHECKSPACE();
316                         *dst++ = t;
317                         goto again;
318                 case 0:
319                 case UNVIS_NOCHAR:
320                         break;
321                 case UNVIS_SYNBAD:
322                         errno = EINVAL;
323                         return -1;
324                 default:
325                         errno = EINVAL;
326                         return -1;
327                 }
328         }
329         if (unvis(&t, c, &state, UNVIS_END) == UNVIS_VALID) {
330                 CHECKSPACE();
331                 *dst++ = t;
332         }
333         CHECKSPACE();
334         *dst = '\0';
335         return (int)(dst - start);
336 }