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