xref: /openbsd/lib/libc/stdio/vfwscanf.c (revision 2c53affb)
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  */
102 int
__vfwscanf(FILE * __restrict fp,const wchar_t * __restrict fmt,__va_list ap)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 		 */
160 again:		c = *fmt++;
161 		switch (c) {
162 		case '%':
163 literal:
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 	}
784 input_failure:
785 	return (nconversions != 0 ? nassigned : EOF);
786 match_failure:
787 	return (nassigned);
788 }
789 
790 int
vfwscanf(FILE * __restrict fp,const wchar_t * __restrict fmt,__va_list ap)791 vfwscanf(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 }
800 DEF_STRONG(vfwscanf);
801