xref: /netbsd-src/lib/libc/stdio/vfwscanf.c (revision 388550b026d49b7f7b7480b1113bf82bb8d6a480)
1*388550b0Srillig /*	$NetBSD: vfwscanf.c,v 1.13 2022/04/19 20:32:16 rillig Exp $	*/
2f432bbb6Schristos 
3f432bbb6Schristos /*-
4f432bbb6Schristos  * Copyright (c) 1990, 1993
5f432bbb6Schristos  *	The Regents of the University of California.  All rights reserved.
6f432bbb6Schristos  *
7f432bbb6Schristos  * This code is derived from software contributed to Berkeley by
8f432bbb6Schristos  * Chris Torek.
9f432bbb6Schristos  *
10f432bbb6Schristos  * Redistribution and use in source and binary forms, with or without
11f432bbb6Schristos  * modification, are permitted provided that the following conditions
12f432bbb6Schristos  * are met:
13f432bbb6Schristos  * 1. Redistributions of source code must retain the above copyright
14f432bbb6Schristos  *    notice, this list of conditions and the following disclaimer.
15f432bbb6Schristos  * 2. Redistributions in binary form must reproduce the above copyright
16f432bbb6Schristos  *    notice, this list of conditions and the following disclaimer in the
17f432bbb6Schristos  *    documentation and/or other materials provided with the distribution.
18f432bbb6Schristos  * 3. All advertising materials mentioning features or use of this software
19f432bbb6Schristos  *    must display the following acknowledgement:
20f432bbb6Schristos  *	This product includes software developed by the University of
21f432bbb6Schristos  *	California, Berkeley and its contributors.
22f432bbb6Schristos  * 4. Neither the name of the University nor the names of its contributors
23f432bbb6Schristos  *    may be used to endorse or promote products derived from this software
24f432bbb6Schristos  *    without specific prior written permission.
25f432bbb6Schristos  *
26f432bbb6Schristos  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27f432bbb6Schristos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28f432bbb6Schristos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29f432bbb6Schristos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30f432bbb6Schristos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31f432bbb6Schristos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32f432bbb6Schristos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33f432bbb6Schristos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34f432bbb6Schristos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35f432bbb6Schristos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36f432bbb6Schristos  * SUCH DAMAGE.
37f432bbb6Schristos  */
38f432bbb6Schristos 
39f432bbb6Schristos #include <sys/cdefs.h>
40f432bbb6Schristos #if defined(LIBC_SCCS) && !defined(lint)
41f432bbb6Schristos #if 0
42f432bbb6Schristos static char sccsid[] = "@(#)ftell.c	8.2 (Berkeley) 5/4/95";
43f432bbb6Schristos __FBSDID("$FreeBSD: src/lib/libc/stdio/vfwscanf.c,v 1.12 2004/05/02 20:13:29 obrien Exp $");
44f432bbb6Schristos #else
45*388550b0Srillig __RCSID("$NetBSD: vfwscanf.c,v 1.13 2022/04/19 20:32:16 rillig Exp $");
46f432bbb6Schristos #endif
47f432bbb6Schristos #endif /* LIBC_SCCS and not lint */
48f432bbb6Schristos 
49f432bbb6Schristos #include "namespace.h"
50f432bbb6Schristos #include <ctype.h>
51f432bbb6Schristos #include <inttypes.h>
52c5e820caSchristos #include <assert.h>
53f432bbb6Schristos #include <stdio.h>
54f432bbb6Schristos #include <stdlib.h>
55f432bbb6Schristos #include <stddef.h>
56f432bbb6Schristos #include <stdarg.h>
57f432bbb6Schristos #include <string.h>
58f432bbb6Schristos #include <limits.h>
59f432bbb6Schristos #include <wchar.h>
60f432bbb6Schristos #include <wctype.h>
61f432bbb6Schristos 
62f432bbb6Schristos #include "reentrant.h"
63f432bbb6Schristos #include "local.h"
64f432bbb6Schristos 
65f432bbb6Schristos #include <locale.h>
669790c07aSjoerg #include "setlocale_local.h"
67f432bbb6Schristos 
68f432bbb6Schristos #define	BUF		513	/* Maximum length of numeric string. */
69f432bbb6Schristos 
70f432bbb6Schristos /*
71f432bbb6Schristos  * Flags used during conversion.
72f432bbb6Schristos  */
73f432bbb6Schristos #define	LONG		0x01	/* l: long or double */
74f432bbb6Schristos #define	LONGDBL		0x02	/* L: long double */
75f432bbb6Schristos #define	SHORT		0x04	/* h: short */
76f432bbb6Schristos #define	SUPPRESS	0x08	/* *: suppress assignment */
77f432bbb6Schristos #define	POINTER		0x10	/* p: void * (as hex) */
78f432bbb6Schristos #define	NOSKIP		0x20	/* [ or c: do not skip blanks */
79f432bbb6Schristos #define	LONGLONG	0x400	/* ll: quad_t (+ deprecated q: quad) */
80f432bbb6Schristos #define	INTMAXT		0x800	/* j: intmax_t */
81f432bbb6Schristos #define	PTRDIFFT	0x1000	/* t: ptrdiff_t */
82f432bbb6Schristos #define	SIZET		0x2000	/* z: size_t */
83f432bbb6Schristos #define	SHORTSHORT	0x4000	/* hh: char */
84f432bbb6Schristos #define	UNSIGNED	0x8000	/* %[oupxX] conversions */
85f432bbb6Schristos 
86f432bbb6Schristos /*
87f432bbb6Schristos  * The following are used in integral conversions only:
88f432bbb6Schristos  * SIGNOK, NDIGITS, PFXOK, and NZDIGITS
89f432bbb6Schristos  */
90f432bbb6Schristos #define	SIGNOK		0x40	/* +/- is (still) legal */
91f432bbb6Schristos #define	NDIGITS		0x80	/* no digits detected */
92f432bbb6Schristos #define	PFXOK		0x100	/* 0x prefix is (still) legal */
93f432bbb6Schristos #define	NZDIGITS	0x200	/* no zero digits detected */
94f432bbb6Schristos #define	HAVESIGN	0x10000	/* sign detected */
95f432bbb6Schristos 
96f432bbb6Schristos /*
97f432bbb6Schristos  * Conversion types.
98f432bbb6Schristos  */
99f432bbb6Schristos #define	CT_CHAR		0	/* %c conversion */
100f432bbb6Schristos #define	CT_CCL		1	/* %[...] conversion */
101f432bbb6Schristos #define	CT_STRING	2	/* %s conversion */
102f432bbb6Schristos #define	CT_INT		3	/* %[dioupxX] conversion */
103f432bbb6Schristos #define	CT_FLOAT	4	/* %[efgEFG] conversion */
104f432bbb6Schristos 
105f42f5177Spooka #ifndef NO_FLOATING_POINT
1069790c07aSjoerg static int parsefloat(FILE *, wchar_t *, wchar_t *, locale_t);
107f42f5177Spooka #endif
108f432bbb6Schristos 
109f432bbb6Schristos #define	INCCL(_c)	\
110f432bbb6Schristos 	(cclcompl ? (wmemchr(ccls, (_c), (size_t)(ccle - ccls)) == NULL) : \
111f432bbb6Schristos 	(wmemchr(ccls, (_c), (size_t)(ccle - ccls)) != NULL))
112f432bbb6Schristos 
113f432bbb6Schristos /*
114f432bbb6Schristos  * MT-safe version.
115f432bbb6Schristos  */
116f432bbb6Schristos int
vfwscanf(FILE * __restrict fp,const wchar_t * __restrict fmt,va_list ap)117f432bbb6Schristos vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
118f432bbb6Schristos {
119e0ac190eSjoerg 	return vfwscanf_l(fp, _current_locale(), fmt, ap);
1209790c07aSjoerg }
1219790c07aSjoerg 
1229790c07aSjoerg int
vfwscanf_l(FILE * __restrict fp,locale_t loc,const wchar_t * __restrict fmt,va_list ap)1239790c07aSjoerg vfwscanf_l(FILE * __restrict fp, locale_t loc, const wchar_t * __restrict fmt,
1249790c07aSjoerg     va_list ap)
1259790c07aSjoerg {
126f432bbb6Schristos 	int ret;
127f432bbb6Schristos 
128f432bbb6Schristos 	FLOCKFILE(fp);
129f432bbb6Schristos 	_SET_ORIENTATION(fp, 1);
1309790c07aSjoerg 	ret = __vfwscanf_unlocked_l(fp, loc, fmt, ap);
131f432bbb6Schristos 	FUNLOCKFILE(fp);
132526d9427Schristos 	return ret;
133f432bbb6Schristos }
134f432bbb6Schristos 
1355022bd79Schristos #define SCANF_SKIP_SPACE() \
1365022bd79Schristos do { \
1375022bd79Schristos 	wint_t tc; \
1385022bd79Schristos  \
1399790c07aSjoerg 	while ((tc = __fgetwc_unlock(fp)) != WEOF && iswspace_l(tc, loc)) \
1405022bd79Schristos 		continue; \
1415022bd79Schristos 	if (tc != WEOF) \
1425022bd79Schristos 		ungetwc(tc, fp); \
143*388550b0Srillig } while (0)
1445022bd79Schristos 
145f432bbb6Schristos /*
146f432bbb6Schristos  * Non-MT-safe version.
147f432bbb6Schristos  */
148f432bbb6Schristos int
__vfwscanf_unlocked_l(FILE * __restrict fp,locale_t loc,const wchar_t * __restrict fmt,va_list ap)1499790c07aSjoerg __vfwscanf_unlocked_l(FILE * __restrict fp, locale_t loc,
1509790c07aSjoerg     const wchar_t * __restrict fmt, va_list ap)
151f432bbb6Schristos {
152f432bbb6Schristos 	wint_t c;		/* character from format, or conversion */
153f432bbb6Schristos 	size_t width;		/* field width, or 0 */
154f432bbb6Schristos 	wchar_t *p;		/* points into all kinds of strings */
155f432bbb6Schristos 	int n;			/* handy integer */
156f432bbb6Schristos 	int flags;		/* flags as defined above */
157f432bbb6Schristos 	wchar_t *p0;		/* saves original value of p when necessary */
158f432bbb6Schristos 	int nassigned;		/* number of fields assigned */
159f432bbb6Schristos 	int nconversions;	/* number of conversions */
160c5e820caSchristos 	size_t nread;		/* number of characters consumed from fp */
161f432bbb6Schristos 	int base;		/* base argument to conversion function */
162f432bbb6Schristos 	wchar_t buf[BUF];	/* buffer for numeric conversions */
163f432bbb6Schristos 	const wchar_t *ccls;	/* character class start */
164f432bbb6Schristos 	const wchar_t *ccle;	/* character class end */
165f432bbb6Schristos 	int cclcompl;		/* ccl is complemented? */
166f432bbb6Schristos 	wint_t wi;		/* handy wint_t */
167f432bbb6Schristos 	char *mbp;		/* multibyte string pointer for %c %s %[ */
168f432bbb6Schristos 	size_t nconv;		/* number of bytes in mb. conversion */
169f432bbb6Schristos 	static const mbstate_t initial;
170f432bbb6Schristos 	mbstate_t mbs;
1719790c07aSjoerg 	char mbbuf[MB_LEN_MAX];	/* temporary mb. character buffer */
172f432bbb6Schristos 	/* `basefix' is used to avoid `if' tests in the integer scanner */
173f432bbb6Schristos 	static short basefix[17] =
174f432bbb6Schristos 		{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
175f432bbb6Schristos 
176f432bbb6Schristos 	nassigned = 0;
177f432bbb6Schristos 	nconversions = 0;
178f432bbb6Schristos 	nread = 0;
179f432bbb6Schristos 	ccls = ccle = NULL;
180ef7b3cd6Slukem 	base = 0;
181ef7b3cd6Slukem 	cclcompl = 0;
182ef7b3cd6Slukem 	mbp = NULL;
18376fd4199Sjustin 	p = NULL; /* XXXgcc */
184f432bbb6Schristos 	for (;;) {
185f432bbb6Schristos 		c = *fmt++;
186f432bbb6Schristos 		if (c == 0)
187526d9427Schristos 			return nassigned;
1889790c07aSjoerg 		if (iswspace_l(c, loc)) {
189f432bbb6Schristos 			while ((c = __fgetwc_unlock(fp)) != WEOF &&
1909790c07aSjoerg 			    iswspace_l(c, loc))
191f432bbb6Schristos 				;
192f432bbb6Schristos 			if (c != WEOF)
193f432bbb6Schristos 				ungetwc(c, fp);
194f432bbb6Schristos 			continue;
195f432bbb6Schristos 		}
196f432bbb6Schristos 		if (c != '%')
197f432bbb6Schristos 			goto literal;
198f432bbb6Schristos 		width = 0;
199f432bbb6Schristos 		flags = 0;
200f432bbb6Schristos 		/*
201f432bbb6Schristos 		 * switch on the format.  continue if done;
202f432bbb6Schristos 		 * break once format type is derived.
203f432bbb6Schristos 		 */
204f432bbb6Schristos again:		c = *fmt++;
205f432bbb6Schristos 		switch (c) {
206f432bbb6Schristos 		case '%':
2075022bd79Schristos 			SCANF_SKIP_SPACE();
208f432bbb6Schristos literal:
209f432bbb6Schristos 			if ((wi = __fgetwc_unlock(fp)) == WEOF)
210f432bbb6Schristos 				goto input_failure;
211f432bbb6Schristos 			if (wi != c) {
212f432bbb6Schristos 				ungetwc(wi, fp);
213f432bbb6Schristos 				goto input_failure;
214f432bbb6Schristos 			}
215f432bbb6Schristos 			nread++;
216f432bbb6Schristos 			continue;
217f432bbb6Schristos 
218f432bbb6Schristos 		case '*':
219f432bbb6Schristos 			flags |= SUPPRESS;
220f432bbb6Schristos 			goto again;
221f432bbb6Schristos 		case 'j':
222f432bbb6Schristos 			flags |= INTMAXT;
223f432bbb6Schristos 			goto again;
224f432bbb6Schristos 		case 'l':
225f432bbb6Schristos 			if (flags & LONG) {
226f432bbb6Schristos 				flags &= ~LONG;
227f432bbb6Schristos 				flags |= LONGLONG;
228f432bbb6Schristos 			} else
229f432bbb6Schristos 				flags |= LONG;
230f432bbb6Schristos 			goto again;
231f432bbb6Schristos 		case 'q':
232f432bbb6Schristos 			flags |= LONGLONG;	/* not quite */
233f432bbb6Schristos 			goto again;
234f432bbb6Schristos 		case 't':
235f432bbb6Schristos 			flags |= PTRDIFFT;
236f432bbb6Schristos 			goto again;
237f432bbb6Schristos 		case 'z':
238f432bbb6Schristos 			flags |= SIZET;
239f432bbb6Schristos 			goto again;
240f432bbb6Schristos 		case 'L':
241f432bbb6Schristos 			flags |= LONGDBL;
242f432bbb6Schristos 			goto again;
243f432bbb6Schristos 		case 'h':
244f432bbb6Schristos 			if (flags & SHORT) {
245f432bbb6Schristos 				flags &= ~SHORT;
246f432bbb6Schristos 				flags |= SHORTSHORT;
247f432bbb6Schristos 			} else
248f432bbb6Schristos 				flags |= SHORT;
249f432bbb6Schristos 			goto again;
250f432bbb6Schristos 
251f432bbb6Schristos 		case '0': case '1': case '2': case '3': case '4':
252f432bbb6Schristos 		case '5': case '6': case '7': case '8': case '9':
253f432bbb6Schristos 			width = width * 10 + c - '0';
254f432bbb6Schristos 			goto again;
255f432bbb6Schristos 
256f432bbb6Schristos 		/*
257f432bbb6Schristos 		 * Conversions.
258f432bbb6Schristos 		 */
259f432bbb6Schristos 		case 'd':
260f432bbb6Schristos 			c = CT_INT;
261f432bbb6Schristos 			base = 10;
262f432bbb6Schristos 			break;
263f432bbb6Schristos 
264f432bbb6Schristos 		case 'i':
265f432bbb6Schristos 			c = CT_INT;
266f432bbb6Schristos 			base = 0;
267f432bbb6Schristos 			break;
268f432bbb6Schristos 
269f432bbb6Schristos 		case 'o':
270f432bbb6Schristos 			c = CT_INT;
271f432bbb6Schristos 			flags |= UNSIGNED;
272f432bbb6Schristos 			base = 8;
273f432bbb6Schristos 			break;
274f432bbb6Schristos 
275f432bbb6Schristos 		case 'u':
276f432bbb6Schristos 			c = CT_INT;
277f432bbb6Schristos 			flags |= UNSIGNED;
278f432bbb6Schristos 			base = 10;
279f432bbb6Schristos 			break;
280f432bbb6Schristos 
281f432bbb6Schristos 		case 'X':
282f432bbb6Schristos 		case 'x':
283f432bbb6Schristos 			flags |= PFXOK;	/* enable 0x prefixing */
284f432bbb6Schristos 			c = CT_INT;
285f432bbb6Schristos 			flags |= UNSIGNED;
286f432bbb6Schristos 			base = 16;
287f432bbb6Schristos 			break;
288f432bbb6Schristos 
289f432bbb6Schristos #ifndef NO_FLOATING_POINT
290f432bbb6Schristos 		case 'A': case 'E': case 'F': case 'G':
291f432bbb6Schristos 		case 'a': case 'e': case 'f': case 'g':
292f432bbb6Schristos 			c = CT_FLOAT;
293f432bbb6Schristos 			break;
294f432bbb6Schristos #endif
295f432bbb6Schristos 
296f432bbb6Schristos 		case 'S':
297f432bbb6Schristos 			flags |= LONG;
298f432bbb6Schristos 			/* FALLTHROUGH */
299f432bbb6Schristos 		case 's':
300f432bbb6Schristos 			c = CT_STRING;
301f432bbb6Schristos 			break;
302f432bbb6Schristos 
303f432bbb6Schristos 		case '[':
304f432bbb6Schristos 			ccls = fmt;
305f432bbb6Schristos 			if (*fmt == '^') {
306f432bbb6Schristos 				cclcompl = 1;
307f432bbb6Schristos 				fmt++;
308f432bbb6Schristos 			} else
309f432bbb6Schristos 				cclcompl = 0;
310f432bbb6Schristos 			if (*fmt == ']')
311f432bbb6Schristos 				fmt++;
312f432bbb6Schristos 			while (*fmt != '\0' && *fmt != ']')
313f432bbb6Schristos 				fmt++;
314f432bbb6Schristos 			ccle = fmt;
315f432bbb6Schristos 			fmt++;
316f432bbb6Schristos 			flags |= NOSKIP;
317f432bbb6Schristos 			c = CT_CCL;
318f432bbb6Schristos 			break;
319f432bbb6Schristos 
320f432bbb6Schristos 		case 'C':
321f432bbb6Schristos 			flags |= LONG;
322f432bbb6Schristos 			/* FALLTHROUGH */
323f432bbb6Schristos 		case 'c':
324f432bbb6Schristos 			flags |= NOSKIP;
325f432bbb6Schristos 			c = CT_CHAR;
326f432bbb6Schristos 			break;
327f432bbb6Schristos 
328f432bbb6Schristos 		case 'p':	/* pointer format is like hex */
329f432bbb6Schristos 			flags |= POINTER | PFXOK;
330f432bbb6Schristos 			c = CT_INT;		/* assumes sizeof(uintmax_t) */
331f432bbb6Schristos 			flags |= UNSIGNED;	/*      >= sizeof(uintptr_t) */
332f432bbb6Schristos 			base = 16;
333f432bbb6Schristos 			break;
334f432bbb6Schristos 
335f432bbb6Schristos 		case 'n':
336f432bbb6Schristos 			nconversions++;
337f432bbb6Schristos 			if (flags & SUPPRESS)	/* ??? */
338f432bbb6Schristos 				continue;
339f432bbb6Schristos 			if (flags & SHORTSHORT)
340c5e820caSchristos 				*va_arg(ap, char *) = (char)nread;
341f432bbb6Schristos 			else if (flags & SHORT)
342c5e820caSchristos 				*va_arg(ap, short *) = (short)nread;
343f432bbb6Schristos 			else if (flags & LONG)
344f432bbb6Schristos 				*va_arg(ap, long *) = nread;
345f432bbb6Schristos 			else if (flags & LONGLONG)
346f432bbb6Schristos 				*va_arg(ap, quad_t *) = nread;
347f432bbb6Schristos 			else if (flags & INTMAXT)
348f432bbb6Schristos 				*va_arg(ap, intmax_t *) = nread;
349f432bbb6Schristos 			else if (flags & SIZET)
350f432bbb6Schristos 				*va_arg(ap, size_t *) = nread;
351f432bbb6Schristos 			else if (flags & PTRDIFFT)
352f432bbb6Schristos 				*va_arg(ap, ptrdiff_t *) = nread;
353f432bbb6Schristos 			else
354c5e820caSchristos 				*va_arg(ap, int *) = (int)nread;
355f432bbb6Schristos 			continue;
356f432bbb6Schristos 
357f432bbb6Schristos 		default:
358f432bbb6Schristos 			goto match_failure;
359f432bbb6Schristos 
360f432bbb6Schristos 		/*
361f432bbb6Schristos 		 * Disgusting backwards compatibility hack.	XXX
362f432bbb6Schristos 		 */
363f432bbb6Schristos 		case '\0':	/* compat */
364526d9427Schristos 			return EOF;
365f432bbb6Schristos 		}
366f432bbb6Schristos 
367f432bbb6Schristos 		/*
368f432bbb6Schristos 		 * Consume leading white space, except for formats
369f432bbb6Schristos 		 * that suppress this.
370f432bbb6Schristos 		 */
371f432bbb6Schristos 		if ((flags & NOSKIP) == 0) {
3729790c07aSjoerg 			while ((wi = __fgetwc_unlock(fp)) != WEOF &&
3739790c07aSjoerg 			       iswspace_l(wi, loc))
374f432bbb6Schristos 				nread++;
375f432bbb6Schristos 			if (wi == WEOF)
376f432bbb6Schristos 				goto input_failure;
377f432bbb6Schristos 			ungetwc(wi, fp);
378f432bbb6Schristos 		}
379f432bbb6Schristos 
380f432bbb6Schristos 		/*
381f432bbb6Schristos 		 * Do the conversion.
382f432bbb6Schristos 		 */
383f432bbb6Schristos 		switch (c) {
384f432bbb6Schristos 
385f432bbb6Schristos 		case CT_CHAR:
386f432bbb6Schristos 			/* scan arbitrary characters (sets NOSKIP) */
387f432bbb6Schristos 			if (width == 0)
388f432bbb6Schristos 				width = 1;
389f432bbb6Schristos 			if (flags & LONG) {
390f432bbb6Schristos 				if (!(flags & SUPPRESS))
391f432bbb6Schristos 					p = va_arg(ap, wchar_t *);
392f432bbb6Schristos 				n = 0;
393f432bbb6Schristos 				while (width-- != 0 &&
394f432bbb6Schristos 				    (wi = __fgetwc_unlock(fp)) != WEOF) {
395f432bbb6Schristos 					if (!(flags & SUPPRESS))
396f432bbb6Schristos 						*p++ = (wchar_t)wi;
397f432bbb6Schristos 					n++;
398f432bbb6Schristos 				}
399f432bbb6Schristos 				if (n == 0)
400f432bbb6Schristos 					goto input_failure;
401f432bbb6Schristos 				nread += n;
402f432bbb6Schristos 				if (!(flags & SUPPRESS))
403f432bbb6Schristos 					nassigned++;
404f432bbb6Schristos 			} else {
405f432bbb6Schristos 				if (!(flags & SUPPRESS))
406f432bbb6Schristos 					mbp = va_arg(ap, char *);
407f432bbb6Schristos 				n = 0;
408f432bbb6Schristos 				mbs = initial;
409f432bbb6Schristos 				while (width != 0 &&
410f432bbb6Schristos 				    (wi = __fgetwc_unlock(fp)) != WEOF) {
4119790c07aSjoerg 					if (width >= MB_CUR_MAX_L(loc) &&
412f432bbb6Schristos 					    !(flags & SUPPRESS)) {
4139790c07aSjoerg 						nconv = wcrtomb_l(mbp, wi,
4149790c07aSjoerg 						    &mbs, loc);
415f432bbb6Schristos 						if (nconv == (size_t)-1)
416f432bbb6Schristos 							goto input_failure;
417f432bbb6Schristos 					} else {
4189790c07aSjoerg 						nconv = wcrtomb_l(mbbuf, wi,
4199790c07aSjoerg 						    &mbs, loc);
420f432bbb6Schristos 						if (nconv == (size_t)-1)
421f432bbb6Schristos 							goto input_failure;
422f432bbb6Schristos 						if (nconv > width) {
423f432bbb6Schristos 							ungetwc(wi, fp);
424f432bbb6Schristos 							break;
425f432bbb6Schristos 						}
426f432bbb6Schristos 						if (!(flags & SUPPRESS))
427f432bbb6Schristos 							memcpy(mbp, mbbuf,
428f432bbb6Schristos 							    nconv);
429f432bbb6Schristos 					}
430f432bbb6Schristos 					if (!(flags & SUPPRESS))
431f432bbb6Schristos 						mbp += nconv;
432f432bbb6Schristos 					width -= nconv;
433f432bbb6Schristos 					n++;
434f432bbb6Schristos 				}
435f432bbb6Schristos 				if (n == 0)
436f432bbb6Schristos 					goto input_failure;
437f432bbb6Schristos 				nread += n;
438f432bbb6Schristos 				if (!(flags & SUPPRESS))
439f432bbb6Schristos 					nassigned++;
440f432bbb6Schristos 			}
441f432bbb6Schristos 			nconversions++;
442f432bbb6Schristos 			break;
443f432bbb6Schristos 
444f432bbb6Schristos 		case CT_CCL:
445f432bbb6Schristos 			/* scan a (nonempty) character class (sets NOSKIP) */
446f432bbb6Schristos 			if (width == 0)
447f432bbb6Schristos 				width = (size_t)~0;	/* `infinity' */
448f432bbb6Schristos 			/* take only those things in the class */
449f432bbb6Schristos 			if ((flags & SUPPRESS) && (flags & LONG)) {
450f432bbb6Schristos 				n = 0;
451f432bbb6Schristos 				while ((wi = __fgetwc_unlock(fp)) != WEOF &&
452f432bbb6Schristos 				    width-- != 0 && INCCL(wi))
453f432bbb6Schristos 					n++;
454f432bbb6Schristos 				if (wi != WEOF)
455f432bbb6Schristos 					ungetwc(wi, fp);
456f432bbb6Schristos 				if (n == 0)
457f432bbb6Schristos 					goto match_failure;
458f432bbb6Schristos 			} else if (flags & LONG) {
459f432bbb6Schristos 				p0 = p = va_arg(ap, wchar_t *);
460f432bbb6Schristos 				while ((wi = __fgetwc_unlock(fp)) != WEOF &&
461f432bbb6Schristos 				    width-- != 0 && INCCL(wi))
462f432bbb6Schristos 					*p++ = (wchar_t)wi;
463f432bbb6Schristos 				if (wi != WEOF)
464f432bbb6Schristos 					ungetwc(wi, fp);
465c5e820caSchristos 				_DIAGASSERT(__type_fit(int, p - p0));
466c5e820caSchristos 				n = (int)(p - p0);
467f432bbb6Schristos 				if (n == 0)
468f432bbb6Schristos 					goto match_failure;
469f432bbb6Schristos 				*p = 0;
470f432bbb6Schristos 				nassigned++;
471f432bbb6Schristos 			} else {
472f432bbb6Schristos 				if (!(flags & SUPPRESS))
473f432bbb6Schristos 					mbp = va_arg(ap, char *);
474f432bbb6Schristos 				n = 0;
475f432bbb6Schristos 				mbs = initial;
476f432bbb6Schristos 				while ((wi = __fgetwc_unlock(fp)) != WEOF &&
477f432bbb6Schristos 				    width != 0 && INCCL(wi)) {
4789790c07aSjoerg 					if (width >= MB_CUR_MAX_L(loc) &&
479f432bbb6Schristos 					   !(flags & SUPPRESS)) {
4809790c07aSjoerg 						nconv = wcrtomb_l(mbp, wi,
4819790c07aSjoerg 						    &mbs, loc);
482f432bbb6Schristos 						if (nconv == (size_t)-1)
483f432bbb6Schristos 							goto input_failure;
484f432bbb6Schristos 					} else {
4859790c07aSjoerg 						nconv = wcrtomb_l(mbbuf, wi,
4869790c07aSjoerg 						    &mbs, loc);
487f432bbb6Schristos 						if (nconv == (size_t)-1)
488f432bbb6Schristos 							goto input_failure;
489f432bbb6Schristos 						if (nconv > width)
490f432bbb6Schristos 							break;
491f432bbb6Schristos 						if (!(flags & SUPPRESS))
492f432bbb6Schristos 							memcpy(mbp, mbbuf,
493f432bbb6Schristos 							    nconv);
494f432bbb6Schristos 					}
495f432bbb6Schristos 					if (!(flags & SUPPRESS))
496f432bbb6Schristos 						mbp += nconv;
497f432bbb6Schristos 					width -= nconv;
498f432bbb6Schristos 					n++;
499f432bbb6Schristos 				}
500f432bbb6Schristos 				if (wi != WEOF)
501f432bbb6Schristos 					ungetwc(wi, fp);
502f432bbb6Schristos 				if (!(flags & SUPPRESS)) {
503f432bbb6Schristos 					*mbp = 0;
504f432bbb6Schristos 					nassigned++;
505f432bbb6Schristos 				}
506f432bbb6Schristos 			}
507f432bbb6Schristos 			nread += n;
508f432bbb6Schristos 			nconversions++;
509f432bbb6Schristos 			break;
510f432bbb6Schristos 
511f432bbb6Schristos 		case CT_STRING:
512f432bbb6Schristos 			/* like CCL, but zero-length string OK, & no NOSKIP */
513f432bbb6Schristos 			if (width == 0)
514f432bbb6Schristos 				width = (size_t)~0;
515f432bbb6Schristos 			if ((flags & SUPPRESS) && (flags & LONG)) {
516f432bbb6Schristos 				while ((wi = __fgetwc_unlock(fp)) != WEOF &&
517f432bbb6Schristos 				    width-- != 0 &&
5189790c07aSjoerg 				    !iswspace_l(wi, loc))
519f432bbb6Schristos 					nread++;
520f432bbb6Schristos 				if (wi != WEOF)
521f432bbb6Schristos 					ungetwc(wi, fp);
522f432bbb6Schristos 			} else if (flags & LONG) {
523f432bbb6Schristos 				p0 = p = va_arg(ap, wchar_t *);
524f432bbb6Schristos 				while ((wi = __fgetwc_unlock(fp)) != WEOF &&
525f432bbb6Schristos 				    width-- != 0 &&
5269790c07aSjoerg 				    !iswspace_l(wi, loc)) {
527f432bbb6Schristos 					*p++ = (wchar_t)wi;
528f432bbb6Schristos 					nread++;
529f432bbb6Schristos 				}
530f432bbb6Schristos 				if (wi != WEOF)
531f432bbb6Schristos 					ungetwc(wi, fp);
532f432bbb6Schristos 				*p = '\0';
533f432bbb6Schristos 				nassigned++;
534f432bbb6Schristos 			} else {
535f432bbb6Schristos 				if (!(flags & SUPPRESS))
536f432bbb6Schristos 					mbp = va_arg(ap, char *);
537f432bbb6Schristos 				mbs = initial;
538f432bbb6Schristos 				while ((wi = __fgetwc_unlock(fp)) != WEOF &&
539f432bbb6Schristos 				    width != 0 &&
5409790c07aSjoerg 				    !iswspace_l(wi, loc)) {
5419790c07aSjoerg 					if (width >= MB_CUR_MAX_L(loc) &&
542f432bbb6Schristos 					    !(flags & SUPPRESS)) {
5439790c07aSjoerg 						nconv = wcrtomb_l(mbp, wi,
5449790c07aSjoerg 						    &mbs, loc);
545f432bbb6Schristos 						if (nconv == (size_t)-1)
546f432bbb6Schristos 							goto input_failure;
547f432bbb6Schristos 					} else {
5489790c07aSjoerg 						nconv = wcrtomb_l(mbbuf, wi,
5499790c07aSjoerg 						    &mbs, loc);
550f432bbb6Schristos 						if (nconv == (size_t)-1)
551f432bbb6Schristos 							goto input_failure;
552f432bbb6Schristos 						if (nconv > width)
553f432bbb6Schristos 							break;
554f432bbb6Schristos 						if (!(flags & SUPPRESS))
555f432bbb6Schristos 							memcpy(mbp, mbbuf,
556f432bbb6Schristos 							    nconv);
557f432bbb6Schristos 					}
558f432bbb6Schristos 					if (!(flags & SUPPRESS))
559f432bbb6Schristos 						mbp += nconv;
560f432bbb6Schristos 					width -= nconv;
561f432bbb6Schristos 					nread++;
562f432bbb6Schristos 				}
563f432bbb6Schristos 				if (wi != WEOF)
564f432bbb6Schristos 					ungetwc(wi, fp);
565f432bbb6Schristos 				if (!(flags & SUPPRESS)) {
566f432bbb6Schristos 					*mbp = 0;
567f432bbb6Schristos 					nassigned++;
568f432bbb6Schristos 				}
569f432bbb6Schristos 			}
570f432bbb6Schristos 			nconversions++;
571f432bbb6Schristos 			continue;
572f432bbb6Schristos 
573f432bbb6Schristos 		case CT_INT:
574f432bbb6Schristos 			/* scan an integer as if by the conversion function */
575f432bbb6Schristos 			if (width == 0 || width > sizeof(buf) /
576f432bbb6Schristos 			    sizeof(*buf) - 1)
577f432bbb6Schristos 				width = sizeof(buf) / sizeof(*buf) - 1;
578f432bbb6Schristos 			flags |= SIGNOK | NDIGITS | NZDIGITS;
579f432bbb6Schristos 			for (p = buf; width; width--) {
580f432bbb6Schristos 				c = __fgetwc_unlock(fp);
581f432bbb6Schristos 				/*
582f432bbb6Schristos 				 * Switch on the character; `goto ok'
583f432bbb6Schristos 				 * if we accept it as a part of number.
584f432bbb6Schristos 				 */
585f432bbb6Schristos 				switch (c) {
586f432bbb6Schristos 
587f432bbb6Schristos 				/*
588f432bbb6Schristos 				 * The digit 0 is always legal, but is
589f432bbb6Schristos 				 * special.  For %i conversions, if no
590f432bbb6Schristos 				 * digits (zero or nonzero) have been
591f432bbb6Schristos 				 * scanned (only signs), we will have
592f432bbb6Schristos 				 * base==0.  In that case, we should set
593f432bbb6Schristos 				 * it to 8 and enable 0x prefixing.
594f432bbb6Schristos 				 * Also, if we have not scanned zero digits
595f432bbb6Schristos 				 * before this, do not turn off prefixing
596f432bbb6Schristos 				 * (someone else will turn it off if we
597f432bbb6Schristos 				 * have scanned any nonzero digits).
598f432bbb6Schristos 				 */
599f432bbb6Schristos 				case '0':
600f432bbb6Schristos 					if (base == 0) {
601f432bbb6Schristos 						base = 8;
602f432bbb6Schristos 						flags |= PFXOK;
603f432bbb6Schristos 					}
604f432bbb6Schristos 					if (flags & NZDIGITS)
605f432bbb6Schristos 					    flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
606f432bbb6Schristos 					else
607f432bbb6Schristos 					    flags &= ~(SIGNOK|PFXOK|NDIGITS);
608f432bbb6Schristos 					goto ok;
609f432bbb6Schristos 
610f432bbb6Schristos 				/* 1 through 7 always legal */
611f432bbb6Schristos 				case '1': case '2': case '3':
612f432bbb6Schristos 				case '4': case '5': case '6': case '7':
613f432bbb6Schristos 					base = basefix[base];
614f432bbb6Schristos 					flags &= ~(SIGNOK | PFXOK | NDIGITS);
615f432bbb6Schristos 					goto ok;
616f432bbb6Schristos 
617f432bbb6Schristos 				/* digits 8 and 9 ok iff decimal or hex */
618f432bbb6Schristos 				case '8': case '9':
619f432bbb6Schristos 					base = basefix[base];
620f432bbb6Schristos 					if (base <= 8)
621f432bbb6Schristos 						break;	/* not legal here */
622f432bbb6Schristos 					flags &= ~(SIGNOK | PFXOK | NDIGITS);
623f432bbb6Schristos 					goto ok;
624f432bbb6Schristos 
625f432bbb6Schristos 				/* letters ok iff hex */
626f432bbb6Schristos 				case 'A': case 'B': case 'C':
627f432bbb6Schristos 				case 'D': case 'E': case 'F':
628f432bbb6Schristos 				case 'a': case 'b': case 'c':
629f432bbb6Schristos 				case 'd': case 'e': case 'f':
630f432bbb6Schristos 					/* no need to fix base here */
631f432bbb6Schristos 					if (base <= 10)
632f432bbb6Schristos 						break;	/* not legal here */
633f432bbb6Schristos 					flags &= ~(SIGNOK | PFXOK | NDIGITS);
634f432bbb6Schristos 					goto ok;
635f432bbb6Schristos 
636f432bbb6Schristos 				/* sign ok only as first character */
637f432bbb6Schristos 				case '+': case '-':
638f432bbb6Schristos 					if (flags & SIGNOK) {
639f432bbb6Schristos 						flags &= ~SIGNOK;
640f432bbb6Schristos 						flags |= HAVESIGN;
641f432bbb6Schristos 						goto ok;
642f432bbb6Schristos 					}
643f432bbb6Schristos 					break;
644f432bbb6Schristos 
645f432bbb6Schristos 				/*
646f432bbb6Schristos 				 * x ok iff flag still set & 2nd char (or
647f432bbb6Schristos 				 * 3rd char if we have a sign).
648f432bbb6Schristos 				 */
649f432bbb6Schristos 				case 'x': case 'X':
650f432bbb6Schristos 					if (flags & PFXOK && p ==
651f432bbb6Schristos 					    buf + 1 + !!(flags & HAVESIGN)) {
652f432bbb6Schristos 						base = 16;	/* if %i */
653f432bbb6Schristos 						flags &= ~PFXOK;
654f432bbb6Schristos 						goto ok;
655f432bbb6Schristos 					}
656f432bbb6Schristos 					break;
657f432bbb6Schristos 				}
658f432bbb6Schristos 
659f432bbb6Schristos 				/*
660f432bbb6Schristos 				 * If we got here, c is not a legal character
661f432bbb6Schristos 				 * for a number.  Stop accumulating digits.
662f432bbb6Schristos 				 */
663f432bbb6Schristos 				if (c != WEOF)
664f432bbb6Schristos 					ungetwc(c, fp);
665f432bbb6Schristos 				break;
666f432bbb6Schristos 		ok:
667f432bbb6Schristos 				/*
668f432bbb6Schristos 				 * c is legal: store it and look at the next.
669f432bbb6Schristos 				 */
670f432bbb6Schristos 				*p++ = (wchar_t)c;
671f432bbb6Schristos 			}
672f432bbb6Schristos 			/*
673f432bbb6Schristos 			 * If we had only a sign, it is no good; push
674f432bbb6Schristos 			 * back the sign.  If the number ends in `x',
675f432bbb6Schristos 			 * it was [sign] '0' 'x', so push back the x
676f432bbb6Schristos 			 * and treat it as [sign] '0'.
677f432bbb6Schristos 			 */
678f432bbb6Schristos 			if (flags & NDIGITS) {
679f432bbb6Schristos 				if (p > buf)
680f432bbb6Schristos 					ungetwc(*--p, fp);
681f432bbb6Schristos 				goto match_failure;
682f432bbb6Schristos 			}
683f432bbb6Schristos 			c = p[-1];
684f432bbb6Schristos 			if (c == 'x' || c == 'X') {
685f432bbb6Schristos 				--p;
686f432bbb6Schristos 				ungetwc(c, fp);
687f432bbb6Schristos 			}
688f432bbb6Schristos 			if ((flags & SUPPRESS) == 0) {
689f432bbb6Schristos 				uintmax_t res;
690f432bbb6Schristos 
691f432bbb6Schristos 				*p = 0;
692f432bbb6Schristos 				if ((flags & UNSIGNED) == 0)
6939790c07aSjoerg 				    res = wcstoimax_l(buf, NULL, base, loc);
694f432bbb6Schristos 				else
6959790c07aSjoerg 				    res = wcstoumax_l(buf, NULL, base, loc);
696f432bbb6Schristos 				if (flags & POINTER)
697f432bbb6Schristos 					*va_arg(ap, void **) =
698f432bbb6Schristos 							(void *)(uintptr_t)res;
699f432bbb6Schristos 				else if (flags & SHORTSHORT)
700f432bbb6Schristos 					*va_arg(ap, char *) = (char)res;
701f432bbb6Schristos 				else if (flags & SHORT)
702f432bbb6Schristos 					*va_arg(ap, short *) = (short)res;
703f432bbb6Schristos 				else if (flags & LONG)
704f432bbb6Schristos 					*va_arg(ap, long *) = (long)res;
705f432bbb6Schristos 				else if (flags & LONGLONG)
706f432bbb6Schristos 					*va_arg(ap, quad_t *) = res;
707f432bbb6Schristos 				else if (flags & INTMAXT)
708f432bbb6Schristos 					*va_arg(ap, intmax_t *) = res;
709f432bbb6Schristos 				else if (flags & PTRDIFFT)
710f432bbb6Schristos 					*va_arg(ap, ptrdiff_t *) = (ptrdiff_t)res;
711f432bbb6Schristos 				else if (flags & SIZET)
712f432bbb6Schristos 					*va_arg(ap, size_t *) = (size_t)res;
713f432bbb6Schristos 				else
714f432bbb6Schristos 					*va_arg(ap, int *) = (int)res;
715f432bbb6Schristos 				nassigned++;
716f432bbb6Schristos 			}
717c5e820caSchristos 			_DIAGASSERT(__type_fit(int, p - buf));
718c5e820caSchristos 			nread += (int)(p - buf);
719f432bbb6Schristos 			nconversions++;
720f432bbb6Schristos 			break;
721f432bbb6Schristos 
722f432bbb6Schristos #ifndef NO_FLOATING_POINT
723f432bbb6Schristos 		case CT_FLOAT:
724f432bbb6Schristos 			/* scan a floating point number as if by strtod */
725f432bbb6Schristos 			if (width == 0 || width > sizeof(buf) /
726f432bbb6Schristos 			    sizeof(*buf) - 1)
727f432bbb6Schristos 				width = sizeof(buf) / sizeof(*buf) - 1;
7289790c07aSjoerg 			if ((width = parsefloat(fp, buf, buf + width, loc)) == 0)
729f432bbb6Schristos 				goto match_failure;
730f432bbb6Schristos 			if ((flags & SUPPRESS) == 0) {
731f432bbb6Schristos 				if (flags & LONGDBL) {
7329790c07aSjoerg 					long double res = wcstold_l(buf, &p,
7339790c07aSjoerg 					    loc);
734f432bbb6Schristos 					*va_arg(ap, long double *) = res;
735f432bbb6Schristos 				} else
736f432bbb6Schristos 				if (flags & LONG) {
7379790c07aSjoerg 					double res = wcstod_l(buf, &p, loc);
738f432bbb6Schristos 					*va_arg(ap, double *) = res;
739f432bbb6Schristos 				} else {
7409790c07aSjoerg 					float res = wcstof_l(buf, &p, loc);
741f432bbb6Schristos 					*va_arg(ap, float *) = res;
742f432bbb6Schristos 				}
743f432bbb6Schristos #ifdef DEBUG
744eb7fe437Schristos 				if (p - buf != (ptrdiff_t)width)
745f432bbb6Schristos 					abort();
746f432bbb6Schristos #endif
747f432bbb6Schristos 				nassigned++;
748f432bbb6Schristos 			}
749f432bbb6Schristos 			nread += width;
750f432bbb6Schristos 			nconversions++;
751f432bbb6Schristos 			break;
752f432bbb6Schristos #endif /* !NO_FLOATING_POINT */
753f432bbb6Schristos 		}
754f432bbb6Schristos 	}
755f432bbb6Schristos input_failure:
756526d9427Schristos 	return nconversions != 0 ? nassigned : EOF;
757f432bbb6Schristos match_failure:
758526d9427Schristos 	return nassigned;
759f432bbb6Schristos }
760f432bbb6Schristos 
761f432bbb6Schristos #ifndef NO_FLOATING_POINT
762f432bbb6Schristos static int
parsefloat(FILE * fp,wchar_t * buf,wchar_t * end,locale_t loc)7639790c07aSjoerg parsefloat(FILE *fp, wchar_t *buf, wchar_t *end, locale_t loc)
764f432bbb6Schristos {
765f432bbb6Schristos 	wchar_t *commit, *p;
766f432bbb6Schristos 	int infnanpos = 0;
767f432bbb6Schristos 	enum {
768f432bbb6Schristos 		S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX,
769f432bbb6Schristos 		S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
770f432bbb6Schristos 	} state = S_START;
771f432bbb6Schristos 	wchar_t c;
7729790c07aSjoerg 	wchar_t decpt = (wchar_t)(unsigned char)*localeconv_l(loc)->decimal_point;
773f432bbb6Schristos 	int gotmantdig = 0, ishex = 0;
774f432bbb6Schristos 
775f432bbb6Schristos 	/*
776f432bbb6Schristos 	 * We set commit = p whenever the string we have read so far
777f432bbb6Schristos 	 * constitutes a valid representation of a floating point
778f432bbb6Schristos 	 * number by itself.  At some point, the parse will complete
779f432bbb6Schristos 	 * or fail, and we will ungetc() back to the last commit point.
780f432bbb6Schristos 	 * To ensure that the file offset gets updated properly, it is
781f432bbb6Schristos 	 * always necessary to read at least one character that doesn't
782f432bbb6Schristos 	 * match; thus, we can't short-circuit "infinity" or "nan(...)".
783f432bbb6Schristos 	 */
784f432bbb6Schristos 	commit = buf - 1;
785f432bbb6Schristos 	c = WEOF;
786f432bbb6Schristos 	for (p = buf; p < end; ) {
787f432bbb6Schristos 		if ((c = __fgetwc_unlock(fp)) == WEOF)
788f432bbb6Schristos 			break;
789f432bbb6Schristos reswitch:
790f432bbb6Schristos 		switch (state) {
791f432bbb6Schristos 		case S_START:
792f432bbb6Schristos 			state = S_GOTSIGN;
793f432bbb6Schristos 			if (c == '-' || c == '+')
794f432bbb6Schristos 				break;
795f432bbb6Schristos 			else
796f432bbb6Schristos 				goto reswitch;
797f432bbb6Schristos 		case S_GOTSIGN:
798f432bbb6Schristos 			switch (c) {
799f432bbb6Schristos 			case '0':
800f432bbb6Schristos 				state = S_MAYBEHEX;
801f432bbb6Schristos 				commit = p;
802f432bbb6Schristos 				break;
803f432bbb6Schristos 			case 'I':
804f432bbb6Schristos 			case 'i':
805f432bbb6Schristos 				state = S_INF;
806f432bbb6Schristos 				break;
807f432bbb6Schristos 			case 'N':
808f432bbb6Schristos 			case 'n':
809f432bbb6Schristos 				state = S_NAN;
810f432bbb6Schristos 				break;
811f432bbb6Schristos 			default:
812f432bbb6Schristos 				state = S_DIGITS;
813f432bbb6Schristos 				goto reswitch;
814f432bbb6Schristos 			}
815f432bbb6Schristos 			break;
816f432bbb6Schristos 		case S_INF:
817f432bbb6Schristos 			if (infnanpos > 6 ||
818f432bbb6Schristos 			    (c != "nfinity"[infnanpos] &&
819f432bbb6Schristos 			     c != "NFINITY"[infnanpos]))
820f432bbb6Schristos 				goto parsedone;
821f432bbb6Schristos 			if (infnanpos == 1 || infnanpos == 6)
822f432bbb6Schristos 				commit = p;	/* inf or infinity */
823f432bbb6Schristos 			infnanpos++;
824f432bbb6Schristos 			break;
825f432bbb6Schristos 		case S_NAN:
826f432bbb6Schristos 			switch (infnanpos) {
827f432bbb6Schristos 			case -1:	/* XXX kludge to deal with nan(...) */
828f432bbb6Schristos 				goto parsedone;
829f432bbb6Schristos 			case 0:
830f432bbb6Schristos 				if (c != 'A' && c != 'a')
831f432bbb6Schristos 					goto parsedone;
832f432bbb6Schristos 				break;
833f432bbb6Schristos 			case 1:
834f432bbb6Schristos 				if (c != 'N' && c != 'n')
835f432bbb6Schristos 					goto parsedone;
836f432bbb6Schristos 				else
837f432bbb6Schristos 					commit = p;
838f432bbb6Schristos 				break;
839f432bbb6Schristos 			case 2:
840f432bbb6Schristos 				if (c != '(')
841f432bbb6Schristos 					goto parsedone;
842f432bbb6Schristos 				break;
843f432bbb6Schristos 			default:
844f432bbb6Schristos 				if (c == ')') {
845f432bbb6Schristos 					commit = p;
846f432bbb6Schristos 					infnanpos = -2;
8479790c07aSjoerg 				} else if (!iswalnum_l(c, loc) && c != '_')
848f432bbb6Schristos 					goto parsedone;
849f432bbb6Schristos 				break;
850f432bbb6Schristos 			}
851f432bbb6Schristos 			infnanpos++;
852f432bbb6Schristos 			break;
853f432bbb6Schristos 		case S_MAYBEHEX:
854f432bbb6Schristos 			state = S_DIGITS;
855f432bbb6Schristos 			if (c == 'X' || c == 'x') {
856f432bbb6Schristos 				ishex = 1;
857f432bbb6Schristos 				break;
858f432bbb6Schristos 			} else {	/* we saw a '0', but no 'x' */
859f432bbb6Schristos 				gotmantdig = 1;
860f432bbb6Schristos 				goto reswitch;
861f432bbb6Schristos 			}
862f432bbb6Schristos 		case S_DIGITS:
8639790c07aSjoerg 			if ((ishex && iswxdigit_l(c, loc)) ||
8649790c07aSjoerg 			    iswdigit_l(c, loc))
865f432bbb6Schristos 				gotmantdig = 1;
866f432bbb6Schristos 			else {
867f432bbb6Schristos 				state = S_FRAC;
868f432bbb6Schristos 				if (c != decpt)
869f432bbb6Schristos 					goto reswitch;
870f432bbb6Schristos 			}
871f432bbb6Schristos 			if (gotmantdig)
872f432bbb6Schristos 				commit = p;
873f432bbb6Schristos 			break;
874f432bbb6Schristos 		case S_FRAC:
875f432bbb6Schristos 			if (((c == 'E' || c == 'e') && !ishex) ||
876f432bbb6Schristos 			    ((c == 'P' || c == 'p') && ishex)) {
877f432bbb6Schristos 				if (!gotmantdig)
878f432bbb6Schristos 					goto parsedone;
879f432bbb6Schristos 				else
880f432bbb6Schristos 					state = S_EXP;
8819790c07aSjoerg 			} else if ((ishex && iswxdigit_l(c, loc)) ||
8829790c07aSjoerg 			    iswdigit_l(c, loc)) {
883f432bbb6Schristos 				commit = p;
884f432bbb6Schristos 				gotmantdig = 1;
885f432bbb6Schristos 			} else
886f432bbb6Schristos 				goto parsedone;
887f432bbb6Schristos 			break;
888f432bbb6Schristos 		case S_EXP:
889f432bbb6Schristos 			state = S_EXPDIGITS;
890f432bbb6Schristos 			if (c == '-' || c == '+')
891f432bbb6Schristos 				break;
892f432bbb6Schristos 			else
893f432bbb6Schristos 				goto reswitch;
894f432bbb6Schristos 		case S_EXPDIGITS:
8959790c07aSjoerg 			if (iswdigit_l(c, loc))
896f432bbb6Schristos 				commit = p;
897f432bbb6Schristos 			else
898f432bbb6Schristos 				goto parsedone;
899f432bbb6Schristos 			break;
900f432bbb6Schristos 		default:
901f432bbb6Schristos 			abort();
902f432bbb6Schristos 		}
903f432bbb6Schristos 		*p++ = c;
904f432bbb6Schristos 		c = WEOF;
905f432bbb6Schristos 	}
906f432bbb6Schristos 
907f432bbb6Schristos parsedone:
908f432bbb6Schristos 	if (c != WEOF)
909f432bbb6Schristos 		ungetwc(c, fp);
910f432bbb6Schristos 	while (commit < --p)
911f432bbb6Schristos 		ungetwc(*p, fp);
912f432bbb6Schristos 	*++commit = '\0';
913c5e820caSchristos 	_DIAGASSERT(__type_fit(int, commit - buf));
914c5e820caSchristos 	return (int)(commit - buf);
915f432bbb6Schristos }
916f432bbb6Schristos #endif
917