11573Srgrimes/*-
21573Srgrimes * Copyright (c) 1990, 1993
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
5227753Stheraven * Copyright (c) 2011 The FreeBSD Foundation
6227753Stheraven * All rights reserved.
7227753Stheraven * Portions of this software were developed by David Chisnall
8227753Stheraven * under sponsorship from the FreeBSD Foundation.
9227753Stheraven *
101573Srgrimes * This code is derived from software contributed to Berkeley by
111573Srgrimes * Chris Torek.
121573Srgrimes *
131573Srgrimes * Redistribution and use in source and binary forms, with or without
141573Srgrimes * modification, are permitted provided that the following conditions
151573Srgrimes * are met:
161573Srgrimes * 1. Redistributions of source code must retain the above copyright
171573Srgrimes *    notice, this list of conditions and the following disclaimer.
181573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
191573Srgrimes *    notice, this list of conditions and the following disclaimer in the
201573Srgrimes *    documentation and/or other materials provided with the distribution.
21249808Semaste * 3. Neither the name of the University nor the names of its contributors
221573Srgrimes *    may be used to endorse or promote products derived from this software
231573Srgrimes *    without specific prior written permission.
241573Srgrimes *
251573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
261573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
271573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
281573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
291573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
301573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
311573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
321573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
331573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
341573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
351573Srgrimes * SUCH DAMAGE.
361573Srgrimes */
371573Srgrimes
381573Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
391573Srgrimesstatic char sccsid[] = "@(#)vfscanf.c	8.1 (Berkeley) 6/4/93";
401573Srgrimes#endif /* LIBC_SCCS and not lint */
4192986Sobrien#include <sys/cdefs.h>
4292986Sobrien__FBSDID("$FreeBSD: stable/10/lib/libc/stdio/vfscanf.c 321074 2017-07-17 14:09:34Z kib $");
431573Srgrimes
4471579Sdeischen#include "namespace.h"
4595137Sfenner#include <ctype.h>
4695137Sfenner#include <inttypes.h>
471573Srgrimes#include <stdio.h>
481573Srgrimes#include <stdlib.h>
4995137Sfenner#include <stddef.h>
501573Srgrimes#include <stdarg.h>
5121757Sache#include <string.h>
52103854Stjr#include <wchar.h>
53103854Stjr#include <wctype.h>
5471579Sdeischen#include "un-namespace.h"
5521757Sache
5621757Sache#include "collate.h"
5771579Sdeischen#include "libc_private.h"
581573Srgrimes#include "local.h"
59227753Stheraven#include "xlocale_private.h"
601573Srgrimes
61128819Sdas#ifndef NO_FLOATING_POINT
6272289Sache#include <locale.h>
6372289Sache#endif
6472289Sache
651573Srgrimes#define	BUF		513	/* Maximum length of numeric string. */
661573Srgrimes
671573Srgrimes/*
681573Srgrimes * Flags used during conversion.
691573Srgrimes */
701573Srgrimes#define	LONG		0x01	/* l: long or double */
7131359Sbde#define	LONGDBL		0x02	/* L: long double */
721573Srgrimes#define	SHORT		0x04	/* h: short */
7395137Sfenner#define	SUPPRESS	0x08	/* *: suppress assignment */
7495137Sfenner#define	POINTER		0x10	/* p: void * (as hex) */
7595137Sfenner#define	NOSKIP		0x20	/* [ or c: do not skip blanks */
7695137Sfenner#define	LONGLONG	0x400	/* ll: long long (+ deprecated q: quad) */
7795137Sfenner#define	INTMAXT		0x800	/* j: intmax_t */
7895137Sfenner#define	PTRDIFFT	0x1000	/* t: ptrdiff_t */
7995137Sfenner#define	SIZET		0x2000	/* z: size_t */
8095137Sfenner#define	SHORTSHORT	0x4000	/* hh: char */
8195137Sfenner#define	UNSIGNED	0x8000	/* %[oupxX] conversions */
821573Srgrimes
831573Srgrimes/*
84116967Sdas * The following are used in integral conversions only:
85116967Sdas * SIGNOK, NDIGITS, PFXOK, and NZDIGITS
861573Srgrimes */
871573Srgrimes#define	SIGNOK		0x40	/* +/- is (still) legal */
881573Srgrimes#define	NDIGITS		0x80	/* no digits detected */
891573Srgrimes#define	PFXOK		0x100	/* 0x prefix is (still) legal */
901573Srgrimes#define	NZDIGITS	0x200	/* no zero digits detected */
91125282Sdas#define	HAVESIGN	0x10000	/* sign detected */
921573Srgrimes
931573Srgrimes/*
941573Srgrimes * Conversion types.
951573Srgrimes */
961573Srgrimes#define	CT_CHAR		0	/* %c conversion */
971573Srgrimes#define	CT_CCL		1	/* %[...] conversion */
981573Srgrimes#define	CT_STRING	2	/* %s conversion */
9995137Sfenner#define	CT_INT		3	/* %[dioupxX] conversion */
10095137Sfenner#define	CT_FLOAT	4	/* %[efgEFG] conversion */
1011573Srgrimes
10295137Sfennerstatic const u_char *__sccl(char *, const u_char *);
103157381Sphk#ifndef NO_FLOATING_POINT
104227753Stheravenstatic int parsefloat(FILE *, char *, char *, locale_t);
105157381Sphk#endif
1061573Srgrimes
107105098Stjr__weak_reference(__vfscanf, vfscanf);
108105098Stjr
1091573Srgrimes/*
110234585Sdas * Conversion functions are passed a pointer to this object instead of
111234585Sdas * a real parameter to indicate that the assignment-suppression (*)
112234585Sdas * flag was specified.  We could use a NULL pointer to indicate this,
113234585Sdas * but that would mask bugs in applications that call scanf() with a
114234585Sdas * NULL pointer.
115234585Sdas */
116234585Sdasstatic const int suppress;
117234585Sdas#define	SUPPRESS_PTR	((void *)&suppress)
118234585Sdas
119234585Sdasstatic const mbstate_t initial_mbs;
120234585Sdas
121234585Sdas/*
122234585Sdas * The following conversion functions return the number of characters consumed,
123234585Sdas * or -1 on input failure.  Character class conversion returns 0 on match
124234585Sdas * failure.
125234585Sdas */
126234585Sdas
127234585Sdasstatic __inline int
128234836Sdumbbellconvert_char(FILE *fp, char * p, int width)
129234585Sdas{
130234799Sdas	int n;
131234585Sdas
132234585Sdas	if (p == SUPPRESS_PTR) {
133234585Sdas		size_t sum = 0;
134234585Sdas		for (;;) {
135234585Sdas			if ((n = fp->_r) < width) {
136234585Sdas				sum += n;
137234585Sdas				width -= n;
138234585Sdas				fp->_p += n;
139234585Sdas				if (__srefill(fp)) {
140234585Sdas					if (sum == 0)
141234585Sdas						return (-1);
142234585Sdas					break;
143234585Sdas				}
144234585Sdas			} else {
145234585Sdas				sum += width;
146234585Sdas				fp->_r -= width;
147234585Sdas				fp->_p += width;
148234585Sdas				break;
149234585Sdas			}
150234585Sdas		}
151234799Sdas		return (sum);
152234585Sdas	} else {
153234585Sdas		size_t r = __fread(p, 1, width, fp);
154234836Sdumbbell
155234585Sdas		if (r == 0)
156234585Sdas			return (-1);
157234799Sdas		return (r);
158234585Sdas	}
159234585Sdas}
160234585Sdas
161234585Sdasstatic __inline int
162234799Sdasconvert_wchar(FILE *fp, wchar_t *wcp, int width, locale_t locale)
163234585Sdas{
164234585Sdas	mbstate_t mbs;
165234585Sdas	int n, nread;
166234799Sdas	wint_t wi;
167234585Sdas
168234799Sdas	mbs = initial_mbs;
169234585Sdas	n = 0;
170234799Sdas	while (width-- != 0 &&
171234799Sdas	    (wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF) {
172234799Sdas		if (wcp != SUPPRESS_PTR)
173234799Sdas			*wcp++ = (wchar_t)wi;
174234799Sdas		n += nread;
175234585Sdas	}
176234799Sdas	if (n == 0)
177234799Sdas		return (-1);
178234799Sdas	return (n);
179234585Sdas}
180234585Sdas
181234585Sdasstatic __inline int
182234836Sdumbbellconvert_ccl(FILE *fp, char * p, int width, const char *ccltab)
183234585Sdas{
184234585Sdas	char *p0;
185234585Sdas	int n;
186234585Sdas
187234585Sdas	if (p == SUPPRESS_PTR) {
188234585Sdas		n = 0;
189234585Sdas		while (ccltab[*fp->_p]) {
190234585Sdas			n++, fp->_r--, fp->_p++;
191234585Sdas			if (--width == 0)
192234585Sdas				break;
193234585Sdas			if (fp->_r <= 0 && __srefill(fp)) {
194234585Sdas				if (n == 0)
195234585Sdas					return (-1);
196234585Sdas				break;
197234585Sdas			}
198234585Sdas		}
199234585Sdas	} else {
200234585Sdas		p0 = p;
201234585Sdas		while (ccltab[*fp->_p]) {
202234585Sdas			fp->_r--;
203234585Sdas			*p++ = *fp->_p++;
204234585Sdas			if (--width == 0)
205234585Sdas				break;
206234585Sdas			if (fp->_r <= 0 && __srefill(fp)) {
207234585Sdas				if (p == p0)
208234585Sdas					return (-1);
209234585Sdas				break;
210234585Sdas			}
211234585Sdas		}
212234585Sdas		n = p - p0;
213234585Sdas		if (n == 0)
214234585Sdas			return (0);
215234585Sdas		*p = 0;
216234585Sdas	}
217234585Sdas	return (n);
218234585Sdas}
219234585Sdas
220234585Sdasstatic __inline int
221234799Sdasconvert_wccl(FILE *fp, wchar_t *wcp, int width, const char *ccltab,
222234799Sdas    locale_t locale)
223234585Sdas{
224234585Sdas	mbstate_t mbs;
225234799Sdas	wint_t wi;
226234799Sdas	int n, nread;
227234585Sdas
228234799Sdas	mbs = initial_mbs;
229234587Sdas	n = 0;
230234799Sdas	if (wcp == SUPPRESS_PTR) {
231234799Sdas		while ((wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF &&
232234799Sdas		    width-- != 0 && ccltab[wctob(wi)])
233234799Sdas			n += nread;
234234799Sdas		if (wi != WEOF)
235234799Sdas			__ungetwc(wi, fp, __get_locale());
236234799Sdas	} else {
237234799Sdas		while ((wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF &&
238234799Sdas		    width-- != 0 && ccltab[wctob(wi)]) {
239234799Sdas			*wcp++ = (wchar_t)wi;
240234799Sdas			n += nread;
241234585Sdas		}
242234799Sdas		if (wi != WEOF)
243234799Sdas			__ungetwc(wi, fp, __get_locale());
244234799Sdas		if (n == 0)
245234799Sdas			return (0);
246234799Sdas		*wcp = 0;
247234585Sdas	}
248234799Sdas	return (n);
249234585Sdas}
250234585Sdas
251234585Sdasstatic __inline int
252234836Sdumbbellconvert_string(FILE *fp, char * p, int width)
253234585Sdas{
254234585Sdas	char *p0;
255234585Sdas	int n;
256234585Sdas
257234585Sdas	if (p == SUPPRESS_PTR) {
258234585Sdas		n = 0;
259234585Sdas		while (!isspace(*fp->_p)) {
260234585Sdas			n++, fp->_r--, fp->_p++;
261234585Sdas			if (--width == 0)
262234585Sdas				break;
263234585Sdas			if (fp->_r <= 0 && __srefill(fp))
264234585Sdas				break;
265234585Sdas		}
266234585Sdas	} else {
267234585Sdas		p0 = p;
268234585Sdas		while (!isspace(*fp->_p)) {
269234585Sdas			fp->_r--;
270234585Sdas			*p++ = *fp->_p++;
271234585Sdas			if (--width == 0)
272234585Sdas				break;
273234585Sdas			if (fp->_r <= 0 && __srefill(fp))
274234585Sdas				break;
275234585Sdas		}
276234585Sdas		*p = 0;
277234585Sdas		n = p - p0;
278234585Sdas	}
279234585Sdas	return (n);
280234585Sdas}
281234585Sdas
282234585Sdasstatic __inline int
283234799Sdasconvert_wstring(FILE *fp, wchar_t *wcp, int width, locale_t locale)
284234585Sdas{
285234585Sdas	mbstate_t mbs;
286234799Sdas	wint_t wi;
287234799Sdas	int n, nread;
288234585Sdas
289234799Sdas	mbs = initial_mbs;
290234799Sdas	n = 0;
291234799Sdas	if (wcp == SUPPRESS_PTR) {
292234799Sdas		while ((wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF &&
293234799Sdas		    width-- != 0 && !iswspace(wi))
294234799Sdas			n += nread;
295234799Sdas		if (wi != WEOF)
296234799Sdas			__ungetwc(wi, fp, __get_locale());
297234799Sdas	} else {
298234799Sdas		while ((wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF &&
299234799Sdas		    width-- != 0 && !iswspace(wi)) {
300234799Sdas			*wcp++ = (wchar_t)wi;
301234799Sdas			n += nread;
302234585Sdas		}
303234799Sdas		if (wi != WEOF)
304234799Sdas			__ungetwc(wi, fp, __get_locale());
305234799Sdas		*wcp = '\0';
306234585Sdas	}
307234799Sdas	return (n);
308234585Sdas}
309234585Sdas
310234585Sdas/*
311234585Sdas * Read an integer, storing it in buf.  The only relevant bit in the
312234585Sdas * flags argument is PFXOK.
313234585Sdas *
314234585Sdas * Return 0 on a match failure, and the number of characters read
315234585Sdas * otherwise.
316234585Sdas */
317234585Sdasstatic __inline int
318234585Sdasparseint(FILE *fp, char * __restrict buf, int width, int base, int flags)
319234585Sdas{
320234585Sdas	/* `basefix' is used to avoid `if' tests */
321234585Sdas	static const short basefix[17] =
322234585Sdas		{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
323234585Sdas	char *p;
324234585Sdas	int c;
325234585Sdas
326234585Sdas	flags |= SIGNOK | NDIGITS | NZDIGITS;
327234585Sdas	for (p = buf; width; width--) {
328234585Sdas		c = *fp->_p;
329234585Sdas		/*
330234585Sdas		 * Switch on the character; `goto ok' if we accept it
331234585Sdas		 * as a part of number.
332234585Sdas		 */
333234585Sdas		switch (c) {
334234585Sdas
335234585Sdas		/*
336234585Sdas		 * The digit 0 is always legal, but is special.  For
337234585Sdas		 * %i conversions, if no digits (zero or nonzero) have
338234585Sdas		 * been scanned (only signs), we will have base==0.
339234585Sdas		 * In that case, we should set it to 8 and enable 0x
340234585Sdas		 * prefixing.  Also, if we have not scanned zero
341234585Sdas		 * digits before this, do not turn off prefixing
342234585Sdas		 * (someone else will turn it off if we have scanned
343234585Sdas		 * any nonzero digits).
344234585Sdas		 */
345234585Sdas		case '0':
346234585Sdas			if (base == 0) {
347234585Sdas				base = 8;
348234585Sdas				flags |= PFXOK;
349234585Sdas			}
350234585Sdas			if (flags & NZDIGITS)
351234585Sdas				flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
352234585Sdas			else
353234585Sdas				flags &= ~(SIGNOK|PFXOK|NDIGITS);
354234585Sdas			goto ok;
355234585Sdas
356234585Sdas		/* 1 through 7 always legal */
357234585Sdas		case '1': case '2': case '3':
358234585Sdas		case '4': case '5': case '6': case '7':
359234585Sdas			base = basefix[base];
360234585Sdas			flags &= ~(SIGNOK | PFXOK | NDIGITS);
361234585Sdas			goto ok;
362234585Sdas
363234585Sdas		/* digits 8 and 9 ok iff decimal or hex */
364234585Sdas		case '8': case '9':
365234585Sdas			base = basefix[base];
366234585Sdas			if (base <= 8)
367234585Sdas				break;	/* not legal here */
368234585Sdas			flags &= ~(SIGNOK | PFXOK | NDIGITS);
369234585Sdas			goto ok;
370234585Sdas
371234585Sdas		/* letters ok iff hex */
372234585Sdas		case 'A': case 'B': case 'C':
373234585Sdas		case 'D': case 'E': case 'F':
374234585Sdas		case 'a': case 'b': case 'c':
375234585Sdas		case 'd': case 'e': case 'f':
376234585Sdas			/* no need to fix base here */
377234585Sdas			if (base <= 10)
378234585Sdas				break;	/* not legal here */
379234585Sdas			flags &= ~(SIGNOK | PFXOK | NDIGITS);
380234585Sdas			goto ok;
381234585Sdas
382234585Sdas		/* sign ok only as first character */
383234585Sdas		case '+': case '-':
384234585Sdas			if (flags & SIGNOK) {
385234585Sdas				flags &= ~SIGNOK;
386234585Sdas				flags |= HAVESIGN;
387234585Sdas				goto ok;
388234585Sdas			}
389234585Sdas			break;
390234836Sdumbbell
391234585Sdas		/*
392234585Sdas		 * x ok iff flag still set & 2nd char (or 3rd char if
393234585Sdas		 * we have a sign).
394234585Sdas		 */
395234585Sdas		case 'x': case 'X':
396234585Sdas			if (flags & PFXOK && p ==
397234585Sdas			    buf + 1 + !!(flags & HAVESIGN)) {
398234585Sdas				base = 16;	/* if %i */
399234585Sdas				flags &= ~PFXOK;
400234585Sdas				goto ok;
401234585Sdas			}
402234585Sdas			break;
403234585Sdas		}
404234585Sdas
405234585Sdas		/*
406234585Sdas		 * If we got here, c is not a legal character for a
407234585Sdas		 * number.  Stop accumulating digits.
408234585Sdas		 */
409234585Sdas		break;
410234585Sdas	ok:
411234585Sdas		/*
412234585Sdas		 * c is legal: store it and look at the next.
413234585Sdas		 */
414234585Sdas		*p++ = c;
415234585Sdas		if (--fp->_r > 0)
416234585Sdas			fp->_p++;
417234585Sdas		else if (__srefill(fp))
418234585Sdas			break;		/* EOF */
419234585Sdas	}
420234585Sdas	/*
421234585Sdas	 * If we had only a sign, it is no good; push back the sign.
422234585Sdas	 * If the number ends in `x', it was [sign] '0' 'x', so push
423234585Sdas	 * back the x and treat it as [sign] '0'.
424234585Sdas	 */
425234585Sdas	if (flags & NDIGITS) {
426234585Sdas		if (p > buf)
427234585Sdas			(void) __ungetc(*(u_char *)--p, fp);
428234585Sdas		return (0);
429234585Sdas	}
430234585Sdas	c = ((u_char *)p)[-1];
431234585Sdas	if (c == 'x' || c == 'X') {
432234585Sdas		--p;
433234585Sdas		(void) __ungetc(c, fp);
434234585Sdas	}
435234585Sdas	return (p - buf);
436234585Sdas}
437234585Sdas
438234585Sdas/*
43971579Sdeischen * __vfscanf - MT-safe version
4401573Srgrimes */
44116586Sjraynardint
44271579Sdeischen__vfscanf(FILE *fp, char const *fmt0, va_list ap)
4431573Srgrimes{
44471579Sdeischen	int ret;
44571579Sdeischen
446321074Skib	FLOCKFILE_CANCELSAFE(fp);
447227753Stheraven	ret = __svfscanf(fp, __get_locale(), fmt0, ap);
448321074Skib	FUNLOCKFILE_CANCELSAFE();
44971579Sdeischen	return (ret);
45071579Sdeischen}
451227753Stheravenint
452227753Stheravenvfscanf_l(FILE *fp, locale_t locale, char const *fmt0, va_list ap)
453227753Stheraven{
454227753Stheraven	int ret;
455227753Stheraven	FIX_LOCALE(locale);
45671579Sdeischen
457321074Skib	FLOCKFILE_CANCELSAFE(fp);
458227753Stheraven	ret = __svfscanf(fp, locale, fmt0, ap);
459321074Skib	FUNLOCKFILE_CANCELSAFE();
460227753Stheraven	return (ret);
461227753Stheraven}
462227753Stheraven
46371579Sdeischen/*
46471579Sdeischen * __svfscanf - non-MT-safe version of __vfscanf
46571579Sdeischen */
46671579Sdeischenint
467227753Stheraven__svfscanf(FILE *fp, locale_t locale, const char *fmt0, va_list ap)
46871579Sdeischen{
469234585Sdas#define	GETARG(type)	((flags & SUPPRESS) ? SUPPRESS_PTR : va_arg(ap, type))
47095137Sfenner	const u_char *fmt = (const u_char *)fmt0;
47171579Sdeischen	int c;			/* character from format, or conversion */
47271579Sdeischen	size_t width;		/* field width, or 0 */
47371579Sdeischen	int flags;		/* flags as defined above */
4741573Srgrimes	int nassigned;		/* number of fields assigned */
47523352Sbde	int nconversions;	/* number of conversions */
476234585Sdas	int nr;			/* characters read by the current conversion */
4771573Srgrimes	int nread;		/* number of characters consumed from fp */
47895137Sfenner	int base;		/* base argument to conversion function */
4791573Srgrimes	char ccltab[256];	/* character class table for %[...] */
480234585Sdas	char buf[BUF];		/* buffer for numeric conversions */
4811573Srgrimes
482101776Stjr	ORIENT(fp, -1);
483101776Stjr
4841573Srgrimes	nassigned = 0;
48523352Sbde	nconversions = 0;
4861573Srgrimes	nread = 0;
4871573Srgrimes	for (;;) {
4881573Srgrimes		c = *fmt++;
4891573Srgrimes		if (c == 0)
4901573Srgrimes			return (nassigned);
4911573Srgrimes		if (isspace(c)) {
49239644Sobrien			while ((fp->_r > 0 || __srefill(fp) == 0) && isspace(*fp->_p))
4931573Srgrimes				nread++, fp->_r--, fp->_p++;
4941573Srgrimes			continue;
4951573Srgrimes		}
4961573Srgrimes		if (c != '%')
4971573Srgrimes			goto literal;
4981573Srgrimes		width = 0;
4991573Srgrimes		flags = 0;
5001573Srgrimes		/*
5011573Srgrimes		 * switch on the format.  continue if done;
5021573Srgrimes		 * break once format type is derived.
5031573Srgrimes		 */
5041573Srgrimesagain:		c = *fmt++;
5051573Srgrimes		switch (c) {
5061573Srgrimes		case '%':
5071573Srgrimesliteral:
5081573Srgrimes			if (fp->_r <= 0 && __srefill(fp))
5091573Srgrimes				goto input_failure;
5101573Srgrimes			if (*fp->_p != c)
5111573Srgrimes				goto match_failure;
5121573Srgrimes			fp->_r--, fp->_p++;
5131573Srgrimes			nread++;
5141573Srgrimes			continue;
5151573Srgrimes
5161573Srgrimes		case '*':
5171573Srgrimes			flags |= SUPPRESS;
5181573Srgrimes			goto again;
51995137Sfenner		case 'j':
52095137Sfenner			flags |= INTMAXT;
52195137Sfenner			goto again;
5221573Srgrimes		case 'l':
52395137Sfenner			if (flags & LONG) {
52495137Sfenner				flags &= ~LONG;
52595137Sfenner				flags |= LONGLONG;
52695137Sfenner			} else
52795137Sfenner				flags |= LONG;
5281573Srgrimes			goto again;
52927151Sjkh		case 'q':
53095137Sfenner			flags |= LONGLONG;	/* not quite */
53127151Sjkh			goto again;
53295137Sfenner		case 't':
53395137Sfenner			flags |= PTRDIFFT;
53495137Sfenner			goto again;
53595137Sfenner		case 'z':
53695137Sfenner			flags |= SIZET;
53795137Sfenner			goto again;
5381573Srgrimes		case 'L':
5391573Srgrimes			flags |= LONGDBL;
5401573Srgrimes			goto again;
5411573Srgrimes		case 'h':
54295137Sfenner			if (flags & SHORT) {
54395137Sfenner				flags &= ~SHORT;
54495137Sfenner				flags |= SHORTSHORT;
54595137Sfenner			} else
54695137Sfenner				flags |= SHORT;
5471573Srgrimes			goto again;
5481573Srgrimes
5491573Srgrimes		case '0': case '1': case '2': case '3': case '4':
5501573Srgrimes		case '5': case '6': case '7': case '8': case '9':
5511573Srgrimes			width = width * 10 + c - '0';
5521573Srgrimes			goto again;
5531573Srgrimes
5541573Srgrimes		/*
5551573Srgrimes		 * Conversions.
5561573Srgrimes		 */
5571573Srgrimes		case 'd':
5581573Srgrimes			c = CT_INT;
5591573Srgrimes			base = 10;
5601573Srgrimes			break;
5611573Srgrimes
5621573Srgrimes		case 'i':
5631573Srgrimes			c = CT_INT;
5641573Srgrimes			base = 0;
5651573Srgrimes			break;
5661573Srgrimes
5671573Srgrimes		case 'o':
5681573Srgrimes			c = CT_INT;
56995137Sfenner			flags |= UNSIGNED;
5701573Srgrimes			base = 8;
5711573Srgrimes			break;
5721573Srgrimes
5731573Srgrimes		case 'u':
5741573Srgrimes			c = CT_INT;
57595137Sfenner			flags |= UNSIGNED;
5761573Srgrimes			base = 10;
5771573Srgrimes			break;
5781573Srgrimes
57995137Sfenner		case 'X':
5801573Srgrimes		case 'x':
5811573Srgrimes			flags |= PFXOK;	/* enable 0x prefixing */
5821573Srgrimes			c = CT_INT;
58395137Sfenner			flags |= UNSIGNED;
5841573Srgrimes			base = 16;
5851573Srgrimes			break;
5861573Srgrimes
587128819Sdas#ifndef NO_FLOATING_POINT
588116967Sdas		case 'A': case 'E': case 'F': case 'G':
589116967Sdas		case 'a': case 'e': case 'f': case 'g':
5901573Srgrimes			c = CT_FLOAT;
5911573Srgrimes			break;
5921573Srgrimes#endif
5931573Srgrimes
59495137Sfenner		case 'S':
59595137Sfenner			flags |= LONG;
59695137Sfenner			/* FALLTHROUGH */
5971573Srgrimes		case 's':
5981573Srgrimes			c = CT_STRING;
5991573Srgrimes			break;
6001573Srgrimes
6011573Srgrimes		case '[':
6021573Srgrimes			fmt = __sccl(ccltab, fmt);
6031573Srgrimes			flags |= NOSKIP;
6041573Srgrimes			c = CT_CCL;
6051573Srgrimes			break;
6061573Srgrimes
60795137Sfenner		case 'C':
60895137Sfenner			flags |= LONG;
60995137Sfenner			/* FALLTHROUGH */
6101573Srgrimes		case 'c':
6111573Srgrimes			flags |= NOSKIP;
6121573Srgrimes			c = CT_CHAR;
6131573Srgrimes			break;
6141573Srgrimes
6151573Srgrimes		case 'p':	/* pointer format is like hex */
6161573Srgrimes			flags |= POINTER | PFXOK;
61795137Sfenner			c = CT_INT;		/* assumes sizeof(uintmax_t) */
61895137Sfenner			flags |= UNSIGNED;	/*      >= sizeof(uintptr_t) */
6191573Srgrimes			base = 16;
6201573Srgrimes			break;
6211573Srgrimes
6221573Srgrimes		case 'n':
6231573Srgrimes			if (flags & SUPPRESS)	/* ??? */
6241573Srgrimes				continue;
62595137Sfenner			if (flags & SHORTSHORT)
62695137Sfenner				*va_arg(ap, char *) = nread;
62795137Sfenner			else if (flags & SHORT)
6281573Srgrimes				*va_arg(ap, short *) = nread;
6291573Srgrimes			else if (flags & LONG)
6301573Srgrimes				*va_arg(ap, long *) = nread;
63195137Sfenner			else if (flags & LONGLONG)
63295137Sfenner				*va_arg(ap, long long *) = nread;
63395137Sfenner			else if (flags & INTMAXT)
63495137Sfenner				*va_arg(ap, intmax_t *) = nread;
63595137Sfenner			else if (flags & SIZET)
63695137Sfenner				*va_arg(ap, size_t *) = nread;
63795137Sfenner			else if (flags & PTRDIFFT)
63895137Sfenner				*va_arg(ap, ptrdiff_t *) = nread;
6391573Srgrimes			else
6401573Srgrimes				*va_arg(ap, int *) = nread;
6411573Srgrimes			continue;
6421573Srgrimes
64395137Sfenner		default:
64495137Sfenner			goto match_failure;
64595137Sfenner
6461573Srgrimes		/*
64795137Sfenner		 * Disgusting backwards compatibility hack.	XXX
6481573Srgrimes		 */
6491573Srgrimes		case '\0':	/* compat */
6501573Srgrimes			return (EOF);
6511573Srgrimes		}
6521573Srgrimes
6531573Srgrimes		/*
6541573Srgrimes		 * We have a conversion that requires input.
6551573Srgrimes		 */
6561573Srgrimes		if (fp->_r <= 0 && __srefill(fp))
6571573Srgrimes			goto input_failure;
6581573Srgrimes
6591573Srgrimes		/*
6601573Srgrimes		 * Consume leading white space, except for formats
6611573Srgrimes		 * that suppress this.
6621573Srgrimes		 */
6631573Srgrimes		if ((flags & NOSKIP) == 0) {
6641573Srgrimes			while (isspace(*fp->_p)) {
6651573Srgrimes				nread++;
6661573Srgrimes				if (--fp->_r > 0)
6671573Srgrimes					fp->_p++;
6681573Srgrimes				else if (__srefill(fp))
6691573Srgrimes					goto input_failure;
6701573Srgrimes			}
6711573Srgrimes			/*
6721573Srgrimes			 * Note that there is at least one character in
6731573Srgrimes			 * the buffer, so conversions that do not set NOSKIP
6741573Srgrimes			 * ca no longer result in an input failure.
6751573Srgrimes			 */
6761573Srgrimes		}
6771573Srgrimes
6781573Srgrimes		/*
6791573Srgrimes		 * Do the conversion.
6801573Srgrimes		 */
6811573Srgrimes		switch (c) {
6821573Srgrimes
6831573Srgrimes		case CT_CHAR:
6841573Srgrimes			/* scan arbitrary characters (sets NOSKIP) */
6851573Srgrimes			if (width == 0)
6861573Srgrimes				width = 1;
687105247Stjr			if (flags & LONG) {
688234585Sdas				nr = convert_wchar(fp, GETARG(wchar_t *),
689234799Sdas				    width, locale);
6901573Srgrimes			} else {
691234585Sdas				nr = convert_char(fp, GETARG(char *), width);
6921573Srgrimes			}
693234585Sdas			if (nr < 0)
694234585Sdas				goto input_failure;
6951573Srgrimes			break;
6961573Srgrimes
6971573Srgrimes		case CT_CCL:
6981573Srgrimes			/* scan a (nonempty) character class (sets NOSKIP) */
6991573Srgrimes			if (width == 0)
70016586Sjraynard				width = (size_t)~0;	/* `infinity' */
701105247Stjr			if (flags & LONG) {
702234585Sdas				nr = convert_wccl(fp, GETARG(wchar_t *), width,
703234799Sdas				    ccltab, locale);
704234585Sdas			} else {
705234585Sdas				nr = convert_ccl(fp, GETARG(char *), width,
706234585Sdas				    ccltab);
707234585Sdas			}
708234585Sdas			if (nr <= 0) {
709234585Sdas				if (nr < 0)
710103854Stjr					goto input_failure;
711234585Sdas				else /* nr == 0 */
712103854Stjr					goto match_failure;
7131573Srgrimes			}
7141573Srgrimes			break;
7151573Srgrimes
7161573Srgrimes		case CT_STRING:
7171573Srgrimes			/* like CCL, but zero-length string OK, & no NOSKIP */
7181573Srgrimes			if (width == 0)
71916586Sjraynard				width = (size_t)~0;
720105247Stjr			if (flags & LONG) {
721234585Sdas				nr = convert_wstring(fp, GETARG(wchar_t *),
722234799Sdas				    width, locale);
7231573Srgrimes			} else {
724234585Sdas				nr = convert_string(fp, GETARG(char *), width);
7251573Srgrimes			}
726234585Sdas			if (nr < 0)
727234585Sdas				goto input_failure;
728234585Sdas			break;
7291573Srgrimes
7301573Srgrimes		case CT_INT:
73195137Sfenner			/* scan an integer as if by the conversion function */
7321573Srgrimes#ifdef hardway
7331573Srgrimes			if (width == 0 || width > sizeof(buf) - 1)
7341573Srgrimes				width = sizeof(buf) - 1;
7351573Srgrimes#else
7361573Srgrimes			/* size_t is unsigned, hence this optimisation */
7371573Srgrimes			if (--width > sizeof(buf) - 2)
7381573Srgrimes				width = sizeof(buf) - 2;
7391573Srgrimes			width++;
7401573Srgrimes#endif
741234585Sdas			nr = parseint(fp, buf, width, base, flags);
742234585Sdas			if (nr == 0)
7431573Srgrimes				goto match_failure;
7441573Srgrimes			if ((flags & SUPPRESS) == 0) {
74595137Sfenner				uintmax_t res;
7461573Srgrimes
747234585Sdas				buf[nr] = '\0';
74895137Sfenner				if ((flags & UNSIGNED) == 0)
749227753Stheraven				    res = strtoimax_l(buf, (char **)NULL, base, locale);
75095137Sfenner				else
751227753Stheraven				    res = strtoumax_l(buf, (char **)NULL, base, locale);
7521573Srgrimes				if (flags & POINTER)
75327151Sjkh					*va_arg(ap, void **) =
75495137Sfenner							(void *)(uintptr_t)res;
75595137Sfenner				else if (flags & SHORTSHORT)
75695137Sfenner					*va_arg(ap, char *) = res;
7571573Srgrimes				else if (flags & SHORT)
7581573Srgrimes					*va_arg(ap, short *) = res;
7591573Srgrimes				else if (flags & LONG)
7601573Srgrimes					*va_arg(ap, long *) = res;
76195137Sfenner				else if (flags & LONGLONG)
76295137Sfenner					*va_arg(ap, long long *) = res;
76395137Sfenner				else if (flags & INTMAXT)
76495137Sfenner					*va_arg(ap, intmax_t *) = res;
76595137Sfenner				else if (flags & PTRDIFFT)
76695137Sfenner					*va_arg(ap, ptrdiff_t *) = res;
76795137Sfenner				else if (flags & SIZET)
76895137Sfenner					*va_arg(ap, size_t *) = res;
7691573Srgrimes				else
7701573Srgrimes					*va_arg(ap, int *) = res;
7711573Srgrimes			}
7721573Srgrimes			break;
7731573Srgrimes
774128819Sdas#ifndef NO_FLOATING_POINT
7751573Srgrimes		case CT_FLOAT:
7761573Srgrimes			/* scan a floating point number as if by strtod */
7771573Srgrimes			if (width == 0 || width > sizeof(buf) - 1)
7781573Srgrimes				width = sizeof(buf) - 1;
779234585Sdas			nr = parsefloat(fp, buf, buf + width, locale);
780234585Sdas			if (nr == 0)
781116967Sdas				goto match_failure;
7821573Srgrimes			if ((flags & SUPPRESS) == 0) {
783116967Sdas				if (flags & LONGDBL) {
784234585Sdas					long double res = strtold_l(buf, NULL,
785234585Sdas					    locale);
78631359Sbde					*va_arg(ap, long double *) = res;
787116967Sdas				} else if (flags & LONG) {
788234585Sdas					double res = strtod_l(buf, NULL,
789234585Sdas					    locale);
7901573Srgrimes					*va_arg(ap, double *) = res;
791116967Sdas				} else {
792234585Sdas					float res = strtof_l(buf, NULL, locale);
7931573Srgrimes					*va_arg(ap, float *) = res;
794116967Sdas				}
7951573Srgrimes			}
7961573Srgrimes			break;
797128819Sdas#endif /* !NO_FLOATING_POINT */
7981573Srgrimes		}
799234585Sdas		if (!(flags & SUPPRESS))
800234585Sdas			nassigned++;
801234585Sdas		nread += nr;
802234585Sdas		nconversions++;
8031573Srgrimes	}
8041573Srgrimesinput_failure:
80523352Sbde	return (nconversions != 0 ? nassigned : EOF);
8061573Srgrimesmatch_failure:
8071573Srgrimes	return (nassigned);
8081573Srgrimes}
8091573Srgrimes
8101573Srgrimes/*
8111573Srgrimes * Fill in the given table from the scanset at the given format
8121573Srgrimes * (just after `[').  Return a pointer to the character past the
8131573Srgrimes * closing `]'.  The table has a 1 wherever characters should be
8141573Srgrimes * considered part of the scanset.
8151573Srgrimes */
81695137Sfennerstatic const u_char *
817291336Sngie__sccl(char *tab, const u_char *fmt)
8181573Srgrimes{
81992889Sobrien	int c, n, v, i;
820227753Stheraven	struct xlocale_collate *table =
821227753Stheraven		(struct xlocale_collate*)__get_locale()->components[XLC_COLLATE];
8221573Srgrimes
8231573Srgrimes	/* first `clear' the whole table */
8241573Srgrimes	c = *fmt++;		/* first char hat => negated scanset */
8251573Srgrimes	if (c == '^') {
8261573Srgrimes		v = 1;		/* default => accept */
8271573Srgrimes		c = *fmt++;	/* get new first char */
8281573Srgrimes	} else
8291573Srgrimes		v = 0;		/* default => reject */
83022308Sache
83122308Sache	/* XXX: Will not work if sizeof(tab*) > sizeof(char) */
83221757Sache	(void) memset(tab, v, 256);
83322308Sache
8341573Srgrimes	if (c == 0)
8351573Srgrimes		return (fmt - 1);/* format ended before closing ] */
8361573Srgrimes
8371573Srgrimes	/*
8381573Srgrimes	 * Now set the entries corresponding to the actual scanset
8391573Srgrimes	 * to the opposite of the above.
8401573Srgrimes	 *
8411573Srgrimes	 * The first character may be ']' (or '-') without being special;
8421573Srgrimes	 * the last character may be '-'.
8431573Srgrimes	 */
8441573Srgrimes	v = 1 - v;
8451573Srgrimes	for (;;) {
8461573Srgrimes		tab[c] = v;		/* take character c */
8471573Srgrimesdoswitch:
8481573Srgrimes		n = *fmt++;		/* and examine the next */
8491573Srgrimes		switch (n) {
8501573Srgrimes
8511573Srgrimes		case 0:			/* format ended too soon */
8521573Srgrimes			return (fmt - 1);
8531573Srgrimes
8541573Srgrimes		case '-':
8551573Srgrimes			/*
8561573Srgrimes			 * A scanset of the form
8571573Srgrimes			 *	[01+-]
8581573Srgrimes			 * is defined as `the digit 0, the digit 1,
8591573Srgrimes			 * the character +, the character -', but
8601573Srgrimes			 * the effect of a scanset such as
8611573Srgrimes			 *	[a-zA-Z0-9]
8621573Srgrimes			 * is implementation defined.  The V7 Unix
8631573Srgrimes			 * scanf treats `a-z' as `the letters a through
8641573Srgrimes			 * z', but treats `a-a' as `the letter a, the
8651573Srgrimes			 * character -, and the letter a'.
8661573Srgrimes			 *
8671573Srgrimes			 * For compatibility, the `-' is not considerd
8681573Srgrimes			 * to define a range if the character following
8691573Srgrimes			 * it is either a close bracket (required by ANSI)
8701573Srgrimes			 * or is not numerically greater than the character
8711573Srgrimes			 * we just stored in the table (c).
8721573Srgrimes			 */
8731573Srgrimes			n = *fmt;
87424627Sache			if (n == ']'
875227753Stheraven			    || (table->__collate_load_error ? n < c :
876303185Sache				__collate_range_cmp(n, c) < 0
87724631Sache			       )
87824627Sache			   ) {
8791573Srgrimes				c = '-';
8801573Srgrimes				break;	/* resume the for(;;) */
8811573Srgrimes			}
8821573Srgrimes			fmt++;
88321757Sache			/* fill in the range */
884227753Stheraven			if (table->__collate_load_error) {
88524627Sache				do {
88624627Sache					tab[++c] = v;
88724627Sache				} while (c < n);
88824627Sache			} else {
88924627Sache				for (i = 0; i < 256; i ++)
890303185Sache					if (__collate_range_cmp(c, i) <= 0 &&
891303185Sache					    __collate_range_cmp(i, n) <= 0
89224627Sache					   )
89324627Sache						tab[i] = v;
89424627Sache			}
8951573Srgrimes#if 1	/* XXX another disgusting compatibility hack */
89621757Sache			c = n;
8971573Srgrimes			/*
8981573Srgrimes			 * Alas, the V7 Unix scanf also treats formats
8991573Srgrimes			 * such as [a-c-e] as `the letters a through e'.
9001573Srgrimes			 * This too is permitted by the standard....
9011573Srgrimes			 */
9021573Srgrimes			goto doswitch;
9031573Srgrimes#else
9041573Srgrimes			c = *fmt++;
9051573Srgrimes			if (c == 0)
9061573Srgrimes				return (fmt - 1);
9071573Srgrimes			if (c == ']')
9081573Srgrimes				return (fmt);
9091573Srgrimes#endif
9101573Srgrimes			break;
9111573Srgrimes
9121573Srgrimes		case ']':		/* end of scanset */
9131573Srgrimes			return (fmt);
9141573Srgrimes
9151573Srgrimes		default:		/* just another character */
9161573Srgrimes			c = n;
9171573Srgrimes			break;
9181573Srgrimes		}
9191573Srgrimes	}
9201573Srgrimes	/* NOTREACHED */
9211573Srgrimes}
922116967Sdas
923128819Sdas#ifndef NO_FLOATING_POINT
924116967Sdasstatic int
925227753Stheravenparsefloat(FILE *fp, char *buf, char *end, locale_t locale)
926116967Sdas{
927116967Sdas	char *commit, *p;
928187422Sdas	int infnanpos = 0, decptpos = 0;
929116967Sdas	enum {
930187422Sdas		S_START, S_GOTSIGN, S_INF, S_NAN, S_DONE, S_MAYBEHEX,
931187422Sdas		S_DIGITS, S_DECPT, S_FRAC, S_EXP, S_EXPDIGITS
932116967Sdas	} state = S_START;
933116967Sdas	unsigned char c;
934227753Stheraven	const char *decpt = localeconv_l(locale)->decimal_point;
935116967Sdas	_Bool gotmantdig = 0, ishex = 0;
936116967Sdas
937116967Sdas	/*
938116967Sdas	 * We set commit = p whenever the string we have read so far
939116967Sdas	 * constitutes a valid representation of a floating point
940116967Sdas	 * number by itself.  At some point, the parse will complete
941116967Sdas	 * or fail, and we will ungetc() back to the last commit point.
942116967Sdas	 * To ensure that the file offset gets updated properly, it is
943116967Sdas	 * always necessary to read at least one character that doesn't
944116967Sdas	 * match; thus, we can't short-circuit "infinity" or "nan(...)".
945116967Sdas	 */
946116967Sdas	commit = buf - 1;
947116967Sdas	for (p = buf; p < end; ) {
948116967Sdas		c = *fp->_p;
949116967Sdasreswitch:
950116967Sdas		switch (state) {
951116967Sdas		case S_START:
952116967Sdas			state = S_GOTSIGN;
953116967Sdas			if (c == '-' || c == '+')
954116967Sdas				break;
955116967Sdas			else
956116967Sdas				goto reswitch;
957116967Sdas		case S_GOTSIGN:
958116967Sdas			switch (c) {
959116967Sdas			case '0':
960116967Sdas				state = S_MAYBEHEX;
961116967Sdas				commit = p;
962116967Sdas				break;
963116967Sdas			case 'I':
964116967Sdas			case 'i':
965116967Sdas				state = S_INF;
966116967Sdas				break;
967116967Sdas			case 'N':
968116967Sdas			case 'n':
969116967Sdas				state = S_NAN;
970116967Sdas				break;
971116967Sdas			default:
972116967Sdas				state = S_DIGITS;
973116967Sdas				goto reswitch;
974116967Sdas			}
975116967Sdas			break;
976116967Sdas		case S_INF:
977116967Sdas			if (infnanpos > 6 ||
978116967Sdas			    (c != "nfinity"[infnanpos] &&
979116967Sdas			     c != "NFINITY"[infnanpos]))
980116967Sdas				goto parsedone;
981116967Sdas			if (infnanpos == 1 || infnanpos == 6)
982116967Sdas				commit = p;	/* inf or infinity */
983116967Sdas			infnanpos++;
984116967Sdas			break;
985116967Sdas		case S_NAN:
986116967Sdas			switch (infnanpos) {
987116967Sdas			case 0:
988116967Sdas				if (c != 'A' && c != 'a')
989116967Sdas					goto parsedone;
990116967Sdas				break;
991116967Sdas			case 1:
992116967Sdas				if (c != 'N' && c != 'n')
993116967Sdas					goto parsedone;
994116967Sdas				else
995116967Sdas					commit = p;
996116967Sdas				break;
997116967Sdas			case 2:
998116967Sdas				if (c != '(')
999116967Sdas					goto parsedone;
1000116967Sdas				break;
1001116967Sdas			default:
1002116967Sdas				if (c == ')') {
1003116967Sdas					commit = p;
1004187422Sdas					state = S_DONE;
1005116967Sdas				} else if (!isalnum(c) && c != '_')
1006116967Sdas					goto parsedone;
1007116967Sdas				break;
1008116967Sdas			}
1009116967Sdas			infnanpos++;
1010116967Sdas			break;
1011187422Sdas		case S_DONE:
1012187422Sdas			goto parsedone;
1013116967Sdas		case S_MAYBEHEX:
1014116967Sdas			state = S_DIGITS;
1015116967Sdas			if (c == 'X' || c == 'x') {
1016116967Sdas				ishex = 1;
1017116967Sdas				break;
1018116967Sdas			} else {	/* we saw a '0', but no 'x' */
1019116967Sdas				gotmantdig = 1;
1020116967Sdas				goto reswitch;
1021116967Sdas			}
1022116967Sdas		case S_DIGITS:
1023187422Sdas			if ((ishex && isxdigit(c)) || isdigit(c)) {
1024116967Sdas				gotmantdig = 1;
1025187422Sdas				commit = p;
1026187422Sdas				break;
1027187422Sdas			} else {
1028187422Sdas				state = S_DECPT;
1029187422Sdas				goto reswitch;
1030187422Sdas			}
1031187422Sdas		case S_DECPT:
1032187422Sdas			if (c == decpt[decptpos]) {
1033187422Sdas				if (decpt[++decptpos] == '\0') {
1034187422Sdas					/* We read the complete decpt seq. */
1035187422Sdas					state = S_FRAC;
1036187422Sdas					if (gotmantdig)
1037187422Sdas						commit = p;
1038187422Sdas				}
1039187422Sdas				break;
1040187422Sdas			} else if (!decptpos) {
1041187422Sdas				/* We didn't read any decpt characters. */
1042116967Sdas				state = S_FRAC;
1043187422Sdas				goto reswitch;
1044187422Sdas			} else {
1045187422Sdas				/*
1046187422Sdas				 * We read part of a multibyte decimal point,
1047187422Sdas				 * but the rest is invalid, so bail.
1048187422Sdas				 */
1049187422Sdas				goto parsedone;
1050116967Sdas			}
1051116967Sdas		case S_FRAC:
1052124175Snectar			if (((c == 'E' || c == 'e') && !ishex) ||
1053124175Snectar			    ((c == 'P' || c == 'p') && ishex)) {
1054116967Sdas				if (!gotmantdig)
1055116967Sdas					goto parsedone;
1056116967Sdas				else
1057116967Sdas					state = S_EXP;
1058124175Snectar			} else if ((ishex && isxdigit(c)) || isdigit(c)) {
1059116967Sdas				commit = p;
1060116967Sdas				gotmantdig = 1;
1061116967Sdas			} else
1062116967Sdas				goto parsedone;
1063116967Sdas			break;
1064116967Sdas		case S_EXP:
1065116967Sdas			state = S_EXPDIGITS;
1066116967Sdas			if (c == '-' || c == '+')
1067116967Sdas				break;
1068116967Sdas			else
1069116967Sdas				goto reswitch;
1070116967Sdas		case S_EXPDIGITS:
1071116967Sdas			if (isdigit(c))
1072116967Sdas				commit = p;
1073116967Sdas			else
1074116967Sdas				goto parsedone;
1075116967Sdas			break;
1076116967Sdas		default:
1077116967Sdas			abort();
1078116967Sdas		}
1079116967Sdas		*p++ = c;
1080116967Sdas		if (--fp->_r > 0)
1081116967Sdas			fp->_p++;
1082116967Sdas		else if (__srefill(fp))
1083116967Sdas			break;	/* EOF */
1084116967Sdas	}
1085116967Sdas
1086116967Sdasparsedone:
1087116967Sdas	while (commit < --p)
1088116967Sdas		__ungetc(*(u_char *)p, fp);
1089116967Sdas	*++commit = '\0';
1090116967Sdas	return (commit - buf);
1091116967Sdas}
1092116967Sdas#endif
1093