1/*	$OpenBSD: vfscanf.c,v 1.31 2014/03/19 05:17:01 guenther Exp $ */
2/*-
3 * Copyright (c) 1990, 1993
4 *	The Regents of the University of California.  All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * Chris Torek.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include <ctype.h>
35#include <wctype.h>
36#include <inttypes.h>
37#include <stdarg.h>
38#include <stddef.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include "local.h"
43
44#ifdef FLOATING_POINT
45#include "floatio.h"
46#endif
47
48#define	BUF		513	/* Maximum length of numeric string. */
49
50/*
51 * Flags used during conversion.
52 */
53#define	LONG		0x00001	/* l: long or double */
54#define	LONGDBL		0x00002	/* L: long double */
55#define	SHORT		0x00004	/* h: short */
56#define	SHORTSHORT	0x00008	/* hh: 8 bit integer */
57#define LLONG		0x00010	/* ll: long long (+ deprecated q: quad) */
58#define	POINTER		0x00020	/* p: void * (as hex) */
59#define	SIZEINT		0x00040	/* z: (signed) size_t */
60#define	MAXINT		0x00080	/* j: intmax_t */
61#define	PTRINT		0x00100	/* t: ptrdiff_t */
62#define	NOSKIP		0x00200	/* [ or c: do not skip blanks */
63#define	SUPPRESS	0x00400	/* *: suppress assignment */
64#define	UNSIGNED	0x00800	/* %[oupxX] conversions */
65
66/*
67 * The following are used in numeric conversions only:
68 * SIGNOK, HAVESIGN, NDIGITS, DPTOK, and EXPOK are for floating point;
69 * SIGNOK, HAVESIGN, NDIGITS, PFXOK, and NZDIGITS are for integral.
70 */
71#define	SIGNOK		0x01000	/* +/- is (still) legal */
72#define	HAVESIGN	0x02000	/* sign detected */
73#define	NDIGITS		0x04000	/* no digits detected */
74
75#define	DPTOK		0x08000	/* (float) decimal point is still legal */
76#define	EXPOK		0x10000	/* (float) exponent (e+3, etc) still legal */
77
78#define	PFXOK		0x08000	/* 0x prefix is (still) legal */
79#define	NZDIGITS	0x10000	/* no zero digits detected */
80
81/*
82 * Conversion types.
83 */
84#define	CT_CHAR		0	/* %c conversion */
85#define	CT_CCL		1	/* %[...] conversion */
86#define	CT_STRING	2	/* %s conversion */
87#define	CT_INT		3	/* integer, i.e., strtoimax or strtoumax */
88#define	CT_FLOAT	4	/* floating, i.e., strtod */
89
90#define u_char unsigned char
91#define u_long unsigned long
92
93static u_char *__sccl(char *, u_char *);
94
95/*
96 * Internal, unlocked version of vfscanf
97 */
98int
99__svfscanf(FILE *fp, const char *fmt0, __va_list ap)
100{
101	u_char *fmt = (u_char *)fmt0;
102	int c;		/* character from format, or conversion */
103	size_t width;	/* field width, or 0 */
104	char *p;	/* points into all kinds of strings */
105	int n;		/* handy integer */
106	int flags;	/* flags as defined above */
107	char *p0;	/* saves original value of p when necessary */
108	int nassigned;		/* number of fields assigned */
109	int nread;		/* number of characters consumed from fp */
110	int base;		/* base argument to strtoimax/strtouimax */
111	char ccltab[256];	/* character class table for %[...] */
112	char buf[BUF];		/* buffer for numeric conversions */
113#ifdef SCANF_WIDE_CHAR
114	wchar_t *wcp;		/* handy wide character pointer */
115	size_t nconv;		/* length of multibyte sequence converted */
116	mbstate_t mbs;
117#endif
118
119	/* `basefix' is used to avoid `if' tests in the integer scanner */
120	static short basefix[17] =
121		{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
122
123	_SET_ORIENTATION(fp, -1);
124
125	nassigned = 0;
126	nread = 0;
127	base = 0;		/* XXX just to keep gcc happy */
128	for (;;) {
129		c = *fmt++;
130		if (c == 0)
131			return (nassigned);
132		if (isspace(c)) {
133			while ((fp->_r > 0 || __srefill(fp) == 0) &&
134			    isspace(*fp->_p))
135				nread++, fp->_r--, fp->_p++;
136			continue;
137		}
138		if (c != '%')
139			goto literal;
140		width = 0;
141		flags = 0;
142		/*
143		 * switch on the format.  continue if done;
144		 * break once format type is derived.
145		 */
146again:		c = *fmt++;
147		switch (c) {
148		case '%':
149literal:
150			if (fp->_r <= 0 && __srefill(fp))
151				goto input_failure;
152			if (*fp->_p != c)
153				goto match_failure;
154			fp->_r--, fp->_p++;
155			nread++;
156			continue;
157
158		case '*':
159			flags |= SUPPRESS;
160			goto again;
161		case 'j':
162			flags |= MAXINT;
163			goto again;
164		case 'L':
165			flags |= LONGDBL;
166			goto again;
167		case 'h':
168			if (*fmt == 'h') {
169				fmt++;
170				flags |= SHORTSHORT;
171			} else {
172				flags |= SHORT;
173			}
174			goto again;
175		case 'l':
176			if (*fmt == 'l') {
177				fmt++;
178				flags |= LLONG;
179			} else {
180				flags |= LONG;
181			}
182			goto again;
183		case 'q':
184			flags |= LLONG;		/* deprecated */
185			goto again;
186		case 't':
187			flags |= PTRINT;
188			goto again;
189		case 'z':
190			flags |= SIZEINT;
191			goto again;
192
193		case '0': case '1': case '2': case '3': case '4':
194		case '5': case '6': case '7': case '8': case '9':
195			width = width * 10 + c - '0';
196			goto again;
197
198		/*
199		 * Conversions.
200		 * Those marked `compat' are for 4.[123]BSD compatibility.
201		 *
202		 * (According to ANSI, E and X formats are supposed
203		 * to the same as e and x.  Sorry about that.)
204		 */
205		case 'D':	/* compat */
206			flags |= LONG;
207			/* FALLTHROUGH */
208		case 'd':
209			c = CT_INT;
210			base = 10;
211			break;
212
213		case 'i':
214			c = CT_INT;
215			base = 0;
216			break;
217
218		case 'O':	/* compat */
219			flags |= LONG;
220			/* FALLTHROUGH */
221		case 'o':
222			c = CT_INT;
223			flags |= UNSIGNED;
224			base = 8;
225			break;
226
227		case 'u':
228			c = CT_INT;
229			flags |= UNSIGNED;
230			base = 10;
231			break;
232
233		case 'X':
234		case 'x':
235			flags |= PFXOK;	/* enable 0x prefixing */
236			c = CT_INT;
237			flags |= UNSIGNED;
238			base = 16;
239			break;
240
241#ifdef FLOATING_POINT
242		case 'e': case 'E':
243		case 'f': case 'F':
244		case 'g': case 'G':
245		case 'a': case 'A':
246			c = CT_FLOAT;
247			break;
248#endif
249
250		case 's':
251			c = CT_STRING;
252			break;
253
254		case '[':
255			fmt = __sccl(ccltab, fmt);
256			flags |= NOSKIP;
257			c = CT_CCL;
258			break;
259
260		case 'c':
261			flags |= NOSKIP;
262			c = CT_CHAR;
263			break;
264
265		case 'p':	/* pointer format is like hex */
266			flags |= POINTER | PFXOK;
267			c = CT_INT;
268			flags |= UNSIGNED;
269			base = 16;
270			break;
271
272		case 'n':
273			if (flags & SUPPRESS)
274				continue;
275			if (flags & SHORTSHORT)
276				*va_arg(ap, signed char *) = nread;
277			else if (flags & SHORT)
278				*va_arg(ap, short *) = nread;
279			else if (flags & LONG)
280				*va_arg(ap, long *) = nread;
281			else if (flags & SIZEINT)
282				*va_arg(ap, ssize_t *) = nread;
283			else if (flags & PTRINT)
284				*va_arg(ap, ptrdiff_t *) = nread;
285			else if (flags & LLONG)
286				*va_arg(ap, long long *) = nread;
287			else if (flags & MAXINT)
288				*va_arg(ap, intmax_t *) = nread;
289			else
290				*va_arg(ap, int *) = nread;
291			continue;
292
293		/*
294		 * Disgusting backwards compatibility hacks.	XXX
295		 */
296		case '\0':	/* compat */
297			return (EOF);
298
299		default:	/* compat */
300			if (isupper(c))
301				flags |= LONG;
302			c = CT_INT;
303			base = 10;
304			break;
305		}
306
307		/*
308		 * We have a conversion that requires input.
309		 */
310		if (fp->_r <= 0 && __srefill(fp))
311			goto input_failure;
312
313		/*
314		 * Consume leading white space, except for formats
315		 * that suppress this.
316		 */
317		if ((flags & NOSKIP) == 0) {
318			while (isspace(*fp->_p)) {
319				nread++;
320				if (--fp->_r > 0)
321					fp->_p++;
322				else if (__srefill(fp))
323					goto input_failure;
324			}
325			/*
326			 * Note that there is at least one character in
327			 * the buffer, so conversions that do not set NOSKIP
328			 * ca no longer result in an input failure.
329			 */
330		}
331
332		/*
333		 * Do the conversion.
334		 */
335		switch (c) {
336
337		case CT_CHAR:
338			/* scan arbitrary characters (sets NOSKIP) */
339			if (width == 0)
340				width = 1;
341#ifdef SCANF_WIDE_CHAR
342			if (flags & LONG) {
343				if ((flags & SUPPRESS) == 0)
344					wcp = va_arg(ap, wchar_t *);
345				else
346					wcp = NULL;
347				n = 0;
348				while (width != 0) {
349					if (n == MB_CUR_MAX) {
350						fp->_flags |= __SERR;
351						goto input_failure;
352					}
353					buf[n++] = *fp->_p;
354					fp->_p++;
355					fp->_r--;
356					bzero(&mbs, sizeof(mbs));
357					nconv = mbrtowc(wcp, buf, n, &mbs);
358					if (nconv == (size_t)-1) {
359						fp->_flags |= __SERR;
360						goto input_failure;
361					}
362					if (nconv == 0 && !(flags & SUPPRESS))
363						*wcp = L'\0';
364					if (nconv != (size_t)-2) {
365						nread += n;
366						width--;
367						if (!(flags & SUPPRESS))
368							wcp++;
369						n = 0;
370					}
371					if (fp->_r <= 0 && __srefill(fp)) {
372						if (n != 0) {
373							fp->_flags |= __SERR;
374							goto input_failure;
375						}
376						break;
377					}
378				}
379				if (!(flags & SUPPRESS))
380					nassigned++;
381			} else
382#endif /* SCANF_WIDE_CHAR */
383			if (flags & SUPPRESS) {
384				size_t sum = 0;
385				for (;;) {
386					if ((n = fp->_r) < width) {
387						sum += n;
388						width -= n;
389						fp->_p += n;
390						if (__srefill(fp)) {
391							if (sum == 0)
392							    goto input_failure;
393							break;
394						}
395					} else {
396						sum += width;
397						fp->_r -= width;
398						fp->_p += width;
399						break;
400					}
401				}
402				nread += sum;
403			} else {
404				size_t r = fread((void *)va_arg(ap, char *), 1,
405				    width, fp);
406
407				if (r == 0)
408					goto input_failure;
409				nread += r;
410				nassigned++;
411			}
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#ifdef SCANF_WIDE_CHAR
419			/* take only those things in the class */
420			if (flags & LONG) {
421				wchar_t twc;
422				int nchars;
423
424				if ((flags & SUPPRESS) == 0)
425					wcp = va_arg(ap, wchar_t *);
426				else
427					wcp = &twc;
428				n = 0;
429				nchars = 0;
430				while (width != 0) {
431					if (n == MB_CUR_MAX) {
432						fp->_flags |= __SERR;
433						goto input_failure;
434					}
435					buf[n++] = *fp->_p;
436					fp->_p++;
437					fp->_r--;
438					bzero(&mbs, sizeof(mbs));
439					nconv = mbrtowc(wcp, buf, n, &mbs);
440					if (nconv == (size_t)-1) {
441						fp->_flags |= __SERR;
442						goto input_failure;
443					}
444					if (nconv == 0)
445						*wcp = L'\0';
446					if (nconv != (size_t)-2) {
447						if (wctob(*wcp) != EOF &&
448						    !ccltab[wctob(*wcp)]) {
449							while (n != 0) {
450								n--;
451								ungetc(buf[n],
452								    fp);
453							}
454							break;
455						}
456						nread += n;
457						width--;
458						if (!(flags & SUPPRESS))
459							wcp++;
460						nchars++;
461						n = 0;
462					}
463					if (fp->_r <= 0 && __srefill(fp)) {
464						if (n != 0) {
465							fp->_flags |= __SERR;
466							goto input_failure;
467						}
468						break;
469					}
470				}
471				if (n != 0) {
472					fp->_flags |= __SERR;
473					goto input_failure;
474				}
475				n = nchars;
476				if (n == 0)
477					goto match_failure;
478				if (!(flags & SUPPRESS)) {
479					*wcp = L'\0';
480					nassigned++;
481				}
482			} else
483#endif /* SCANF_WIDE_CHAR */
484			/* take only those things in the class */
485			if (flags & SUPPRESS) {
486				n = 0;
487				while (ccltab[*fp->_p]) {
488					n++, fp->_r--, fp->_p++;
489					if (--width == 0)
490						break;
491					if (fp->_r <= 0 && __srefill(fp)) {
492						if (n == 0)
493							goto input_failure;
494						break;
495					}
496				}
497				if (n == 0)
498					goto match_failure;
499			} else {
500				p0 = p = va_arg(ap, char *);
501				while (ccltab[*fp->_p]) {
502					fp->_r--;
503					*p++ = *fp->_p++;
504					if (--width == 0)
505						break;
506					if (fp->_r <= 0 && __srefill(fp)) {
507						if (p == p0)
508							goto input_failure;
509						break;
510					}
511				}
512				n = p - p0;
513				if (n == 0)
514					goto match_failure;
515				*p = '\0';
516				nassigned++;
517			}
518			nread += n;
519			break;
520
521		case CT_STRING:
522			/* like CCL, but zero-length string OK, & no NOSKIP */
523			if (width == 0)
524				width = (size_t)~0;
525#ifdef SCANF_WIDE_CHAR
526			if (flags & LONG) {
527				wchar_t twc;
528
529				if ((flags & SUPPRESS) == 0)
530					wcp = va_arg(ap, wchar_t *);
531				else
532					wcp = &twc;
533				n = 0;
534				while (!isspace(*fp->_p) && width != 0) {
535					if (n == MB_CUR_MAX) {
536						fp->_flags |= __SERR;
537						goto input_failure;
538					}
539					buf[n++] = *fp->_p;
540					fp->_p++;
541					fp->_r--;
542					bzero(&mbs, sizeof(mbs));
543					nconv = mbrtowc(wcp, buf, n, &mbs);
544					if (nconv == (size_t)-1) {
545						fp->_flags |= __SERR;
546						goto input_failure;
547					}
548					if (nconv == 0)
549						*wcp = L'\0';
550					if (nconv != (size_t)-2) {
551						if (iswspace(*wcp)) {
552							while (n != 0) {
553								n--;
554								ungetc(buf[n],
555								    fp);
556							}
557							break;
558						}
559						nread += n;
560						width--;
561						if (!(flags & SUPPRESS))
562							wcp++;
563						n = 0;
564					}
565					if (fp->_r <= 0 && __srefill(fp)) {
566						if (n != 0) {
567							fp->_flags |= __SERR;
568							goto input_failure;
569						}
570						break;
571					}
572				}
573				if (!(flags & SUPPRESS)) {
574					*wcp = L'\0';
575					nassigned++;
576				}
577			} else
578#endif /* SCANF_WIDE_CHAR */
579			if (flags & SUPPRESS) {
580				n = 0;
581				while (!isspace(*fp->_p)) {
582					n++, fp->_r--, fp->_p++;
583					if (--width == 0)
584						break;
585					if (fp->_r <= 0 && __srefill(fp))
586						break;
587				}
588				nread += n;
589			} else {
590				p0 = p = va_arg(ap, char *);
591				while (!isspace(*fp->_p)) {
592					fp->_r--;
593					*p++ = *fp->_p++;
594					if (--width == 0)
595						break;
596					if (fp->_r <= 0 && __srefill(fp))
597						break;
598				}
599				*p = '\0';
600				nread += p - p0;
601				nassigned++;
602			}
603			continue;
604
605		case CT_INT:
606			/* scan an integer as if by strtoimax/strtoumax */
607#ifdef hardway
608			if (width == 0 || width > sizeof(buf) - 1)
609				width = sizeof(buf) - 1;
610#else
611			/* size_t is unsigned, hence this optimisation */
612			if (--width > sizeof(buf) - 2)
613				width = sizeof(buf) - 2;
614			width++;
615#endif
616			flags |= SIGNOK | NDIGITS | NZDIGITS;
617			for (p = buf; width; width--) {
618				c = *fp->_p;
619				/*
620				 * Switch on the character; `goto ok'
621				 * if we accept it as a part of number.
622				 */
623				switch (c) {
624
625				/*
626				 * The digit 0 is always legal, but is
627				 * special.  For %i conversions, if no
628				 * digits (zero or nonzero) have been
629				 * scanned (only signs), we will have
630				 * base==0.  In that case, we should set
631				 * it to 8 and enable 0x prefixing.
632				 * Also, if we have not scanned zero digits
633				 * before this, do not turn off prefixing
634				 * (someone else will turn it off if we
635				 * have scanned any nonzero digits).
636				 */
637				case '0':
638					if (base == 0) {
639						base = 8;
640						flags |= PFXOK;
641					}
642					if (flags & NZDIGITS)
643					    flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
644					else
645					    flags &= ~(SIGNOK|PFXOK|NDIGITS);
646					goto ok;
647
648				/* 1 through 7 always legal */
649				case '1': case '2': case '3':
650				case '4': case '5': case '6': case '7':
651					base = basefix[base];
652					flags &= ~(SIGNOK | PFXOK | NDIGITS);
653					goto ok;
654
655				/* digits 8 and 9 ok iff decimal or hex */
656				case '8': case '9':
657					base = basefix[base];
658					if (base <= 8)
659						break;	/* not legal here */
660					flags &= ~(SIGNOK | PFXOK | NDIGITS);
661					goto ok;
662
663				/* letters ok iff hex */
664				case 'A': case 'B': case 'C':
665				case 'D': case 'E': case 'F':
666				case 'a': case 'b': case 'c':
667				case 'd': case 'e': case 'f':
668					/* no need to fix base here */
669					if (base <= 10)
670						break;	/* not legal here */
671					flags &= ~(SIGNOK | PFXOK | NDIGITS);
672					goto ok;
673
674				/* sign ok only as first character */
675				case '+': case '-':
676					if (flags & SIGNOK) {
677						flags &= ~SIGNOK;
678						flags |= HAVESIGN;
679						goto ok;
680					}
681					break;
682
683				/*
684				 * x ok iff flag still set and 2nd char (or
685				 * 3rd char if we have a sign).
686				 */
687				case 'x': case 'X':
688					if ((flags & PFXOK) && p ==
689					    buf + 1 + !!(flags & HAVESIGN)) {
690						base = 16;	/* if %i */
691						flags &= ~PFXOK;
692						goto ok;
693					}
694					break;
695				}
696
697				/*
698				 * If we got here, c is not a legal character
699				 * for a number.  Stop accumulating digits.
700				 */
701				break;
702		ok:
703				/*
704				 * c is legal: store it and look at the next.
705				 */
706				*p++ = c;
707				if (--fp->_r > 0)
708					fp->_p++;
709				else if (__srefill(fp))
710					break;		/* EOF */
711			}
712			/*
713			 * If we had only a sign, it is no good; push
714			 * back the sign.  If the number ends in `x',
715			 * it was [sign] '0' 'x', so push back the x
716			 * and treat it as [sign] '0'.
717			 */
718			if (flags & NDIGITS) {
719				if (p > buf)
720					(void) ungetc(*(u_char *)--p, fp);
721				goto match_failure;
722			}
723			c = ((u_char *)p)[-1];
724			if (c == 'x' || c == 'X') {
725				--p;
726				(void) ungetc(c, fp);
727			}
728			if ((flags & SUPPRESS) == 0) {
729				uintmax_t res;
730
731				*p = '\0';
732				if (flags & UNSIGNED)
733					res = strtoumax(buf, NULL, base);
734				else
735					res = strtoimax(buf, NULL, base);
736				if (flags & POINTER)
737					*va_arg(ap, void **) =
738					    (void *)(uintptr_t)res;
739				else if (flags & MAXINT)
740					*va_arg(ap, intmax_t *) = res;
741				else if (flags & LLONG)
742					*va_arg(ap, long long *) = res;
743				else if (flags & SIZEINT)
744					*va_arg(ap, ssize_t *) = res;
745				else if (flags & PTRINT)
746					*va_arg(ap, ptrdiff_t *) = res;
747				else if (flags & LONG)
748					*va_arg(ap, long *) = res;
749				else if (flags & SHORT)
750					*va_arg(ap, short *) = res;
751				else if (flags & SHORTSHORT)
752					*va_arg(ap, signed char *) = res;
753				else
754					*va_arg(ap, int *) = res;
755				nassigned++;
756			}
757			nread += p - buf;
758			break;
759
760#ifdef FLOATING_POINT
761		case CT_FLOAT:
762			/* scan a floating point number as if by strtod */
763#ifdef hardway
764			if (width == 0 || width > sizeof(buf) - 1)
765				width = sizeof(buf) - 1;
766#else
767			/* size_t is unsigned, hence this optimisation */
768			if (--width > sizeof(buf) - 2)
769				width = sizeof(buf) - 2;
770			width++;
771#endif
772			flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
773			for (p = buf; width; width--) {
774				c = *fp->_p;
775				/*
776				 * This code mimicks the integer conversion
777				 * code, but is much simpler.
778				 */
779				switch (c) {
780
781				case '0': case '1': case '2': case '3':
782				case '4': case '5': case '6': case '7':
783				case '8': case '9':
784					flags &= ~(SIGNOK | NDIGITS);
785					goto fok;
786
787				case '+': case '-':
788					if (flags & SIGNOK) {
789						flags &= ~SIGNOK;
790						goto fok;
791					}
792					break;
793				case '.':
794					if (flags & DPTOK) {
795						flags &= ~(SIGNOK | DPTOK);
796						goto fok;
797					}
798					break;
799				case 'e': case 'E':
800					/* no exponent without some digits */
801					if ((flags&(NDIGITS|EXPOK)) == EXPOK) {
802						flags =
803						    (flags & ~(EXPOK|DPTOK)) |
804						    SIGNOK | NDIGITS;
805						goto fok;
806					}
807					break;
808				}
809				break;
810		fok:
811				*p++ = c;
812				if (--fp->_r > 0)
813					fp->_p++;
814				else if (__srefill(fp))
815					break;	/* EOF */
816			}
817			/*
818			 * If no digits, might be missing exponent digits
819			 * (just give back the exponent) or might be missing
820			 * regular digits, but had sign and/or decimal point.
821			 */
822			if (flags & NDIGITS) {
823				if (flags & EXPOK) {
824					/* no digits at all */
825					while (p > buf)
826						ungetc(*(u_char *)--p, fp);
827					goto match_failure;
828				}
829				/* just a bad exponent (e and maybe sign) */
830				c = *(u_char *)--p;
831				if (c != 'e' && c != 'E') {
832					(void) ungetc(c, fp);/* sign */
833					c = *(u_char *)--p;
834				}
835				(void) ungetc(c, fp);
836			}
837			if ((flags & SUPPRESS) == 0) {
838				*p = '\0';
839				if (flags & LONGDBL) {
840					long double res = strtold(buf,
841					    (char **)NULL);
842					*va_arg(ap, long double *) = res;
843				} else if (flags & LONG) {
844					double res = strtod(buf, (char **)NULL);
845					*va_arg(ap, double *) = res;
846				} else {
847					float res = strtof(buf, (char **)NULL);
848					*va_arg(ap, float *) = res;
849				}
850				nassigned++;
851			}
852			nread += p - buf;
853			break;
854#endif /* FLOATING_POINT */
855		}
856	}
857input_failure:
858	if (nassigned == 0)
859		nassigned = -1;
860match_failure:
861	return (nassigned);
862}
863
864/*
865 * Fill in the given table from the scanset at the given format
866 * (just after `[').  Return a pointer to the character past the
867 * closing `]'.  The table has a 1 wherever characters should be
868 * considered part of the scanset.
869 */
870static u_char *
871__sccl(char *tab, u_char *fmt)
872{
873	int c, n, v;
874
875	/* first `clear' the whole table */
876	c = *fmt++;		/* first char hat => negated scanset */
877	if (c == '^') {
878		v = 1;		/* default => accept */
879		c = *fmt++;	/* get new first char */
880	} else
881		v = 0;		/* default => reject */
882	/* should probably use memset here */
883	for (n = 0; n < 256; n++)
884		tab[n] = v;
885	if (c == 0)
886		return (fmt - 1);/* format ended before closing ] */
887
888	/*
889	 * Now set the entries corresponding to the actual scanset
890	 * to the opposite of the above.
891	 *
892	 * The first character may be ']' (or '-') without being special;
893	 * the last character may be '-'.
894	 */
895	v = 1 - v;
896	for (;;) {
897		tab[c] = v;		/* take character c */
898doswitch:
899		n = *fmt++;		/* and examine the next */
900		switch (n) {
901
902		case 0:			/* format ended too soon */
903			return (fmt - 1);
904
905		case '-':
906			/*
907			 * A scanset of the form
908			 *	[01+-]
909			 * is defined as `the digit 0, the digit 1,
910			 * the character +, the character -', but
911			 * the effect of a scanset such as
912			 *	[a-zA-Z0-9]
913			 * is implementation defined.  The V7 Unix
914			 * scanf treats `a-z' as `the letters a through
915			 * z', but treats `a-a' as `the letter a, the
916			 * character -, and the letter a'.
917			 *
918			 * For compatibility, the `-' is not considerd
919			 * to define a range if the character following
920			 * it is either a close bracket (required by ANSI)
921			 * or is not numerically greater than the character
922			 * we just stored in the table (c).
923			 */
924			n = *fmt;
925			if (n == ']' || n < c) {
926				c = '-';
927				break;	/* resume the for(;;) */
928			}
929			fmt++;
930			do {		/* fill in the range */
931				tab[++c] = v;
932			} while (c < n);
933#if 1	/* XXX another disgusting compatibility hack */
934			/*
935			 * Alas, the V7 Unix scanf also treats formats
936			 * such as [a-c-e] as `the letters a through e'.
937			 * This too is permitted by the standard....
938			 */
939			goto doswitch;
940#else
941			c = *fmt++;
942			if (c == 0)
943				return (fmt - 1);
944			if (c == ']')
945				return (fmt);
946#endif
947			break;
948
949		case ']':		/* end of scanset */
950			return (fmt);
951
952		default:		/* just another character */
953			c = n;
954			break;
955		}
956	}
957	/* NOTREACHED */
958}
959
960int
961vfscanf(FILE *fp, const char *fmt0, __va_list ap)
962{
963	int r;
964
965	FLOCKFILE(fp);
966	r = __svfscanf(fp, fmt0, ap);
967	FUNLOCKFILE(fp);
968	return (r);
969}
970