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