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