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