vfwscanf.c revision 187422
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 187422 2009-01-19 06:19:51Z 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
106static const mbstate_t initial_mbs;
107
108/*
109 * MT-safe version.
110 */
111int
112vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
113{
114	int ret;
115
116	FLOCKFILE(fp);
117	ORIENT(fp, 1);
118	ret = __vfwscanf(fp, fmt, ap);
119	FUNLOCKFILE(fp);
120	return (ret);
121}
122
123/*
124 * Non-MT-safe version.
125 */
126int
127__vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
128{
129	wint_t c;		/* character from format, or conversion */
130	size_t width;		/* field width, or 0 */
131	wchar_t *p;		/* points into all kinds of strings */
132	int n;			/* handy integer */
133	int flags;		/* flags as defined above */
134	wchar_t *p0;		/* saves original value of p when necessary */
135	int nassigned;		/* number of fields assigned */
136	int nconversions;	/* number of conversions */
137	int nread;		/* number of characters consumed from fp */
138	int base;		/* base argument to conversion function */
139	wchar_t buf[BUF];	/* buffer for numeric conversions */
140	const wchar_t *ccls;	/* character class start */
141	const wchar_t *ccle;	/* character class end */
142	int cclcompl;		/* ccl is complemented? */
143	wint_t wi;		/* handy wint_t */
144	char *mbp;		/* multibyte string pointer for %c %s %[ */
145	size_t nconv;		/* number of bytes in mb. conversion */
146	char mbbuf[MB_LEN_MAX];	/* temporary mb. character buffer */
147	mbstate_t mbs;
148
149	/* `basefix' is used to avoid `if' tests in the integer scanner */
150	static short basefix[17] =
151		{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
152
153	nassigned = 0;
154	nconversions = 0;
155	nread = 0;
156	ccls = ccle = NULL;
157	for (;;) {
158		c = *fmt++;
159		if (c == 0)
160			return (nassigned);
161		if (iswspace(c)) {
162			while ((c = __fgetwc(fp)) != WEOF &&
163			    iswspace(c))
164				;
165			if (c != WEOF)
166				__ungetwc(c, fp);
167			continue;
168		}
169		if (c != '%')
170			goto literal;
171		width = 0;
172		flags = 0;
173		/*
174		 * switch on the format.  continue if done;
175		 * break once format type is derived.
176		 */
177again:		c = *fmt++;
178		switch (c) {
179		case '%':
180literal:
181			if ((wi = __fgetwc(fp)) == WEOF)
182				goto input_failure;
183			if (wi != c) {
184				__ungetwc(wi, fp);
185				goto input_failure;
186			}
187			nread++;
188			continue;
189
190		case '*':
191			flags |= SUPPRESS;
192			goto again;
193		case 'j':
194			flags |= INTMAXT;
195			goto again;
196		case 'l':
197			if (flags & LONG) {
198				flags &= ~LONG;
199				flags |= LONGLONG;
200			} else
201				flags |= LONG;
202			goto again;
203		case 'q':
204			flags |= LONGLONG;	/* not quite */
205			goto again;
206		case 't':
207			flags |= PTRDIFFT;
208			goto again;
209		case 'z':
210			flags |= SIZET;
211			goto again;
212		case 'L':
213			flags |= LONGDBL;
214			goto again;
215		case 'h':
216			if (flags & SHORT) {
217				flags &= ~SHORT;
218				flags |= SHORTSHORT;
219			} else
220				flags |= SHORT;
221			goto again;
222
223		case '0': case '1': case '2': case '3': case '4':
224		case '5': case '6': case '7': case '8': case '9':
225			width = width * 10 + c - '0';
226			goto again;
227
228		/*
229		 * Conversions.
230		 */
231		case 'd':
232			c = CT_INT;
233			base = 10;
234			break;
235
236		case 'i':
237			c = CT_INT;
238			base = 0;
239			break;
240
241		case 'o':
242			c = CT_INT;
243			flags |= UNSIGNED;
244			base = 8;
245			break;
246
247		case 'u':
248			c = CT_INT;
249			flags |= UNSIGNED;
250			base = 10;
251			break;
252
253		case 'X':
254		case 'x':
255			flags |= PFXOK;	/* enable 0x prefixing */
256			c = CT_INT;
257			flags |= UNSIGNED;
258			base = 16;
259			break;
260
261#ifndef NO_FLOATING_POINT
262		case 'A': case 'E': case 'F': case 'G':
263		case 'a': case 'e': case 'f': case 'g':
264			c = CT_FLOAT;
265			break;
266#endif
267
268		case 'S':
269			flags |= LONG;
270			/* FALLTHROUGH */
271		case 's':
272			c = CT_STRING;
273			break;
274
275		case '[':
276			ccls = fmt;
277			if (*fmt == '^') {
278				cclcompl = 1;
279				fmt++;
280			} else
281				cclcompl = 0;
282			if (*fmt == ']')
283				fmt++;
284			while (*fmt != '\0' && *fmt != ']')
285				fmt++;
286			ccle = fmt;
287			fmt++;
288			flags |= NOSKIP;
289			c = CT_CCL;
290			break;
291
292		case 'C':
293			flags |= LONG;
294			/* FALLTHROUGH */
295		case 'c':
296			flags |= NOSKIP;
297			c = CT_CHAR;
298			break;
299
300		case 'p':	/* pointer format is like hex */
301			flags |= POINTER | PFXOK;
302			c = CT_INT;		/* assumes sizeof(uintmax_t) */
303			flags |= UNSIGNED;	/*      >= sizeof(uintptr_t) */
304			base = 16;
305			break;
306
307		case 'n':
308			nconversions++;
309			if (flags & SUPPRESS)	/* ??? */
310				continue;
311			if (flags & SHORTSHORT)
312				*va_arg(ap, char *) = nread;
313			else if (flags & SHORT)
314				*va_arg(ap, short *) = nread;
315			else if (flags & LONG)
316				*va_arg(ap, long *) = nread;
317			else if (flags & LONGLONG)
318				*va_arg(ap, long long *) = nread;
319			else if (flags & INTMAXT)
320				*va_arg(ap, intmax_t *) = nread;
321			else if (flags & SIZET)
322				*va_arg(ap, size_t *) = nread;
323			else if (flags & PTRDIFFT)
324				*va_arg(ap, ptrdiff_t *) = nread;
325			else
326				*va_arg(ap, int *) = nread;
327			continue;
328
329		default:
330			goto match_failure;
331
332		/*
333		 * Disgusting backwards compatibility hack.	XXX
334		 */
335		case '\0':	/* compat */
336			return (EOF);
337		}
338
339		/*
340		 * Consume leading white space, except for formats
341		 * that suppress this.
342		 */
343		if ((flags & NOSKIP) == 0) {
344			while ((wi = __fgetwc(fp)) != WEOF && iswspace(wi))
345				nread++;
346			if (wi == WEOF)
347				goto input_failure;
348			__ungetwc(wi, fp);
349		}
350
351		/*
352		 * Do the conversion.
353		 */
354		switch (c) {
355
356		case CT_CHAR:
357			/* scan arbitrary characters (sets NOSKIP) */
358			if (width == 0)
359				width = 1;
360			if (flags & LONG) {
361				if (!(flags & SUPPRESS))
362					p = va_arg(ap, wchar_t *);
363				n = 0;
364				while (width-- != 0 &&
365				    (wi = __fgetwc(fp)) != WEOF) {
366					if (!(flags & SUPPRESS))
367						*p++ = (wchar_t)wi;
368					n++;
369				}
370				if (n == 0)
371					goto input_failure;
372				nread += n;
373				if (!(flags & SUPPRESS))
374					nassigned++;
375			} else {
376				if (!(flags & SUPPRESS))
377					mbp = va_arg(ap, char *);
378				n = 0;
379				mbs = initial_mbs;
380				while (width != 0 &&
381				    (wi = __fgetwc(fp)) != WEOF) {
382					if (width >= MB_CUR_MAX &&
383					    !(flags & SUPPRESS)) {
384						nconv = wcrtomb(mbp, wi, &mbs);
385						if (nconv == (size_t)-1)
386							goto input_failure;
387					} else {
388						nconv = wcrtomb(mbbuf, wi,
389						    &mbs);
390						if (nconv == (size_t)-1)
391							goto input_failure;
392						if (nconv > width) {
393							__ungetwc(wi, fp);
394							break;
395						}
396						if (!(flags & SUPPRESS))
397							memcpy(mbp, mbbuf,
398							    nconv);
399					}
400					if (!(flags & SUPPRESS))
401						mbp += nconv;
402					width -= nconv;
403					n++;
404				}
405				if (n == 0)
406					goto input_failure;
407				nread += n;
408				if (!(flags & SUPPRESS))
409					nassigned++;
410			}
411			nconversions++;
412			break;
413
414		case CT_CCL:
415			/* scan a (nonempty) character class (sets NOSKIP) */
416			if (width == 0)
417				width = (size_t)~0;	/* `infinity' */
418			/* take only those things in the class */
419			if ((flags & SUPPRESS) && (flags & LONG)) {
420				n = 0;
421				while ((wi = __fgetwc(fp)) != WEOF &&
422				    width-- != 0 && INCCL(wi))
423					n++;
424				if (wi != WEOF)
425					__ungetwc(wi, fp);
426				if (n == 0)
427					goto match_failure;
428			} else if (flags & LONG) {
429				p0 = p = va_arg(ap, wchar_t *);
430				while ((wi = __fgetwc(fp)) != WEOF &&
431				    width-- != 0 && INCCL(wi))
432					*p++ = (wchar_t)wi;
433				if (wi != WEOF)
434					__ungetwc(wi, fp);
435				n = p - p0;
436				if (n == 0)
437					goto match_failure;
438				*p = 0;
439				nassigned++;
440			} else {
441				if (!(flags & SUPPRESS))
442					mbp = va_arg(ap, char *);
443				n = 0;
444				mbs = initial_mbs;
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, &mbs);
450						if (nconv == (size_t)-1)
451							goto input_failure;
452					} else {
453						nconv = wcrtomb(mbbuf, wi,
454						    &mbs);
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				mbs = initial_mbs;
506				while ((wi = __fgetwc(fp)) != WEOF &&
507				    width != 0 &&
508				    !iswspace(wi)) {
509					if (width >= MB_CUR_MAX &&
510					    !(flags & SUPPRESS)) {
511						nconv = wcrtomb(mbp, wi, &mbs);
512						if (nconv == (size_t)-1)
513							goto input_failure;
514					} else {
515						nconv = wcrtomb(mbbuf, wi,
516						    &mbs);
517						if (nconv == (size_t)-1)
518							goto input_failure;
519						if (nconv > width)
520							break;
521						if (!(flags & SUPPRESS))
522							memcpy(mbp, mbbuf,
523							    nconv);
524					}
525					if (!(flags & SUPPRESS))
526						mbp += nconv;
527					width -= nconv;
528					nread++;
529				}
530				if (wi != WEOF)
531					__ungetwc(wi, fp);
532				if (!(flags & SUPPRESS)) {
533					*mbp = 0;
534					nassigned++;
535				}
536			}
537			nconversions++;
538			continue;
539
540		case CT_INT:
541			/* scan an integer as if by the conversion function */
542			if (width == 0 || width > sizeof(buf) /
543			    sizeof(*buf) - 1)
544				width = sizeof(buf) / sizeof(*buf) - 1;
545			flags |= SIGNOK | NDIGITS | NZDIGITS;
546			for (p = buf; width; width--) {
547				c = __fgetwc(fp);
548				/*
549				 * Switch on the character; `goto ok'
550				 * if we accept it as a part of number.
551				 */
552				switch (c) {
553
554				/*
555				 * The digit 0 is always legal, but is
556				 * special.  For %i conversions, if no
557				 * digits (zero or nonzero) have been
558				 * scanned (only signs), we will have
559				 * base==0.  In that case, we should set
560				 * it to 8 and enable 0x prefixing.
561				 * Also, if we have not scanned zero digits
562				 * before this, do not turn off prefixing
563				 * (someone else will turn it off if we
564				 * have scanned any nonzero digits).
565				 */
566				case '0':
567					if (base == 0) {
568						base = 8;
569						flags |= PFXOK;
570					}
571					if (flags & NZDIGITS)
572					    flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
573					else
574					    flags &= ~(SIGNOK|PFXOK|NDIGITS);
575					goto ok;
576
577				/* 1 through 7 always legal */
578				case '1': case '2': case '3':
579				case '4': case '5': case '6': case '7':
580					base = basefix[base];
581					flags &= ~(SIGNOK | PFXOK | NDIGITS);
582					goto ok;
583
584				/* digits 8 and 9 ok iff decimal or hex */
585				case '8': case '9':
586					base = basefix[base];
587					if (base <= 8)
588						break;	/* not legal here */
589					flags &= ~(SIGNOK | PFXOK | NDIGITS);
590					goto ok;
591
592				/* letters ok iff hex */
593				case 'A': case 'B': case 'C':
594				case 'D': case 'E': case 'F':
595				case 'a': case 'b': case 'c':
596				case 'd': case 'e': case 'f':
597					/* no need to fix base here */
598					if (base <= 10)
599						break;	/* not legal here */
600					flags &= ~(SIGNOK | PFXOK | NDIGITS);
601					goto ok;
602
603				/* sign ok only as first character */
604				case '+': case '-':
605					if (flags & SIGNOK) {
606						flags &= ~SIGNOK;
607						flags |= HAVESIGN;
608						goto ok;
609					}
610					break;
611
612				/*
613				 * x ok iff flag still set & 2nd char (or
614				 * 3rd char if we have a sign).
615				 */
616				case 'x': case 'X':
617					if (flags & PFXOK && p ==
618					    buf + 1 + !!(flags & HAVESIGN)) {
619						base = 16;	/* if %i */
620						flags &= ~PFXOK;
621						goto ok;
622					}
623					break;
624				}
625
626				/*
627				 * If we got here, c is not a legal character
628				 * for a number.  Stop accumulating digits.
629				 */
630				if (c != WEOF)
631					__ungetwc(c, fp);
632				break;
633		ok:
634				/*
635				 * c is legal: store it and look at the next.
636				 */
637				*p++ = (wchar_t)c;
638			}
639			/*
640			 * If we had only a sign, it is no good; push
641			 * back the sign.  If the number ends in `x',
642			 * it was [sign] '0' 'x', so push back the x
643			 * and treat it as [sign] '0'.
644			 */
645			if (flags & NDIGITS) {
646				if (p > buf)
647					__ungetwc(*--p, fp);
648				goto match_failure;
649			}
650			c = p[-1];
651			if (c == 'x' || c == 'X') {
652				--p;
653				__ungetwc(c, fp);
654			}
655			if ((flags & SUPPRESS) == 0) {
656				uintmax_t res;
657
658				*p = 0;
659				if ((flags & UNSIGNED) == 0)
660				    res = wcstoimax(buf, NULL, base);
661				else
662				    res = wcstoumax(buf, NULL, base);
663				if (flags & POINTER)
664					*va_arg(ap, void **) =
665							(void *)(uintptr_t)res;
666				else if (flags & SHORTSHORT)
667					*va_arg(ap, char *) = res;
668				else if (flags & SHORT)
669					*va_arg(ap, short *) = res;
670				else if (flags & LONG)
671					*va_arg(ap, long *) = res;
672				else if (flags & LONGLONG)
673					*va_arg(ap, long long *) = res;
674				else if (flags & INTMAXT)
675					*va_arg(ap, intmax_t *) = res;
676				else if (flags & PTRDIFFT)
677					*va_arg(ap, ptrdiff_t *) = res;
678				else if (flags & SIZET)
679					*va_arg(ap, size_t *) = res;
680				else
681					*va_arg(ap, int *) = res;
682				nassigned++;
683			}
684			nread += p - buf;
685			nconversions++;
686			break;
687
688#ifndef NO_FLOATING_POINT
689		case CT_FLOAT:
690			/* scan a floating point number as if by strtod */
691			if (width == 0 || width > sizeof(buf) /
692			    sizeof(*buf) - 1)
693				width = sizeof(buf) / sizeof(*buf) - 1;
694			if ((width = parsefloat(fp, buf, buf + width)) == 0)
695				goto match_failure;
696			if ((flags & SUPPRESS) == 0) {
697				if (flags & LONGDBL) {
698					long double res = wcstold(buf, &p);
699					*va_arg(ap, long double *) = res;
700				} else if (flags & LONG) {
701					double res = wcstod(buf, &p);
702					*va_arg(ap, double *) = res;
703				} else {
704					float res = wcstof(buf, &p);
705					*va_arg(ap, float *) = res;
706				}
707				nassigned++;
708			}
709			nread += width;
710			nconversions++;
711			break;
712#endif /* !NO_FLOATING_POINT */
713		}
714	}
715input_failure:
716	return (nconversions != 0 ? nassigned : EOF);
717match_failure:
718	return (nassigned);
719}
720
721#ifndef NO_FLOATING_POINT
722static int
723parsefloat(FILE *fp, wchar_t *buf, wchar_t *end)
724{
725	mbstate_t mbs;
726	size_t nconv;
727	wchar_t *commit, *p;
728	int infnanpos = 0;
729	enum {
730		S_START, S_GOTSIGN, S_INF, S_NAN, S_DONE, S_MAYBEHEX,
731		S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
732	} state = S_START;
733	wchar_t c;
734	wchar_t decpt;
735	_Bool gotmantdig = 0, ishex = 0;
736
737	mbs = initial_mbs;
738	nconv = mbrtowc(&decpt, localeconv()->decimal_point, MB_CUR_MAX, &mbs);
739	if (nconv == (size_t)-1 || nconv == (size_t)-2)
740		decpt = '.';	/* failsafe */
741
742	/*
743	 * We set commit = p whenever the string we have read so far
744	 * constitutes a valid representation of a floating point
745	 * number by itself.  At some point, the parse will complete
746	 * or fail, and we will ungetc() back to the last commit point.
747	 * To ensure that the file offset gets updated properly, it is
748	 * always necessary to read at least one character that doesn't
749	 * match; thus, we can't short-circuit "infinity" or "nan(...)".
750	 */
751	commit = buf - 1;
752	c = WEOF;
753	for (p = buf; p < end; ) {
754		if ((c = __fgetwc(fp)) == WEOF)
755			break;
756reswitch:
757		switch (state) {
758		case S_START:
759			state = S_GOTSIGN;
760			if (c == '-' || c == '+')
761				break;
762			else
763				goto reswitch;
764		case S_GOTSIGN:
765			switch (c) {
766			case '0':
767				state = S_MAYBEHEX;
768				commit = p;
769				break;
770			case 'I':
771			case 'i':
772				state = S_INF;
773				break;
774			case 'N':
775			case 'n':
776				state = S_NAN;
777				break;
778			default:
779				state = S_DIGITS;
780				goto reswitch;
781			}
782			break;
783		case S_INF:
784			if (infnanpos > 6 ||
785			    (c != "nfinity"[infnanpos] &&
786			     c != "NFINITY"[infnanpos]))
787				goto parsedone;
788			if (infnanpos == 1 || infnanpos == 6)
789				commit = p;	/* inf or infinity */
790			infnanpos++;
791			break;
792		case S_NAN:
793			switch (infnanpos) {
794			case 0:
795				if (c != 'A' && c != 'a')
796					goto parsedone;
797				break;
798			case 1:
799				if (c != 'N' && c != 'n')
800					goto parsedone;
801				else
802					commit = p;
803				break;
804			case 2:
805				if (c != '(')
806					goto parsedone;
807				break;
808			default:
809				if (c == ')') {
810					commit = p;
811					state = S_DONE;
812				} else if (!iswalnum(c) && c != '_')
813					goto parsedone;
814				break;
815			}
816			infnanpos++;
817			break;
818		case S_DONE:
819			goto parsedone;
820		case S_MAYBEHEX:
821			state = S_DIGITS;
822			if (c == 'X' || c == 'x') {
823				ishex = 1;
824				break;
825			} else {	/* we saw a '0', but no 'x' */
826				gotmantdig = 1;
827				goto reswitch;
828			}
829		case S_DIGITS:
830			if ((ishex && iswxdigit(c)) || iswdigit(c))
831				gotmantdig = 1;
832			else {
833				state = S_FRAC;
834				if (c != decpt)
835					goto reswitch;
836			}
837			if (gotmantdig)
838				commit = p;
839			break;
840		case S_FRAC:
841			if (((c == 'E' || c == 'e') && !ishex) ||
842			    ((c == 'P' || c == 'p') && ishex)) {
843				if (!gotmantdig)
844					goto parsedone;
845				else
846					state = S_EXP;
847			} else if ((ishex && iswxdigit(c)) || iswdigit(c)) {
848				commit = p;
849				gotmantdig = 1;
850			} else
851				goto parsedone;
852			break;
853		case S_EXP:
854			state = S_EXPDIGITS;
855			if (c == '-' || c == '+')
856				break;
857			else
858				goto reswitch;
859		case S_EXPDIGITS:
860			if (iswdigit(c))
861				commit = p;
862			else
863				goto parsedone;
864			break;
865		default:
866			abort();
867		}
868		*p++ = c;
869		c = WEOF;
870	}
871
872parsedone:
873	if (c != WEOF)
874		__ungetwc(c, fp);
875	while (commit < --p)
876		__ungetwc(*p, fp);
877	*++commit = '\0';
878	return (commit - buf);
879}
880#endif
881