Add back spaces around ellipses in case statements
[people/sha0/syslinux.git] / com32 / lib / vsscanf.c
1 /*
2  * vsscanf.c
3  *
4  * vsscanf(), from which the rest of the scanf()
5  * family is built
6  */
7
8 #include <ctype.h>
9 #include <stdarg.h>
10 #include <stddef.h>
11 #include <inttypes.h>
12 #include <string.h>
13 #include <limits.h>
14 #include <stdio.h>
15
16 #ifndef LONG_BIT
17 #define LONG_BIT (CHAR_BIT*sizeof(long))
18 #endif
19
20 enum flags {
21     FL_SPLAT = 0x01,            /* Drop the value, do not assign */
22     FL_INV = 0x02,              /* Character-set with inverse */
23     FL_WIDTH = 0x04,            /* Field width specified */
24     FL_MINUS = 0x08,            /* Negative number */
25 };
26
27 enum ranks {
28     rank_char = -2,
29     rank_short = -1,
30     rank_int = 0,
31     rank_long = 1,
32     rank_longlong = 2,
33     rank_ptr = INT_MAX          /* Special value used for pointers */
34 };
35
36 #define MIN_RANK        rank_char
37 #define MAX_RANK        rank_longlong
38
39 #define INTMAX_RANK     rank_longlong
40 #define SIZE_T_RANK     rank_long
41 #define PTRDIFF_T_RANK  rank_long
42
43 enum bail {
44     bail_none = 0,              /* No error condition */
45     bail_eof,                   /* Hit EOF */
46     bail_err                    /* Conversion mismatch */
47 };
48
49 static inline const char *skipspace(const char *p)
50 {
51     while (isspace((unsigned char)*p))
52         p++;
53     return p;
54 }
55
56 #undef set_bit
57 static inline void set_bit(unsigned long *bitmap, unsigned int bit)
58 {
59     bitmap[bit / LONG_BIT] |= 1UL << (bit % LONG_BIT);
60 }
61
62 #undef test_bit
63 static inline int test_bit(unsigned long *bitmap, unsigned int bit)
64 {
65     return (int)(bitmap[bit / LONG_BIT] >> (bit % LONG_BIT)) & 1;
66 }
67
68 int vsscanf(const char *buffer, const char *format, va_list ap)
69 {
70     const char *p = format;
71     char ch;
72     const char *q = buffer;
73     const char *qq;
74     uintmax_t val = 0;
75     int rank = rank_int;        /* Default rank */
76     unsigned int width = UINT_MAX;
77     int base;
78     enum flags flags = 0;
79     enum {
80         st_normal,              /* Ground state */
81         st_flags,               /* Special flags */
82         st_width,               /* Field width */
83         st_modifiers,           /* Length or conversion modifiers */
84         st_match_init,          /* Initial state of %[ sequence */
85         st_match,               /* Main state of %[ sequence */
86         st_match_range,         /* After - in a %[ sequence */
87     } state = st_normal;
88     char *sarg = NULL;          /* %s %c or %[ string argument */
89     enum bail bail = bail_none;
90     int sign;
91     int converted = 0;          /* Successful conversions */
92     unsigned long matchmap[((1 << CHAR_BIT) + (LONG_BIT - 1)) / LONG_BIT];
93     int matchinv = 0;           /* Is match map inverted? */
94     unsigned char range_start = 0;
95
96     while ((ch = *p++) && !bail) {
97         switch (state) {
98         case st_normal:
99             if (ch == '%') {
100                 state = st_flags;
101                 flags = 0;
102                 rank = rank_int;
103                 width = UINT_MAX;
104             } else if (isspace((unsigned char)ch)) {
105                 q = skipspace(q);
106             } else {
107                 if (*q == ch)
108                     q++;
109                 else
110                     bail = bail_err;    /* Match failure */
111             }
112             break;
113
114         case st_flags:
115             switch (ch) {
116             case '*':
117                 flags |= FL_SPLAT;
118                 break;
119             case '0' ... '9':
120                 width = (ch - '0');
121                 state = st_width;
122                 flags |= FL_WIDTH;
123                 break;
124             default:
125                 state = st_modifiers;
126                 p--;            /* Process this character again */
127                 break;
128             }
129             break;
130
131         case st_width:
132             if (ch >= '0' && ch <= '9') {
133                 width = width * 10 + (ch - '0');
134             } else {
135                 state = st_modifiers;
136                 p--;            /* Process this character again */
137             }
138             break;
139
140         case st_modifiers:
141             switch (ch) {
142                 /* Length modifiers - nonterminal sequences */
143             case 'h':
144                 rank--;         /* Shorter rank */
145                 break;
146             case 'l':
147                 rank++;         /* Longer rank */
148                 break;
149             case 'j':
150                 rank = INTMAX_RANK;
151                 break;
152             case 'z':
153                 rank = SIZE_T_RANK;
154                 break;
155             case 't':
156                 rank = PTRDIFF_T_RANK;
157                 break;
158             case 'L':
159             case 'q':
160                 rank = rank_longlong;   /* long double/long long */
161                 break;
162
163             default:
164                 /* Output modifiers - terminal sequences */
165                 state = st_normal;      /* Next state will be normal */
166                 if (rank < MIN_RANK)    /* Canonicalize rank */
167                     rank = MIN_RANK;
168                 else if (rank > MAX_RANK)
169                     rank = MAX_RANK;
170
171                 switch (ch) {
172                 case 'P':       /* Upper case pointer */
173                 case 'p':       /* Pointer */
174 #if 0                           /* Enable this to allow null pointers by name */
175                     q = skipspace(q);
176                     if (!isdigit((unsigned char)*q)) {
177                         static const char *const nullnames[] =
178                             { "null", "nul", "nil", "(null)", "(nul)", "(nil)",
179 0 };
180                         const char *const *np;
181
182                         /* Check to see if it's a null pointer by name */
183                         for (np = nullnames; *np; np++) {
184                             if (!strncasecmp(q, *np, strlen(*np))) {
185                                 val = (uintmax_t) ((void *)NULL);
186                                 goto set_integer;
187                             }
188                         }
189                         /* Failure */
190                         bail = bail_err;
191                         break;
192                     }
193                     /* else */
194 #endif
195                     rank = rank_ptr;
196                     base = 0;
197                     sign = 0;
198                     goto scan_int;
199
200                 case 'i':       /* Base-independent integer */
201                     base = 0;
202                     sign = 1;
203                     goto scan_int;
204
205                 case 'd':       /* Decimal integer */
206                     base = 10;
207                     sign = 1;
208                     goto scan_int;
209
210                 case 'o':       /* Octal integer */
211                     base = 8;
212                     sign = 0;
213                     goto scan_int;
214
215                 case 'u':       /* Unsigned decimal integer */
216                     base = 10;
217                     sign = 0;
218                     goto scan_int;
219
220                 case 'x':       /* Hexadecimal integer */
221                 case 'X':
222                     base = 16;
223                     sign = 0;
224                     goto scan_int;
225
226                 case 'n':       /* Number of characters consumed */
227                     val = (q - buffer);
228                     goto set_integer;
229
230 scan_int:
231                     q = skipspace(q);
232                     if (!*q) {
233                         bail = bail_eof;
234                         break;
235                     }
236                     val = strntoumax(q, (char **)&qq, base, width);
237                     if (qq == q) {
238                         bail = bail_err;
239                         break;
240                     }
241                     q = qq;
242                     converted++;
243                     /* fall through */
244
245 set_integer:
246                     if (!(flags & FL_SPLAT)) {
247                         switch (rank) {
248                         case rank_char:
249                             *va_arg(ap, unsigned char *) = (unsigned char)val;
250                             break;
251                         case rank_short:
252                             *va_arg(ap, unsigned short *) = (unsigned short)val;
253                             break;
254                         case rank_int:
255                             *va_arg(ap, unsigned int *) = (unsigned int)val;
256                             break;
257                         case rank_long:
258                             *va_arg(ap, unsigned long *) = (unsigned long)val;
259                             break;
260                         case rank_longlong:
261                             *va_arg(ap, unsigned long long *) =
262                                 (unsigned long long)val;
263                             break;
264                         case rank_ptr:
265                             *va_arg(ap, void **) = (void *)(uintptr_t) val;
266                             break;
267                         }
268                     }
269                     break;
270
271                 case 'c':       /* Character */
272                     width = (flags & FL_WIDTH) ? width : 1;     /* Default width == 1 */
273                     sarg = va_arg(ap, char *);
274                     while (width--) {
275                         if (!*q) {
276                             bail = bail_eof;
277                             break;
278                         }
279                         *sarg++ = *q++;
280                     }
281                     if (!bail)
282                         converted++;
283                     break;
284
285                 case 's':       /* String */
286                     {
287                         char *sp;
288                         sp = sarg = va_arg(ap, char *);
289                         while (width-- && *q && !isspace((unsigned char)*q)) {
290                             *sp++ = *q++;
291                         }
292                         if (sarg != sp) {
293                             *sp = '\0'; /* Terminate output */
294                             converted++;
295                         } else {
296                             bail = bail_eof;
297                         }
298                     }
299                     break;
300
301                 case '[':       /* Character range */
302                     sarg = va_arg(ap, char *);
303                     state = st_match_init;
304                     matchinv = 0;
305                     memset(matchmap, 0, sizeof matchmap);
306                     break;
307
308                 case '%':       /* %% sequence */
309                     if (*q == '%')
310                         q++;
311                     else
312                         bail = bail_err;
313                     break;
314
315                 default:        /* Anything else */
316                     bail = bail_err;    /* Unknown sequence */
317                     break;
318                 }
319             }
320             break;
321
322         case st_match_init:     /* Initial state for %[ match */
323             if (ch == '^' && !(flags & FL_INV)) {
324                 matchinv = 1;
325             } else {
326                 set_bit(matchmap, (unsigned char)ch);
327                 state = st_match;
328             }
329             break;
330
331         case st_match:          /* Main state for %[ match */
332             if (ch == ']') {
333                 goto match_run;
334             } else if (ch == '-') {
335                 range_start = (unsigned char)ch;
336                 state = st_match_range;
337             } else {
338                 set_bit(matchmap, (unsigned char)ch);
339             }
340             break;
341
342         case st_match_range:    /* %[ match after - */
343             if (ch == ']') {
344                 set_bit(matchmap, (unsigned char)'-');  /* - was last character */
345                 goto match_run;
346             } else {
347                 int i;
348                 for (i = range_start; i < (unsigned char)ch; i++)
349                     set_bit(matchmap, i);
350                 state = st_match;
351             }
352             break;
353
354 match_run:                      /* Match expression finished */
355             qq = q;
356             while (width && *q
357                    && test_bit(matchmap, (unsigned char)*q) ^ matchinv) {
358                 *sarg++ = *q++;
359             }
360             if (q != qq) {
361                 *sarg = '\0';
362                 converted++;
363             } else {
364                 bail = *q ? bail_err : bail_eof;
365             }
366             break;
367         }
368     }
369
370     if (bail == bail_eof && !converted)
371         converted = -1;         /* Return EOF (-1) */
372
373     return converted;
374 }