]> git.saurik.com Git - apple/libc.git/blob - stdio/vfwscanf-fbsd.c
2bee85d933d171c5c94fa8821eca69e160cb11e6
[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 goto literal;
194 width = 0;
195 flags = 0;
196 /*
197 * switch on the format. continue if done;
198 * break once format type is derived.
199 */
200 again: c = *fmt++;
201 switch (c) {
202 case '%':
203 literal:
204 if ((wi = __fgetwc(fp, loc)) == WEOF)
205 goto input_failure;
206 if (wi != c) {
207 __ungetwc(wi, fp, loc);
208 goto match_failure;
209 }
210 nread++;
211 continue;
212
213 case '$':
214 index = width;
215 if (index < 1 || index > NL_ARGMAX || fmt[-3] != '%') {
216 goto input_failure;
217 }
218 width = 0;
219 va_end(ap);
220 va_copy(ap, ap_orig); /* reset to %1$ */
221 for (; index > 1; index--) {
222 va_arg(ap, void*);
223 }
224 goto again;
225 case '*':
226 flags |= SUPPRESS;
227 goto again;
228 case 'j':
229 flags |= INTMAXT;
230 goto again;
231 case 'l':
232 if (flags & LONG) {
233 flags &= ~LONG;
234 flags |= LONGLONG;
235 } else
236 flags |= LONG;
237 goto again;
238 case 'q':
239 flags |= LONGLONG; /* not quite */
240 goto again;
241 case 't':
242 flags |= PTRDIFFT;
243 goto again;
244 case 'z':
245 flags |= SIZET;
246 goto again;
247 case 'L':
248 flags |= LONGDBL;
249 goto again;
250 case 'h':
251 if (flags & SHORT) {
252 flags &= ~SHORT;
253 flags |= SHORTSHORT;
254 } else
255 flags |= SHORT;
256 goto again;
257
258 case '0': case '1': case '2': case '3': case '4':
259 case '5': case '6': case '7': case '8': case '9':
260 width = width * 10 + c - '0';
261 goto again;
262
263 /*
264 * Conversions.
265 */
266 case 'd':
267 c = CT_INT;
268 base = 10;
269 break;
270
271 case 'i':
272 c = CT_INT;
273 base = 0;
274 break;
275
276 case 'o':
277 c = CT_INT;
278 flags |= UNSIGNED;
279 base = 8;
280 break;
281
282 case 'u':
283 c = CT_INT;
284 flags |= UNSIGNED;
285 base = 10;
286 break;
287
288 case 'X':
289 case 'x':
290 flags |= PFXOK; /* enable 0x prefixing */
291 c = CT_INT;
292 flags |= UNSIGNED;
293 base = 16;
294 break;
295
296 #ifndef NO_FLOATING_POINT
297 case 'A': case 'E': case 'F': case 'G':
298 case 'a': case 'e': case 'f': case 'g':
299 c = CT_FLOAT;
300 break;
301 #endif
302
303 case 'S':
304 flags |= LONG;
305 /* FALLTHROUGH */
306 case 's':
307 c = CT_STRING;
308 break;
309
310 case '[':
311 ccls = fmt;
312 if (*fmt == '^') {
313 cclcompl = 1;
314 fmt++;
315 } else
316 cclcompl = 0;
317 if (*fmt == ']')
318 fmt++;
319 while (*fmt != '\0' && *fmt != ']')
320 fmt++;
321 ccle = fmt;
322 fmt++;
323 flags |= NOSKIP;
324 c = CT_CCL;
325 break;
326
327 case 'C':
328 flags |= LONG;
329 /* FALLTHROUGH */
330 case 'c':
331 flags |= NOSKIP;
332 c = CT_CHAR;
333 break;
334
335 case 'p': /* pointer format is like hex */
336 flags |= POINTER | PFXOK;
337 c = CT_INT; /* assumes sizeof(uintmax_t) */
338 flags |= UNSIGNED; /* >= sizeof(uintptr_t) */
339 base = 16;
340 break;
341
342 case 'n':
343 if (flags & SUPPRESS) /* ??? */
344 continue;
345 if (flags & SHORTSHORT)
346 *va_arg(ap, char *) = nread;
347 else if (flags & SHORT)
348 *va_arg(ap, short *) = nread;
349 else if (flags & LONG)
350 *va_arg(ap, long *) = nread;
351 else if (flags & LONGLONG)
352 *va_arg(ap, long long *) = nread;
353 else if (flags & INTMAXT)
354 *va_arg(ap, intmax_t *) = nread;
355 else if (flags & SIZET)
356 *va_arg(ap, size_t *) = nread;
357 else if (flags & PTRDIFFT)
358 *va_arg(ap, ptrdiff_t *) = nread;
359 else
360 *va_arg(ap, int *) = nread;
361 continue;
362
363 default:
364 goto match_failure;
365
366 /*
367 * Disgusting backwards compatibility hack. XXX
368 */
369 case '\0': /* compat */
370 return (EOF);
371 }
372
373 /*
374 * Consume leading white space, except for formats
375 * that suppress this.
376 */
377 if ((flags & NOSKIP) == 0) {
378 while ((wi = __fgetwc(fp, loc)) != WEOF && iswspace_l(wi, loc))
379 nread++;
380 if (wi == WEOF)
381 goto input_failure;
382 __ungetwc(wi, fp, loc);
383 }
384
385 /*
386 * Do the conversion.
387 */
388 switch (c) {
389
390 case CT_CHAR:
391 /* scan arbitrary characters (sets NOSKIP) */
392 if (width == 0)
393 width = 1;
394 if (flags & LONG) {
395 if (!(flags & SUPPRESS))
396 p = va_arg(ap, wchar_t *);
397 n = 0;
398 while (width-- != 0 &&
399 (wi = __fgetwc(fp, loc)) != WEOF) {
400 if (!(flags & SUPPRESS))
401 *p++ = (wchar_t)wi;
402 n++;
403 }
404 if (n == 0)
405 goto input_failure;
406 nread += n;
407 if (!(flags & SUPPRESS))
408 nassigned++;
409 } else {
410 if (!(flags & SUPPRESS))
411 mbp = va_arg(ap, char *);
412 n = 0;
413 mbs = initial;
414 while (width != 0 &&
415 (wi = __fgetwc(fp, loc)) != WEOF) {
416 if (width >= mb_cur_max &&
417 !(flags & SUPPRESS)) {
418 nconv = wcrtomb_l(mbp, wi, &mbs, loc);
419 if (nconv == (size_t)-1)
420 goto input_failure;
421 } else {
422 nconv = wcrtomb_l(mbbuf, wi,
423 &mbs, loc);
424 if (nconv == (size_t)-1)
425 goto input_failure;
426 if (nconv > width) {
427 __ungetwc(wi, fp, loc);
428 break;
429 }
430 if (!(flags & SUPPRESS))
431 memcpy(mbp, mbbuf,
432 nconv);
433 }
434 if (!(flags & SUPPRESS))
435 mbp += nconv;
436 width -= nconv;
437 n++;
438 }
439 if (n == 0)
440 goto input_failure;
441 nread += n;
442 if (!(flags & SUPPRESS))
443 nassigned++;
444 }
445 break;
446
447 case CT_CCL:
448 /* scan a (nonempty) character class (sets NOSKIP) */
449 if (width == 0)
450 width = (size_t)~0; /* `infinity' */
451 /* take only those things in the class */
452 if ((flags & SUPPRESS) && (flags & LONG)) {
453 n = 0;
454 while ((wi = __fgetwc(fp, loc)) != WEOF &&
455 width-- != 0 && INCCL(wi))
456 n++;
457 if (wi != WEOF)
458 __ungetwc(wi, fp, loc);
459 if (n == 0)
460 goto match_failure;
461 } else if (flags & LONG) {
462 p0 = p = va_arg(ap, wchar_t *);
463 while ((wi = __fgetwc(fp, loc)) != WEOF &&
464 width-- != 0 && INCCL(wi))
465 *p++ = (wchar_t)wi;
466 if (wi != WEOF)
467 __ungetwc(wi, fp, loc);
468 n = p - p0;
469 if (n == 0)
470 goto match_failure;
471 *p = 0;
472 nassigned++;
473 } else {
474 if (!(flags & SUPPRESS))
475 mbp = va_arg(ap, char *);
476 n = 0;
477 mbs = initial;
478 while ((wi = __fgetwc(fp, loc)) != WEOF &&
479 width != 0 && INCCL(wi)) {
480 if (width >= mb_cur_max &&
481 !(flags & SUPPRESS)) {
482 nconv = wcrtomb_l(mbp, wi, &mbs, loc);
483 if (nconv == (size_t)-1)
484 goto input_failure;
485 } else {
486 nconv = wcrtomb_l(mbbuf, wi,
487 &mbs, loc);
488 if (nconv == (size_t)-1)
489 goto input_failure;
490 if (nconv > width)
491 break;
492 if (!(flags & SUPPRESS))
493 memcpy(mbp, mbbuf,
494 nconv);
495 }
496 if (!(flags & SUPPRESS))
497 mbp += nconv;
498 width -= nconv;
499 n++;
500 }
501 if (wi != WEOF)
502 __ungetwc(wi, fp, loc);
503 if (n == 0)
504 goto match_failure;
505 if (!(flags & SUPPRESS)) {
506 *mbp = 0;
507 nassigned++;
508 }
509 }
510 nread += n;
511 break;
512
513 case CT_STRING:
514 /* like CCL, but zero-length string OK, & no NOSKIP */
515 if (width == 0)
516 width = (size_t)~0;
517 if ((flags & SUPPRESS) && (flags & LONG)) {
518 while ((wi = __fgetwc(fp, loc)) != WEOF &&
519 width-- != 0 &&
520 !iswspace_l(wi, loc))
521 nread++;
522 if (wi != WEOF)
523 __ungetwc(wi, fp, loc);
524 } else if (flags & LONG) {
525 p0 = p = va_arg(ap, wchar_t *);
526 while ((wi = __fgetwc(fp, loc)) != WEOF &&
527 width-- != 0 &&
528 !iswspace_l(wi, loc)) {
529 *p++ = (wchar_t)wi;
530 nread++;
531 }
532 if (wi != WEOF)
533 __ungetwc(wi, fp, loc);
534 *p = '\0';
535 nassigned++;
536 } else {
537 if (!(flags & SUPPRESS))
538 mbp = va_arg(ap, char *);
539 mbs = initial;
540 while ((wi = __fgetwc(fp, loc)) != WEOF &&
541 width != 0 &&
542 !iswspace_l(wi, loc)) {
543 if (width >= mb_cur_max &&
544 !(flags & SUPPRESS)) {
545 nconv = wcrtomb_l(mbp, wi, &mbs, loc);
546 if (nconv == (size_t)-1)
547 goto input_failure;
548 } else {
549 nconv = wcrtomb_l(mbbuf, wi,
550 &mbs, loc);
551 if (nconv == (size_t)-1)
552 goto input_failure;
553 if (nconv > width)
554 break;
555 if (!(flags & SUPPRESS))
556 memcpy(mbp, mbbuf,
557 nconv);
558 }
559 if (!(flags & SUPPRESS))
560 mbp += nconv;
561 width -= nconv;
562 nread++;
563 }
564 if (wi != WEOF)
565 __ungetwc(wi, fp, loc);
566 if (!(flags & SUPPRESS)) {
567 *mbp = 0;
568 nassigned++;
569 }
570 }
571 continue;
572
573 case CT_INT:
574 /* scan an integer as if by the conversion function */
575 if (width == 0 || width > sizeof(buf) /
576 sizeof(*buf) - 1)
577 width = sizeof(buf) / sizeof(*buf) - 1;
578 flags |= SIGNOK | NDIGITS | NZDIGITS;
579 for (p = buf; width; width--) {
580 c = __fgetwc(fp, loc);
581 /*
582 * Switch on the character; `goto ok'
583 * if we accept it as a part of number.
584 */
585 switch (c) {
586
587 /*
588 * The digit 0 is always legal, but is
589 * special. For %i conversions, if no
590 * digits (zero or nonzero) have been
591 * scanned (only signs), we will have
592 * base==0. In that case, we should set
593 * it to 8 and enable 0x prefixing.
594 * Also, if we have not scanned zero digits
595 * before this, do not turn off prefixing
596 * (someone else will turn it off if we
597 * have scanned any nonzero digits).
598 */
599 case '0':
600 if (base == 0) {
601 base = 8;
602 flags |= PFXOK;
603 }
604 if (flags & NZDIGITS)
605 flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
606 else
607 flags &= ~(SIGNOK|PFXOK|NDIGITS);
608 goto ok;
609
610 /* 1 through 7 always legal */
611 case '1': case '2': case '3':
612 case '4': case '5': case '6': case '7':
613 base = basefix[base];
614 flags &= ~(SIGNOK | PFXOK | NDIGITS);
615 goto ok;
616
617 /* digits 8 and 9 ok iff decimal or hex */
618 case '8': case '9':
619 base = basefix[base];
620 if (base <= 8)
621 break; /* not legal here */
622 flags &= ~(SIGNOK | PFXOK | NDIGITS);
623 goto ok;
624
625 /* letters ok iff hex */
626 case 'A': case 'B': case 'C':
627 case 'D': case 'E': case 'F':
628 case 'a': case 'b': case 'c':
629 case 'd': case 'e': case 'f':
630 /* no need to fix base here */
631 if (base <= 10)
632 break; /* not legal here */
633 flags &= ~(SIGNOK | PFXOK | NDIGITS);
634 goto ok;
635
636 /* sign ok only as first character */
637 case '+': case '-':
638 if (flags & SIGNOK) {
639 flags &= ~SIGNOK;
640 flags |= HAVESIGN;
641 goto ok;
642 }
643 break;
644
645 /*
646 * x ok iff flag still set & 2nd char (or
647 * 3rd char if we have a sign).
648 */
649 case 'x': case 'X':
650 if (flags & PFXOK && p ==
651 buf + 1 + !!(flags & HAVESIGN)) {
652 base = 16; /* if %i */
653 flags &= ~PFXOK;
654 goto ok;
655 }
656 break;
657 }
658
659 /*
660 * If we got here, c is not a legal character
661 * for a number. Stop accumulating digits.
662 */
663 if (c != WEOF)
664 __ungetwc(c, fp, loc);
665 break;
666 ok:
667 /*
668 * c is legal: store it and look at the next.
669 */
670 *p++ = (wchar_t)c;
671 }
672 /*
673 * If we had only a sign, it is no good; push
674 * back the sign. If the number ends in `x',
675 * it was [sign] '0' 'x', so push back the x
676 * and treat it as [sign] '0'.
677 */
678 if (flags & NDIGITS) {
679 if (p > buf)
680 __ungetwc(*--p, fp, loc);
681 goto match_failure;
682 }
683 c = p[-1];
684 if (c == 'x' || c == 'X') {
685 --p;
686 __ungetwc(c, fp, loc);
687 }
688 if ((flags & SUPPRESS) == 0) {
689 uintmax_t res;
690
691 *p = 0;
692 if ((flags & UNSIGNED) == 0)
693 res = wcstoimax_l(buf, NULL, base, loc);
694 else
695 res = wcstoumax_l(buf, NULL, base, loc);
696 if (flags & POINTER)
697 *va_arg(ap, void **) =
698 (void *)(uintptr_t)res;
699 else if (flags & SHORTSHORT)
700 *va_arg(ap, char *) = res;
701 else if (flags & SHORT)
702 *va_arg(ap, short *) = res;
703 else if (flags & LONG)
704 *va_arg(ap, long *) = res;
705 else if (flags & LONGLONG)
706 *va_arg(ap, long long *) = res;
707 else if (flags & INTMAXT)
708 *va_arg(ap, intmax_t *) = res;
709 else if (flags & PTRDIFFT)
710 *va_arg(ap, ptrdiff_t *) = res;
711 else if (flags & SIZET)
712 *va_arg(ap, size_t *) = res;
713 else
714 *va_arg(ap, int *) = res;
715 nassigned++;
716 }
717 nread += p - buf;
718 break;
719
720 #ifndef NO_FLOATING_POINT
721 case CT_FLOAT:
722 {
723 wchar_t *pbuf;
724 /* scan a floating point number as if by strtod */
725 if ((width = parsefloat(fp, &pbuf, width, loc)) == 0)
726 goto match_failure;
727 if ((flags & SUPPRESS) == 0) {
728 if (flags & LONGDBL) {
729 long double res = wcstold_l(pbuf, &p, loc);
730 *va_arg(ap, long double *) = res;
731 } else if (flags & LONG) {
732 double res = wcstod_l(pbuf, &p, loc);
733 *va_arg(ap, double *) = res;
734 } else {
735 float res = wcstof_l(pbuf, &p, loc);
736 *va_arg(ap, float *) = res;
737 }
738 if (__scanfdebug && p - pbuf != width)
739 abort();
740 nassigned++;
741 }
742 nread += width;
743 break;
744 }
745 #endif /* !NO_FLOATING_POINT */
746 }
747 }
748 input_failure:
749 return (nassigned ? nassigned : EOF);
750 match_failure:
751 return (nassigned);
752 }
753
754 #ifndef NO_FLOATING_POINT
755 static int
756 parsefloat(FILE *fp, wchar_t **buf, size_t width, locale_t loc)
757 {
758 wchar_t *commit, *p;
759 int infnanpos = 0;
760 enum {
761 S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX,
762 S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
763 } state = S_START;
764 wchar_t c;
765 char *decimal_point;
766 wchar_t decpt;
767 _Bool gotmantdig = 0, ishex = 0;
768 static wchar_t *b = NULL;
769 static size_t bsiz = 0;
770 wchar_t *e;
771 size_t s;
772
773 if (bsiz == 0) {
774 b = (wchar_t *)malloc(BUF * sizeof(wchar_t));
775 if (b == NULL) {
776 *buf = NULL;
777 return 0;
778 }
779 bsiz = BUF;
780 }
781 s = (width == 0 ? bsiz : (width + 1));
782 if (s > bsiz) {
783 b = (wchar_t *)reallocf(b, s * sizeof(wchar_t));
784 if (b == NULL) {
785 bsiz = 0;
786 *buf = NULL;
787 return 0;
788 }
789 bsiz = s;
790 }
791 e = b + (s - 1);
792 /*
793 * We set commit = p whenever the string we have read so far
794 * constitutes a valid representation of a floating point
795 * number by itself. At some point, the parse will complete
796 * or fail, and we will ungetc() back to the last commit point.
797 * To ensure that the file offset gets updated properly, it is
798 * always necessary to read at least one character that doesn't
799 * match; thus, we can't short-circuit "infinity" or "nan(...)".
800 */
801 commit = b - 1;
802 c = WEOF;
803 decimal_point = localeconv_l(loc)->decimal_point;
804 mbtowc_l(&decpt, decimal_point, strlen(decimal_point), loc);
805 for (p = b; width == 0 || p < e; ) {
806 if ((c = __fgetwc(fp, loc)) == WEOF)
807 break;
808 reswitch:
809 switch (state) {
810 case S_START:
811 state = S_GOTSIGN;
812 if (c == '-' || c == '+')
813 break;
814 else
815 goto reswitch;
816 case S_GOTSIGN:
817 switch (c) {
818 case '0':
819 state = S_MAYBEHEX;
820 commit = p;
821 break;
822 case 'I':
823 case 'i':
824 state = S_INF;
825 break;
826 case 'N':
827 case 'n':
828 state = S_NAN;
829 break;
830 default:
831 state = S_DIGITS;
832 goto reswitch;
833 }
834 break;
835 case S_INF:
836 if (infnanpos > 6 ||
837 (c != "nfinity"[infnanpos] &&
838 c != "NFINITY"[infnanpos]))
839 goto parsedone;
840 if (infnanpos == 1 || infnanpos == 6)
841 commit = p; /* inf or infinity */
842 infnanpos++;
843 break;
844 case S_NAN:
845 switch (infnanpos) {
846 case -1: /* XXX kludge to deal with nan(...) */
847 goto parsedone;
848 case 0:
849 if (c != 'A' && c != 'a')
850 goto parsedone;
851 break;
852 case 1:
853 if (c != 'N' && c != 'n')
854 goto parsedone;
855 else
856 commit = p;
857 break;
858 case 2:
859 if (c != '(')
860 goto parsedone;
861 break;
862 default:
863 if (c == ')') {
864 commit = p;
865 infnanpos = -2;
866 } else if (!iswalnum_l(c, loc) && c != '_')
867 goto parsedone;
868 break;
869 }
870 infnanpos++;
871 break;
872 case S_MAYBEHEX:
873 state = S_DIGITS;
874 if (c == 'X' || c == 'x') {
875 ishex = 1;
876 break;
877 } else { /* we saw a '0', but no 'x' */
878 gotmantdig = 1;
879 goto reswitch;
880 }
881 case S_DIGITS:
882 if ((ishex && iswxdigit_l(c, loc)) || iswdigit_l(c, loc))
883 gotmantdig = 1;
884 else {
885 state = S_FRAC;
886 if (c != decpt)
887 goto reswitch;
888 }
889 if (gotmantdig)
890 commit = p;
891 break;
892 case S_FRAC:
893 if (((c == 'E' || c == 'e') && !ishex) ||
894 ((c == 'P' || c == 'p') && ishex)) {
895 if (!gotmantdig)
896 goto parsedone;
897 else
898 state = S_EXP;
899 } else if ((ishex && iswxdigit_l(c, loc)) || iswdigit_l(c, loc)) {
900 commit = p;
901 gotmantdig = 1;
902 } else
903 goto parsedone;
904 break;
905 case S_EXP:
906 state = S_EXPDIGITS;
907 if (c == '-' || c == '+')
908 break;
909 else
910 goto reswitch;
911 case S_EXPDIGITS:
912 if (iswdigit_l(c, loc))
913 commit = p;
914 else
915 goto parsedone;
916 break;
917 default:
918 abort();
919 }
920 if (p >= e) {
921 ssize_t diff = (p - b);
922 ssize_t com = (commit - b);
923 s += BUF;
924 b = (wchar_t *)reallocf(b, s * sizeof(wchar_t));
925 if (b == NULL) {
926 bsiz = 0;
927 *buf = NULL;
928 return 0;
929 }
930 bsiz = s;
931 e = b + (s - 1);
932 p = b + diff;
933 commit = b + com;
934 }
935 *p++ = c;
936 c = WEOF;
937 }
938
939 parsedone:
940 if (c != WEOF)
941 __ungetwc(c, fp, loc);
942 while (commit < --p)
943 __ungetwc(*p, fp, loc);
944 *++commit = '\0';
945 *buf = b;
946 return (commit - b);
947 }
948 #endif