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