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