Adding upstream version 3.35.
[syslinux-debian/hramrach.git] / com32 / lib / vsscanf.c
blob1882765d6c7fdf9851f179c43fb8661513e9d990
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 *
50 skipspace(const char *p)
52 while ( isspace((unsigned char)*p) ) p++;
53 return p;
56 #undef set_bit
57 static inline void
58 set_bit(unsigned long *bitmap, unsigned int bit)
60 bitmap[bit/LONG_BIT] |= 1UL << (bit%LONG_BIT);
63 #undef test_bit
64 static inline int
65 test_bit(unsigned long *bitmap, unsigned int bit)
67 return (int)(bitmap[bit/LONG_BIT] >> (bit%LONG_BIT)) & 1;
70 int vsscanf(const char *buffer, const char *format, va_list ap)
72 const char *p = format;
73 char ch;
74 const char *q = buffer;
75 const char *qq;
76 uintmax_t val = 0;
77 int rank = rank_int; /* Default rank */
78 unsigned int width = UINT_MAX;
79 int base;
80 enum flags flags = 0;
81 enum {
82 st_normal, /* Ground state */
83 st_flags, /* Special flags */
84 st_width, /* Field width */
85 st_modifiers, /* Length or conversion modifiers */
86 st_match_init, /* Initial state of %[ sequence */
87 st_match, /* Main state of %[ sequence */
88 st_match_range, /* After - in a %[ sequence */
89 } state = st_normal;
90 char *sarg = NULL; /* %s %c or %[ string argument */
91 enum bail bail = bail_none;
92 int sign;
93 int converted = 0; /* Successful conversions */
94 unsigned long matchmap[((1 << CHAR_BIT)+(LONG_BIT-1))/LONG_BIT];
95 int matchinv = 0; /* Is match map inverted? */
96 unsigned char range_start = 0;
98 while ( (ch = *p++) && !bail ) {
99 switch ( state ) {
100 case st_normal:
101 if ( ch == '%' ) {
102 state = st_flags;
103 flags = 0; rank = rank_int; 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)", 0 };
179 const char * const *np;
181 /* Check to see if it's a null pointer by name */
182 for ( np = nullnames ; *np ; np++ ) {
183 if ( !strncasecmp(q, *np, strlen(*np)) ) {
184 val = (uintmax_t)((void *)NULL);
185 goto set_integer;
188 /* Failure */
189 bail = bail_err;
190 break;
192 /* else */
193 #endif
194 rank = rank_ptr;
195 base = 0; sign = 0;
196 goto scan_int;
198 case 'i': /* Base-independent integer */
199 base = 0; sign = 1;
200 goto scan_int;
202 case 'd': /* Decimal integer */
203 base = 10; sign = 1;
204 goto scan_int;
206 case 'o': /* Octal integer */
207 base = 8; sign = 0;
208 goto scan_int;
210 case 'u': /* Unsigned decimal integer */
211 base = 10; sign = 0;
212 goto scan_int;
214 case 'x': /* Hexadecimal integer */
215 case 'X':
216 base = 16; sign = 0;
217 goto scan_int;
219 case 'n': /* Number of characters consumed */
220 val = (q-buffer);
221 goto set_integer;
223 scan_int:
224 q = skipspace(q);
225 if ( !*q ) {
226 bail = bail_eof;
227 break;
229 val = strntoumax(q, (char **)&qq, base, width);
230 if ( qq == q ) {
231 bail = bail_err;
232 break;
234 q = qq;
235 converted++;
236 /* fall through */
238 set_integer:
239 if ( !(flags & FL_SPLAT) ) {
240 switch(rank) {
241 case rank_char:
242 *va_arg(ap, unsigned char *) = (unsigned char)val;
243 break;
244 case rank_short:
245 *va_arg(ap, unsigned short *) = (unsigned short)val;
246 break;
247 case rank_int:
248 *va_arg(ap, unsigned int *) = (unsigned int)val;
249 break;
250 case rank_long:
251 *va_arg(ap, unsigned long *) = (unsigned long)val;
252 break;
253 case rank_longlong:
254 *va_arg(ap, unsigned long long *) = (unsigned long long)val;
255 break;
256 case rank_ptr:
257 *va_arg(ap, void **) = (void *)(uintptr_t)val;
258 break;
261 break;
263 case 'c': /* Character */
264 width = (flags & FL_WIDTH) ? width : 1; /* Default width == 1 */
265 sarg = va_arg(ap, char *);
266 while ( width-- ) {
267 if ( !*q ) {
268 bail = bail_eof;
269 break;
271 *sarg++ = *q++;
273 if ( !bail )
274 converted++;
275 break;
277 case 's': /* String */
279 char *sp;
280 sp = sarg = va_arg(ap, char *);
281 while ( width-- && *q && !isspace((unsigned char)*q) ) {
282 *sp++ = *q++;
284 if ( sarg != sp ) {
285 *sp = '\0'; /* Terminate output */
286 converted++;
287 } else {
288 bail = bail_eof;
291 break;
293 case '[': /* Character range */
294 sarg = va_arg(ap, char *);
295 state = st_match_init;
296 matchinv = 0;
297 memset(matchmap, 0, sizeof matchmap);
298 break;
300 case '%': /* %% sequence */
301 if ( *q == '%' )
302 q++;
303 else
304 bail = bail_err;
305 break;
307 default: /* Anything else */
308 bail = bail_err; /* Unknown sequence */
309 break;
312 break;
314 case st_match_init: /* Initial state for %[ match */
315 if ( ch == '^' && !(flags & FL_INV) ) {
316 matchinv = 1;
317 } else {
318 set_bit(matchmap, (unsigned char)ch);
319 state = st_match;
321 break;
323 case st_match: /* Main state for %[ match */
324 if ( ch == ']' ) {
325 goto match_run;
326 } else if ( ch == '-' ) {
327 range_start = (unsigned char)ch;
328 state = st_match_range;
329 } else {
330 set_bit(matchmap, (unsigned char)ch);
332 break;
334 case st_match_range: /* %[ match after - */
335 if ( ch == ']' ) {
336 set_bit(matchmap, (unsigned char)'-'); /* - was last character */
337 goto match_run;
338 } else {
339 int i;
340 for ( i = range_start ; i < (unsigned char)ch ; i++ )
341 set_bit(matchmap, i);
342 state = st_match;
344 break;
346 match_run: /* Match expression finished */
347 qq = q;
348 while ( width && *q && test_bit(matchmap, (unsigned char)*q)^matchinv ) {
349 *sarg++ = *q++;
351 if ( q != qq ) {
352 *sarg = '\0';
353 converted++;
354 } else {
355 bail = *q ? bail_err : bail_eof;
357 break;
361 if ( bail == bail_eof && !converted )
362 converted = -1; /* Return EOF (-1) */
364 return converted;