xref: /csrg-svn/lib/libc/stdio/vfscanf.c (revision 46077)
1*46077Sbostic /*-
2*46077Sbostic  * Copyright (c) 1990 The Regents of the University of California.
3*46077Sbostic  * All rights reserved.
4*46077Sbostic  *
5*46077Sbostic  * This code is derived from software contributed to Berkeley by
6*46077Sbostic  * Chris Torek.
7*46077Sbostic  *
8*46077Sbostic  * %sccs.include.redist.c%
9*46077Sbostic  */
10*46077Sbostic 
1126640Sdonn #if defined(LIBC_SCCS) && !defined(lint)
12*46077Sbostic static char sccsid[] = "@(#)vfscanf.c	5.3 (Berkeley) 01/20/91";
13*46077Sbostic #endif /* LIBC_SCCS and not lint */
149415Smckusick 
15*46077Sbostic #include <sys/stdc.h>
169415Smckusick #include <stdio.h>
17*46077Sbostic #include <ctype.h>
18*46077Sbostic #include <stdlib.h>
19*46077Sbostic #if __STDC__
20*46077Sbostic #include <stdarg.h>
21*46077Sbostic #else
22*46077Sbostic #include <varargs.h>
23*46077Sbostic #endif
24*46077Sbostic #include "local.h"
259415Smckusick 
26*46077Sbostic #define FLOATING_POINT
279415Smckusick 
28*46077Sbostic #ifdef FLOATING_POINT
29*46077Sbostic #include "floatio.h"
30*46077Sbostic #define	BUF	(MAXEXP+MAXFRACT+3)	/* 3 = sign + decimal point + NUL */
31*46077Sbostic #else
32*46077Sbostic #define	BUF	40
33*46077Sbostic #endif
349415Smckusick 
35*46077Sbostic /*
36*46077Sbostic  * Flags used during conversion.
37*46077Sbostic  */
38*46077Sbostic #define	LONG		0x01	/* l: long or double */
39*46077Sbostic #define	LONGDBL		0x02	/* L: long double; unimplemented */
40*46077Sbostic #define	SHORT		0x04	/* h: short */
41*46077Sbostic #define	SUPPRESS	0x08	/* suppress assignment */
42*46077Sbostic #define	POINTER		0x10	/* weird %p pointer (`fake hex') */
43*46077Sbostic #define	NOSKIP		0x20	/* do not skip blanks */
449415Smckusick 
45*46077Sbostic /*
46*46077Sbostic  * The following are used in numeric conversions only:
47*46077Sbostic  * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point;
48*46077Sbostic  * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral.
49*46077Sbostic  */
50*46077Sbostic #define	SIGNOK		0x40	/* +/- is (still) legal */
51*46077Sbostic #define	NDIGITS		0x80	/* no digits detected */
529415Smckusick 
53*46077Sbostic #define	DPTOK		0x100	/* (float) decimal point is still legal */
54*46077Sbostic #define	EXPOK		0x200	/* (float) exponent (e+3, etc) still legal */
55*46077Sbostic 
56*46077Sbostic #define	PFXOK		0x100	/* 0x prefix is (still) legal */
57*46077Sbostic #define	NZDIGITS	0x200	/* no zero digits detected */
58*46077Sbostic 
59*46077Sbostic /*
60*46077Sbostic  * Conversion types.
61*46077Sbostic  */
62*46077Sbostic #define	CT_CHAR		0	/* %c conversion */
63*46077Sbostic #define	CT_CCL		1	/* %[...] conversion */
64*46077Sbostic #define	CT_STRING	2	/* %s conversion */
65*46077Sbostic #define	CT_INT		3	/* integer, i.e., strtol or strtoul */
66*46077Sbostic #define	CT_FLOAT	4	/* floating, i.e., strtod */
67*46077Sbostic 
68*46077Sbostic #define u_char unsigned char
69*46077Sbostic #define u_long unsigned long
70*46077Sbostic 
71*46077Sbostic static u_char *__sccl();
72*46077Sbostic 
73*46077Sbostic /*
74*46077Sbostic  * vfscanf
75*46077Sbostic  */
76*46077Sbostic __svfscanf(fp, fmt0, ap)
77*46077Sbostic 	register FILE *fp;
78*46077Sbostic 	char const *fmt0;
79*46077Sbostic 	va_list ap;
809415Smckusick {
81*46077Sbostic 	register u_char *fmt = (u_char *)fmt0;
82*46077Sbostic 	register int c;		/* character from format, or conversion */
83*46077Sbostic 	register size_t width;	/* field width, or 0 */
84*46077Sbostic 	register char *p;	/* points into all kinds of strings */
85*46077Sbostic 	register int n;		/* handy integer */
86*46077Sbostic 	register int flags;	/* flags as defined above */
87*46077Sbostic 	register char *p0;	/* saves original value of p when necessary */
88*46077Sbostic 	int nassigned;		/* number of fields assigned */
89*46077Sbostic 	int nread;		/* number of characters consumed from fp */
90*46077Sbostic 	int base;		/* base argument to strtol/strtoul */
91*46077Sbostic 	u_long (*ccfn)();	/* conversion function (strtol/strtoul) */
92*46077Sbostic 	char ccltab[256];	/* character class table for %[...] */
93*46077Sbostic 	char buf[BUF];		/* buffer for numeric conversions */
949415Smckusick 
95*46077Sbostic 	/* `basefix' is used to avoid `if' tests in the integer scanner */
96*46077Sbostic 	static short basefix[17] =
97*46077Sbostic 		{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
98*46077Sbostic 
99*46077Sbostic 	nassigned = 0;
100*46077Sbostic 	nread = 0;
101*46077Sbostic 	for (;;) {
102*46077Sbostic 		c = *fmt++;
103*46077Sbostic 		if (c == 0)
104*46077Sbostic 			return (nassigned);
105*46077Sbostic 		if (isspace(c)) {
106*46077Sbostic 			for (;;) {
107*46077Sbostic 				if (fp->_r <= 0 && __srefill(fp))
108*46077Sbostic 					return (nassigned);
109*46077Sbostic 				if (!isspace(*fp->_p))
110*46077Sbostic 					break;
111*46077Sbostic 				nread++, fp->_r--, fp->_p++;
112*46077Sbostic 			}
113*46077Sbostic 			continue;
1149415Smckusick 		}
115*46077Sbostic 		if (c != '%')
116*46077Sbostic 			goto literal;
117*46077Sbostic 		width = 0;
118*46077Sbostic 		flags = 0;
119*46077Sbostic 		/*
120*46077Sbostic 		 * switch on the format.  continue if done;
121*46077Sbostic 		 * break once format type is derived.
122*46077Sbostic 		 */
123*46077Sbostic again:		c = *fmt++;
124*46077Sbostic 		switch (c) {
125*46077Sbostic 		case '%':
126*46077Sbostic literal:
127*46077Sbostic 			if (fp->_r <= 0 && __srefill(fp))
128*46077Sbostic 				goto input_failure;
129*46077Sbostic 			if (*fp->_p != c)
130*46077Sbostic 				goto match_failure;
131*46077Sbostic 			fp->_r--, fp->_p++;
132*46077Sbostic 			nread++;
133*46077Sbostic 			continue;
134*46077Sbostic 
135*46077Sbostic 		case '*':
136*46077Sbostic 			flags |= SUPPRESS;
137*46077Sbostic 			goto again;
138*46077Sbostic 		case 'l':
139*46077Sbostic 			flags |= LONG;
140*46077Sbostic 			goto again;
141*46077Sbostic 		case 'L':
142*46077Sbostic 			flags |= LONGDBL;
143*46077Sbostic 			goto again;
144*46077Sbostic 		case 'h':
145*46077Sbostic 			flags |= SHORT;
146*46077Sbostic 			goto again;
147*46077Sbostic 
148*46077Sbostic 		case '0': case '1': case '2': case '3': case '4':
149*46077Sbostic 		case '5': case '6': case '7': case '8': case '9':
150*46077Sbostic 			width = width * 10 + c - '0';
151*46077Sbostic 			goto again;
152*46077Sbostic 
153*46077Sbostic 		/*
154*46077Sbostic 		 * Conversions.
155*46077Sbostic 		 * Those marked `compat' are for 4.[123]BSD compatibility.
156*46077Sbostic 		 *
157*46077Sbostic 		 * (According to ANSI, E and X formats are supposed
158*46077Sbostic 		 * to the same as e and x.  Sorry about that.)
159*46077Sbostic 		 */
160*46077Sbostic 		case 'D':	/* compat */
161*46077Sbostic 			flags |= LONG;
162*46077Sbostic 			/* FALLTHROUGH */
163*46077Sbostic 		case 'd':
164*46077Sbostic 			c = CT_INT;
165*46077Sbostic 			ccfn = (u_long (*)())strtol;
166*46077Sbostic 			base = 10;
167*46077Sbostic 			break;
168*46077Sbostic 
169*46077Sbostic 		case 'i':
170*46077Sbostic 			c = CT_INT;
171*46077Sbostic 			ccfn = (u_long (*)())strtol;
172*46077Sbostic 			base = 0;
173*46077Sbostic 			break;
174*46077Sbostic 
175*46077Sbostic 		case 'O':	/* compat */
176*46077Sbostic 			flags |= LONG;
177*46077Sbostic 			/* FALLTHROUGH */
178*46077Sbostic 		case 'o':
179*46077Sbostic 			c = CT_INT;
180*46077Sbostic 			ccfn = strtoul;
181*46077Sbostic 			base = 8;
182*46077Sbostic 			break;
183*46077Sbostic 
184*46077Sbostic 		case 'u':
185*46077Sbostic 			c = CT_INT;
186*46077Sbostic 			ccfn = strtoul;
187*46077Sbostic 			base = 10;
188*46077Sbostic 			break;
189*46077Sbostic 
190*46077Sbostic 		case 'X':	/* compat   XXX */
191*46077Sbostic 			flags |= LONG;
192*46077Sbostic 			/* FALLTHROUGH */
193*46077Sbostic 		case 'x':
194*46077Sbostic 			flags |= PFXOK;	/* enable 0x prefixing */
195*46077Sbostic 			c = CT_INT;
196*46077Sbostic 			ccfn = strtoul;
197*46077Sbostic 			base = 16;
198*46077Sbostic 			break;
199*46077Sbostic 
200*46077Sbostic #ifdef FLOATING_POINT
201*46077Sbostic 		case 'E':	/* compat   XXX */
202*46077Sbostic 		case 'F':	/* compat */
203*46077Sbostic 			flags |= LONG;
204*46077Sbostic 			/* FALLTHROUGH */
205*46077Sbostic 		case 'e': case 'f': case 'g':
206*46077Sbostic 			c = CT_FLOAT;
207*46077Sbostic 			break;
208*46077Sbostic #endif
209*46077Sbostic 
210*46077Sbostic 		case 's':
211*46077Sbostic 			c = CT_STRING;
212*46077Sbostic 			break;
213*46077Sbostic 
214*46077Sbostic 		case '[':
215*46077Sbostic 			fmt = __sccl(ccltab, fmt);
216*46077Sbostic 			flags |= NOSKIP;
217*46077Sbostic 			c = CT_CCL;
218*46077Sbostic 			break;
219*46077Sbostic 
220*46077Sbostic 		case 'c':
221*46077Sbostic 			flags |= NOSKIP;
222*46077Sbostic 			c = CT_CHAR;
223*46077Sbostic 			break;
224*46077Sbostic 
225*46077Sbostic 		case 'p':	/* pointer format is like hex */
226*46077Sbostic 			flags |= POINTER | PFXOK;
227*46077Sbostic 			c = CT_INT;
228*46077Sbostic 			ccfn = strtoul;
229*46077Sbostic 			base = 16;
230*46077Sbostic 			break;
231*46077Sbostic 
232*46077Sbostic 		case 'n':
233*46077Sbostic 			if (flags & SUPPRESS)	/* ??? */
234*46077Sbostic 				continue;
235*46077Sbostic 			if (flags & SHORT)
236*46077Sbostic 				*va_arg(ap, short *) = nread;
237*46077Sbostic 			else if (flags & LONG)
238*46077Sbostic 				*va_arg(ap, long *) = nread;
239*46077Sbostic 			else
240*46077Sbostic 				*va_arg(ap, int *) = nread;
241*46077Sbostic 			continue;
242*46077Sbostic 
243*46077Sbostic 		/*
244*46077Sbostic 		 * Disgusting backwards compatibility hacks.	XXX
245*46077Sbostic 		 */
246*46077Sbostic 		case '\0':	/* compat */
247*46077Sbostic 			return (EOF);
248*46077Sbostic 
249*46077Sbostic 		default:	/* compat */
250*46077Sbostic 			if (isupper(c))
251*46077Sbostic 				flags |= LONG;
252*46077Sbostic 			c = CT_INT;
253*46077Sbostic 			ccfn = (u_long (*)())strtol;
254*46077Sbostic 			base = 10;
255*46077Sbostic 			break;
2569415Smckusick 		}
2579415Smckusick 
258*46077Sbostic 		/*
259*46077Sbostic 		 * We have a conversion that requires input.
260*46077Sbostic 		 */
261*46077Sbostic 		if (fp->_r <= 0 && __srefill(fp))
262*46077Sbostic 			goto input_failure;
2639415Smckusick 
264*46077Sbostic 		/*
265*46077Sbostic 		 * Consume leading white space, except for formats
266*46077Sbostic 		 * that suppress this.
267*46077Sbostic 		 */
268*46077Sbostic 		if ((flags & NOSKIP) == 0) {
269*46077Sbostic 			while (isspace(*fp->_p)) {
270*46077Sbostic 				nread++;
271*46077Sbostic 				if (--fp->_r > 0)
272*46077Sbostic 					fp->_p++;
273*46077Sbostic 				else if (__srefill(fp))
274*46077Sbostic 					goto input_failure;
275*46077Sbostic 			}
276*46077Sbostic 			/*
277*46077Sbostic 			 * Note that there is at least one character in
278*46077Sbostic 			 * the buffer, so conversions that do not set NOSKIP
279*46077Sbostic 			 * ca no longer result in an input failure.
280*46077Sbostic 			 */
2819415Smckusick 		}
2829415Smckusick 
283*46077Sbostic 		/*
284*46077Sbostic 		 * Do the conversion.
285*46077Sbostic 		 */
286*46077Sbostic 		switch (c) {
2879415Smckusick 
288*46077Sbostic 		case CT_CHAR:
289*46077Sbostic 			/* scan arbitrary characters (sets NOSKIP) */
290*46077Sbostic 			if (width == 0)
291*46077Sbostic 				width = 1;
292*46077Sbostic 			if (flags & SUPPRESS) {
293*46077Sbostic 				size_t sum = 0;
294*46077Sbostic 				for (;;) {
295*46077Sbostic 					if ((n = fp->_r) < width) {
296*46077Sbostic 						sum += n;
297*46077Sbostic 						width -= n;
298*46077Sbostic 						fp->_p += n;
299*46077Sbostic 						if (__srefill(fp)) {
300*46077Sbostic 							if (sum == 0)
301*46077Sbostic 							    goto input_failure;
302*46077Sbostic 							break;
303*46077Sbostic 						}
304*46077Sbostic 					} else {
305*46077Sbostic 						sum += width;
306*46077Sbostic 						fp->_r -= width;
307*46077Sbostic 						fp->_p += width;
308*46077Sbostic 						break;
309*46077Sbostic 					}
310*46077Sbostic 				}
311*46077Sbostic 				nread += sum;
312*46077Sbostic 			} else {
313*46077Sbostic 				size_t r = fread((void *)va_arg(ap, char *), 1,
314*46077Sbostic 				    width, fp);
315*46077Sbostic 
316*46077Sbostic 				if (r == 0)
317*46077Sbostic 					goto input_failure;
318*46077Sbostic 				nread += r;
319*46077Sbostic 				nassigned++;
320*46077Sbostic 			}
321*46077Sbostic 			break;
322*46077Sbostic 
323*46077Sbostic 		case CT_CCL:
324*46077Sbostic 			/* scan a (nonempty) character class (sets NOSKIP) */
325*46077Sbostic 			if (width == 0)
326*46077Sbostic 				width = ~0;	/* `infinity' */
327*46077Sbostic 			/* take only those things in the class */
328*46077Sbostic 			if (flags & SUPPRESS) {
329*46077Sbostic 				n = 0;
330*46077Sbostic 				while (ccltab[*fp->_p]) {
331*46077Sbostic 					n++, fp->_r--, fp->_p++;
332*46077Sbostic 					if (--width == 0)
333*46077Sbostic 						break;
334*46077Sbostic 					if (fp->_r <= 0 && __srefill(fp)) {
335*46077Sbostic 						if (n == 0)
336*46077Sbostic 							goto input_failure;
337*46077Sbostic 						break;
338*46077Sbostic 					}
339*46077Sbostic 				}
340*46077Sbostic 				if (n == 0)
341*46077Sbostic 					goto match_failure;
342*46077Sbostic 			} else {
343*46077Sbostic 				p0 = p = va_arg(ap, char *);
344*46077Sbostic 				while (ccltab[*fp->_p]) {
345*46077Sbostic 					fp->_r--;
346*46077Sbostic 					*p++ = *fp->_p++;
347*46077Sbostic 					if (--width == 0)
348*46077Sbostic 						break;
349*46077Sbostic 					if (fp->_r <= 0 && __srefill(fp)) {
350*46077Sbostic 						if (p == p0)
351*46077Sbostic 							goto input_failure;
352*46077Sbostic 						break;
353*46077Sbostic 					}
354*46077Sbostic 				}
355*46077Sbostic 				n = p - p0;
356*46077Sbostic 				if (n == 0)
357*46077Sbostic 					goto match_failure;
358*46077Sbostic 				*p = 0;
359*46077Sbostic 				nassigned++;
360*46077Sbostic 			}
361*46077Sbostic 			nread += n;
362*46077Sbostic 			break;
363*46077Sbostic 
364*46077Sbostic 		case CT_STRING:
365*46077Sbostic 			/* like CCL, but zero-length string OK, & no NOSKIP */
366*46077Sbostic 			if (width == 0)
367*46077Sbostic 				width = ~0;
368*46077Sbostic 			if (flags & SUPPRESS) {
369*46077Sbostic 				n = 0;
370*46077Sbostic 				while (!isspace(*fp->_p)) {
371*46077Sbostic 					n++, fp->_r--, fp->_p++;
372*46077Sbostic 					if (--width == 0)
373*46077Sbostic 						break;
374*46077Sbostic 					if (fp->_r <= 0 && __srefill(fp))
375*46077Sbostic 						break;
376*46077Sbostic 				}
377*46077Sbostic 				nread += n;
378*46077Sbostic 			} else {
379*46077Sbostic 				p0 = p = va_arg(ap, char *);
380*46077Sbostic 				while (!isspace(*fp->_p)) {
381*46077Sbostic 					fp->_r--;
382*46077Sbostic 					*p++ = *fp->_p++;
383*46077Sbostic 					if (--width == 0)
384*46077Sbostic 						break;
385*46077Sbostic 					if (fp->_r <= 0 && __srefill(fp))
386*46077Sbostic 						break;
387*46077Sbostic 				}
388*46077Sbostic 				*p = 0;
389*46077Sbostic 				nread += p - p0;
390*46077Sbostic 				nassigned++;
391*46077Sbostic 			}
3929415Smckusick 			continue;
393*46077Sbostic 
394*46077Sbostic 		case CT_INT:
395*46077Sbostic 			/* scan an integer as if by strtol/strtoul */
396*46077Sbostic #ifdef hardway
397*46077Sbostic 			if (width == 0 || width > sizeof(buf) - 1)
398*46077Sbostic 				width = sizeof(buf) - 1;
399*46077Sbostic #else
400*46077Sbostic 			/* size_t is unsigned, hence this optimisation */
401*46077Sbostic 			if (--width > sizeof(buf) - 2)
402*46077Sbostic 				width = sizeof(buf) - 2;
403*46077Sbostic 			width++;
404*46077Sbostic #endif
405*46077Sbostic 			flags |= SIGNOK | NDIGITS | NZDIGITS;
406*46077Sbostic 			for (p = buf; width; width--) {
407*46077Sbostic 				c = *fp->_p;
408*46077Sbostic 				/*
409*46077Sbostic 				 * Switch on the character; `goto ok'
410*46077Sbostic 				 * if we accept it as a part of number.
411*46077Sbostic 				 */
412*46077Sbostic 				switch (c) {
413*46077Sbostic 
414*46077Sbostic 				/*
415*46077Sbostic 				 * The digit 0 is always legal, but is
416*46077Sbostic 				 * special.  For %i conversions, if no
417*46077Sbostic 				 * digits (zero or nonzero) have been
418*46077Sbostic 				 * scanned (only signs), we will have
419*46077Sbostic 				 * base==0.  In that case, we should set
420*46077Sbostic 				 * it to 8 and enable 0x prefixing.
421*46077Sbostic 				 * Also, if we have not scanned zero digits
422*46077Sbostic 				 * before this, do not turn off prefixing
423*46077Sbostic 				 * (someone else will turn it off if we
424*46077Sbostic 				 * have scanned any nonzero digits).
425*46077Sbostic 				 */
426*46077Sbostic 				case '0':
427*46077Sbostic 					if (base == 0) {
428*46077Sbostic 						base = 8;
429*46077Sbostic 						flags |= PFXOK;
430*46077Sbostic 					}
431*46077Sbostic 					if (flags & NZDIGITS)
432*46077Sbostic 					    flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
433*46077Sbostic 					else
434*46077Sbostic 					    flags &= ~(SIGNOK|PFXOK|NDIGITS);
435*46077Sbostic 					goto ok;
436*46077Sbostic 
437*46077Sbostic 				/* 1 through 7 always legal */
438*46077Sbostic 				case '1': case '2': case '3':
439*46077Sbostic 				case '4': case '5': case '6': case '7':
440*46077Sbostic 					base = basefix[base];
441*46077Sbostic 					flags &= ~(SIGNOK | PFXOK | NDIGITS);
442*46077Sbostic 					goto ok;
443*46077Sbostic 
444*46077Sbostic 				/* digits 8 and 9 ok iff decimal or hex */
445*46077Sbostic 				case '8': case '9':
446*46077Sbostic 					base = basefix[base];
447*46077Sbostic 					if (base <= 8)
448*46077Sbostic 						break;	/* not legal here */
449*46077Sbostic 					flags &= ~(SIGNOK | PFXOK | NDIGITS);
450*46077Sbostic 					goto ok;
451*46077Sbostic 
452*46077Sbostic 				/* letters ok iff hex */
453*46077Sbostic 				case 'A': case 'B': case 'C':
454*46077Sbostic 				case 'D': case 'E': case 'F':
455*46077Sbostic 				case 'a': case 'b': case 'c':
456*46077Sbostic 				case 'd': case 'e': case 'f':
457*46077Sbostic 					/* no need to fix base here */
458*46077Sbostic 					if (base <= 10)
459*46077Sbostic 						break;	/* not legal here */
460*46077Sbostic 					flags &= ~(SIGNOK | PFXOK | NDIGITS);
461*46077Sbostic 					goto ok;
462*46077Sbostic 
463*46077Sbostic 				/* sign ok only as first character */
464*46077Sbostic 				case '+': case '-':
465*46077Sbostic 					if (flags & SIGNOK) {
466*46077Sbostic 						flags &= ~SIGNOK;
467*46077Sbostic 						goto ok;
468*46077Sbostic 					}
469*46077Sbostic 					break;
470*46077Sbostic 
471*46077Sbostic 				/* x ok iff flag still set & 2nd char */
472*46077Sbostic 				case 'x': case 'X':
473*46077Sbostic 					if (flags & PFXOK && p == buf + 1) {
474*46077Sbostic 						base = 16;	/* if %i */
475*46077Sbostic 						flags &= ~PFXOK;
476*46077Sbostic 						goto ok;
477*46077Sbostic 					}
478*46077Sbostic 					break;
479*46077Sbostic 				}
480*46077Sbostic 
481*46077Sbostic 				/*
482*46077Sbostic 				 * If we got here, c is not a legal character
483*46077Sbostic 				 * for a number.  Stop accumulating digits.
484*46077Sbostic 				 */
4859415Smckusick 				break;
486*46077Sbostic 		ok:
487*46077Sbostic 				/*
488*46077Sbostic 				 * c is legal: store it and look at the next.
489*46077Sbostic 				 */
490*46077Sbostic 				*p++ = c;
491*46077Sbostic 				if (--fp->_r > 0)
492*46077Sbostic 					fp->_p++;
493*46077Sbostic 				else if (__srefill(fp))
494*46077Sbostic 					break;		/* EOF */
495*46077Sbostic 			}
496*46077Sbostic 			/*
497*46077Sbostic 			 * If we had only a sign, it is no good; push
498*46077Sbostic 			 * back the sign.  If the number ends in `x',
499*46077Sbostic 			 * it was [sign] '0' 'x', so push back the x
500*46077Sbostic 			 * and treat it as [sign] '0'.
501*46077Sbostic 			 */
502*46077Sbostic 			if (flags & NDIGITS) {
503*46077Sbostic 				if (p > buf)
504*46077Sbostic 					(void) ungetc(*(u_char *)--p, fp);
505*46077Sbostic 				goto match_failure;
506*46077Sbostic 			}
507*46077Sbostic 			c = ((u_char *)p)[-1];
508*46077Sbostic 			if (c == 'x' || c == 'X') {
509*46077Sbostic 				--p;
510*46077Sbostic 				(void) ungetc(c, fp);
511*46077Sbostic 			}
512*46077Sbostic 			if ((flags & SUPPRESS) == 0) {
513*46077Sbostic 				u_long res;
514*46077Sbostic 
515*46077Sbostic 				*p = 0;
516*46077Sbostic 				res = (*ccfn)(buf, (char **)NULL, base);
517*46077Sbostic 				if (flags & POINTER)
518*46077Sbostic 					*va_arg(ap, void **) = (void *)res;
519*46077Sbostic 				else if (flags & SHORT)
520*46077Sbostic 					*va_arg(ap, short *) = res;
521*46077Sbostic 				else if (flags & LONG)
522*46077Sbostic 					*va_arg(ap, long *) = res;
523*46077Sbostic 				else
524*46077Sbostic 					*va_arg(ap, int *) = res;
525*46077Sbostic 				nassigned++;
526*46077Sbostic 			}
527*46077Sbostic 			nread += p - buf;
5289415Smckusick 			break;
5299415Smckusick 
530*46077Sbostic #ifdef FLOATING_POINT
531*46077Sbostic 		case CT_FLOAT:
532*46077Sbostic 			/* scan a floating point number as if by strtod */
533*46077Sbostic #ifdef hardway
534*46077Sbostic 			if (width == 0 || width > sizeof(buf) - 1)
535*46077Sbostic 				width = sizeof(buf) - 1;
536*46077Sbostic #else
537*46077Sbostic 			/* size_t is unsigned, hence this optimisation */
538*46077Sbostic 			if (--width > sizeof(buf) - 2)
539*46077Sbostic 				width = sizeof(buf) - 2;
540*46077Sbostic 			width++;
541*46077Sbostic #endif
542*46077Sbostic 			flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
543*46077Sbostic 			for (p = buf; width; width--) {
544*46077Sbostic 				c = *fp->_p;
545*46077Sbostic 				/*
546*46077Sbostic 				 * This code mimicks the integer conversion
547*46077Sbostic 				 * code, but is much simpler.
548*46077Sbostic 				 */
549*46077Sbostic 				switch (c) {
5509415Smckusick 
551*46077Sbostic 				case '0': case '1': case '2': case '3':
552*46077Sbostic 				case '4': case '5': case '6': case '7':
553*46077Sbostic 				case '8': case '9':
554*46077Sbostic 					flags &= ~(SIGNOK | NDIGITS);
555*46077Sbostic 					goto fok;
5569415Smckusick 
557*46077Sbostic 				case '+': case '-':
558*46077Sbostic 					if (flags & SIGNOK) {
559*46077Sbostic 						flags &= ~SIGNOK;
560*46077Sbostic 						goto fok;
561*46077Sbostic 					}
562*46077Sbostic 					break;
563*46077Sbostic 				case '.':
564*46077Sbostic 					if (flags & DPTOK) {
565*46077Sbostic 						flags &= ~(SIGNOK | DPTOK);
566*46077Sbostic 						goto fok;
567*46077Sbostic 					}
568*46077Sbostic 					break;
569*46077Sbostic 				case 'e': case 'E':
570*46077Sbostic 					/* no exponent without some digits */
571*46077Sbostic 					if ((flags&(NDIGITS|EXPOK)) == EXPOK) {
572*46077Sbostic 						flags =
573*46077Sbostic 						    (flags & ~(EXPOK|DPTOK)) |
574*46077Sbostic 						    SIGNOK | NDIGITS;
575*46077Sbostic 						goto fok;
576*46077Sbostic 					}
577*46077Sbostic 					break;
578*46077Sbostic 				}
579*46077Sbostic 				break;
580*46077Sbostic 		fok:
581*46077Sbostic 				*p++ = c;
582*46077Sbostic 				if (--fp->_r > 0)
583*46077Sbostic 					fp->_p++;
584*46077Sbostic 				else if (__srefill(fp))
585*46077Sbostic 					break;	/* EOF */
586*46077Sbostic 			}
587*46077Sbostic 			/*
588*46077Sbostic 			 * If no digits, might be missing exponent digits
589*46077Sbostic 			 * (just give back the exponent) or might be missing
590*46077Sbostic 			 * regular digits, but had sign and/or decimal point.
591*46077Sbostic 			 */
592*46077Sbostic 			if (flags & NDIGITS) {
593*46077Sbostic 				if (flags & EXPOK) {
594*46077Sbostic 					/* no digits at all */
595*46077Sbostic 					while (p > buf)
596*46077Sbostic 						ungetc(*(u_char *)--p, fp);
597*46077Sbostic 					goto match_failure;
598*46077Sbostic 				}
599*46077Sbostic 				/* just a bad exponent (e and maybe sign) */
600*46077Sbostic 				c = *(u_char *)--p;
601*46077Sbostic 				if (c != 'e' && c != 'E') {
602*46077Sbostic 					(void) ungetc(c, fp);/* sign */
603*46077Sbostic 					c = *(u_char *)--p;
604*46077Sbostic 				}
605*46077Sbostic 				(void) ungetc(c, fp);
606*46077Sbostic 			}
607*46077Sbostic 			if ((flags & SUPPRESS) == 0) {
608*46077Sbostic 				double res;
6099415Smckusick 
610*46077Sbostic 				*p = 0;
611*46077Sbostic 				res = atof(buf);
612*46077Sbostic 				if (flags & LONG)
613*46077Sbostic 					*va_arg(ap, double *) = res;
614*46077Sbostic 				else
615*46077Sbostic 					*va_arg(ap, float *) = res;
616*46077Sbostic 				nassigned++;
617*46077Sbostic 			}
618*46077Sbostic 			nread += p - buf;
619*46077Sbostic 			break;
620*46077Sbostic #endif /* FLOATING_POINT */
621*46077Sbostic 		}
6229415Smckusick 	}
623*46077Sbostic input_failure:
624*46077Sbostic 	return (nassigned ? nassigned : -1);
625*46077Sbostic match_failure:
626*46077Sbostic 	return (nassigned);
6279415Smckusick }
6289415Smckusick 
629*46077Sbostic /*
630*46077Sbostic  * Fill in the given table from the scanset at the given format
631*46077Sbostic  * (just after `[').  Return a pointer to the character past the
632*46077Sbostic  * closing `]'.  The table has a 1 wherever characters should be
633*46077Sbostic  * considered part of the scanset.
634*46077Sbostic  */
635*46077Sbostic static u_char *
636*46077Sbostic __sccl(tab, fmt)
637*46077Sbostic 	register char *tab;
638*46077Sbostic 	register u_char *fmt;
6399415Smckusick {
640*46077Sbostic 	register int c, n, v;
6419415Smckusick 
642*46077Sbostic 	/* first `clear' the whole table */
643*46077Sbostic 	c = *fmt++;		/* first char hat => negated scanset */
644*46077Sbostic 	if (c == '^') {
645*46077Sbostic 		v = 1;		/* default => accept */
646*46077Sbostic 		c = *fmt++;	/* get new first char */
6479415Smckusick 	} else
648*46077Sbostic 		v = 0;		/* default => reject */
649*46077Sbostic 	/* should probably use memset here */
650*46077Sbostic 	for (n = 0; n < 256; n++)
651*46077Sbostic 		tab[n] = v;
652*46077Sbostic 	if (c == 0)
653*46077Sbostic 		return (fmt - 1);/* format ended before closing ] */
6549415Smckusick 
655*46077Sbostic 	/*
656*46077Sbostic 	 * Now set the entries corresponding to the actual scanset
657*46077Sbostic 	 * to the opposite of the above.
658*46077Sbostic 	 *
659*46077Sbostic 	 * The first character may be ']' (or '-') without being special;
660*46077Sbostic 	 * the last character may be '-'.
661*46077Sbostic 	 */
662*46077Sbostic 	v = 1 - v;
663*46077Sbostic 	for (;;) {
664*46077Sbostic 		tab[c] = v;		/* take character c */
665*46077Sbostic doswitch:
666*46077Sbostic 		n = *fmt++;		/* and examine the next */
667*46077Sbostic 		switch (n) {
6689415Smckusick 
669*46077Sbostic 		case 0:			/* format ended too soon */
670*46077Sbostic 			return (fmt - 1);
671*46077Sbostic 
672*46077Sbostic 		case '-':
673*46077Sbostic 			/*
674*46077Sbostic 			 * A scanset of the form
675*46077Sbostic 			 *	[01+-]
676*46077Sbostic 			 * is defined as `the digit 0, the digit 1,
677*46077Sbostic 			 * the character +, the character -', but
678*46077Sbostic 			 * the effect of a scanset such as
679*46077Sbostic 			 *	[a-zA-Z0-9]
680*46077Sbostic 			 * is implementation defined.  The V7 Unix
681*46077Sbostic 			 * scanf treats `a-z' as `the letters a through
682*46077Sbostic 			 * z', but treats `a-a' as `the letter a, the
683*46077Sbostic 			 * character -, and the letter a'.
684*46077Sbostic 			 *
685*46077Sbostic 			 * For compatibility, the `-' is not considerd
686*46077Sbostic 			 * to define a range if the character following
687*46077Sbostic 			 * it is either a close bracket (required by ANSI)
688*46077Sbostic 			 * or is not numerically greater than the character
689*46077Sbostic 			 * we just stored in the table (c).
690*46077Sbostic 			 */
691*46077Sbostic 			n = *fmt;
692*46077Sbostic 			if (n == ']' || n < c) {
693*46077Sbostic 				c = '-';
694*46077Sbostic 				break;	/* resume the for(;;) */
695*46077Sbostic 			}
696*46077Sbostic 			fmt++;
697*46077Sbostic 			do {		/* fill in the range */
698*46077Sbostic 				tab[++c] = v;
699*46077Sbostic 			} while (c < n);
700*46077Sbostic #if 1	/* XXX another disgusting compatibility hack */
701*46077Sbostic 			/*
702*46077Sbostic 			 * Alas, the V7 Unix scanf also treats formats
703*46077Sbostic 			 * such as [a-c-e] as `the letters a through e'.
704*46077Sbostic 			 * This too is permitted by the standard....
705*46077Sbostic 			 */
706*46077Sbostic 			goto doswitch;
707*46077Sbostic #else
708*46077Sbostic 			c = *fmt++;
709*46077Sbostic 			if (c == 0)
710*46077Sbostic 				return (fmt - 1);
711*46077Sbostic 			if (c == ']')
712*46077Sbostic 				return (fmt);
713*46077Sbostic #endif
714*46077Sbostic 			break;
715*46077Sbostic 
716*46077Sbostic 		case ']':		/* end of scanset */
717*46077Sbostic 			return (fmt);
718*46077Sbostic 
719*46077Sbostic 		default:		/* just another character */
720*46077Sbostic 			c = n;
721*46077Sbostic 			break;
722*46077Sbostic 		}
7239415Smckusick 	}
724*46077Sbostic 	/* NOTREACHED */
7259415Smckusick }
726