Fix a compiler warning
[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 #include <sys/types.h>
33
34 #include <assert.h>
35 #include <ctype.h>
36 #include <stdint.h>
37 #include <stdio.h>
38 #include <errno.h>
39
40 #define IN_LIBDHCPCD
41 #include "dhcpcd.h"
42
43 #ifndef __arraycount
44 #define __arraycount(__x)       (sizeof(__x) / sizeof(__x[0]))
45 #endif
46
47 /*
48  * decode driven by state machine
49  */
50 #define S_GROUND        0       /* haven't seen escape char */
51 #define S_START         1       /* start decoding special sequence */
52 #define S_META          2       /* metachar started (M) */
53 #define S_META1         3       /* metachar more, regular char (-) */
54 #define S_CTRL          4       /* control char started (^) */
55 #define S_OCTAL2        5       /* octal digit 2 */
56 #define S_OCTAL3        6       /* octal digit 3 */
57 #define S_HEX           7       /* mandatory hex digit */
58 #define S_HEX1          8       /* http hex digit */
59 #define S_HEX2          9       /* http hex digit 2 */
60
61 #define isoctal(c)      (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
62 #define xtod(c)         (isdigit(c) ? (c - '0') : ((tolower(c) - 'a') + 10))
63
64 #define _VIS_END        0x0800  /* for unvis */
65 #define UNVIS_END       _VIS_END
66
67 /*
68  * unvis return codes
69  */
70 #define UNVIS_VALID      1      /* character valid */
71 #define UNVIS_VALIDPUSH  2      /* character valid, push back passed char */
72 #define UNVIS_NOCHAR     3      /* valid sequence, no character produced */
73 #define UNVIS_SYNBAD    -1      /* unrecognized escape sequence */
74 #define UNVIS_ERROR     -2      /* decoder in unknown state (unrecoverable) */
75
76 /*
77  * unvis - decode characters previously encoded by vis
78  */
79 static int
80 unvis(char *cp, char c, int *astate, int flag)
81 {
82         unsigned char uc = (unsigned char)c;
83         unsigned char st;
84
85 /*
86  * Bottom 8 bits of astate hold the state machine state.
87  * Top 8 bits hold the current character in the http 1866 nv string decoding
88  */
89 #define GS(a)           ((a) & 0xff)
90 #define SS(a, b)        (((uint32_t)(a) << 24) | (b))
91 #define GI(a)           ((uint32_t)(a) >> 24)
92
93         st = GS(*astate);
94
95         if (flag & UNVIS_END) {
96                 switch (st) {
97                 case S_OCTAL2:
98                 case S_OCTAL3:
99                 case S_HEX2:
100                         *astate = SS(0, S_GROUND);
101                         return UNVIS_VALID;
102                 case S_GROUND:
103                         return UNVIS_NOCHAR;
104                 default:
105                         return UNVIS_SYNBAD;
106                 }
107         }
108
109         switch (st) {
110
111         case S_GROUND:
112                 *cp = 0;
113                 if (c == '\\') {
114                         *astate = SS(0, S_START);
115                         return UNVIS_NOCHAR;
116                 }
117                 *cp = c;
118                 return UNVIS_VALID;
119
120         case S_START:
121                 switch(c) {
122                 case '\\':
123                         *cp = c;
124                         *astate = SS(0, S_GROUND);
125                         return UNVIS_VALID;
126                 case '0': case '1': case '2': case '3':
127                 case '4': case '5': case '6': case '7':
128                         *cp = (c - '0');
129                         *astate = SS(0, S_OCTAL2);
130                         return UNVIS_NOCHAR;
131                 case 'M':
132                         *cp = (char)0200;
133                         *astate = SS(0, S_META);
134                         return UNVIS_NOCHAR;
135                 case '^':
136                         *astate = SS(0, S_CTRL);
137                         return UNVIS_NOCHAR;
138                 case 'n':
139                         *cp = '\n';
140                         *astate = SS(0, S_GROUND);
141                         return UNVIS_VALID;
142                 case 'r':
143                         *cp = '\r';
144                         *astate = SS(0, S_GROUND);
145                         return UNVIS_VALID;
146                 case 'b':
147                         *cp = '\b';
148                         *astate = SS(0, S_GROUND);
149                         return UNVIS_VALID;
150                 case 'a':
151                         *cp = '\007';
152                         *astate = SS(0, S_GROUND);
153                         return UNVIS_VALID;
154                 case 'v':
155                         *cp = '\v';
156                         *astate = SS(0, S_GROUND);
157                         return UNVIS_VALID;
158                 case 't':
159                         *cp = '\t';
160                         *astate = SS(0, S_GROUND);
161                         return UNVIS_VALID;
162                 case 'f':
163                         *cp = '\f';
164                         *astate = SS(0, S_GROUND);
165                         return UNVIS_VALID;
166                 case 's':
167                         *cp = ' ';
168                         *astate = SS(0, S_GROUND);
169                         return UNVIS_VALID;
170                 case 'E':
171                         *cp = '\033';
172                         *astate = SS(0, S_GROUND);
173                         return UNVIS_VALID;
174                 case 'x':
175                         *astate = SS(0, S_HEX);
176                         return UNVIS_NOCHAR;
177                 case '\n':
178                         /*
179                          * hidden newline
180                          */
181                         *astate = SS(0, S_GROUND);
182                         return UNVIS_NOCHAR;
183                 case '$':
184                         /*
185                          * hidden marker
186                          */
187                         *astate = SS(0, S_GROUND);
188                         return UNVIS_NOCHAR;
189                 default:
190                         if (isgraph(c)) {
191                                 *cp = c;
192                                 *astate = SS(0, S_GROUND);
193                                 return UNVIS_VALID;
194                         }
195                 }
196                 goto bad;
197
198         case S_META:
199                 if (c == '-')
200                         *astate = SS(0, S_META1);
201                 else if (c == '^')
202                         *astate = SS(0, S_CTRL);
203                 else 
204                         goto bad;
205                 return UNVIS_NOCHAR;
206
207         case S_META1:
208                 *astate = SS(0, S_GROUND);
209                 *cp |= c;
210                 return UNVIS_VALID;
211
212         case S_CTRL:
213                 if (c == '?')
214                         *cp |= 0177;
215                 else
216                         *cp |= c & 037;
217                 *astate = SS(0, S_GROUND);
218                 return UNVIS_VALID;
219
220         case S_OCTAL2:  /* second possible octal digit */
221                 if (isoctal(uc)) {
222                         /*
223                          * yes - and maybe a third
224                          */
225                         *cp = (char)((*cp << 3) + (c - '0'));
226                         *astate = SS(0, S_OCTAL3);
227                         return UNVIS_NOCHAR;
228                 }
229                 /*
230                  * no - done with current sequence, push back passed char
231                  */
232                 *astate = SS(0, S_GROUND);
233                 return UNVIS_VALIDPUSH;
234
235         case S_OCTAL3:  /* third possible octal digit */
236                 *astate = SS(0, S_GROUND);
237                 if (isoctal(uc)) {
238                         *cp = (char)((*cp << 3) + (c - '0'));
239                         return UNVIS_VALID;
240                 }
241                 /*
242                  * we were done, push back passed char
243                  */
244                 return UNVIS_VALIDPUSH;
245
246         case S_HEX:
247                 if (!isxdigit(uc))
248                         goto bad;
249                 /*FALLTHROUGH*/
250         case S_HEX1:
251                 if (isxdigit(uc)) {
252                         *cp = (char)xtod(uc);
253                         *astate = SS(0, S_HEX2);
254                         return UNVIS_NOCHAR;
255                 }
256                 /*
257                  * no - done with current sequence, push back passed char
258                  */
259                 *astate = SS(0, S_GROUND);
260                 return UNVIS_VALIDPUSH;
261
262         case S_HEX2:
263                 *astate = S_GROUND;
264                 if (isxdigit(uc)) {
265                         *cp = (char)(xtod(uc) | (*cp << 4));
266                         return UNVIS_VALID;
267                 }
268                 return UNVIS_VALIDPUSH;
269
270         default:
271         bad:
272                 /*
273                  * decoder in unknown state - (probably uninitialized)
274                  */
275                 *astate = SS(0, S_GROUND);
276                 return UNVIS_SYNBAD;
277         }
278 }
279
280 /*
281  * strnunvisx - decode src into dst
282  *
283  *      Number of chars decoded into dst is returned, -1 on error.
284  *      Dst is null terminated.
285  */
286
287 int
288 dhcpcd_strnunvis(char *dst, size_t dlen, const char *src)
289 {
290         char c;
291         char t = '\0', *start = dst;
292         int state = 0;
293
294 #define CHECKSPACE() \
295         do { \
296                 if (dlen-- == 0) { \
297                         errno = ENOSPC; \
298                         return -1; \
299                 } \
300         } while (/*CONSTCOND*/0)
301
302         while ((c = *src++) != '\0') {
303  again:
304                 switch (unvis(&t, c, &state, 0)) {
305                 case UNVIS_VALID:
306                         CHECKSPACE();
307                         *dst++ = t;
308                         break;
309                 case UNVIS_VALIDPUSH:
310                         CHECKSPACE();
311                         *dst++ = t;
312                         goto again;
313                 case 0:
314                 case UNVIS_NOCHAR:
315                         break;
316                 case UNVIS_SYNBAD:
317                         errno = EINVAL;
318                         return -1;
319                 default:
320                         errno = EINVAL;
321                         return -1;
322                 }
323         }
324         if (unvis(&t, c, &state, UNVIS_END) == UNVIS_VALID) {
325                 CHECKSPACE();
326                 *dst++ = t;
327         }
328         CHECKSPACE();
329         *dst = '\0';
330         return (int)(dst - start);
331 }