xref: /dragonfly/lib/libc/stdio/vfwscanf.c (revision d9f85b33)
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  * Copyright (c) 2011 The FreeBSD Foundation
9  * All rights reserved.
10  * Portions of this software were developed by David Chisnall
11  * under sponsorship from the FreeBSD Foundation.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  * @(#)vfscanf.c	8.1 (Berkeley) 6/4/93
38  * $FreeBSD: head/lib/libc/stdio/vfwscanf.c 249808 2013-04-23 13:33:13Z emaste $
39  */
40 
41 #include "namespace.h"
42 #include <ctype.h>
43 #include <inttypes.h>
44 #include <limits.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <stddef.h>
48 #include <stdarg.h>
49 #include <string.h>
50 #include <wchar.h>
51 #include <wctype.h>
52 #include "un-namespace.h"
53 
54 #include "libc_private.h"
55 #include "local.h"
56 #include "xlocale_private.h"
57 
58 #define	BUF		513	/* Maximum length of numeric string. */
59 
60 /*
61  * Flags used during conversion.
62  */
63 #define	LONG		0x01	/* l: long or double */
64 #define	LONGDBL		0x02	/* L: long double */
65 #define	SHORT		0x04	/* h: short */
66 #define	SUPPRESS	0x08	/* *: suppress assignment */
67 #define	POINTER		0x10	/* p: void * (as hex) */
68 #define	NOSKIP		0x20	/* [ or c: do not skip blanks */
69 #define	LONGLONG	0x400	/* ll: long long (+ deprecated q: quad) */
70 #define	INTMAXT		0x800	/* j: intmax_t */
71 #define	PTRDIFFT	0x1000	/* t: ptrdiff_t */
72 #define	SIZET		0x2000	/* z: size_t */
73 #define	SHORTSHORT	0x4000	/* hh: char */
74 #define	UNSIGNED	0x8000	/* %[oupxX] conversions */
75 
76 /*
77  * The following are used in integral conversions only:
78  * SIGNOK, NDIGITS, PFXOK, and NZDIGITS
79  */
80 #define	SIGNOK		0x40	/* +/- is (still) legal */
81 #define	NDIGITS		0x80	/* no digits detected */
82 #define	PFXOK		0x100	/* 0x prefix is (still) legal */
83 #define	NZDIGITS	0x200	/* no zero digits detected */
84 #define	HAVESIGN	0x10000	/* sign detected */
85 
86 /*
87  * Conversion types.
88  */
89 #define	CT_CHAR		0	/* %c conversion */
90 #define	CT_CCL		1	/* %[...] conversion */
91 #define	CT_STRING	2	/* %s conversion */
92 #define	CT_INT		3	/* %[dioupxX] conversion */
93 #define	CT_FLOAT	4	/* %[efgEFG] conversion */
94 
95 #ifndef NO_FLOATING_POINT
96 static int parsefloat(FILE *, wchar_t *, wchar_t *, locale_t);
97 #endif
98 
99 struct ccl {
100 	const wchar_t *start;	/* character class start */
101 	const wchar_t *end;	/* character class end */
102 	int compl;		/* ccl is complemented? */
103 };
104 
105 static __inline int
106 inccl(const struct ccl *ccl, wint_t wi)
107 {
108 
109 	if (ccl->compl) {
110 		return (wmemchr(ccl->start, wi, ccl->end - ccl->start)
111 		    == NULL);
112 	} else {
113 		return (wmemchr(ccl->start, wi, ccl->end - ccl->start) != NULL);
114 	}
115 }
116 
117 /*
118  * Conversion functions are passed a pointer to this object instead of
119  * a real parameter to indicate that the assignment-suppression (*)
120  * flag was specified.  We could use a NULL pointer to indicate this,
121  * but that would mask bugs in applications that call scanf() with a
122  * NULL pointer.
123  */
124 static const int suppress;
125 #define	SUPPRESS_PTR	((void *)&suppress)
126 
127 static const mbstate_t initial_mbs;
128 
129 /*
130  * The following conversion functions return the number of characters consumed,
131  * or -1 on input failure.  Character class conversion returns 0 on match
132  * failure.
133  */
134 
135 static __inline int
136 convert_char(FILE *fp, char * mbp, int width, locale_t locale)
137 {
138 	mbstate_t mbs;
139 	size_t nconv;
140 	wint_t wi;
141 	int n;
142 
143 	n = 0;
144 	mbs = initial_mbs;
145 	while (width-- != 0 && (wi = __fgetwc(fp, locale)) != WEOF) {
146 		if (mbp != SUPPRESS_PTR) {
147 			nconv = wcrtomb(mbp, wi, &mbs);
148 			if (nconv == (size_t)-1)
149 				return (-1);
150 			mbp += nconv;
151 		}
152 		n++;
153 	}
154 	if (n == 0)
155 		return (-1);
156 	return (n);
157 }
158 
159 static __inline int
160 convert_wchar(FILE *fp, wchar_t *wcp, int width, locale_t locale)
161 {
162 	wint_t wi;
163 	int n;
164 
165 	n = 0;
166 	while (width-- != 0 && (wi = __fgetwc(fp, locale)) != WEOF) {
167 		if (wcp != SUPPRESS_PTR)
168 			*wcp++ = (wchar_t)wi;
169 		n++;
170 	}
171 	if (n == 0)
172 		return (-1);
173 	return (n);
174 }
175 
176 static __inline int
177 convert_ccl(FILE *fp, char * mbp, int width, const struct ccl *ccl,
178     locale_t locale)
179 {
180 	mbstate_t mbs;
181 	size_t nconv;
182 	wint_t wi;
183 	int n;
184 
185 	n = 0;
186 	mbs = initial_mbs;
187 	while ((wi = __fgetwc(fp, locale)) != WEOF &&
188 	    width-- != 0 && inccl(ccl, wi)) {
189 		if (mbp != SUPPRESS_PTR) {
190 			nconv = wcrtomb(mbp, wi, &mbs);
191 			if (nconv == (size_t)-1)
192 				return (-1);
193 			mbp += nconv;
194 		}
195 		n++;
196 	}
197 	if (wi != WEOF)
198 		__ungetwc(wi, fp, locale);
199 	if (mbp != SUPPRESS_PTR)
200 		*mbp = 0;
201 	return (n);
202 }
203 
204 static __inline int
205 convert_wccl(FILE *fp, wchar_t *wcp, int width, const struct ccl *ccl,
206     locale_t locale)
207 {
208 	wchar_t *wcp0;
209 	wint_t wi;
210 	int n;
211 
212 	if (wcp == SUPPRESS_PTR) {
213 		n = 0;
214 		while ((wi = __fgetwc(fp, locale)) != WEOF &&
215 		    width-- != 0 && inccl(ccl, wi))
216 			n++;
217 		if (wi != WEOF)
218 			__ungetwc(wi, fp, locale);
219 	} else {
220 		wcp0 = wcp;
221 		while ((wi = __fgetwc(fp, locale)) != WEOF &&
222 		    width-- != 0 && inccl(ccl, wi))
223 			*wcp++ = (wchar_t)wi;
224 		if (wi != WEOF)
225 			__ungetwc(wi, fp, locale);
226 		n = wcp - wcp0;
227 		if (n == 0)
228 			return (0);
229 		*wcp = 0;
230 	}
231 	return (n);
232 }
233 
234 static __inline int
235 convert_string(FILE *fp, char * mbp, int width, locale_t locale)
236 {
237 	mbstate_t mbs;
238 	size_t nconv;
239 	wint_t wi;
240 	int nread;
241 
242 	mbs = initial_mbs;
243 	nread = 0;
244 	while ((wi = __fgetwc(fp, locale)) != WEOF && width-- != 0 &&
245 	    !iswspace(wi)) {
246 		if (mbp != SUPPRESS_PTR) {
247 			nconv = wcrtomb(mbp, wi, &mbs);
248 			if (nconv == (size_t)-1)
249 				return (-1);
250 			mbp += nconv;
251 		}
252 		nread++;
253 	}
254 	if (wi != WEOF)
255 		__ungetwc(wi, fp, locale);
256 	if (mbp != SUPPRESS_PTR)
257 		*mbp = 0;
258 	return (nread);
259 }
260 
261 static __inline int
262 convert_wstring(FILE *fp, wchar_t *wcp, int width, locale_t locale)
263 {
264 	wchar_t *wcp0 __unused;
265 	wint_t wi;
266 	int nread;
267 
268 	nread = 0;
269 	if (wcp == SUPPRESS_PTR) {
270 		while ((wi = __fgetwc(fp, locale)) != WEOF &&
271 		    width-- != 0 && !iswspace(wi))
272 			nread++;
273 		if (wi != WEOF)
274 			__ungetwc(wi, fp, locale);
275 	} else {
276 		wcp0 = wcp;
277 		while ((wi = __fgetwc(fp, locale)) != WEOF &&
278 		    width-- != 0 && !iswspace(wi)) {
279 			*wcp++ = (wchar_t)wi;
280 			nread++;
281 		}
282 		if (wi != WEOF)
283 			__ungetwc(wi, fp, locale);
284 		*wcp = '\0';
285 	}
286 	return (nread);
287 }
288 
289 /*
290  * Read an integer, storing it in buf.  The only relevant bit in the
291  * flags argument is PFXOK.
292  *
293  * Return 0 on a match failure, and the number of characters read
294  * otherwise.
295  */
296 static __inline int
297 parseint(FILE *fp, wchar_t *buf, int width, int base, int flags,
298     locale_t locale)
299 {
300 	/* `basefix' is used to avoid `if' tests */
301 	static const short basefix[17] =
302 		{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
303 	wchar_t *wcp;
304 	int c;
305 
306 	flags |= SIGNOK | NDIGITS | NZDIGITS;
307 	for (wcp = buf; width; width--) {
308 		c = __fgetwc(fp, locale);
309 		/*
310 		 * Switch on the character; `goto ok' if we accept it
311 		 * as a part of number.
312 		 */
313 		switch (c) {
314 
315 		/*
316 		 * The digit 0 is always legal, but is special.  For
317 		 * %i conversions, if no digits (zero or nonzero) have
318 		 * been scanned (only signs), we will have base==0.
319 		 * In that case, we should set it to 8 and enable 0x
320 		 * prefixing.  Also, if we have not scanned zero
321 		 * digits before this, do not turn off prefixing
322 		 * (someone else will turn it off if we have scanned
323 		 * any nonzero digits).
324 		 */
325 		case '0':
326 			if (base == 0) {
327 				base = 8;
328 				flags |= PFXOK;
329 			}
330 			if (flags & NZDIGITS)
331 				flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
332 			else
333 				flags &= ~(SIGNOK|PFXOK|NDIGITS);
334 			goto ok;
335 
336 		/* 1 through 7 always legal */
337 		case '1': case '2': case '3':
338 		case '4': case '5': case '6': case '7':
339 			base = basefix[base];
340 			flags &= ~(SIGNOK | PFXOK | NDIGITS);
341 			goto ok;
342 
343 		/* digits 8 and 9 ok iff decimal or hex */
344 		case '8': case '9':
345 			base = basefix[base];
346 			if (base <= 8)
347 				break;	/* not legal here */
348 			flags &= ~(SIGNOK | PFXOK | NDIGITS);
349 			goto ok;
350 
351 		/* letters ok iff hex */
352 		case 'A': case 'B': case 'C':
353 		case 'D': case 'E': case 'F':
354 		case 'a': case 'b': case 'c':
355 		case 'd': case 'e': case 'f':
356 			/* no need to fix base here */
357 			if (base <= 10)
358 				break;	/* not legal here */
359 			flags &= ~(SIGNOK | PFXOK | NDIGITS);
360 			goto ok;
361 
362 		/* sign ok only as first character */
363 		case '+': case '-':
364 			if (flags & SIGNOK) {
365 				flags &= ~SIGNOK;
366 				flags |= HAVESIGN;
367 				goto ok;
368 			}
369 			break;
370 
371 		/*
372 		 * x ok iff flag still set & 2nd char (or 3rd char if
373 		 * we have a sign).
374 		 */
375 		case 'x': case 'X':
376 			if (flags & PFXOK && wcp ==
377 			    buf + 1 + !!(flags & HAVESIGN)) {
378 				base = 16;	/* if %i */
379 				flags &= ~PFXOK;
380 				goto ok;
381 			}
382 			break;
383 		}
384 
385 		/*
386 		 * If we got here, c is not a legal character for a
387 		 * number.  Stop accumulating digits.
388 		 */
389 		if (c != WEOF)
390 			__ungetwc(c, fp, locale);
391 		break;
392 	ok:
393 		/*
394 		 * c is legal: store it and look at the next.
395 		 */
396 		*wcp++ = (wchar_t)c;
397 	}
398 	/*
399 	 * If we had only a sign, it is no good; push back the sign.
400 	 * If the number ends in `x', it was [sign] '0' 'x', so push
401 	 * back the x and treat it as [sign] '0'.
402 	 */
403 	if (flags & NDIGITS) {
404 		if (wcp > buf)
405 			__ungetwc(*--wcp, fp, locale);
406 		return (0);
407 	}
408 	c = wcp[-1];
409 	if (c == 'x' || c == 'X') {
410 		--wcp;
411 		__ungetwc(c, fp, locale);
412 	}
413 	return (wcp - buf);
414 }
415 
416 /*
417  * MT-safe version.
418  */
419 int
420 vfwscanf_l(FILE * __restrict fp, locale_t locale,
421 		const wchar_t * __restrict fmt, va_list ap)
422 {
423 	int ret;
424 	FIX_LOCALE(locale);
425 
426 	FLOCKFILE(fp);
427 	ORIENT(fp, 1);
428 	ret = __vfwscanf(fp, locale, fmt, ap);
429 	FUNLOCKFILE(fp);
430 	return (ret);
431 }
432 int
433 vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
434 {
435 	return vfwscanf_l(fp, __get_locale(), fmt, ap);
436 }
437 
438 /*
439  * Non-MT-safe version.
440  */
441 int
442 __vfwscanf(FILE * __restrict fp, locale_t locale,
443 		const wchar_t * __restrict fmt, va_list ap)
444 {
445 #define	GETARG(type)	((flags & SUPPRESS) ? SUPPRESS_PTR : va_arg(ap, type))
446 	wint_t c;		/* character from format, or conversion */
447 	size_t width;		/* field width, or 0 */
448 	int flags;		/* flags as defined above */
449 	int nassigned;		/* number of fields assigned */
450 	int nconversions;	/* number of conversions */
451 	int nr;			/* characters read by the current conversion */
452 	int nread;		/* number of characters consumed from fp */
453 	int base;		/* base argument to conversion function */
454 	struct ccl ccl;		/* character class info */
455 	wchar_t buf[BUF];	/* buffer for numeric conversions */
456 	wint_t wi;		/* handy wint_t */
457 
458 	nassigned = 0;
459 	nconversions = 0;
460 	nread = 0;
461 	ccl.start = ccl.end = NULL;
462 	for (;;) {
463 		c = *fmt++;
464 		if (c == 0)
465 			return (nassigned);
466 		if (iswspace(c)) {
467 			while ((c = __fgetwc(fp, locale)) != WEOF &&
468 			    iswspace_l(c, locale))
469 				nread++;
470 			if (c != WEOF)
471 				__ungetwc(c, fp, locale);
472 			continue;
473 		}
474 		if (c != '%')
475 			goto literal;
476 		width = 0;
477 		flags = 0;
478 		/*
479 		 * switch on the format.  continue if done;
480 		 * break once format type is derived.
481 		 */
482 again:		c = *fmt++;
483 		switch (c) {
484 		case '%':
485 literal:
486 			if ((wi = __fgetwc(fp, locale)) == WEOF)
487 				goto input_failure;
488 			if (wi != c) {
489 				__ungetwc(wi, fp, locale);
490 				goto input_failure;
491 			}
492 			nread++;
493 			continue;
494 
495 		case '*':
496 			flags |= SUPPRESS;
497 			goto again;
498 		case 'j':
499 			flags |= INTMAXT;
500 			goto again;
501 		case 'l':
502 			if (flags & LONG) {
503 				flags &= ~LONG;
504 				flags |= LONGLONG;
505 			} else
506 				flags |= LONG;
507 			goto again;
508 		case 'q':
509 			flags |= LONGLONG;	/* not quite */
510 			goto again;
511 		case 't':
512 			flags |= PTRDIFFT;
513 			goto again;
514 		case 'z':
515 			flags |= SIZET;
516 			goto again;
517 		case 'L':
518 			flags |= LONGDBL;
519 			goto again;
520 		case 'h':
521 			if (flags & SHORT) {
522 				flags &= ~SHORT;
523 				flags |= SHORTSHORT;
524 			} else
525 				flags |= SHORT;
526 			goto again;
527 
528 		case '0': case '1': case '2': case '3': case '4':
529 		case '5': case '6': case '7': case '8': case '9':
530 			width = width * 10 + c - '0';
531 			goto again;
532 
533 		/*
534 		 * Conversions.
535 		 */
536 		case 'd':
537 			c = CT_INT;
538 			base = 10;
539 			break;
540 
541 		case 'i':
542 			c = CT_INT;
543 			base = 0;
544 			break;
545 
546 		case 'o':
547 			c = CT_INT;
548 			flags |= UNSIGNED;
549 			base = 8;
550 			break;
551 
552 		case 'u':
553 			c = CT_INT;
554 			flags |= UNSIGNED;
555 			base = 10;
556 			break;
557 
558 		case 'X':
559 		case 'x':
560 			flags |= PFXOK;	/* enable 0x prefixing */
561 			c = CT_INT;
562 			flags |= UNSIGNED;
563 			base = 16;
564 			break;
565 
566 #ifndef NO_FLOATING_POINT
567 		case 'A': case 'E': case 'F': case 'G':
568 		case 'a': case 'e': case 'f': case 'g':
569 			c = CT_FLOAT;
570 			break;
571 #endif
572 
573 		case 'S':
574 			flags |= LONG;
575 			/* FALLTHROUGH */
576 		case 's':
577 			c = CT_STRING;
578 			break;
579 
580 		case '[':
581 			ccl.start = fmt;
582 			if (*fmt == '^') {
583 				ccl.compl = 1;
584 				fmt++;
585 			} else
586 				ccl.compl = 0;
587 			if (*fmt == ']')
588 				fmt++;
589 			while (*fmt != '\0' && *fmt != ']')
590 				fmt++;
591 			ccl.end = fmt;
592 			fmt++;
593 			flags |= NOSKIP;
594 			c = CT_CCL;
595 			break;
596 
597 		case 'C':
598 			flags |= LONG;
599 			/* FALLTHROUGH */
600 		case 'c':
601 			flags |= NOSKIP;
602 			c = CT_CHAR;
603 			break;
604 
605 		case 'p':	/* pointer format is like hex */
606 			flags |= POINTER | PFXOK;
607 			c = CT_INT;		/* assumes sizeof(uintmax_t) */
608 			flags |= UNSIGNED;	/*      >= sizeof(uintptr_t) */
609 			base = 16;
610 			break;
611 
612 		case 'n':
613 			if (flags & SUPPRESS)	/* ??? */
614 				continue;
615 			if (flags & SHORTSHORT)
616 				*va_arg(ap, char *) = nread;
617 			else if (flags & SHORT)
618 				*va_arg(ap, short *) = nread;
619 			else if (flags & LONG)
620 				*va_arg(ap, long *) = nread;
621 			else if (flags & LONGLONG)
622 				*va_arg(ap, long long *) = nread;
623 			else if (flags & INTMAXT)
624 				*va_arg(ap, intmax_t *) = nread;
625 			else if (flags & SIZET)
626 				*va_arg(ap, size_t *) = nread;
627 			else if (flags & PTRDIFFT)
628 				*va_arg(ap, ptrdiff_t *) = nread;
629 			else
630 				*va_arg(ap, int *) = nread;
631 			continue;
632 
633 		default:
634 			goto match_failure;
635 
636 		/*
637 		 * Disgusting backwards compatibility hack.	XXX
638 		 */
639 		case '\0':	/* compat */
640 			return (EOF);
641 		}
642 
643 		/*
644 		 * Consume leading white space, except for formats
645 		 * that suppress this.
646 		 */
647 		if ((flags & NOSKIP) == 0) {
648 			while ((wi = __fgetwc(fp, locale)) != WEOF && iswspace(wi))
649 				nread++;
650 			if (wi == WEOF)
651 				goto input_failure;
652 			__ungetwc(wi, fp, locale);
653 		}
654 
655 		/*
656 		 * Do the conversion.
657 		 */
658 		switch (c) {
659 
660 		case CT_CHAR:
661 			/* scan arbitrary characters (sets NOSKIP) */
662 			if (width == 0)
663 				width = 1;
664 			if (flags & LONG) {
665 				nr = convert_wchar(fp, GETARG(wchar_t *), width,
666 				    locale);
667 			} else {
668 				nr = convert_char(fp, GETARG(char *), width,
669 				    locale);
670 			}
671 			if (nr < 0)
672 				goto input_failure;
673 			break;
674 
675 		case CT_CCL:
676 			/* scan a (nonempty) character class (sets NOSKIP) */
677 			if (width == 0)
678 				width = (size_t)~0;	/* `infinity' */
679 			/* take only those things in the class */
680 			if (flags & LONG) {
681 				nr = convert_wccl(fp, GETARG(wchar_t *), width,
682 				    &ccl, locale);
683 			} else {
684 				nr = convert_ccl(fp, GETARG(char *), width,
685 				    &ccl, locale);
686 			}
687 			if (nr <= 0) {
688 				if (nr < 0)
689 					goto input_failure;
690 				else /* nr == 0 */
691 					goto match_failure;
692 			}
693 			break;
694 
695 		case CT_STRING:
696 			/* like CCL, but zero-length string OK, & no NOSKIP */
697 			if (width == 0)
698 				width = (size_t)~0;
699 			if (flags & LONG) {
700 				nr = convert_wstring(fp, GETARG(wchar_t *),
701 				    width, locale);
702 			} else {
703 				nr = convert_string(fp, GETARG(char *), width,
704 				    locale);
705 			}
706 			if (nr < 0)
707 				goto input_failure;
708 			break;
709 
710 		case CT_INT:
711 			/* scan an integer as if by the conversion function */
712 			if (width == 0 || width > sizeof(buf) /
713 			    sizeof(*buf) - 1)
714 				width = sizeof(buf) / sizeof(*buf) - 1;
715 
716 			nr = parseint(fp, buf, width, base, flags, locale);
717 			if (nr == 0)
718 				goto match_failure;
719 			if ((flags & SUPPRESS) == 0) {
720 				uintmax_t res;
721 
722 				buf[nr] = L'\0';
723 				if ((flags & UNSIGNED) == 0)
724 				    res = wcstoimax(buf, NULL, base);
725 				else
726 				    res = wcstoumax(buf, NULL, base);
727 				if (flags & POINTER)
728 					*va_arg(ap, void **) =
729 							(void *)(uintptr_t)res;
730 				else if (flags & SHORTSHORT)
731 					*va_arg(ap, char *) = res;
732 				else if (flags & SHORT)
733 					*va_arg(ap, short *) = res;
734 				else if (flags & LONG)
735 					*va_arg(ap, long *) = res;
736 				else if (flags & LONGLONG)
737 					*va_arg(ap, long long *) = res;
738 				else if (flags & INTMAXT)
739 					*va_arg(ap, intmax_t *) = res;
740 				else if (flags & PTRDIFFT)
741 					*va_arg(ap, ptrdiff_t *) = res;
742 				else if (flags & SIZET)
743 					*va_arg(ap, size_t *) = res;
744 				else
745 					*va_arg(ap, int *) = res;
746 			}
747 			break;
748 
749 #ifndef NO_FLOATING_POINT
750 		case CT_FLOAT:
751 			/* scan a floating point number as if by strtod */
752 			if (width == 0 || width > sizeof(buf) /
753 			    sizeof(*buf) - 1)
754 				width = sizeof(buf) / sizeof(*buf) - 1;
755 			nr = parsefloat(fp, buf, buf + width, locale);
756 			if (nr == 0)
757 				goto match_failure;
758 			if ((flags & SUPPRESS) == 0) {
759 				if (flags & LONGDBL) {
760 					long double res = wcstold(buf, NULL);
761 					*va_arg(ap, long double *) = res;
762 				} else if (flags & LONG) {
763 					double res = wcstod(buf, NULL);
764 					*va_arg(ap, double *) = res;
765 				} else {
766 					float res = wcstof(buf, NULL);
767 					*va_arg(ap, float *) = res;
768 				}
769 			}
770 			break;
771 #endif /* !NO_FLOATING_POINT */
772 		}
773 		if (!(flags & SUPPRESS))
774 			nassigned++;
775 		nread += nr;
776 		nconversions++;
777 	}
778 input_failure:
779 	return (nconversions != 0 ? nassigned : EOF);
780 match_failure:
781 	return (nassigned);
782 }
783 
784 #ifndef NO_FLOATING_POINT
785 static int
786 parsefloat(FILE *fp, wchar_t *buf, wchar_t *end, locale_t locale)
787 {
788 	mbstate_t mbs;
789 	size_t nconv;
790 	wchar_t *commit, *p;
791 	int infnanpos = 0;
792 	enum {
793 		S_START, S_GOTSIGN, S_INF, S_NAN, S_DONE, S_MAYBEHEX,
794 		S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
795 	} state = S_START;
796 	wchar_t c;
797 	wchar_t decpt;
798 	_Bool gotmantdig = 0, ishex = 0;
799 
800 	mbs = initial_mbs;
801 	nconv = mbrtowc(&decpt, localeconv()->decimal_point, MB_CUR_MAX, &mbs);
802 	if (nconv == (size_t)-1 || nconv == (size_t)-2)
803 		decpt = '.';	/* failsafe */
804 
805 	/*
806 	 * We set commit = p whenever the string we have read so far
807 	 * constitutes a valid representation of a floating point
808 	 * number by itself.  At some point, the parse will complete
809 	 * or fail, and we will ungetc() back to the last commit point.
810 	 * To ensure that the file offset gets updated properly, it is
811 	 * always necessary to read at least one character that doesn't
812 	 * match; thus, we can't short-circuit "infinity" or "nan(...)".
813 	 */
814 	commit = buf - 1;
815 	c = WEOF;
816 	for (p = buf; p < end; ) {
817 		if ((c = __fgetwc(fp, locale)) == WEOF)
818 			break;
819 reswitch:
820 		switch (state) {
821 		case S_START:
822 			state = S_GOTSIGN;
823 			if (c == '-' || c == '+')
824 				break;
825 			else
826 				goto reswitch;
827 		case S_GOTSIGN:
828 			switch (c) {
829 			case '0':
830 				state = S_MAYBEHEX;
831 				commit = p;
832 				break;
833 			case 'I':
834 			case 'i':
835 				state = S_INF;
836 				break;
837 			case 'N':
838 			case 'n':
839 				state = S_NAN;
840 				break;
841 			default:
842 				state = S_DIGITS;
843 				goto reswitch;
844 			}
845 			break;
846 		case S_INF:
847 			if (infnanpos > 6 ||
848 			    (c != "nfinity"[infnanpos] &&
849 			     c != "NFINITY"[infnanpos]))
850 				goto parsedone;
851 			if (infnanpos == 1 || infnanpos == 6)
852 				commit = p;	/* inf or infinity */
853 			infnanpos++;
854 			break;
855 		case S_NAN:
856 			switch (infnanpos) {
857 			case 0:
858 				if (c != 'A' && c != 'a')
859 					goto parsedone;
860 				break;
861 			case 1:
862 				if (c != 'N' && c != 'n')
863 					goto parsedone;
864 				else
865 					commit = p;
866 				break;
867 			case 2:
868 				if (c != '(')
869 					goto parsedone;
870 				break;
871 			default:
872 				if (c == ')') {
873 					commit = p;
874 					state = S_DONE;
875 				} else if (!iswalnum(c) && c != '_')
876 					goto parsedone;
877 				break;
878 			}
879 			infnanpos++;
880 			break;
881 		case S_DONE:
882 			goto parsedone;
883 		case S_MAYBEHEX:
884 			state = S_DIGITS;
885 			if (c == 'X' || c == 'x') {
886 				ishex = 1;
887 				break;
888 			} else {	/* we saw a '0', but no 'x' */
889 				gotmantdig = 1;
890 				goto reswitch;
891 			}
892 		case S_DIGITS:
893 			if ((ishex && iswxdigit(c)) || iswdigit(c))
894 				gotmantdig = 1;
895 			else {
896 				state = S_FRAC;
897 				if (c != decpt)
898 					goto reswitch;
899 			}
900 			if (gotmantdig)
901 				commit = p;
902 			break;
903 		case S_FRAC:
904 			if (((c == 'E' || c == 'e') && !ishex) ||
905 			    ((c == 'P' || c == 'p') && ishex)) {
906 				if (!gotmantdig)
907 					goto parsedone;
908 				else
909 					state = S_EXP;
910 			} else if ((ishex && iswxdigit(c)) || iswdigit(c)) {
911 				commit = p;
912 				gotmantdig = 1;
913 			} else
914 				goto parsedone;
915 			break;
916 		case S_EXP:
917 			state = S_EXPDIGITS;
918 			if (c == '-' || c == '+')
919 				break;
920 			else
921 				goto reswitch;
922 		case S_EXPDIGITS:
923 			if (iswdigit(c))
924 				commit = p;
925 			else
926 				goto parsedone;
927 			break;
928 		default:
929 			abort();
930 		}
931 		*p++ = c;
932 		c = WEOF;
933 	}
934 
935 parsedone:
936 	if (c != WEOF)
937 		__ungetwc(c, fp, locale);
938 	while (commit < --p)
939 		__ungetwc(*p, fp, locale);
940 	*++commit = '\0';
941 	return (commit - buf);
942 }
943 #endif
944