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