Adding upstream version 3.86+dfsg.
[syslinux-debian/hramrach.git] / com32 / lib / vsscanf.c
blob153dbbddf28e753e7cff2318e2feb7bdb73bfa3c
1 /*
2 * vsscanf.c
4 * vsscanf(), from which the rest of the scanf()
5 * family is built
6 */
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>
16 #ifndef LONG_BIT
17 #define LONG_BIT (CHAR_BIT*sizeof(long))
18 #endif
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 */
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 */
36 #define MIN_RANK rank_char
37 #define MAX_RANK rank_longlong
39 #define INTMAX_RANK rank_longlong
40 #define SIZE_T_RANK rank_long
41 #define PTRDIFF_T_RANK rank_long
43 enum bail {
44 bail_none = 0, /* No error condition */
45 bail_eof, /* Hit EOF */
46 bail_err /* Conversion mismatch */
49 static inline const char *skipspace(const char *p)
51 while (isspace((unsigned char)*p))
52 p++;
53 return p;
56 #undef set_bit
57 static inline void set_bit(unsigned long *bitmap, unsigned int bit)
59 bitmap[bit / LONG_BIT] |= 1UL << (bit % LONG_BIT);
62 #undef test_bit
63 static inline int test_bit(unsigned long *bitmap, unsigned int bit)
65 return (int)(bitmap[bit / LONG_BIT] >> (bit % LONG_BIT)) & 1;
68 int vsscanf(const char *buffer, const char *format, va_list ap)
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;
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 */
112 break;
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;
129 break;
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 */
138 break;
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;
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;
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;
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;
189 /* Failure */
190 bail = bail_err;
191 break;
193 /* else */
194 #endif
195 rank = rank_ptr;
196 base = 0;
197 sign = 0;
198 goto scan_int;
200 case 'i': /* Base-independent integer */
201 base = 0;
202 sign = 1;
203 goto scan_int;
205 case 'd': /* Decimal integer */
206 base = 10;
207 sign = 1;
208 goto scan_int;
210 case 'o': /* Octal integer */
211 base = 8;
212 sign = 0;
213 goto scan_int;
215 case 'u': /* Unsigned decimal integer */
216 base = 10;
217 sign = 0;
218 goto scan_int;
220 case 'x': /* Hexadecimal integer */
221 case 'X':
222 base = 16;
223 sign = 0;
224 goto scan_int;
226 case 'n': /* Number of characters consumed */
227 val = (q - buffer);
228 goto set_integer;
230 scan_int:
231 q = skipspace(q);
232 if (!*q) {
233 bail = bail_eof;
234 break;
236 val = strntoumax(q, (char **)&qq, base, width);
237 if (qq == q) {
238 bail = bail_err;
239 break;
241 q = qq;
242 converted++;
243 /* fall through */
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;
269 break;
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;
279 *sarg++ = *q++;
281 if (!bail)
282 converted++;
283 break;
285 case 's': /* String */
287 char *sp;
288 sp = sarg = va_arg(ap, char *);
289 while (width-- && *q && !isspace((unsigned char)*q)) {
290 *sp++ = *q++;
292 if (sarg != sp) {
293 *sp = '\0'; /* Terminate output */
294 converted++;
295 } else {
296 bail = bail_eof;
299 break;
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;
308 case '%': /* %% sequence */
309 if (*q == '%')
310 q++;
311 else
312 bail = bail_err;
313 break;
315 default: /* Anything else */
316 bail = bail_err; /* Unknown sequence */
317 break;
320 break;
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;
329 break;
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);
340 break;
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;
352 break;
354 match_run: /* Match expression finished */
355 qq = q;
356 while (width && *q
357 && test_bit(matchmap, (unsigned char)*q) ^ matchinv) {
358 *sarg++ = *q++;
360 if (q != qq) {
361 *sarg = '\0';
362 converted++;
363 } else {
364 bail = *q ? bail_err : bail_eof;
366 break;
370 if (bail == bail_eof && !converted)
371 converted = -1; /* Return EOF (-1) */
373 return converted;