vfwscanf.c revision 128844
131921Sbrian/*-
231921Sbrian * Copyright (c) 1990, 1993
331921Sbrian *	The Regents of the University of California.  All rights reserved.
431921Sbrian *
531921Sbrian * This code is derived from software contributed to Berkeley by
631921Sbrian * Chris Torek.
731921Sbrian *
831921Sbrian * Redistribution and use in source and binary forms, with or without
931921Sbrian * modification, are permitted provided that the following conditions
1031921Sbrian * are met:
1131921Sbrian * 1. Redistributions of source code must retain the above copyright
1231921Sbrian *    notice, this list of conditions and the following disclaimer.
1331921Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1431921Sbrian *    notice, this list of conditions and the following disclaimer in the
1531921Sbrian *    documentation and/or other materials provided with the distribution.
1631921Sbrian * 3. All advertising materials mentioning features or use of this software
1731921Sbrian *    must display the following acknowledgement:
1831921Sbrian *	This product includes software developed by the University of
1931921Sbrian *	California, Berkeley and its contributors.
2031921Sbrian * 4. Neither the name of the University nor the names of its contributors
2131921Sbrian *    may be used to endorse or promote products derived from this software
2231921Sbrian *    without specific prior written permission.
2331921Sbrian *
2431921Sbrian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2531921Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2636285Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2723840Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2823840Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2923840Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3030715Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3123840Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3230715Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3323840Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3431343Sbrian * SUCH DAMAGE.
3523840Sbrian */
3634331Sbrian
3723840Sbrian#if 0
3823840Sbrian#if defined(LIBC_SCCS) && !defined(lint)
3923840Sbrianstatic char sccsid[] = "@(#)vfscanf.c	8.1 (Berkeley) 6/4/93";
4023840Sbrian#endif /* LIBC_SCCS and not lint */
4123840Sbrian#endif
4228679Sbrian#include <sys/cdefs.h>
4328679Sbrian__FBSDID("$FreeBSD: head/lib/libc/stdio/vfwscanf.c 128844 2004-05-02 20:13:29Z obrien $");
4428679Sbrian
4528679Sbrian#include "namespace.h"
4623840Sbrian#include <ctype.h>
4723840Sbrian#include <inttypes.h>
4823840Sbrian#include <stdio.h>
4923840Sbrian#include <stdlib.h>
5028679Sbrian#include <stddef.h>
5128679Sbrian#include <stdarg.h>
5223840Sbrian#include <string.h>
5323840Sbrian#include <wchar.h>
5428679Sbrian#include <wctype.h>
5536285Sbrian#include "un-namespace.h"
5628679Sbrian
5728679Sbrian#include "libc_private.h"
5823840Sbrian#include "local.h"
5928679Sbrian
6028679Sbrian#ifndef NO_FLOATING_POINT
6136285Sbrian#include <locale.h>
6228679Sbrian#endif
6328679Sbrian
6428679Sbrian#define	BUF		513	/* Maximum length of numeric string. */
6528679Sbrian
6628679Sbrian/*
6728679Sbrian * Flags used during conversion.
6828679Sbrian */
6928679Sbrian#define	LONG		0x01	/* l: long or double */
7028679Sbrian#define	LONGDBL		0x02	/* L: long double */
7128679Sbrian#define	SHORT		0x04	/* h: short */
7228679Sbrian#define	SUPPRESS	0x08	/* *: suppress assignment */
7328679Sbrian#define	POINTER		0x10	/* p: void * (as hex) */
7428679Sbrian#define	NOSKIP		0x20	/* [ or c: do not skip blanks */
7523840Sbrian#define	LONGLONG	0x400	/* ll: long long (+ deprecated q: quad) */
7623840Sbrian#define	INTMAXT		0x800	/* j: intmax_t */
7723840Sbrian#define	PTRDIFFT	0x1000	/* t: ptrdiff_t */
7823840Sbrian#define	SIZET		0x2000	/* z: size_t */
7923840Sbrian#define	SHORTSHORT	0x4000	/* hh: char */
8028679Sbrian#define	UNSIGNED	0x8000	/* %[oupxX] conversions */
8136285Sbrian
8228679Sbrian/*
8328679Sbrian * The following are used in integral conversions only:
8428679Sbrian * SIGNOK, NDIGITS, PFXOK, and NZDIGITS
8523840Sbrian */
8628679Sbrian#define	SIGNOK		0x40	/* +/- is (still) legal */
8728679Sbrian#define	NDIGITS		0x80	/* no digits detected */
8828679Sbrian#define	PFXOK		0x100	/* 0x prefix is (still) legal */
8928679Sbrian#define	NZDIGITS	0x200	/* no zero digits detected */
9028679Sbrian#define	HAVESIGN	0x10000	/* sign detected */
9128679Sbrian
9228679Sbrian/*
9328679Sbrian * Conversion types.
9428679Sbrian */
9523840Sbrian#define	CT_CHAR		0	/* %c conversion */
96#define	CT_CCL		1	/* %[...] conversion */
97#define	CT_STRING	2	/* %s conversion */
98#define	CT_INT		3	/* %[dioupxX] conversion */
99#define	CT_FLOAT	4	/* %[efgEFG] conversion */
100
101static int parsefloat(FILE *, wchar_t *, wchar_t *);
102
103extern int __scanfdebug;
104
105#define	INCCL(_c)	\
106	(cclcompl ? (wmemchr(ccls, (_c), ccle - ccls) == NULL) : \
107	(wmemchr(ccls, (_c), ccle - ccls) != NULL))
108
109/*
110 * MT-safe version.
111 */
112int
113vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
114{
115	int ret;
116
117	FLOCKFILE(fp);
118	ORIENT(fp, 1);
119	ret = __vfwscanf(fp, fmt, ap);
120	FUNLOCKFILE(fp);
121	return (ret);
122}
123
124/*
125 * Non-MT-safe version.
126 */
127int
128__vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
129{
130	wint_t c;		/* character from format, or conversion */
131	size_t width;		/* field width, or 0 */
132	wchar_t *p;		/* points into all kinds of strings */
133	int n;			/* handy integer */
134	int flags;		/* flags as defined above */
135	wchar_t *p0;		/* saves original value of p when necessary */
136	int nassigned;		/* number of fields assigned */
137	int nconversions;	/* number of conversions */
138	int nread;		/* number of characters consumed from fp */
139	int base;		/* base argument to conversion function */
140	wchar_t buf[BUF];	/* buffer for numeric conversions */
141	const wchar_t *ccls;	/* character class start */
142	const wchar_t *ccle;	/* character class end */
143	int cclcompl;		/* ccl is complemented? */
144	wint_t wi;		/* handy wint_t */
145	char *mbp;		/* multibyte string pointer for %c %s %[ */
146	size_t nconv;		/* number of bytes in mb. conversion */
147	char mbbuf[MB_LEN_MAX];	/* temporary mb. character buffer */
148	static const mbstate_t initial;
149	mbstate_t mbs;
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		 */
179again:		c = *fmt++;
180		switch (c) {
181		case '%':
182literal:
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#ifndef NO_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				mbs = initial;
382				while (width != 0 &&
383				    (wi = __fgetwc(fp)) != WEOF) {
384					if (width >= MB_CUR_MAX &&
385					    !(flags & SUPPRESS)) {
386						nconv = wcrtomb(mbp, wi, &mbs);
387						if (nconv == (size_t)-1)
388							goto input_failure;
389					} else {
390						nconv = wcrtomb(mbbuf, wi,
391						    &mbs);
392						if (nconv == (size_t)-1)
393							goto input_failure;
394						if (nconv > width) {
395							__ungetwc(wi, fp);
396							break;
397						}
398						if (!(flags & SUPPRESS))
399							memcpy(mbp, mbbuf,
400							    nconv);
401					}
402					if (!(flags & SUPPRESS))
403						mbp += nconv;
404					width -= nconv;
405					n++;
406				}
407				if (n == 0)
408					goto input_failure;
409				nread += n;
410				if (!(flags & SUPPRESS))
411					nassigned++;
412			}
413			nconversions++;
414			break;
415
416		case CT_CCL:
417			/* scan a (nonempty) character class (sets NOSKIP) */
418			if (width == 0)
419				width = (size_t)~0;	/* `infinity' */
420			/* take only those things in the class */
421			if ((flags & SUPPRESS) && (flags & LONG)) {
422				n = 0;
423				while ((wi = __fgetwc(fp)) != WEOF &&
424				    width-- != 0 && INCCL(wi))
425					n++;
426				if (wi != WEOF)
427					__ungetwc(wi, fp);
428				if (n == 0)
429					goto match_failure;
430			} else if (flags & LONG) {
431				p0 = p = va_arg(ap, wchar_t *);
432				while ((wi = __fgetwc(fp)) != WEOF &&
433				    width-- != 0 && INCCL(wi))
434					*p++ = (wchar_t)wi;
435				if (wi != WEOF)
436					__ungetwc(wi, fp);
437				n = p - p0;
438				if (n == 0)
439					goto match_failure;
440				*p = 0;
441				nassigned++;
442			} else {
443				if (!(flags & SUPPRESS))
444					mbp = va_arg(ap, char *);
445				n = 0;
446				mbs = initial;
447				while ((wi = __fgetwc(fp)) != WEOF &&
448				    width != 0 && INCCL(wi)) {
449					if (width >= MB_CUR_MAX &&
450					   !(flags & SUPPRESS)) {
451						nconv = wcrtomb(mbp, wi, &mbs);
452						if (nconv == (size_t)-1)
453							goto input_failure;
454					} else {
455						nconv = wcrtomb(mbbuf, wi,
456						    &mbs);
457						if (nconv == (size_t)-1)
458							goto input_failure;
459						if (nconv > width)
460							break;
461						if (!(flags & SUPPRESS))
462							memcpy(mbp, mbbuf,
463							    nconv);
464					}
465					if (!(flags & SUPPRESS))
466						mbp += nconv;
467					width -= nconv;
468					n++;
469				}
470				if (wi != WEOF)
471					__ungetwc(wi, fp);
472				if (!(flags & SUPPRESS)) {
473					*mbp = 0;
474					nassigned++;
475				}
476			}
477			nread += n;
478			nconversions++;
479			break;
480
481		case CT_STRING:
482			/* like CCL, but zero-length string OK, & no NOSKIP */
483			if (width == 0)
484				width = (size_t)~0;
485			if ((flags & SUPPRESS) && (flags & LONG)) {
486				while ((wi = __fgetwc(fp)) != WEOF &&
487				    width-- != 0 &&
488				    !iswspace(wi))
489					nread++;
490				if (wi != WEOF)
491					__ungetwc(wi, fp);
492			} else if (flags & LONG) {
493				p0 = p = va_arg(ap, wchar_t *);
494				while ((wi = __fgetwc(fp)) != WEOF &&
495				    width-- != 0 &&
496				    !iswspace(wi)) {
497					*p++ = (wchar_t)wi;
498					nread++;
499				}
500				if (wi != WEOF)
501					__ungetwc(wi, fp);
502				*p = '\0';
503				nassigned++;
504			} else {
505				if (!(flags & SUPPRESS))
506					mbp = va_arg(ap, char *);
507				mbs = initial;
508				while ((wi = __fgetwc(fp)) != WEOF &&
509				    width != 0 &&
510				    !iswspace(wi)) {
511					if (width >= MB_CUR_MAX &&
512					    !(flags & SUPPRESS)) {
513						nconv = wcrtomb(mbp, wi, &mbs);
514						if (nconv == (size_t)-1)
515							goto input_failure;
516					} else {
517						nconv = wcrtomb(mbbuf, wi,
518						    &mbs);
519						if (nconv == (size_t)-1)
520							goto input_failure;
521						if (nconv > width)
522							break;
523						if (!(flags & SUPPRESS))
524							memcpy(mbp, mbbuf,
525							    nconv);
526					}
527					if (!(flags & SUPPRESS))
528						mbp += nconv;
529					width -= nconv;
530					nread++;
531				}
532				if (wi != WEOF)
533					__ungetwc(wi, fp);
534				if (!(flags & SUPPRESS)) {
535					*mbp = 0;
536					nassigned++;
537				}
538			}
539			nconversions++;
540			continue;
541
542		case CT_INT:
543			/* scan an integer as if by the conversion function */
544			if (width == 0 || width > sizeof(buf) /
545			    sizeof(*buf) - 1)
546				width = sizeof(buf) / sizeof(*buf) - 1;
547			flags |= SIGNOK | NDIGITS | NZDIGITS;
548			for (p = buf; width; width--) {
549				c = __fgetwc(fp);
550				/*
551				 * Switch on the character; `goto ok'
552				 * if we accept it as a part of number.
553				 */
554				switch (c) {
555
556				/*
557				 * The digit 0 is always legal, but is
558				 * special.  For %i conversions, if no
559				 * digits (zero or nonzero) have been
560				 * scanned (only signs), we will have
561				 * base==0.  In that case, we should set
562				 * it to 8 and enable 0x prefixing.
563				 * Also, if we have not scanned zero digits
564				 * before this, do not turn off prefixing
565				 * (someone else will turn it off if we
566				 * have scanned any nonzero digits).
567				 */
568				case '0':
569					if (base == 0) {
570						base = 8;
571						flags |= PFXOK;
572					}
573					if (flags & NZDIGITS)
574					    flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
575					else
576					    flags &= ~(SIGNOK|PFXOK|NDIGITS);
577					goto ok;
578
579				/* 1 through 7 always legal */
580				case '1': case '2': case '3':
581				case '4': case '5': case '6': case '7':
582					base = basefix[base];
583					flags &= ~(SIGNOK | PFXOK | NDIGITS);
584					goto ok;
585
586				/* digits 8 and 9 ok iff decimal or hex */
587				case '8': case '9':
588					base = basefix[base];
589					if (base <= 8)
590						break;	/* not legal here */
591					flags &= ~(SIGNOK | PFXOK | NDIGITS);
592					goto ok;
593
594				/* letters ok iff hex */
595				case 'A': case 'B': case 'C':
596				case 'D': case 'E': case 'F':
597				case 'a': case 'b': case 'c':
598				case 'd': case 'e': case 'f':
599					/* no need to fix base here */
600					if (base <= 10)
601						break;	/* not legal here */
602					flags &= ~(SIGNOK | PFXOK | NDIGITS);
603					goto ok;
604
605				/* sign ok only as first character */
606				case '+': case '-':
607					if (flags & SIGNOK) {
608						flags &= ~SIGNOK;
609						flags |= HAVESIGN;
610						goto ok;
611					}
612					break;
613
614				/*
615				 * x ok iff flag still set & 2nd char (or
616				 * 3rd char if we have a sign).
617				 */
618				case 'x': case 'X':
619					if (flags & PFXOK && p ==
620					    buf + 1 + !!(flags & HAVESIGN)) {
621						base = 16;	/* if %i */
622						flags &= ~PFXOK;
623						goto ok;
624					}
625					break;
626				}
627
628				/*
629				 * If we got here, c is not a legal character
630				 * for a number.  Stop accumulating digits.
631				 */
632				if (c != WEOF)
633					__ungetwc(c, fp);
634				break;
635		ok:
636				/*
637				 * c is legal: store it and look at the next.
638				 */
639				*p++ = (wchar_t)c;
640			}
641			/*
642			 * If we had only a sign, it is no good; push
643			 * back the sign.  If the number ends in `x',
644			 * it was [sign] '0' 'x', so push back the x
645			 * and treat it as [sign] '0'.
646			 */
647			if (flags & NDIGITS) {
648				if (p > buf)
649					__ungetwc(*--p, fp);
650				goto match_failure;
651			}
652			c = p[-1];
653			if (c == 'x' || c == 'X') {
654				--p;
655				__ungetwc(c, fp);
656			}
657			if ((flags & SUPPRESS) == 0) {
658				uintmax_t res;
659
660				*p = 0;
661				if ((flags & UNSIGNED) == 0)
662				    res = wcstoimax(buf, NULL, base);
663				else
664				    res = wcstoumax(buf, NULL, base);
665				if (flags & POINTER)
666					*va_arg(ap, void **) =
667							(void *)(uintptr_t)res;
668				else if (flags & SHORTSHORT)
669					*va_arg(ap, char *) = res;
670				else if (flags & SHORT)
671					*va_arg(ap, short *) = res;
672				else if (flags & LONG)
673					*va_arg(ap, long *) = res;
674				else if (flags & LONGLONG)
675					*va_arg(ap, long long *) = res;
676				else if (flags & INTMAXT)
677					*va_arg(ap, intmax_t *) = res;
678				else if (flags & PTRDIFFT)
679					*va_arg(ap, ptrdiff_t *) = res;
680				else if (flags & SIZET)
681					*va_arg(ap, size_t *) = res;
682				else
683					*va_arg(ap, int *) = res;
684				nassigned++;
685			}
686			nread += p - buf;
687			nconversions++;
688			break;
689
690#ifndef NO_FLOATING_POINT
691		case CT_FLOAT:
692			/* scan a floating point number as if by strtod */
693			if (width == 0 || width > sizeof(buf) /
694			    sizeof(*buf) - 1)
695				width = sizeof(buf) / sizeof(*buf) - 1;
696			if ((width = parsefloat(fp, buf, buf + width)) == 0)
697				goto match_failure;
698			if ((flags & SUPPRESS) == 0) {
699				if (flags & LONGDBL) {
700					long double res = wcstold(buf, &p);
701					*va_arg(ap, long double *) = res;
702				} else if (flags & LONG) {
703					double res = wcstod(buf, &p);
704					*va_arg(ap, double *) = res;
705				} else {
706					float res = wcstof(buf, &p);
707					*va_arg(ap, float *) = res;
708				}
709				if (__scanfdebug && p - buf != width)
710					abort();
711				nassigned++;
712			}
713			nread += width;
714			nconversions++;
715			break;
716#endif /* !NO_FLOATING_POINT */
717		}
718	}
719input_failure:
720	return (nconversions != 0 ? nassigned : EOF);
721match_failure:
722	return (nassigned);
723}
724
725#ifndef NO_FLOATING_POINT
726static int
727parsefloat(FILE *fp, wchar_t *buf, wchar_t *end)
728{
729	wchar_t *commit, *p;
730	int infnanpos = 0;
731	enum {
732		S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX,
733		S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
734	} state = S_START;
735	wchar_t c;
736	wchar_t decpt = (wchar_t)(unsigned char)*localeconv()->decimal_point;
737	_Bool gotmantdig = 0, ishex = 0;
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(fp)) == WEOF)
752			break;
753reswitch:
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 -1:	/* XXX kludge to deal with nan(...) */
792				goto parsedone;
793			case 0:
794				if (c != 'A' && c != 'a')
795					goto parsedone;
796				break;
797			case 1:
798				if (c != 'N' && c != 'n')
799					goto parsedone;
800				else
801					commit = p;
802				break;
803			case 2:
804				if (c != '(')
805					goto parsedone;
806				break;
807			default:
808				if (c == ')') {
809					commit = p;
810					infnanpos = -2;
811				} else if (!iswalnum(c) && c != '_')
812					goto parsedone;
813				break;
814			}
815			infnanpos++;
816			break;
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
869parsedone:
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