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