]> git.saurik.com Git - apple/libc.git/blob - stdio/vfwscanf-fbsd.c
603d28fd64b2f926801d2093a918801033e3b3d6
[apple/libc.git] / stdio / vfwscanf-fbsd.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 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #if 0
38 #if defined(LIBC_SCCS) && !defined(lint)
39 static char sccsid[] = "@(#)vfscanf.c 8.1 (Berkeley) 6/4/93";
40 #endif /* LIBC_SCCS and not lint */
41 #endif
42 #include <sys/cdefs.h>
43 __FBSDID("$FreeBSD: src/lib/libc/stdio/vfwscanf.c,v 1.12 2004/05/02 20:13:29 obrien Exp $");
44
45 #include "xlocale_private.h"
46
47 #include "namespace.h"
48 #include <ctype.h>
49 #include <inttypes.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <stddef.h>
53 #include <stdarg.h>
54 #include <string.h>
55 #include <wchar.h>
56 #include <wctype.h>
57 #include "un-namespace.h"
58
59 #include "libc_private.h"
60 #include "local.h"
61
62 #ifndef NO_FLOATING_POINT
63 #include <locale.h>
64 #endif
65
66 #define BUF 513 /* Maximum length of numeric string. */
67
68 /*
69 * Flags used during conversion.
70 */
71 #define LONG 0x01 /* l: long or double */
72 #define LONGDBL 0x02 /* L: long double */
73 #define SHORT 0x04 /* h: short */
74 #define SUPPRESS 0x08 /* *: suppress assignment */
75 #define POINTER 0x10 /* p: void * (as hex) */
76 #define NOSKIP 0x20 /* [ or c: do not skip blanks */
77 #define LONGLONG 0x400 /* ll: long long (+ deprecated q: quad) */
78 #define INTMAXT 0x800 /* j: intmax_t */
79 #define PTRDIFFT 0x1000 /* t: ptrdiff_t */
80 #define SIZET 0x2000 /* z: size_t */
81 #define SHORTSHORT 0x4000 /* hh: char */
82 #define UNSIGNED 0x8000 /* %[oupxX] conversions */
83
84 /*
85 * The following are used in integral conversions only:
86 * SIGNOK, NDIGITS, PFXOK, and NZDIGITS
87 */
88 #define SIGNOK 0x40 /* +/- is (still) legal */
89 #define NDIGITS 0x80 /* no digits detected */
90 #define PFXOK 0x100 /* 0x prefix is (still) legal */
91 #define NZDIGITS 0x200 /* no zero digits detected */
92 #define HAVESIGN 0x10000 /* sign detected */
93
94 /*
95 * Conversion types.
96 */
97 #define CT_CHAR 0 /* %c conversion */
98 #define CT_CCL 1 /* %[...] conversion */
99 #define CT_STRING 2 /* %s conversion */
100 #define CT_INT 3 /* %[dioupxX] conversion */
101 #define CT_FLOAT 4 /* %[efgEFG] conversion */
102
103 #ifndef NO_FLOATING_POINT
104 static int parsefloat(FILE *, wchar_t **, size_t, locale_t loc);
105 #endif /* !NO_FLOATING_POINT */
106
107 extern int __scanfdebug;
108
109 #define INCCL(_c) \
110 (cclcompl ? (wmemchr(ccls, (_c), ccle - ccls) == NULL) : \
111 (wmemchr(ccls, (_c), ccle - ccls) != NULL))
112
113 /*
114 * MT-safe version.
115 */
116 int
117 vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
118 {
119 int ret;
120
121 FLOCKFILE(fp);
122 ORIENT(fp, 1);
123 ret = __vfwscanf(fp, __current_locale(), fmt, ap);
124 FUNLOCKFILE(fp);
125 return (ret);
126 }
127
128 int
129 vfwscanf_l(FILE * __restrict fp, locale_t loc, const wchar_t * __restrict fmt,
130 va_list ap)
131 {
132 int ret;
133
134 NORMALIZE_LOCALE(loc);
135 FLOCKFILE(fp);
136 ORIENT(fp, 1);
137 ret = __vfwscanf(fp, loc, fmt, ap);
138 FUNLOCKFILE(fp);
139 return (ret);
140 }
141
142 /*
143 * Non-MT-safe version.
144 */
145 __private_extern__ int
146 __vfwscanf(FILE * __restrict fp, locale_t loc, const wchar_t * __restrict fmt,
147 va_list ap)
148 {
149 wint_t c; /* character from format, or conversion */
150 size_t width; /* field width, or 0 */
151 wchar_t *p; /* points into all kinds of strings */
152 int n; /* handy integer */
153 int flags; /* flags as defined above */
154 wchar_t *p0; /* saves original value of p when necessary */
155 int nassigned; /* number of fields assigned */
156 int nread; /* number of characters consumed from fp */
157 int base; /* base argument to conversion function */
158 wchar_t buf[BUF]; /* buffer for numeric conversions */
159 const wchar_t *ccls; /* character class start */
160 const wchar_t *ccle; /* character class end */
161 int cclcompl; /* ccl is complemented? */
162 wint_t wi; /* handy wint_t */
163 char *mbp; /* multibyte string pointer for %c %s %[ */
164 size_t nconv; /* number of bytes in mb. conversion */
165 char mbbuf[MB_LEN_MAX]; /* temporary mb. character buffer */
166 int index; /* for %index$ */
167 va_list ap_orig; /* to reset ap to first argument */
168 static const mbstate_t initial;
169 mbstate_t mbs;
170 int mb_cur_max = MB_CUR_MAX_L(loc);
171
172 /* `basefix' is used to avoid `if' tests in the integer scanner */
173 static short basefix[17] =
174 { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
175
176 nassigned = 0;
177 nread = 0;
178 ccls = ccle = NULL;
179 va_copy(ap_orig, ap);
180 for (;;) {
181 c = *fmt++;
182 if (c == 0)
183 return (nassigned);
184 if (iswspace_l(c, loc)) {
185 while ((c = __fgetwc(fp, loc)) != WEOF &&
186 iswspace_l(c, loc))
187 ;
188 if (c != WEOF)
189 __ungetwc(c, fp, loc);
190 continue;
191 }
192 if (c != '%') {
193 if ((wi = __fgetwc(fp, loc)) == WEOF)
194 goto input_failure;
195 goto literal;
196 }
197 width = 0;
198 flags = 0;
199 /*
200 * switch on the format. continue if done;
201 * break once format type is derived.
202 */
203 again: c = *fmt++;
204 switch (c) {
205 case '%':
206 /* Consume leading white space */
207 for(;;) {
208 if ((wi = __fgetwc(fp, loc)) == WEOF)
209 goto input_failure;
210 if (!iswspace_l(wi, loc))
211 break;
212 nread++;
213 }
214 literal:
215 if (wi != c) {
216 __ungetwc(wi, fp, loc);
217 goto match_failure;
218 }
219 nread++;
220 continue;
221
222 case '$':
223 index = width;
224 if (index < 1 || index > NL_ARGMAX || fmt[-3] != '%') {
225 goto input_failure;
226 }
227 width = 0;
228 va_end(ap);
229 va_copy(ap, ap_orig); /* reset to %1$ */
230 for (; index > 1; index--) {
231 va_arg(ap, void*);
232 }
233 goto again;
234 case '*':
235 flags |= SUPPRESS;
236 goto again;
237 case 'j':
238 flags |= INTMAXT;
239 goto again;
240 case 'l':
241 if (flags & LONG) {
242 flags &= ~LONG;
243 flags |= LONGLONG;
244 } else
245 flags |= LONG;
246 goto again;
247 case 'q':
248 flags |= LONGLONG; /* not quite */
249 goto again;
250 case 't':
251 flags |= PTRDIFFT;
252 goto again;
253 case 'z':
254 flags |= SIZET;
255 goto again;
256 case 'L':
257 flags |= LONGDBL;
258 goto again;
259 case 'h':
260 if (flags & SHORT) {
261 flags &= ~SHORT;
262 flags |= SHORTSHORT;
263 } else
264 flags |= SHORT;
265 goto again;
266
267 case '0': case '1': case '2': case '3': case '4':
268 case '5': case '6': case '7': case '8': case '9':
269 width = width * 10 + c - '0';
270 goto again;
271
272 /*
273 * Conversions.
274 */
275 case 'd':
276 c = CT_INT;
277 base = 10;
278 break;
279
280 case 'i':
281 c = CT_INT;
282 base = 0;
283 break;
284
285 case 'o':
286 c = CT_INT;
287 flags |= UNSIGNED;
288 base = 8;
289 break;
290
291 case 'u':
292 c = CT_INT;
293 flags |= UNSIGNED;
294 base = 10;
295 break;
296
297 case 'X':
298 case 'x':
299 flags |= PFXOK; /* enable 0x prefixing */
300 c = CT_INT;
301 flags |= UNSIGNED;
302 base = 16;
303 break;
304
305 #ifndef NO_FLOATING_POINT
306 case 'A': case 'E': case 'F': case 'G':
307 case 'a': case 'e': case 'f': case 'g':
308 c = CT_FLOAT;
309 break;
310 #endif
311
312 case 'S':
313 flags |= LONG;
314 /* FALLTHROUGH */
315 case 's':
316 c = CT_STRING;
317 break;
318
319 case '[':
320 ccls = fmt;
321 if (*fmt == '^') {
322 cclcompl = 1;
323 fmt++;
324 } else
325 cclcompl = 0;
326 if (*fmt == ']')
327 fmt++;
328 while (*fmt != '\0' && *fmt != ']')
329 fmt++;
330 ccle = fmt;
331 fmt++;
332 flags |= NOSKIP;
333 c = CT_CCL;
334 break;
335
336 case 'C':
337 flags |= LONG;
338 /* FALLTHROUGH */
339 case 'c':
340 flags |= NOSKIP;
341 c = CT_CHAR;
342 break;
343
344 case 'p': /* pointer format is like hex */
345 flags |= POINTER | PFXOK;
346 c = CT_INT; /* assumes sizeof(uintmax_t) */
347 flags |= UNSIGNED; /* >= sizeof(uintptr_t) */
348 base = 16;
349 break;
350
351 case 'n':
352 if (flags & SUPPRESS) /* ??? */
353 continue;
354 if (flags & SHORTSHORT)
355 *va_arg(ap, char *) = nread;
356 else if (flags & SHORT)
357 *va_arg(ap, short *) = nread;
358 else if (flags & LONG)
359 *va_arg(ap, long *) = nread;
360 else if (flags & LONGLONG)
361 *va_arg(ap, long long *) = nread;
362 else if (flags & INTMAXT)
363 *va_arg(ap, intmax_t *) = nread;
364 else if (flags & SIZET)
365 *va_arg(ap, size_t *) = nread;
366 else if (flags & PTRDIFFT)
367 *va_arg(ap, ptrdiff_t *) = nread;
368 else
369 *va_arg(ap, int *) = nread;
370 continue;
371
372 default:
373 goto match_failure;
374
375 /*
376 * Disgusting backwards compatibility hack. XXX
377 */
378 case '\0': /* compat */
379 return (EOF);
380 }
381
382 /*
383 * Consume leading white space, except for formats
384 * that suppress this.
385 */
386 if ((flags & NOSKIP) == 0) {
387 while ((wi = __fgetwc(fp, loc)) != WEOF && iswspace_l(wi, loc))
388 nread++;
389 if (wi == WEOF)
390 goto input_failure;
391 __ungetwc(wi, fp, loc);
392 }
393
394 /*
395 * Do the conversion.
396 */
397 switch (c) {
398
399 case CT_CHAR:
400 /* scan arbitrary characters (sets NOSKIP) */
401 if (width == 0)
402 width = 1;
403 if (flags & LONG) {
404 if (!(flags & SUPPRESS))
405 p = va_arg(ap, wchar_t *);
406 n = 0;
407 while (width-- != 0 &&
408 (wi = __fgetwc(fp, loc)) != WEOF) {
409 if (!(flags & SUPPRESS))
410 *p++ = (wchar_t)wi;
411 n++;
412 }
413 if (n == 0)
414 goto input_failure;
415 nread += n;
416 if (!(flags & SUPPRESS))
417 nassigned++;
418 } else {
419 if (!(flags & SUPPRESS))
420 mbp = va_arg(ap, char *);
421 n = 0;
422 mbs = initial;
423 while (width != 0 &&
424 (wi = __fgetwc(fp, loc)) != WEOF) {
425 if (width >= mb_cur_max &&
426 !(flags & SUPPRESS)) {
427 nconv = wcrtomb_l(mbp, wi, &mbs, loc);
428 if (nconv == (size_t)-1)
429 goto input_failure;
430 } else {
431 nconv = wcrtomb_l(mbbuf, wi,
432 &mbs, loc);
433 if (nconv == (size_t)-1)
434 goto input_failure;
435 if (nconv > width) {
436 __ungetwc(wi, fp, loc);
437 break;
438 }
439 if (!(flags & SUPPRESS))
440 memcpy(mbp, mbbuf,
441 nconv);
442 }
443 if (!(flags & SUPPRESS))
444 mbp += nconv;
445 width -= nconv;
446 n++;
447 }
448 if (n == 0)
449 goto input_failure;
450 nread += n;
451 if (!(flags & SUPPRESS))
452 nassigned++;
453 }
454 break;
455
456 case CT_CCL:
457 /* scan a (nonempty) character class (sets NOSKIP) */
458 if (width == 0)
459 width = (size_t)~0; /* `infinity' */
460 /* take only those things in the class */
461 if ((flags & SUPPRESS) && (flags & LONG)) {
462 n = 0;
463 while ((wi = __fgetwc(fp, loc)) != WEOF &&
464 width-- != 0 && INCCL(wi))
465 n++;
466 if (wi != WEOF)
467 __ungetwc(wi, fp, loc);
468 if (n == 0)
469 goto match_failure;
470 } else if (flags & LONG) {
471 p0 = p = va_arg(ap, wchar_t *);
472 while ((wi = __fgetwc(fp, loc)) != WEOF &&
473 width-- != 0 && INCCL(wi))
474 *p++ = (wchar_t)wi;
475 if (wi != WEOF)
476 __ungetwc(wi, fp, loc);
477 n = p - p0;
478 if (n == 0)
479 goto match_failure;
480 *p = 0;
481 nassigned++;
482 } else {
483 if (!(flags & SUPPRESS))
484 mbp = va_arg(ap, char *);
485 n = 0;
486 mbs = initial;
487 while ((wi = __fgetwc(fp, loc)) != WEOF &&
488 width != 0 && INCCL(wi)) {
489 if (width >= mb_cur_max &&
490 !(flags & SUPPRESS)) {
491 nconv = wcrtomb_l(mbp, wi, &mbs, loc);
492 if (nconv == (size_t)-1)
493 goto input_failure;
494 } else {
495 nconv = wcrtomb_l(mbbuf, wi,
496 &mbs, loc);
497 if (nconv == (size_t)-1)
498 goto input_failure;
499 if (nconv > width)
500 break;
501 if (!(flags & SUPPRESS))
502 memcpy(mbp, mbbuf,
503 nconv);
504 }
505 if (!(flags & SUPPRESS))
506 mbp += nconv;
507 width -= nconv;
508 n++;
509 }
510 if (wi != WEOF)
511 __ungetwc(wi, fp, loc);
512 if (n == 0)
513 goto match_failure;
514 if (!(flags & SUPPRESS)) {
515 *mbp = 0;
516 nassigned++;
517 }
518 }
519 nread += n;
520 break;
521
522 case CT_STRING:
523 /* like CCL, but zero-length string OK, & no NOSKIP */
524 if (width == 0)
525 width = (size_t)~0;
526 if ((flags & SUPPRESS) && (flags & LONG)) {
527 while ((wi = __fgetwc(fp, loc)) != WEOF &&
528 width-- != 0 &&
529 !iswspace_l(wi, loc))
530 nread++;
531 if (wi != WEOF)
532 __ungetwc(wi, fp, loc);
533 } else if (flags & LONG) {
534 p0 = p = va_arg(ap, wchar_t *);
535 while ((wi = __fgetwc(fp, loc)) != WEOF &&
536 width-- != 0 &&
537 !iswspace_l(wi, loc)) {
538 *p++ = (wchar_t)wi;
539 nread++;
540 }
541 if (wi != WEOF)
542 __ungetwc(wi, fp, loc);
543 *p = '\0';
544 nassigned++;
545 } else {
546 if (!(flags & SUPPRESS))
547 mbp = va_arg(ap, char *);
548 mbs = initial;
549 while ((wi = __fgetwc(fp, loc)) != WEOF &&
550 width != 0 &&
551 !iswspace_l(wi, loc)) {
552 if (width >= mb_cur_max &&
553 !(flags & SUPPRESS)) {
554 nconv = wcrtomb_l(mbp, wi, &mbs, loc);
555 if (nconv == (size_t)-1)
556 goto input_failure;
557 } else {
558 nconv = wcrtomb_l(mbbuf, wi,
559 &mbs, loc);
560 if (nconv == (size_t)-1)
561 goto input_failure;
562 if (nconv > width)
563 break;
564 if (!(flags & SUPPRESS))
565 memcpy(mbp, mbbuf,
566 nconv);
567 }
568 if (!(flags & SUPPRESS))
569 mbp += nconv;
570 width -= nconv;
571 nread++;
572 }
573 if (wi != WEOF)
574 __ungetwc(wi, fp, loc);
575 if (!(flags & SUPPRESS)) {
576 *mbp = 0;
577 nassigned++;
578 }
579 }
580 continue;
581
582 case CT_INT:
583 /* scan an integer as if by the conversion function */
584 if (width == 0 || width > sizeof(buf) /
585 sizeof(*buf) - 1)
586 width = sizeof(buf) / sizeof(*buf) - 1;
587 flags |= SIGNOK | NDIGITS | NZDIGITS;
588 for (p = buf; width; width--) {
589 c = __fgetwc(fp, loc);
590 /*
591 * Switch on the character; `goto ok'
592 * if we accept it as a part of number.
593 */
594 switch (c) {
595
596 /*
597 * The digit 0 is always legal, but is
598 * special. For %i conversions, if no
599 * digits (zero or nonzero) have been
600 * scanned (only signs), we will have
601 * base==0. In that case, we should set
602 * it to 8 and enable 0x prefixing.
603 * Also, if we have not scanned zero digits
604 * before this, do not turn off prefixing
605 * (someone else will turn it off if we
606 * have scanned any nonzero digits).
607 */
608 case '0':
609 if (base == 0) {
610 base = 8;
611 flags |= PFXOK;
612 }
613 if (flags & NZDIGITS)
614 flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
615 else
616 flags &= ~(SIGNOK|PFXOK|NDIGITS);
617 goto ok;
618
619 /* 1 through 7 always legal */
620 case '1': case '2': case '3':
621 case '4': case '5': case '6': case '7':
622 base = basefix[base];
623 flags &= ~(SIGNOK | PFXOK | NDIGITS);
624 goto ok;
625
626 /* digits 8 and 9 ok iff decimal or hex */
627 case '8': case '9':
628 base = basefix[base];
629 if (base <= 8)
630 break; /* not legal here */
631 flags &= ~(SIGNOK | PFXOK | NDIGITS);
632 goto ok;
633
634 /* letters ok iff hex */
635 case 'A': case 'B': case 'C':
636 case 'D': case 'E': case 'F':
637 case 'a': case 'b': case 'c':
638 case 'd': case 'e': case 'f':
639 /* no need to fix base here */
640 if (base <= 10)
641 break; /* not legal here */
642 flags &= ~(SIGNOK | PFXOK | NDIGITS);
643 goto ok;
644
645 /* sign ok only as first character */
646 case '+': case '-':
647 if (flags & SIGNOK) {
648 flags &= ~SIGNOK;
649 flags |= HAVESIGN;
650 goto ok;
651 }
652 break;
653
654 /*
655 * x ok iff flag still set & 2nd char (or
656 * 3rd char if we have a sign).
657 */
658 case 'x': case 'X':
659 if (flags & PFXOK && p ==
660 buf + 1 + !!(flags & HAVESIGN)) {
661 base = 16; /* if %i */
662 flags &= ~PFXOK;
663 goto ok;
664 }
665 break;
666 }
667
668 /*
669 * If we got here, c is not a legal character
670 * for a number. Stop accumulating digits.
671 */
672 if (c != WEOF)
673 __ungetwc(c, fp, loc);
674 break;
675 ok:
676 /*
677 * c is legal: store it and look at the next.
678 */
679 *p++ = (wchar_t)c;
680 }
681 /*
682 * If we had only a sign, it is no good; push
683 * back the sign. If the number ends in `x',
684 * it was [sign] '0' 'x', so push back the x
685 * and treat it as [sign] '0'.
686 */
687 if (flags & NDIGITS) {
688 if (p > buf)
689 __ungetwc(*--p, fp, loc);
690 goto match_failure;
691 }
692 c = p[-1];
693 if (c == 'x' || c == 'X') {
694 --p;
695 __ungetwc(c, fp, loc);
696 }
697 if ((flags & SUPPRESS) == 0) {
698 uintmax_t res;
699
700 *p = 0;
701 if ((flags & UNSIGNED) == 0)
702 res = wcstoimax_l(buf, NULL, base, loc);
703 else
704 res = wcstoumax_l(buf, NULL, base, loc);
705 if (flags & POINTER)
706 *va_arg(ap, void **) =
707 (void *)(uintptr_t)res;
708 else if (flags & SHORTSHORT)
709 *va_arg(ap, char *) = res;
710 else if (flags & SHORT)
711 *va_arg(ap, short *) = res;
712 else if (flags & LONG)
713 *va_arg(ap, long *) = res;
714 else if (flags & LONGLONG)
715 *va_arg(ap, long long *) = res;
716 else if (flags & INTMAXT)
717 *va_arg(ap, intmax_t *) = res;
718 else if (flags & PTRDIFFT)
719 *va_arg(ap, ptrdiff_t *) = res;
720 else if (flags & SIZET)
721 *va_arg(ap, size_t *) = res;
722 else
723 *va_arg(ap, int *) = res;
724 nassigned++;
725 }
726 nread += p - buf;
727 break;
728
729 #ifndef NO_FLOATING_POINT
730 case CT_FLOAT:
731 {
732 wchar_t *pbuf;
733 /* scan a floating point number as if by strtod */
734 if ((width = parsefloat(fp, &pbuf, width, loc)) == 0)
735 goto match_failure;
736 if ((flags & SUPPRESS) == 0) {
737 if (flags & LONGDBL) {
738 long double res = wcstold_l(pbuf, &p, loc);
739 *va_arg(ap, long double *) = res;
740 } else if (flags & LONG) {
741 double res = wcstod_l(pbuf, &p, loc);
742 *va_arg(ap, double *) = res;
743 } else {
744 float res = wcstof_l(pbuf, &p, loc);
745 *va_arg(ap, float *) = res;
746 }
747 if (__scanfdebug && p - pbuf != width)
748 LIBC_ABORT("p - pbuf %ld != width %ld", (long)(p - pbuf), width);
749 nassigned++;
750 }
751 nread += width;
752 break;
753 }
754 #endif /* !NO_FLOATING_POINT */
755 }
756 }
757 input_failure:
758 return (nassigned ? nassigned : EOF);
759 match_failure:
760 return (nassigned);
761 }
762
763 #ifndef NO_FLOATING_POINT
764 extern char *__parsefloat_buf(size_t s); /* see vfscanf-fbsd.c */
765
766 static int
767 parsefloat(FILE *fp, wchar_t **buf, size_t width, locale_t loc)
768 {
769 wchar_t *commit, *p;
770 int infnanpos = 0;
771 enum {
772 S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX,
773 S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
774 } state = S_START;
775 wchar_t c;
776 char *decimal_point;
777 wchar_t decpt;
778 _Bool gotmantdig = 0, ishex = 0;
779 wchar_t *b;
780 wchar_t *e;
781 size_t s;
782
783 s = (width == 0 ? BUF : (width + 1));
784 if ((b = (wchar_t *)__parsefloat_buf(s * sizeof(wchar_t))) == NULL) {
785 *buf = NULL;
786 return 0;
787 }
788 e = b + (s - 1);
789 /*
790 * We set commit = p whenever the string we have read so far
791 * constitutes a valid representation of a floating point
792 * number by itself. At some point, the parse will complete
793 * or fail, and we will ungetc() back to the last commit point.
794 * To ensure that the file offset gets updated properly, it is
795 * always necessary to read at least one character that doesn't
796 * match; thus, we can't short-circuit "infinity" or "nan(...)".
797 */
798 commit = b - 1;
799 c = WEOF;
800 decimal_point = localeconv_l(loc)->decimal_point;
801 mbtowc_l(&decpt, decimal_point, strlen(decimal_point), loc);
802 for (p = b; width == 0 || p < e; ) {
803 if ((c = __fgetwc(fp, loc)) == WEOF)
804 break;
805 reswitch:
806 switch (state) {
807 case S_START:
808 state = S_GOTSIGN;
809 if (c == '-' || c == '+')
810 break;
811 else
812 goto reswitch;
813 case S_GOTSIGN:
814 switch (c) {
815 case '0':
816 state = S_MAYBEHEX;
817 commit = p;
818 break;
819 case 'I':
820 case 'i':
821 state = S_INF;
822 break;
823 case 'N':
824 case 'n':
825 state = S_NAN;
826 break;
827 default:
828 state = S_DIGITS;
829 goto reswitch;
830 }
831 break;
832 case S_INF:
833 if (infnanpos > 6 ||
834 (c != "nfinity"[infnanpos] &&
835 c != "NFINITY"[infnanpos]))
836 goto parsedone;
837 if (infnanpos == 1 || infnanpos == 6)
838 commit = p; /* inf or infinity */
839 infnanpos++;
840 break;
841 case S_NAN:
842 switch (infnanpos) {
843 case -1: /* XXX kludge to deal with nan(...) */
844 goto parsedone;
845 case 0:
846 if (c != 'A' && c != 'a')
847 goto parsedone;
848 break;
849 case 1:
850 if (c != 'N' && c != 'n')
851 goto parsedone;
852 else
853 commit = p;
854 break;
855 case 2:
856 if (c != '(')
857 goto parsedone;
858 break;
859 default:
860 if (c == ')') {
861 commit = p;
862 infnanpos = -2;
863 } else if (!iswalnum_l(c, loc) && c != '_')
864 goto parsedone;
865 break;
866 }
867 infnanpos++;
868 break;
869 case S_MAYBEHEX:
870 state = S_DIGITS;
871 if (c == 'X' || c == 'x') {
872 ishex = 1;
873 break;
874 } else { /* we saw a '0', but no 'x' */
875 gotmantdig = 1;
876 goto reswitch;
877 }
878 case S_DIGITS:
879 if ((ishex && iswxdigit_l(c, loc)) || iswdigit_l(c, loc))
880 gotmantdig = 1;
881 else {
882 state = S_FRAC;
883 if (c != decpt)
884 goto reswitch;
885 }
886 if (gotmantdig)
887 commit = p;
888 break;
889 case S_FRAC:
890 if (((c == 'E' || c == 'e') && !ishex) ||
891 ((c == 'P' || c == 'p') && ishex)) {
892 if (!gotmantdig)
893 goto parsedone;
894 else
895 state = S_EXP;
896 } else if ((ishex && iswxdigit_l(c, loc)) || iswdigit_l(c, loc)) {
897 commit = p;
898 gotmantdig = 1;
899 } else
900 goto parsedone;
901 break;
902 case S_EXP:
903 state = S_EXPDIGITS;
904 if (c == '-' || c == '+')
905 break;
906 else
907 goto reswitch;
908 case S_EXPDIGITS:
909 if (iswdigit_l(c, loc))
910 commit = p;
911 else
912 goto parsedone;
913 break;
914 default:
915 LIBC_ABORT("unknown state %d", state);
916 }
917 if (p >= e) {
918 ssize_t diff = (p - b);
919 ssize_t com = (commit - b);
920 s += BUF;
921 b = (wchar_t *)__parsefloat_buf(s * sizeof(wchar_t));
922 if (b == NULL) {
923 *buf = NULL;
924 return 0;
925 }
926 e = b + (s - 1);
927 p = b + diff;
928 commit = b + com;
929 }
930 *p++ = c;
931 c = WEOF;
932 }
933
934 parsedone:
935 if (c != WEOF)
936 __ungetwc(c, fp, loc);
937 while (commit < --p)
938 __ungetwc(*p, fp, loc);
939 *++commit = '\0';
940 *buf = b;
941 return (commit - b);
942 }
943 #endif