vfwscanf.c revision 106282
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)
40static 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.24 2002/08/13 09:30:41 tjr Exp ");
43#endif
44__FBSDID("$FreeBSD: head/lib/libc/stdio/vfwscanf.c 106282 2002-11-01 05:13:01Z tjr $");
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 numeric conversions only:
87 * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point;
88 * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral.
89 */
90#define	SIGNOK		0x40	/* +/- is (still) legal */
91#define	NDIGITS		0x80	/* no digits detected */
92
93#define	DPTOK		0x100	/* (float) decimal point is still legal */
94#define	EXPOK		0x200	/* (float) exponent (e+3, etc) still legal */
95
96#define	PFXOK		0x100	/* 0x prefix is (still) legal */
97#define	NZDIGITS	0x200	/* no zero digits detected */
98
99/*
100 * Conversion types.
101 */
102#define	CT_CHAR		0	/* %c conversion */
103#define	CT_CCL		1	/* %[...] conversion */
104#define	CT_STRING	2	/* %s conversion */
105#define	CT_INT		3	/* %[dioupxX] conversion */
106#define	CT_FLOAT	4	/* %[efgEFG] conversion */
107
108#define	INCCL(_c)	\
109	(cclcompl ? (wmemchr(ccls, (_c), ccle - ccls) == NULL) : \
110	(wmemchr(ccls, (_c), ccle - ccls) != NULL))
111
112/*
113 * MT-safe version.
114 */
115int
116vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
117{
118	int ret;
119
120	FLOCKFILE(fp);
121	ORIENT(fp, 1);
122	ret = __vfwscanf(fp, fmt, ap);
123	FUNLOCKFILE(fp);
124	return (ret);
125}
126
127/*
128 * Non-MT-safe version.
129 */
130int
131__vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
132{
133	wint_t c;		/* character from format, or conversion */
134	size_t width;		/* field width, or 0 */
135	wchar_t *p;		/* points into all kinds of strings */
136	int n;			/* handy integer */
137	int flags;		/* flags as defined above */
138	wchar_t *p0;		/* saves original value of p when necessary */
139	int nassigned;		/* number of fields assigned */
140	int nconversions;	/* number of conversions */
141	int nread;		/* number of characters consumed from fp */
142	int base;		/* base argument to conversion function */
143	wchar_t buf[BUF];	/* buffer for numeric conversions */
144	const wchar_t *ccls;	/* character class start */
145	const wchar_t *ccle;	/* character class end */
146	int cclcompl;		/* ccl is complemented? */
147	wint_t wi;		/* handy wint_t */
148	char *mbp;		/* multibyte string pointer for %c %s %[ */
149	size_t nconv;		/* number of bytes in mb. conversion */
150	mbstate_t mbs;		/* multibyte state */
151	char mbbuf[MB_LEN_MAX];	/* temporary mb. character buffer */
152
153	/* `basefix' is used to avoid `if' tests in the integer scanner */
154	static short basefix[17] =
155		{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
156#ifdef FLOATING_POINT
157	char decimal_point = localeconv()->decimal_point[0];
158#endif
159
160	nassigned = 0;
161	nconversions = 0;
162	nread = 0;
163	ccls = ccle = NULL;
164	for (;;) {
165		c = *fmt++;
166		if (c == 0)
167			return (nassigned);
168		if (iswspace(c)) {
169			while ((c = __fgetwc(fp)) != WEOF &&
170			    iswspace(c))
171				;
172			if (c != WEOF)
173				__ungetwc(c, fp);
174			continue;
175		}
176		if (c != '%')
177			goto literal;
178		width = 0;
179		flags = 0;
180		/*
181		 * switch on the format.  continue if done;
182		 * break once format type is derived.
183		 */
184again:		c = *fmt++;
185		switch (c) {
186		case '%':
187literal:
188			if ((wi = __fgetwc(fp)) == WEOF)
189				goto input_failure;
190			if (wi != c) {
191				__ungetwc(wi, fp);
192				goto input_failure;
193			}
194			nread++;
195			continue;
196
197		case '*':
198			flags |= SUPPRESS;
199			goto again;
200		case 'j':
201			flags |= INTMAXT;
202			goto again;
203		case 'l':
204			if (flags & LONG) {
205				flags &= ~LONG;
206				flags |= LONGLONG;
207			} else
208				flags |= LONG;
209			goto again;
210		case 'q':
211			flags |= LONGLONG;	/* not quite */
212			goto again;
213		case 't':
214			flags |= PTRDIFFT;
215			goto again;
216		case 'z':
217			flags |= SIZET;
218			goto again;
219		case 'L':
220			flags |= LONGDBL;
221			goto again;
222		case 'h':
223			if (flags & SHORT) {
224				flags &= ~SHORT;
225				flags |= SHORTSHORT;
226			} else
227				flags |= SHORT;
228			goto again;
229
230		case '0': case '1': case '2': case '3': case '4':
231		case '5': case '6': case '7': case '8': case '9':
232			width = width * 10 + c - '0';
233			goto again;
234
235		/*
236		 * Conversions.
237		 */
238		case 'd':
239			c = CT_INT;
240			base = 10;
241			break;
242
243		case 'i':
244			c = CT_INT;
245			base = 0;
246			break;
247
248		case 'o':
249			c = CT_INT;
250			flags |= UNSIGNED;
251			base = 8;
252			break;
253
254		case 'u':
255			c = CT_INT;
256			flags |= UNSIGNED;
257			base = 10;
258			break;
259
260		case 'X':
261		case 'x':
262			flags |= PFXOK;	/* enable 0x prefixing */
263			c = CT_INT;
264			flags |= UNSIGNED;
265			base = 16;
266			break;
267
268#ifdef FLOATING_POINT
269		case 'E': case 'F': case 'G':
270		case 'e': case 'f': case 'g':
271			c = CT_FLOAT;
272			break;
273#endif
274
275		case 'S':
276			flags |= LONG;
277			/* FALLTHROUGH */
278		case 's':
279			c = CT_STRING;
280			break;
281
282		case '[':
283			ccls = fmt;
284			if (*fmt == '^') {
285				cclcompl = 1;
286				fmt++;
287			} else
288				cclcompl = 0;
289			if (*fmt == ']')
290				fmt++;
291			while (*fmt != '\0' && *fmt != ']')
292				fmt++;
293			ccle = fmt;
294			fmt++;
295			flags |= NOSKIP;
296			c = CT_CCL;
297			break;
298
299		case 'C':
300			flags |= LONG;
301			/* FALLTHROUGH */
302		case 'c':
303			flags |= NOSKIP;
304			c = CT_CHAR;
305			break;
306
307		case 'p':	/* pointer format is like hex */
308			flags |= POINTER | PFXOK;
309			c = CT_INT;		/* assumes sizeof(uintmax_t) */
310			flags |= UNSIGNED;	/*      >= sizeof(uintptr_t) */
311			base = 16;
312			break;
313
314		case 'n':
315			nconversions++;
316			if (flags & SUPPRESS)	/* ??? */
317				continue;
318			if (flags & SHORTSHORT)
319				*va_arg(ap, char *) = nread;
320			else if (flags & SHORT)
321				*va_arg(ap, short *) = nread;
322			else if (flags & LONG)
323				*va_arg(ap, long *) = nread;
324			else if (flags & LONGLONG)
325				*va_arg(ap, long long *) = nread;
326			else if (flags & INTMAXT)
327				*va_arg(ap, intmax_t *) = nread;
328			else if (flags & SIZET)
329				*va_arg(ap, size_t *) = nread;
330			else if (flags & PTRDIFFT)
331				*va_arg(ap, ptrdiff_t *) = nread;
332			else
333				*va_arg(ap, int *) = nread;
334			continue;
335
336		default:
337			goto match_failure;
338
339		/*
340		 * Disgusting backwards compatibility hack.	XXX
341		 */
342		case '\0':	/* compat */
343			return (EOF);
344		}
345
346		/*
347		 * Consume leading white space, except for formats
348		 * that suppress this.
349		 */
350		if ((flags & NOSKIP) == 0) {
351			while ((wi = __fgetwc(fp)) != WEOF && iswspace(wi))
352				nread++;
353			if (wi == WEOF)
354				goto input_failure;
355			__ungetwc(wi, fp);
356		}
357
358		/*
359		 * Do the conversion.
360		 */
361		switch (c) {
362
363		case CT_CHAR:
364			/* scan arbitrary characters (sets NOSKIP) */
365			if (width == 0)
366				width = 1;
367			if (flags & LONG) {
368				if (!(flags & SUPPRESS))
369					p = va_arg(ap, wchar_t *);
370				n = 0;
371				while (width-- != 0 &&
372				    (wi = __fgetwc(fp)) != WEOF) {
373					if (!(flags & SUPPRESS))
374						*p++ = (wchar_t)wi;
375					n++;
376				}
377				if (n == 0)
378					goto input_failure;
379				nread += n;
380				if (!(flags & SUPPRESS))
381					nassigned++;
382			} else {
383				if (!(flags & SUPPRESS))
384					mbp = va_arg(ap, char *);
385				n = 0;
386				memset(&mbs, 0, sizeof(mbs));
387				while (width != 0 &&
388				    (wi = __fgetwc(fp)) != WEOF) {
389					if (width >= MB_CUR_MAX &&
390					    !(flags & SUPPRESS)) {
391						nconv = wcrtomb(mbp, wi, &mbs);
392						if (nconv == (size_t)-1)
393							goto input_failure;
394					} else {
395						nconv = wcrtomb(mbbuf, wi,
396						    &mbs);
397						if (nconv == (size_t)-1)
398							goto input_failure;
399						if (nconv > width) {
400							__ungetwc(wi, fp);
401							break;
402						}
403						if (!(flags & SUPPRESS))
404							memcpy(mbp, mbbuf,
405							    nconv);
406					}
407					if (!(flags & SUPPRESS))
408						mbp += nconv;
409					width -= nconv;
410					n++;
411				}
412				if (n == 0)
413					goto input_failure;
414				nread += n;
415				if (!(flags & SUPPRESS))
416					nassigned++;
417			}
418			nconversions++;
419			break;
420
421		case CT_CCL:
422			/* scan a (nonempty) character class (sets NOSKIP) */
423			if (width == 0)
424				width = (size_t)~0;	/* `infinity' */
425			/* take only those things in the class */
426			if ((flags & SUPPRESS) && (flags & LONG)) {
427				n = 0;
428				while ((wi = __fgetwc(fp)) != WEOF &&
429				    width-- != 0 && INCCL(wi))
430					n++;
431				if (wi != WEOF)
432					__ungetwc(wi, fp);
433				if (n == 0)
434					goto match_failure;
435			} else if (flags & LONG) {
436				p0 = p = va_arg(ap, wchar_t *);
437				while ((wi = __fgetwc(fp)) != WEOF &&
438				    width-- != 0 && INCCL(wi))
439					*p++ = (wchar_t)wi;
440				if (wi != WEOF)
441					__ungetwc(wi, fp);
442				n = p - p0;
443				if (n == 0)
444					goto match_failure;
445				*p = 0;
446				nassigned++;
447			} else {
448				if (!(flags & SUPPRESS))
449					mbp = va_arg(ap, char *);
450				n = 0;
451				memset(&mbs, 0, sizeof(mbs));
452				while ((wi = __fgetwc(fp)) != WEOF &&
453				    width != 0 && INCCL(wi)) {
454					if (width >= MB_CUR_MAX &&
455					   !(flags & SUPPRESS)) {
456						nconv = wcrtomb(mbp, wi, &mbs);
457						if (nconv == (size_t)-1)
458							goto input_failure;
459					} else {
460						nconv = wcrtomb(mbbuf, wi,
461						    &mbs);
462						if (nconv == (size_t)-1)
463							goto input_failure;
464						if (nconv > width)
465							break;
466						if (!(flags & SUPPRESS))
467							memcpy(mbp, mbbuf,
468							    nconv);
469					}
470					if (!(flags & SUPPRESS))
471						mbp += nconv;
472					width -= nconv;
473					n++;
474				}
475				if (wi != WEOF)
476					__ungetwc(wi, fp);
477				if (!(flags & SUPPRESS)) {
478					*mbp = 0;
479					nassigned++;
480				}
481			}
482			nread += n;
483			nconversions++;
484			break;
485
486		case CT_STRING:
487			/* like CCL, but zero-length string OK, & no NOSKIP */
488			if (width == 0)
489				width = (size_t)~0;
490			if ((flags & SUPPRESS) && (flags & LONG)) {
491				while ((wi = __fgetwc(fp)) != WEOF &&
492				    width-- != 0 &&
493				    !iswspace(wi))
494					nread++;
495				if (wi != WEOF)
496					__ungetwc(wi, fp);
497			} else if (flags & LONG) {
498				p0 = p = va_arg(ap, wchar_t *);
499				while ((wi = __fgetwc(fp)) != WEOF &&
500				    width-- != 0 &&
501				    !iswspace(wi)) {
502					*p++ = (wchar_t)wi;
503					nread++;
504				}
505				if (wi != WEOF)
506					__ungetwc(wi, fp);
507				*p = '\0';
508				nassigned++;
509			} else {
510				if (!(flags & SUPPRESS))
511					mbp = va_arg(ap, char *);
512				memset(&mbs, 0, sizeof(mbs));
513				while ((wi = __fgetwc(fp)) != WEOF &&
514				    width != 0 &&
515				    !iswspace(wi)) {
516					if (width >= MB_CUR_MAX &&
517					    !(flags & SUPPRESS)) {
518						nconv = wcrtomb(mbp, wi, &mbs);
519						if (nconv == (size_t)-1)
520							goto input_failure;
521					} else {
522						nconv = wcrtomb(mbbuf, wi,
523						    &mbs);
524						if (nconv == (size_t)-1)
525							goto input_failure;
526						if (nconv > width)
527							break;
528						if (!(flags & SUPPRESS))
529							memcpy(mbp, mbbuf,
530							    nconv);
531					}
532					if (!(flags & SUPPRESS))
533						mbp += nconv;
534					width -= nconv;
535					nread++;
536				}
537				if (wi != WEOF)
538					__ungetwc(wi, fp);
539				if (!(flags & SUPPRESS)) {
540					*mbp = 0;
541					nassigned++;
542				}
543			}
544			nconversions++;
545			continue;
546
547		case CT_INT:
548			/* scan an integer as if by the conversion function */
549#ifdef hardway
550			if (width == 0 || width > sizeof(buf) - 1)
551				width = sizeof(buf) - 1;
552#else
553			/* size_t is unsigned, hence this optimisation */
554			if (--width > sizeof(buf) - 2)
555				width = sizeof(buf) - 2;
556			width++;
557#endif
558			flags |= SIGNOK | NDIGITS | NZDIGITS;
559			for (p = buf; width; width--) {
560				c = __fgetwc(fp);
561				/*
562				 * Switch on the character; `goto ok'
563				 * if we accept it as a part of number.
564				 */
565				switch (c) {
566
567				/*
568				 * The digit 0 is always legal, but is
569				 * special.  For %i conversions, if no
570				 * digits (zero or nonzero) have been
571				 * scanned (only signs), we will have
572				 * base==0.  In that case, we should set
573				 * it to 8 and enable 0x prefixing.
574				 * Also, if we have not scanned zero digits
575				 * before this, do not turn off prefixing
576				 * (someone else will turn it off if we
577				 * have scanned any nonzero digits).
578				 */
579				case '0':
580					if (base == 0) {
581						base = 8;
582						flags |= PFXOK;
583					}
584					if (flags & NZDIGITS)
585					    flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
586					else
587					    flags &= ~(SIGNOK|PFXOK|NDIGITS);
588					goto ok;
589
590				/* 1 through 7 always legal */
591				case '1': case '2': case '3':
592				case '4': case '5': case '6': case '7':
593					base = basefix[base];
594					flags &= ~(SIGNOK | PFXOK | NDIGITS);
595					goto ok;
596
597				/* digits 8 and 9 ok iff decimal or hex */
598				case '8': case '9':
599					base = basefix[base];
600					if (base <= 8)
601						break;	/* not legal here */
602					flags &= ~(SIGNOK | PFXOK | NDIGITS);
603					goto ok;
604
605				/* letters ok iff hex */
606				case 'A': case 'B': case 'C':
607				case 'D': case 'E': case 'F':
608				case 'a': case 'b': case 'c':
609				case 'd': case 'e': case 'f':
610					/* no need to fix base here */
611					if (base <= 10)
612						break;	/* not legal here */
613					flags &= ~(SIGNOK | PFXOK | NDIGITS);
614					goto ok;
615
616				/* sign ok only as first character */
617				case '+': case '-':
618					if (flags & SIGNOK) {
619						flags &= ~SIGNOK;
620						goto ok;
621					}
622					break;
623
624				/* x ok iff flag still set & 2nd char */
625				case 'x': case 'X':
626					if (flags & PFXOK && p == buf + 1) {
627						base = 16;	/* if %i */
628						flags &= ~PFXOK;
629						goto ok;
630					}
631					break;
632				}
633
634				/*
635				 * If we got here, c is not a legal character
636				 * for a number.  Stop accumulating digits.
637				 */
638				if (c != WEOF)
639					__ungetwc(c, fp);
640				break;
641		ok:
642				/*
643				 * c is legal: store it and look at the next.
644				 */
645				*p++ = (wchar_t)c;
646			}
647			/*
648			 * If we had only a sign, it is no good; push
649			 * back the sign.  If the number ends in `x',
650			 * it was [sign] '0' 'x', so push back the x
651			 * and treat it as [sign] '0'.
652			 */
653			if (flags & NDIGITS) {
654				if (p > buf)
655					__ungetwc(*--p, fp);
656				goto match_failure;
657			}
658			c = p[-1];
659			if (c == 'x' || c == 'X') {
660				--p;
661				__ungetwc(c, fp);
662			}
663			if ((flags & SUPPRESS) == 0) {
664				uintmax_t res;
665
666				*p = 0;
667				if ((flags & UNSIGNED) == 0)
668				    res = wcstoimax(buf, NULL, base);
669				else
670				    res = wcstoumax(buf, NULL, base);
671				if (flags & POINTER)
672					*va_arg(ap, void **) =
673							(void *)(uintptr_t)res;
674				else if (flags & SHORTSHORT)
675					*va_arg(ap, char *) = res;
676				else if (flags & SHORT)
677					*va_arg(ap, short *) = res;
678				else if (flags & LONG)
679					*va_arg(ap, long *) = res;
680				else if (flags & LONGLONG)
681					*va_arg(ap, long long *) = res;
682				else if (flags & INTMAXT)
683					*va_arg(ap, intmax_t *) = res;
684				else if (flags & PTRDIFFT)
685					*va_arg(ap, ptrdiff_t *) = res;
686				else if (flags & SIZET)
687					*va_arg(ap, size_t *) = res;
688				else
689					*va_arg(ap, int *) = res;
690				nassigned++;
691			}
692			nread += p - buf;
693			nconversions++;
694			break;
695
696#ifdef FLOATING_POINT
697		case CT_FLOAT:
698			/* scan a floating point number as if by strtod */
699#ifdef hardway
700			if (width == 0 || width > sizeof(buf) - 1)
701				width = sizeof(buf) - 1;
702#else
703			/* size_t is unsigned, hence this optimisation */
704			if (--width > sizeof(buf) - 2)
705				width = sizeof(buf) - 2;
706			width++;
707#endif
708			flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
709			for (p = buf; width; width--) {
710				c = __fgetwc(fp);
711				/*
712				 * This code mimicks the integer conversion
713				 * code, but is much simpler.
714				 */
715				switch (c) {
716
717				case '0': case '1': case '2': case '3':
718				case '4': case '5': case '6': case '7':
719				case '8': case '9':
720					flags &= ~(SIGNOK | NDIGITS);
721					goto fok;
722
723				case '+': case '-':
724					if (flags & SIGNOK) {
725						flags &= ~SIGNOK;
726						goto fok;
727					}
728					break;
729				case 'e': case 'E':
730					/* no exponent without some digits */
731					if ((flags&(NDIGITS|EXPOK)) == EXPOK) {
732						flags =
733						    (flags & ~(EXPOK|DPTOK)) |
734						    SIGNOK | NDIGITS;
735						goto fok;
736					}
737					break;
738				default:
739					if (c == (wchar_t)decimal_point &&
740					    (flags & DPTOK)) {
741						flags &= ~(SIGNOK | DPTOK);
742						goto fok;
743					}
744					break;
745				}
746				if (c != WEOF)
747					__ungetwc(c, fp);
748				break;
749		fok:
750				*p++ = c;
751			}
752			/*
753			 * If no digits, might be missing exponent digits
754			 * (just give back the exponent) or might be missing
755			 * regular digits, but had sign and/or decimal point.
756			 */
757			if (flags & NDIGITS) {
758				if (flags & EXPOK) {
759					/* no digits at all */
760					while (p > buf)
761						__ungetwc(*--p, fp);
762					goto match_failure;
763				}
764				/* just a bad exponent (e and maybe sign) */
765				c = *--p;
766				if (c != 'e' && c != 'E') {
767					__ungetwc(c, fp);/* sign */
768					c = *--p;
769				}
770				__ungetwc(c, fp);
771			}
772			if ((flags & SUPPRESS) == 0) {
773				double res;
774
775				*p = 0;
776				/* XXX this loses precision for long doubles. */
777				res = wcstod(buf, NULL);
778				if (flags & LONGDBL)
779					*va_arg(ap, long double *) = res;
780				else if (flags & LONG)
781					*va_arg(ap, double *) = res;
782				else
783					*va_arg(ap, float *) = res;
784				nassigned++;
785			}
786			nread += p - buf;
787			nconversions++;
788			break;
789#endif /* FLOATING_POINT */
790		}
791	}
792input_failure:
793	return (nconversions != 0 ? nassigned : EOF);
794match_failure:
795	return (nassigned);
796}
797