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