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