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