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