]> git.proxmox.com Git - mirror_edk2.git/blob - StdLib/LibC/Stdio/vfscanf.c
Add device abstraction code for the UEFI Console and UEFI Shell-based file systems.
[mirror_edk2.git] / StdLib / LibC / Stdio / vfscanf.c
1 /** @file
2 Implementation of scanf internals for <stdio.h>.
3
4 Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials are licensed and made available
6 under the terms and conditions of the BSD License that accompanies this
7 distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 Copyright (c) 1990, 1993
14 The Regents of the University of California. All rights reserved.
15
16 This code is derived from software contributed to Berkeley by
17 Chris Torek.
18
19 Redistribution and use in source and binary forms, with or without
20 modification, are permitted provided that the following conditions
21 are met:
22 - Redistributions of source code must retain the above copyright
23 notice, this list of conditions and the following disclaimer.
24 - Redistributions in binary form must reproduce the above copyright
25 notice, this list of conditions and the following disclaimer in the
26 documentation and/or other materials provided with the distribution.
27 - Neither the name of the University nor the names of its contributors
28 may be used to endorse or promote products derived from this software
29 without specific prior written permission.
30
31 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
32 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
35 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
36 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
39 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41 POSSIBILITY OF SUCH DAMAGE.
42
43 NetBSD: vfscanf.c,v 1.37.4.1 2007/05/07 19:49:08 pavel Exp
44 FreeBSD: src/lib/libc/stdio/vfscanf.c,v 1.41 2007/01/09 00:28:07 imp Exp
45 vfscanf.c 8.1 (Berkeley) 6/4/93
46 **/
47 #include <LibConfig.h>
48
49 #include "namespace.h"
50 #include <assert.h>
51 #include <ctype.h>
52 #include <errno.h>
53 #include <inttypes.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <stddef.h>
57 #include <stdarg.h>
58 #include <string.h>
59 #include <sys/types.h>
60 #include <wchar.h>
61 #include <wctype.h>
62
63 #include "reentrant.h"
64 #include "local.h"
65
66 #ifndef NO_FLOATING_POINT
67 #include <locale.h>
68 #endif
69
70 /*
71 * Provide an external name for vfscanf. Note, we don't use the normal
72 * namespace.h method; stdio routines explicitly use the internal name
73 * __svfscanf.
74 */
75 #ifdef __weak_alias
76 __weak_alias(vfscanf,__svfscanf)
77 #endif
78
79 #define BUF 513 /* Maximum length of numeric string. */
80
81 /*
82 * Flags used during conversion.
83 */
84 #define LONG 0x0001 /* l: long or double */
85 #define LONGDBL 0x0002 /* L: long double */
86 #define SHORT 0x0004 /* h: short */
87 #define SUPPRESS 0x0008 /* *: suppress assignment */
88 #define POINTER 0x0010 /* p: void * (as hex) */
89 #define NOSKIP 0x0020 /* [ or c: do not skip blanks */
90 #define LONGLONG 0x0400 /* ll: long long (+ deprecated q: quad) */
91 #define INTMAXT 0x0800 /* j: intmax_t */
92 #define PTRDIFFT 0x1000 /* t: ptrdiff_t */
93 #define SIZET 0x2000 /* z: size_t */
94 #define SHORTSHORT 0x4000 /* hh: char */
95 #define UNSIGNED 0x8000 /* %[oupxX] conversions */
96
97 /*
98 * The following are used in integral conversions only:
99 * SIGNOK, NDIGITS, PFXOK, and NZDIGITS
100 */
101 #define SIGNOK 0x00040 /* +/- is (still) legal */
102 #define NDIGITS 0x00080 /* no digits detected */
103 #define PFXOK 0x00100 /* 0x prefix is (still) legal */
104 #define NZDIGITS 0x00200 /* no zero digits detected */
105 #define HAVESIGN 0x10000 /* sign detected */
106
107 /*
108 * Conversion types.
109 */
110 #define CT_CHAR 0 /* %c conversion */
111 #define CT_CCL 1 /* %[...] conversion */
112 #define CT_STRING 2 /* %s conversion */
113 #define CT_INT 3 /* %[dioupxX] conversion */
114 #define CT_FLOAT 4 /* %[efgEFG] conversion */
115
116 static const u_char *__sccl(char *, const u_char *);
117 #ifndef NO_FLOATING_POINT
118 static int parsefloat(FILE *, char *, char *);
119 #endif
120
121 int __scanfdebug = 0;
122
123 #define __collate_load_error /*CONSTCOND*/0
124 static int
125 __collate_range_cmp(int c1, int c2)
126 {
127 static char s1[2], s2[2];
128
129 s1[0] = (char)c1;
130 s2[0] = (char)c2;
131 return strcoll(s1, s2);
132 }
133
134
135 /*
136 * __svfscanf - MT-safe version
137 */
138 int
139 __svfscanf(FILE *fp, char const *fmt0, va_list ap)
140 {
141 int ret;
142
143 if(fp == NULL) {
144 errno = EINVAL;
145 return (EOF);
146 }
147 FLOCKFILE(fp);
148 ret = __svfscanf_unlocked(fp, fmt0, ap);
149 FUNLOCKFILE(fp);
150 return (ret);
151 }
152
153 /*
154 * __svfscanf_unlocked - non-MT-safe version of __svfscanf
155 */
156 int
157 __svfscanf_unlocked(FILE *fp, const char *fmt0, va_list ap)
158 {
159 const u_char *fmt = (const u_char *)fmt0;
160 int c; /* character from format, or conversion */
161 size_t width; /* field width, or 0 */
162 char *p; /* points into all kinds of strings */
163 size_t n; /* handy size_t */
164 int flags; /* flags as defined above */
165 char *p0; /* saves original value of p when necessary */
166 int nassigned; /* number of fields assigned */
167 int nconversions; /* number of conversions */
168 int nread; /* number of characters consumed from fp */
169 int base; /* base argument to conversion function */
170 char ccltab[256]; /* character class table for %[...] */
171 char buf[BUF]; /* buffer for numeric and mb conversions */
172 wchar_t *wcp; /* handy wide character pointer */
173 size_t nconv; /* length of multibyte sequence converted */
174 static const mbstate_t initial = { 0 };
175 mbstate_t mbs;
176
177 /* `basefix' is used to avoid `if' tests in the integer scanner */
178 static const short basefix[17] =
179 { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
180
181 _DIAGASSERT(fp != NULL);
182 _DIAGASSERT(fmt0 != NULL);
183 if(fp == NULL) {
184 errno = EINVAL;
185 return (EOF);
186 }
187
188 _SET_ORIENTATION(fp, -1);
189
190 //Print(L"%a( %d, \"%a\", ...)\n", __func__, fp->_file, fmt0);
191 nassigned = 0;
192 nconversions = 0;
193 nread = 0;
194 base = 0;
195 for (;;) {
196 c = (unsigned char)*fmt++;
197 if (c == 0)
198 return (nassigned);
199 if (isspace(c)) {
200 while ((fp->_r > 0 || __srefill(fp) == 0) &&
201 isspace(*fp->_p))
202 nread++, fp->_r--, fp->_p++;
203 continue;
204 }
205 //Print(L"%a: %d\n", __func__, __LINE__);
206 if (c != '%')
207 goto literal;
208 width = 0;
209 flags = 0;
210 /*
211 * switch on the format. continue if done;
212 * break once format type is derived.
213 */
214 again: c = *fmt++;
215 //Print(L"%a: %d\n", __func__, __LINE__);
216 switch (c) {
217 case '%':
218 literal:
219 //Print(L"%a: %d\n", __func__, __LINE__);
220 if (fp->_r <= 0 && __srefill(fp))
221 goto input_failure;
222 if (*fp->_p != c)
223 goto match_failure;
224 fp->_r--, fp->_p++;
225 nread++;
226 continue;
227
228 case '*':
229 flags |= SUPPRESS;
230 goto again;
231 case 'j':
232 flags |= INTMAXT;
233 goto again;
234 case 'l':
235 if (flags & LONG) {
236 flags &= ~LONG;
237 flags |= LONGLONG;
238 } else
239 flags |= LONG;
240 goto again;
241 case 'q':
242 flags |= LONGLONG; /* not quite */
243 goto again;
244 case 't':
245 flags |= PTRDIFFT;
246 goto again;
247 case 'z':
248 flags |= SIZET;
249 goto again;
250 case 'L':
251 flags |= LONGDBL;
252 goto again;
253 case 'h':
254 if (flags & SHORT) {
255 flags &= ~SHORT;
256 flags |= SHORTSHORT;
257 } else
258 flags |= SHORT;
259 goto again;
260
261 case '0': case '1': case '2': case '3': case '4':
262 case '5': case '6': case '7': case '8': case '9':
263 width = width * 10 + c - '0';
264 goto again;
265
266 /*
267 * Conversions.
268 */
269 case 'd':
270 c = CT_INT;
271 base = 10;
272 break;
273
274 case 'i':
275 c = CT_INT;
276 base = 0;
277 break;
278
279 case 'o':
280 c = CT_INT;
281 flags |= UNSIGNED;
282 base = 8;
283 break;
284
285 case 'u':
286 c = CT_INT;
287 flags |= UNSIGNED;
288 base = 10;
289 break;
290
291 case 'X':
292 case 'x':
293 flags |= PFXOK; /* enable 0x prefixing */
294 c = CT_INT;
295 flags |= UNSIGNED;
296 base = 16;
297 break;
298
299 #ifndef NO_FLOATING_POINT
300 case 'A': case 'E': case 'F': case 'G':
301 case 'a': case 'e': case 'f': case 'g':
302 c = CT_FLOAT;
303 break;
304 #endif
305
306 case 'S':
307 flags |= LONG;
308 /* FALLTHROUGH */
309 case 's':
310 c = CT_STRING;
311 break;
312
313 case '[':
314 fmt = __sccl(ccltab, fmt);
315 flags |= NOSKIP;
316 c = CT_CCL;
317 break;
318
319 case 'C':
320 flags |= LONG;
321 /* FALLTHROUGH */
322 case 'c':
323 flags |= NOSKIP;
324 c = CT_CHAR;
325 break;
326
327 case 'p': /* pointer format is like hex */
328 flags |= POINTER | PFXOK;
329 c = CT_INT; /* assumes sizeof(uintmax_t) */
330 flags |= UNSIGNED; /* >= sizeof(uintptr_t) */
331 base = 16;
332 break;
333
334 case 'n':
335 nconversions++;
336 if (flags & SUPPRESS) /* ??? */
337 continue;
338 if (flags & SHORTSHORT)
339 *va_arg(ap, char *) = (char)nread;
340 else if (flags & SHORT)
341 *va_arg(ap, short *) = (short)nread;
342 else if (flags & LONG)
343 *va_arg(ap, long *) = nread;
344 else if (flags & LONGLONG)
345 *va_arg(ap, long long *) = nread;
346 else if (flags & INTMAXT)
347 *va_arg(ap, intmax_t *) = nread;
348 else if (flags & SIZET)
349 *va_arg(ap, size_t *) = nread;
350 else if (flags & PTRDIFFT)
351 *va_arg(ap, ptrdiff_t *) = nread;
352 else
353 *va_arg(ap, int *) = nread;
354 continue;
355
356 default:
357 goto match_failure;
358
359 /*
360 * Disgusting backwards compatibility hack. XXX
361 */
362 case '\0': /* compat */
363 return (EOF);
364 }
365 //Print(L"%a: %d\n", __func__, __LINE__);
366
367 /*
368 * We have a conversion that requires input.
369 */
370 if (fp->_r <= 0 && __srefill(fp))
371 {
372 //Print(L"%a: %d\n", __func__, __LINE__);
373 goto input_failure;
374 }
375
376 /*
377 * Consume leading white space, except for formats
378 * that suppress this.
379 */
380 if ((flags & NOSKIP) == 0) {
381 while (isspace(*fp->_p)) {
382 nread++;
383 if (--fp->_r > 0)
384 fp->_p++;
385 else if (__srefill(fp))
386 {
387 //Print(L"%a: %d\n", __func__, __LINE__);
388 goto input_failure;
389 }
390 }
391 /*
392 * Note that there is at least one character in
393 * the buffer, so conversions that do not set NOSKIP
394 * ca no longer result in an input failure.
395 */
396 }
397
398 /*
399 * Do the conversion.
400 */
401 //Print(L"%a: %d\n", __func__, __LINE__);
402 switch (c) {
403
404 case CT_CHAR:
405 /* scan arbitrary characters (sets NOSKIP) */
406 if (width == 0)
407 width = 1;
408 if (flags & LONG) {
409 if ((flags & SUPPRESS) == 0)
410 wcp = va_arg(ap, wchar_t *);
411 else
412 wcp = NULL;
413 n = 0;
414 while (width != 0) {
415 if (n == MB_CUR_MAX) {
416 fp->_flags |= __SERR;
417 goto input_failure;
418 }
419 buf[n++] = *fp->_p;
420 fp->_p++;
421 fp->_r--;
422 mbs = initial;
423 nconv = mbrtowc(wcp, buf, n, &mbs);
424 if (nconv == (size_t)-1) {
425 fp->_flags |= __SERR;
426 goto input_failure;
427 }
428 if (nconv == 0 && !(flags & SUPPRESS))
429 *wcp = L'\0';
430 if (nconv != (size_t)-2) {
431 nread += (int)n;
432 width--;
433 if (!(flags & SUPPRESS))
434 wcp++;
435 n = 0;
436 }
437 if (fp->_r <= 0 && __srefill(fp)) {
438 if (n != 0) {
439 fp->_flags |= __SERR;
440 goto input_failure;
441 }
442 break;
443 }
444 }
445 if (!(flags & SUPPRESS))
446 nassigned++;
447 } else if (flags & SUPPRESS) {
448 size_t sum = 0;
449 for (;;) {
450 if ((n = fp->_r) < width) {
451 sum += n;
452 width -= n;
453 fp->_p += n;
454 if (__srefill(fp)) {
455 if (sum == 0)
456 goto input_failure;
457 break;
458 }
459 } else {
460 sum += width;
461 fp->_r -= (int)width;
462 fp->_p += width;
463 break;
464 }
465 }
466 nread += (int)sum;
467 } else {
468 size_t r = fread(va_arg(ap, char *), 1,
469 width, fp);
470
471 if (r == 0)
472 goto input_failure;
473 nread += (int)r;
474 nassigned++;
475 }
476 nconversions++;
477 break;
478
479 case CT_CCL:
480 /* scan a (nonempty) character class (sets NOSKIP) */
481 if (width == 0)
482 width = (size_t)~0; /* `infinity' */
483 /* take only those things in the class */
484 if (flags & LONG) {
485 wchar_t twc;
486 int nchars;
487
488 if ((flags & SUPPRESS) == 0)
489 wcp = va_arg(ap, wchar_t *);
490 else
491 wcp = &twc;
492 n = 0;
493 nchars = 0;
494 while (width != 0) {
495 if (n == MB_CUR_MAX) {
496 fp->_flags |= __SERR;
497 goto input_failure;
498 }
499 buf[n++] = *fp->_p;
500 fp->_p++;
501 fp->_r--;
502 mbs = initial;
503 nconv = mbrtowc(wcp, buf, n, &mbs);
504 if (nconv == (size_t)-1) {
505 fp->_flags |= __SERR;
506 goto input_failure;
507 }
508 if (nconv == 0)
509 *wcp = L'\0';
510 if (nconv != (size_t)-2) {
511 if (wctob(*wcp) != EOF &&
512 !ccltab[wctob(*wcp)]) {
513 while (n != 0) {
514 n--;
515 (void)ungetc(buf[n],
516 fp);
517 }
518 break;
519 }
520 nread += (int)n;
521 width--;
522 if (!(flags & SUPPRESS))
523 wcp++;
524 nchars++;
525 n = 0;
526 }
527 if (fp->_r <= 0 && __srefill(fp)) {
528 if (n != 0) {
529 fp->_flags |= __SERR;
530 goto input_failure;
531 }
532 break;
533 }
534 }
535 if (n != 0) {
536 fp->_flags |= __SERR;
537 goto input_failure;
538 }
539 n = nchars;
540 if (n == 0)
541 goto match_failure;
542 if (!(flags & SUPPRESS)) {
543 *wcp = L'\0';
544 nassigned++;
545 }
546 } else if (flags & SUPPRESS) {
547 n = 0;
548 while (ccltab[*fp->_p]) {
549 n++, fp->_r--, fp->_p++;
550 if (--width == 0)
551 break;
552 if (fp->_r <= 0 && __srefill(fp)) {
553 if (n == 0)
554 goto input_failure;
555 break;
556 }
557 }
558 if (n == 0)
559 goto match_failure;
560 } else {
561 p0 = p = va_arg(ap, char *);
562 while (ccltab[*fp->_p]) {
563 fp->_r--;
564 *p++ = *fp->_p++;
565 if (--width == 0)
566 break;
567 if (fp->_r <= 0 && __srefill(fp)) {
568 if (p == p0)
569 goto input_failure;
570 break;
571 }
572 }
573 n = p - p0;
574 if (n == 0)
575 goto match_failure;
576 *p = 0;
577 nassigned++;
578 }
579 nread += (int)n;
580 nconversions++;
581 break;
582
583 case CT_STRING:
584 /* like CCL, but zero-length string OK, & no NOSKIP */
585 if (width == 0)
586 width = (size_t)~0;
587 if (flags & LONG) {
588 wchar_t twc;
589
590 if ((flags & SUPPRESS) == 0)
591 wcp = va_arg(ap, wchar_t *);
592 else
593 wcp = &twc;
594 n = 0;
595 while (!isspace(*fp->_p) && width != 0) {
596 if (n == MB_CUR_MAX) {
597 fp->_flags |= __SERR;
598 goto input_failure;
599 }
600 buf[n++] = *fp->_p;
601 fp->_p++;
602 fp->_r--;
603 mbs = initial;
604 nconv = mbrtowc(wcp, buf, n, &mbs);
605 if (nconv == (size_t)-1) {
606 fp->_flags |= __SERR;
607 goto input_failure;
608 }
609 if (nconv == 0)
610 *wcp = L'\0';
611 if (nconv != (size_t)-2) {
612 if (iswspace(*wcp)) {
613 while (n != 0) {
614 n--;
615 (void)ungetc(buf[n],
616 fp);
617 }
618 break;
619 }
620 nread += (int)n;
621 width--;
622 if (!(flags & SUPPRESS))
623 wcp++;
624 n = 0;
625 }
626 if (fp->_r <= 0 && __srefill(fp)) {
627 if (n != 0) {
628 fp->_flags |= __SERR;
629 goto input_failure;
630 }
631 break;
632 }
633 }
634 if (!(flags & SUPPRESS)) {
635 *wcp = L'\0';
636 nassigned++;
637 }
638 } else if (flags & SUPPRESS) {
639 n = 0;
640 while (!isspace(*fp->_p)) {
641 n++, fp->_r--, fp->_p++;
642 if (--width == 0)
643 break;
644 if (fp->_r <= 0 && __srefill(fp))
645 break;
646 }
647 nread += (int)n;
648 } else {
649 p0 = p = va_arg(ap, char *);
650 while (!isspace(*fp->_p)) {
651 fp->_r--;
652 *p++ = *fp->_p++;
653 if (--width == 0)
654 break;
655 if (fp->_r <= 0 && __srefill(fp))
656 break;
657 }
658 *p = 0;
659 nread += (int)(p - p0);
660 nassigned++;
661 }
662 nconversions++;
663 continue;
664
665 case CT_INT:
666 //Print(L"%a: %d\n", __func__, __LINE__);
667 /* scan an integer as if by the conversion function */
668 #ifdef hardway
669 if (width == 0 || width > sizeof(buf) - 1)
670 width = sizeof(buf) - 1;
671 #else
672 /* size_t is unsigned, hence this optimisation */
673 if (--width > sizeof(buf) - 2)
674 width = sizeof(buf) - 2;
675 width++;
676 #endif
677 flags |= SIGNOK | NDIGITS | NZDIGITS;
678 for (p = buf; width; width--) {
679 c = *fp->_p;
680 /*
681 * Switch on the character; `goto ok'
682 * if we accept it as a part of number.
683 */
684 switch (c) {
685
686 /*
687 * The digit 0 is always legal, but is
688 * special. For %i conversions, if no
689 * digits (zero or nonzero) have been
690 * scanned (only signs), we will have
691 * base==0. In that case, we should set
692 * it to 8 and enable 0x prefixing.
693 * Also, if we have not scanned zero digits
694 * before this, do not turn off prefixing
695 * (someone else will turn it off if we
696 * have scanned any nonzero digits).
697 */
698 case '0':
699 if (base == 0) {
700 base = 8;
701 flags |= PFXOK;
702 }
703 if (flags & NZDIGITS)
704 flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
705 else
706 flags &= ~(SIGNOK|PFXOK|NDIGITS);
707 goto ok;
708
709 /* 1 through 7 always legal */
710 case '1': case '2': case '3':
711 case '4': case '5': case '6': case '7':
712 base = basefix[base];
713 flags &= ~(SIGNOK | PFXOK | NDIGITS);
714 goto ok;
715
716 /* digits 8 and 9 ok iff decimal or hex */
717 case '8': case '9':
718 base = basefix[base];
719 if (base <= 8)
720 break; /* not legal here */
721 flags &= ~(SIGNOK | PFXOK | NDIGITS);
722 goto ok;
723
724 /* letters ok iff hex */
725 case 'A': case 'B': case 'C':
726 case 'D': case 'E': case 'F':
727 case 'a': case 'b': case 'c':
728 case 'd': case 'e': case 'f':
729 /* no need to fix base here */
730 if (base <= 10)
731 break; /* not legal here */
732 flags &= ~(SIGNOK | PFXOK | NDIGITS);
733 goto ok;
734
735 /* sign ok only as first character */
736 case '+': case '-':
737 if (flags & SIGNOK) {
738 flags &= ~SIGNOK;
739 flags |= HAVESIGN;
740 goto ok;
741 }
742 break;
743
744 /*
745 * x ok iff flag still set & 2nd char (or
746 * 3rd char if we have a sign).
747 */
748 case 'x': case 'X':
749 if (flags & PFXOK && p ==
750 buf + 1 + !!(flags & HAVESIGN)) {
751 base = 16; /* if %i */
752 flags &= ~PFXOK;
753 goto ok;
754 }
755 break;
756 }
757
758 /*
759 * If we got here, c is not a legal character
760 * for a number. Stop accumulating digits.
761 */
762 break;
763 ok:
764 /*
765 * c is legal: store it and look at the next.
766 */
767 *p++ = (char)c;
768 if (--fp->_r > 0)
769 fp->_p++;
770 else if (__srefill(fp))
771 break; /* EOF */
772 }
773 /*
774 * If we had only a sign, it is no good; push
775 * back the sign. If the number ends in `x',
776 * it was [sign] '0' 'x', so push back the x
777 * and treat it as [sign] '0'.
778 */
779 if (flags & NDIGITS) {
780 if (p > buf)
781 (void)ungetc(*(u_char *)--p, fp);
782 goto match_failure;
783 }
784 c = ((u_char *)p)[-1];
785 if (c == 'x' || c == 'X') {
786 --p;
787 (void)ungetc(c, fp);
788 }
789 if ((flags & SUPPRESS) == 0) {
790 //uintmax_t res;
791 // Use a union to get around the truncation warnings.
792 union {
793 uintmax_t umax;
794 intmax_t imax;
795 void *vp;
796 ptrdiff_t pdt;
797 size_t sz;
798 long long ll;
799 long lo;
800 int in;
801 short hw;
802 char ch;
803 } res;
804
805 *p = 0;
806 if ((flags & UNSIGNED) == 0)
807 res.imax = strtoimax(buf, (char **)NULL, base);
808 else
809 res.umax = strtoumax(buf, (char **)NULL, base);
810 if (flags & POINTER)
811 *va_arg(ap, void **) = res.vp;
812 //(void *)((uintptr_t)res);
813 else if (flags & SHORTSHORT)
814 *va_arg(ap, char *) = res.ch;
815 else if (flags & SHORT)
816 *va_arg(ap, short *) = res.hw;
817 else if (flags & LONG)
818 *va_arg(ap, long *) = res.lo;
819 else if (flags & LONGLONG)
820 *va_arg(ap, long long *) = res.ll;
821 else if (flags & INTMAXT)
822 *va_arg(ap, intmax_t *) = res.imax;
823 else if (flags & PTRDIFFT)
824 *va_arg(ap, ptrdiff_t *) = res.pdt;
825 //(ptrdiff_t)res;
826 else if (flags & SIZET)
827 *va_arg(ap, size_t *) = res.sz;
828 else
829 *va_arg(ap, int *) = res.in;
830 nassigned++;
831 }
832 nread += (int)(p - buf);
833 nconversions++;
834 //Print(L"%a: %d\n", __func__, __LINE__);
835 break;
836
837 #ifndef NO_FLOATING_POINT
838 case CT_FLOAT:
839 /* scan a floating point number as if by strtod */
840 if (width == 0 || width > sizeof(buf) - 1)
841 width = sizeof(buf) - 1;
842 if ((width = parsefloat(fp, buf, buf + width)) == 0)
843 goto match_failure;
844 if ((flags & SUPPRESS) == 0) {
845 if (flags & LONGDBL) {
846 long double **mp = (long double **)ap;
847 long double res = strtold(buf, &p);
848
849 *(*mp) = res;
850 ap += sizeof(long double *);
851 /*???*/ //*va_arg(ap, long double *) = res;
852 } else if (flags & LONG) {
853 double res = strtod(buf, &p);
854 *va_arg(ap, double *) = res;
855 } else {
856 float res = strtof(buf, &p);
857 *va_arg(ap, float *) = res;
858 }
859 if (__scanfdebug && p - buf != (ptrdiff_t)width)
860 abort();
861 nassigned++;
862 }
863 nread += (int)width;
864 nconversions++;
865 break;
866 #endif /* !NO_FLOATING_POINT */
867 }
868 }
869 input_failure:
870 //Print(L"%a: %d\n", __func__, __LINE__);
871 return (nconversions != 0 ? nassigned : EOF);
872 match_failure:
873 return (nassigned);
874 }
875
876 /*
877 * Fill in the given table from the scanset at the given format
878 * (just after `['). Return a pointer to the character past the
879 * closing `]'. The table has a 1 wherever characters should be
880 * considered part of the scanset.
881 */
882 static const u_char *
883 __sccl(char *tab, const u_char *fmt)
884 {
885 int c, n, v, i;
886
887 _DIAGASSERT(tab != NULL);
888 _DIAGASSERT(fmt != NULL);
889 /* first `clear' the whole table */
890 c = *fmt++; /* first char hat => negated scanset */
891 if (c == '^') {
892 v = 1; /* default => accept */
893 c = *fmt++; /* get new first char */
894 } else
895 v = 0; /* default => reject */
896
897 /* XXX: Will not work if sizeof(tab*) > sizeof(char) */
898 (void)memset(tab, v, 256);
899
900 if (c == 0)
901 return (fmt - 1);/* format ended before closing ] */
902
903 /*
904 * Now set the entries corresponding to the actual scanset
905 * to the opposite of the above.
906 *
907 * The first character may be ']' (or '-') without being special;
908 * the last character may be '-'.
909 */
910 v = 1 - v;
911 for (;;) {
912 tab[c] = (char)v; /* take character c */
913 doswitch:
914 n = *fmt++; /* and examine the next */
915 switch (n) {
916
917 case 0: /* format ended too soon */
918 return (fmt - 1);
919
920 case '-':
921 /*
922 * A scanset of the form
923 * [01+-]
924 * is defined as `the digit 0, the digit 1,
925 * the character +, the character -', but
926 * the effect of a scanset such as
927 * [a-zA-Z0-9]
928 * is implementation defined. The V7 Unix
929 * scanf treats `a-z' as `the letters a through
930 * z', but treats `a-a' as `the letter a, the
931 * character -, and the letter a'.
932 *
933 * For compatibility, the `-' is not considerd
934 * to define a range if the character following
935 * it is either a close bracket (required by ANSI)
936 * or is not numerically greater than the character
937 * we just stored in the table (c).
938 */
939 n = *fmt;
940 if (n == ']' || (__collate_load_error ? n < c :
941 __collate_range_cmp(n, c) < 0)) {
942 c = '-';
943 break; /* resume the for(;;) */
944 }
945 fmt++;
946 /* fill in the range */
947 if (__collate_load_error) {
948 do
949 tab[++c] = (char)v;
950 while (c < n);
951 } else {
952 for (i = 0; i < 256; i ++)
953 if (__collate_range_cmp(c, i) < 0 &&
954 __collate_range_cmp(i, n) <= 0)
955 tab[i] = (char)v;
956 }
957 #if 1 /* XXX another disgusting compatibility hack */
958 c = n;
959 /*
960 * Alas, the V7 Unix scanf also treats formats
961 * such as [a-c-e] as `the letters a through e'.
962 * This too is permitted by the standard....
963 */
964 goto doswitch;
965 #else
966 c = *fmt++;
967 if (c == 0)
968 return (fmt - 1);
969 if (c == ']')
970 return (fmt);
971 #endif
972
973 case ']': /* end of scanset */
974 return (fmt);
975
976 default: /* just another character */
977 c = n;
978 break;
979 }
980 }
981 /* NOTREACHED */
982 }
983
984 #ifndef NO_FLOATING_POINT
985 static int
986 parsefloat(FILE *fp, char *buf, char *end)
987 {
988 char *commit, *p;
989 int infnanpos = 0;
990 enum {
991 S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX,
992 S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
993 } state = S_START;
994 unsigned char c;
995 char decpt = *localeconv()->decimal_point;
996 _Bool gotmantdig = 0, ishex = 0;
997
998 if(fp == NULL) {
999 errno = EINVAL;
1000 return (EOF);
1001 }
1002
1003 /*
1004 * We set commit = p whenever the string we have read so far
1005 * constitutes a valid representation of a floating point
1006 * number by itself. At some point, the parse will complete
1007 * or fail, and we will ungetc() back to the last commit point.
1008 * To ensure that the file offset gets updated properly, it is
1009 * always necessary to read at least one character that doesn't
1010 * match; thus, we can't short-circuit "infinity" or "nan(...)".
1011 */
1012 commit = buf - 1;
1013 for (p = buf; p < end; ) {
1014 c = *fp->_p;
1015 reswitch:
1016 switch (state) {
1017 case S_START:
1018 state = S_GOTSIGN;
1019 if (c == '-' || c == '+')
1020 break;
1021 else
1022 goto reswitch;
1023 case S_GOTSIGN:
1024 switch (c) {
1025 case '0':
1026 state = S_MAYBEHEX;
1027 commit = p;
1028 break;
1029 case 'I':
1030 case 'i':
1031 state = S_INF;
1032 break;
1033 case 'N':
1034 case 'n':
1035 state = S_NAN;
1036 break;
1037 default:
1038 state = S_DIGITS;
1039 goto reswitch;
1040 }
1041 break;
1042 case S_INF:
1043 if (infnanpos > 6 ||
1044 (c != "nfinity"[infnanpos] &&
1045 c != "NFINITY"[infnanpos]))
1046 goto parsedone;
1047 if (infnanpos == 1 || infnanpos == 6)
1048 commit = p; /* inf or infinity */
1049 infnanpos++;
1050 break;
1051 case S_NAN:
1052 switch (infnanpos) {
1053 case -1: /* XXX kludge to deal with nan(...) */
1054 goto parsedone;
1055 case 0:
1056 if (c != 'A' && c != 'a')
1057 goto parsedone;
1058 break;
1059 case 1:
1060 if (c != 'N' && c != 'n')
1061 goto parsedone;
1062 else
1063 commit = p;
1064 break;
1065 case 2:
1066 if (c != '(')
1067 goto parsedone;
1068 break;
1069 default:
1070 if (c == ')') {
1071 commit = p;
1072 infnanpos = -2;
1073 } else if (!isalnum(c) && c != '_')
1074 goto parsedone;
1075 break;
1076 }
1077 infnanpos++;
1078 break;
1079 case S_MAYBEHEX:
1080 state = S_DIGITS;
1081 if (c == 'X' || c == 'x') {
1082 ishex = 1;
1083 break;
1084 } else { /* we saw a '0', but no 'x' */
1085 gotmantdig = 1;
1086 goto reswitch;
1087 }
1088 case S_DIGITS:
1089 if ((ishex && isxdigit(c)) || isdigit(c))
1090 gotmantdig = 1;
1091 else {
1092 state = S_FRAC;
1093 if (c != decpt)
1094 goto reswitch;
1095 }
1096 if (gotmantdig)
1097 commit = p;
1098 break;
1099 case S_FRAC:
1100 if (((c == 'E' || c == 'e') && !ishex) ||
1101 ((c == 'P' || c == 'p') && ishex)) {
1102 if (!gotmantdig)
1103 goto parsedone;
1104 else
1105 state = S_EXP;
1106 } else if ((ishex && isxdigit(c)) || isdigit(c)) {
1107 commit = p;
1108 gotmantdig = 1;
1109 } else
1110 goto parsedone;
1111 break;
1112 case S_EXP:
1113 state = S_EXPDIGITS;
1114 if (c == '-' || c == '+')
1115 break;
1116 else
1117 goto reswitch;
1118 case S_EXPDIGITS:
1119 if (isdigit(c))
1120 commit = p;
1121 else
1122 goto parsedone;
1123 break;
1124 default:
1125 abort();
1126 }
1127 *p++ = c;
1128 if (--fp->_r > 0)
1129 fp->_p++;
1130 else if (__srefill(fp))
1131 break; /* EOF */
1132 }
1133
1134 parsedone:
1135 while (commit < --p)
1136 (void)ungetc(*(u_char *)p, fp);
1137 *++commit = '\0';
1138 return (int)(commit - buf);
1139 }
1140 #endif