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