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