062327d75c5851e7b014bb5e03742e19e6ab1409
[musl] / src / stdio / __scanf.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdarg.h>
4 #include <ctype.h>
5 #include <wchar.h>
6 #include <wctype.h>
7 #include <limits.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <math.h>
11 #include <float.h>
12
13 #include "__scanf.h"
14
15 static int read(rctx_t *r)
16 {
17         if (--r->w < 0) return r->w = -1;
18         if (r->u) r->u = 0;
19         else r->read(r);
20         return r->c;
21 }
22
23 static void unread(rctx_t *r)
24 {
25         if (r->c < 0 || r->w < 0) return;
26         r->w++;
27         r->u = 1;
28 }
29
30 #define SIZE_hh -2
31 #define SIZE_h  -1
32 #define SIZE_def 0
33 #define SIZE_l   1
34 #define SIZE_ll  2
35 #define SIZE_L   3
36
37 static void store_int(void *dest, int size, int neg, unsigned long long i)
38 {
39         if (!dest) return;
40         if (neg) i = -i;
41         switch (size) {
42         case SIZE_hh:
43                 *(char *)dest = i;
44                 break;
45         case SIZE_h:
46                 *(short *)dest = i;
47                 break;
48         case SIZE_def:
49                 *(int *)dest = i;
50                 break;
51         case SIZE_l:
52                 *(long *)dest = i;
53                 break;
54         case SIZE_ll:
55                 *(long long *)dest = i;
56                 break;
57         }
58 }
59
60 static void *arg_n(va_list ap, unsigned int n)
61 {
62         void *p;
63         unsigned int i;
64         va_list ap2;
65         va_copy(ap2, ap);
66         for (i=n; i>1; i--) va_arg(ap2, void *);
67         p = va_arg(ap2, void *);
68         va_end(ap2);
69         return p;
70 }
71
72 int __scanf(rctx_t *r, const wchar_t *fmt, va_list ap)
73 {
74         int mode=0;
75         int width;
76         int size;
77         const wchar_t *p, *z;
78         int c, l, t, m;
79         long long dummy;
80         char *s;
81         wchar_t *wcs;
82         mbstate_t st;
83         int wide = r->wide;
84         void *dest=NULL;
85         int invert;
86         unsigned long long i=0;
87         int neg=0;
88         int matches=0;
89         long double f;
90         int (*is_space)(int) = r->is_space;
91
92         for (p=fmt; *p; ) {
93                 if (is_space(*p)) {
94                         do p++; while (is_space(*p));
95                         do r->w=1; while (is_space(read(r)));
96                         unread(r);
97                         continue;
98                 } else if (*p != '%' || p[1] == '%') {
99                         if (*p == '%') p++;
100                         r->w = 1;
101                         if ((c = read(r)) < 0)
102                                 goto input_fail;
103                         if (*p++ != c)
104                                 goto match_fail;
105                         continue;
106                 }
107                 p++;
108                 if (mode != 1) {
109                         for (z=p; isdigit(*z); z++);
110                         if (*z != '$' && *z != '*') {
111                                 if (mode == 0) mode = 1;
112                                 else goto fmt_fail;
113                         } else if (*z != '*') {
114                                 int pos = 0;
115                                 mode = 2;
116                                 for (; p<z; p++) {
117                                         pos = 10*pos + *p - '0';
118                                 }
119                                 p++;
120                                 if (!pos) goto fmt_fail;
121                                 dest = arg_n(ap, pos);
122                         }
123                 }
124                 if (*p == '*') {
125                         dest = NULL;
126                         p++;
127                 } else if (mode == 1) {
128                         dest = va_arg(ap, void *);
129                 }
130                 
131                 if (!*p) goto fmt_fail;
132
133                 width = 0;
134                 for (; isdigit(*p); p++) {
135                         width = 10*width + *p - '0';
136                 }
137
138                 size = 0;
139                 switch (*p++) {
140                 case 0:
141                         goto fmt_fail;
142                 case 'h':
143                         if (*p == 'h') p++, size = SIZE_hh;
144                         else size = SIZE_h;
145                         break;
146                 case 'l':
147                         if (*p == 'l') p++, size = SIZE_ll;
148                         else size = SIZE_l;
149                         break;
150                 case 'j':
151                         size = SIZE_ll;
152                         break;
153                 case 'z':
154                 case 't':
155                         size = SIZE_l;
156                         break;
157                 case 'L':
158                         size = SIZE_L;
159                         break;
160                 case 'd': case 'i': case 'o': case 'u': case 'x':
161                 case 'a': case 'e': case 'f': case 'g':
162                 case 'A': case 'E': case 'F': case 'G': case 'X':
163                 case 's': case 'c': case '[':
164                 case 'S': case 'C':
165                 case 'p': case 'n':
166                         p--;
167                         break;
168                 default:
169                         goto fmt_fail;
170                 }
171
172                 t = *p++;
173
174                 switch (t) {
175                 case 'C':
176                 case 'c':
177                         if (width < 1) width = 1;
178                 case 's':
179                         if (size == SIZE_l) t &= ~0x20;
180                 case 'd': case 'i': case 'o': case 'u': case 'x':
181                 case 'a': case 'e': case 'f': case 'g':
182                 case 'A': case 'E': case 'F': case 'G': case 'X':
183                 case '[': case 'S':
184                 case 'p': case 'n':
185                         if (width < 1) width = INT_MAX;
186                         break;
187                 default:
188                         goto fmt_fail;
189                 }
190
191                 r->w = width;
192
193                 if (t != 'n') {
194                         if (read(r) < 0) goto input_fail;
195                         unread(r);
196                 }
197
198                 switch (t) {
199                 case 'n':
200                         store_int(dest, size, 0, r->l - r->u);
201                         /* do not increment match count, etc! */
202                         continue;
203                 case 'C':
204                         wcs = dest ? dest : (void *)&dummy;
205                         st = (mbstate_t){ 0 };
206                         while ((c=read(r)) >= 0) {
207                                 if (wide) {
208                                         if (dest) *wcs++ = c;
209                                 } else {
210                                         char ch = c;
211                                         switch (mbrtowc(wcs, &ch, 1, &st)) {
212                                         case -1:
213                                                 goto enc_fail;
214                                         case -2:
215                                                 break;
216                                         default:
217                                                 if (dest) wcs++;
218                                         }
219                                 }
220                         }
221                         if (r->w > 0) goto match_fail;
222                         break;
223                 case 'c':
224                         s = dest ? dest : (void *)&dummy;
225                         while ((c=read(r)) >= 0) {
226                                 if (wide) {
227                                         if ((l=wctomb(s, c)) < 0)
228                                                 goto enc_fail;
229                                         if (dest) s += l;
230                                 } else {
231                                         if (dest) *s++ = c;
232                                 }
233                         }
234                         if (r->w > 0) goto match_fail;
235                         break;
236                 case '[':
237                         wcs = dest ? dest : (void *)&dummy;
238                         s = dest ? dest : (void *)&dummy;
239                         if (!wide && size == SIZE_l) st = (mbstate_t){ 0 };
240
241                         if (*p == '^') p++, invert = 1;
242                         else invert = 0;
243
244                         if (wide) {
245                                 for (m=0; (c=read(r)) >= 0; m=1) {
246                                         for (z=p; *z && *z != c && (*z != ']' || z==p); z++);
247                                         if (!*z) goto fmt_fail;
248                                         if (*z == c && (*z != ']' || z==p)) {
249                                                 if (invert) break;
250                                         } else {
251                                                 if (!invert) break;
252                                         }
253                                         if (size == SIZE_l) {
254                                                 if (dest) *wcs++ = c;
255                                         } else {
256                                                 if ((l=wctomb(s, c)) < 0)
257                                                         goto enc_fail;
258                                                 if (dest) s += l;
259                                         }
260                                 }
261                                 for (p++; *p && *p != ']'; p++);
262                                 p++;
263                         } else {
264                                 unsigned char scanset[257];
265                                 memset(scanset, invert, sizeof scanset);
266                                 scanset[0] = 0;
267                                 for (z=p; *z && (*z != ']' || z==p); z++)
268                                         scanset[1+*z] = 1-invert;
269                                 if (!*z) goto fmt_fail;
270                                 p=z+1;
271                                 c=0;
272                                 for (m=0; scanset[(c=read(r))+1]; m=1) {
273                                         if (size == SIZE_l) {
274                                                 char ch = c;
275                                                 switch (mbrtowc(wcs, &ch, 1, &st)) {
276                                                 case -1:
277                                                         goto enc_fail;
278                                                 case -2:
279                                                         break;
280                                                 default:
281                                                         if (dest) wcs++;
282                                                 }
283                                         } else {
284                                                 if (dest) *s++ = c;
285                                         }
286                                 }
287                         }
288                         if (!m) goto match_fail;
289                         if (dest) {
290                                 if (size == SIZE_l) *wcs++ = 0;
291                                 else *s++ = 0;
292                         }
293                         break;
294                 default:
295                         /* read unlimited number of spaces, then reset width */
296                         do r->w = 1; while (is_space(c = read(r)));
297                         if (c < 0) goto input_fail;
298                         unread(r);
299                         r->w = width;
300                 }
301
302                 switch (t) {
303                 case 'p':
304                 case 'X':
305                         t = 'x';
306                 case 'd':
307                 case 'i':
308                 case 'o':
309                 case 'u':
310                 case 'x':
311                         i = m = neg = 0;
312                         if ((c=read(r)) == '-') neg=1;
313                         else if (c != '+') unread(r);
314                         switch (t) {
315                         case 'i':
316                         case 'x':
317                                 if ((c=read(r)) != '0') {
318                                         if (t == 'i') t = 'd';
319                                         unread(r);
320                                         break;
321                                 }
322                                 if (((c=read(r))|0x20) != 'x') {
323                                         if (t == 'i') {
324                                                 t = 'o';
325                                                 /* lone 0 is valid octal */
326                                                 if ((unsigned)(c-'0') >= 8) {
327                                                         m = 1;
328                                                         goto int_finish;
329                                                 }
330                                         }
331                                         unread(r);
332                                         break;
333                                 }
334                                 t = 'x';
335                         }
336                 }
337                 
338                 switch (t) {
339                 case 'd':
340                 case 'u':
341                         for (m=0; isdigit(c=read(r)); m=1)
342                                 i = 10*i + c-'0';
343                         goto int_finish;
344                 case 'o':
345                         for (m=0; (unsigned)(c=read(r))-'0' < 8; m=1)
346                                 i = (i<<3) + c-'0';
347                         goto int_finish;
348                 case 'x':
349                         for (m=0; ; m=1) {
350                                 if (isdigit(c=read(r))) {
351                                         i = (i<<4) + c-'0';
352                                 } else if ((unsigned)(c|0x20)-'a' < 6) {
353                                         i = (i<<4) + (c|0x20)-'a'+10;
354                                 } else break;
355                         }
356                 int_finish:
357                         if (!m) goto match_fail;
358                         store_int(dest, size, neg, i);
359                         break;
360                 case 'a':
361                 case 'e':
362                 case 'f':
363                 case 'g':
364                         f = 0.0;
365                         neg = m = 0;
366                         if ((c=read(r)) == '-') neg=1;
367                         else if (c != '+') unread(r);
368                         /* FIXME: check for INF/NAN strings here */
369                         if (read(r)=='0' && (m=1, (read(r)|0x20) == 'x'))
370                                 goto hexfloat;
371                         else unread(r);
372                         for (; isdigit(c=read(r)); m=1)
373                                 f = 10.0 * f + (c-'0');
374                         if (c=='.') {
375                                 double mag = 10.0;
376                                 for (; isdigit(c=read(r)); mag*=10.0)
377                                         f += (c-'0')/mag;
378                         }
379                         if ((c|0x20)=='e') {
380                                 int ex=0, en=0;
381                                 m = 0;
382                                 if ((c=read(r))=='-') en=1;
383                                 else if (c!='+') unread(r);
384                                 for (; isdigit(c=read(r)); m=1)
385                                         if (ex < LDBL_MAX_10_EXP)
386                                                 ex = 10 * ex + (c-'0');
387                                 if (ex > LDBL_MAX_10_EXP)
388                                         f = en ? 0 : INFINITY;
389                                 else {
390                                         if (en) while (ex--) f/=10.0;
391                                         else while (ex--) f*=10.0;
392                                 }
393                         }
394                         goto writefloat;
395 hexfloat:
396                         m = 0;
397                         for (; isxdigit(c=read(r)); m=1)
398                                 if (isdigit(c)) f = 16.0*f + (c-'0');
399                                 else f = 16.0*f + ((c|32)-'a'+10);
400                         if (c=='.') {
401                                 double mag = 1/16.0;
402                                 for (; isxdigit(c=read(r)); mag*=1/16.0)
403                                         if (isdigit(c)) f += (c-'0')*mag;
404                                         else f += ((c|32)-'a'+10)*mag;
405                         }
406                         if ((c|0x20)=='p') {
407                                 int ex=0, en=0;
408                                 m = 0;
409                                 if ((c=read(r))=='-') en=1;
410                                 else if (c!='+') unread(r);
411                                 for (; isdigit(c=read(r)); m=1)
412                                         if (ex < LDBL_MAX_EXP)
413                                                 ex = 10 * ex + (c-'0');
414                                 if (ex > LDBL_MAX_EXP)
415                                         f = en ? 0 : INFINITY;
416                                 else {
417                                         if (en) while (ex--) f*=0.5;
418                                         else while (ex--) f*=2.0;
419                                 }
420                         }
421 writefloat:
422                         if (!m) goto match_fail;
423                         if (neg) f *= -1.0;
424                         if (dest) switch (size) {
425                         case SIZE_def:
426                                 *(float *)dest = f;
427                                 break;
428                         case SIZE_l:
429                                 *(double *)dest = f;
430                                 break;
431                         case SIZE_L:
432                                 *(long double *)dest = f;
433                                 break;
434                         }
435                         break;
436                 case 'S':
437                         wcs = dest ? dest : (void *)&dummy;
438                         st = (mbstate_t){ 0 };
439                         while((c=read(r)) >= 0) {
440                                 if (wide) {
441                                         if (is_space(c)) break;
442                                         if (dest) *wcs++ = c;
443                                 } else {
444                                         char ch = c;
445                                         if (is_space(c)) break;
446                                         switch (mbrtowc(wcs, &ch, 1, &st)) {
447                                         case -1:
448                                                 goto enc_fail;
449                                         case -2:
450                                                 break;
451                                         default:
452                                                 if (dest) wcs++;
453                                         }
454                                 }
455                         }
456                         if (dest) *wcs++ = 0;
457                         break;
458                 case 's':
459                         s = dest ? dest : (void *)&dummy;
460                         while((c=read(r)) >= 0) {
461                                 if (wide) {
462                                         if (is_space(c)) break;
463                                         if ((l=wctomb(s, c)) < 0)
464                                                 goto enc_fail;
465                                         if (dest) s += l;
466                                 } else {
467                                         if (is_space(c)) break;
468                                         if (dest) *s++ = c;
469                                 }
470                         }
471                         if (dest) *s++ = 0;
472                         break;
473                 }
474
475                 /* unread will do nothing if field width was exhausted */
476                 unread(r);
477                 if (dest) matches++;
478         }
479         return matches;
480 enc_fail:
481         errno = EILSEQ;
482 fmt_fail:
483 input_fail:
484         if (!matches) matches--;
485 match_fail:
486         unread(r);
487         return matches;
488 }