xref: /freebsd/lib/libc/stdio/vfscanf.c (revision 7ef62cebc2f965b0f640263e179276928885e33d)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Copyright (c) 2011 The FreeBSD Foundation
8  *
9  * Portions of this software were developed by David Chisnall
10  * under sponsorship from the FreeBSD Foundation.
11  *
12  * This code is derived from software contributed to Berkeley by
13  * Chris Torek.
14  *
15  * Redistribution and use in source and binary forms, with or without
16  * modification, are permitted provided that the following conditions
17  * are met:
18  * 1. Redistributions of source code must retain the above copyright
19  *    notice, this list of conditions and the following disclaimer.
20  * 2. Redistributions in binary form must reproduce the above copyright
21  *    notice, this list of conditions and the following disclaimer in the
22  *    documentation and/or other materials provided with the distribution.
23  * 3. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  */
39 
40 #if defined(LIBC_SCCS) && !defined(lint)
41 static char sccsid[] = "@(#)vfscanf.c	8.1 (Berkeley) 6/4/93";
42 #endif /* LIBC_SCCS and not lint */
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45 
46 #include "namespace.h"
47 #include <ctype.h>
48 #include <inttypes.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <stddef.h>
52 #include <stdarg.h>
53 #include <string.h>
54 #include <wchar.h>
55 #include <wctype.h>
56 #include "un-namespace.h"
57 
58 #include "collate.h"
59 #include "libc_private.h"
60 #include "local.h"
61 #include "xlocale_private.h"
62 
63 #ifndef NO_FLOATING_POINT
64 #include <locale.h>
65 #endif
66 
67 #define	BUF		513	/* Maximum length of numeric string. */
68 
69 /*
70  * Flags used during conversion.
71  */
72 #define	LONG		0x01	/* l: long or double */
73 #define	LONGDBL		0x02	/* L: long double */
74 #define	SHORT		0x04	/* h: short */
75 #define	SUPPRESS	0x08	/* *: suppress assignment */
76 #define	POINTER		0x10	/* p: void * (as hex) */
77 #define	NOSKIP		0x20	/* [ or c: do not skip blanks */
78 #define	LONGLONG	0x400	/* ll: long long (+ deprecated q: quad) */
79 #define	INTMAXT		0x800	/* j: intmax_t */
80 #define	PTRDIFFT	0x1000	/* t: ptrdiff_t */
81 #define	SIZET		0x2000	/* z: size_t */
82 #define	SHORTSHORT	0x4000	/* hh: char */
83 #define	UNSIGNED	0x8000	/* %[oupxX] conversions */
84 
85 /*
86  * The following are used in integral conversions only:
87  * SIGNOK, NDIGITS, PFXOK, and NZDIGITS
88  */
89 #define	SIGNOK		0x40	/* +/- is (still) legal */
90 #define	NDIGITS		0x80	/* no digits detected */
91 #define	PFXOK		0x100	/* 0x prefix is (still) legal */
92 #define	NZDIGITS	0x200	/* no zero digits detected */
93 #define	HAVESIGN	0x10000	/* sign detected */
94 
95 /*
96  * Conversion types.
97  */
98 #define	CT_CHAR		0	/* %c conversion */
99 #define	CT_CCL		1	/* %[...] conversion */
100 #define	CT_STRING	2	/* %s conversion */
101 #define	CT_INT		3	/* %[dioupxX] conversion */
102 #define	CT_FLOAT	4	/* %[efgEFG] conversion */
103 
104 static const u_char *__sccl(char *, const u_char *);
105 #ifndef NO_FLOATING_POINT
106 static int parsefloat(FILE *, char *, char *, locale_t);
107 #endif
108 
109 __weak_reference(__vfscanf, vfscanf);
110 
111 /*
112  * Conversion functions are passed a pointer to this object instead of
113  * a real parameter to indicate that the assignment-suppression (*)
114  * flag was specified.  We could use a NULL pointer to indicate this,
115  * but that would mask bugs in applications that call scanf() with a
116  * NULL pointer.
117  */
118 static const int suppress;
119 #define	SUPPRESS_PTR	((void *)&suppress)
120 
121 static const mbstate_t initial_mbs;
122 
123 /*
124  * The following conversion functions return the number of characters consumed,
125  * or -1 on input failure.  Character class conversion returns 0 on match
126  * failure.
127  */
128 
129 static __inline int
130 convert_char(FILE *fp, char * p, int width)
131 {
132 	int n;
133 
134 	if (p == SUPPRESS_PTR) {
135 		size_t sum = 0;
136 		for (;;) {
137 			if ((n = fp->_r) < width) {
138 				sum += n;
139 				width -= n;
140 				fp->_p += n;
141 				if (__srefill(fp)) {
142 					if (sum == 0)
143 						return (-1);
144 					break;
145 				}
146 			} else {
147 				sum += width;
148 				fp->_r -= width;
149 				fp->_p += width;
150 				break;
151 			}
152 		}
153 		return (sum);
154 	} else {
155 		size_t r = __fread(p, 1, width, fp);
156 
157 		if (r == 0)
158 			return (-1);
159 		return (r);
160 	}
161 }
162 
163 static __inline int
164 convert_wchar(FILE *fp, wchar_t *wcp, int width, locale_t locale)
165 {
166 	mbstate_t mbs;
167 	int n, nread;
168 	wint_t wi;
169 
170 	mbs = initial_mbs;
171 	n = 0;
172 	while (width-- != 0 &&
173 	    (wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF) {
174 		if (wcp != SUPPRESS_PTR)
175 			*wcp++ = (wchar_t)wi;
176 		n += nread;
177 	}
178 	if (n == 0)
179 		return (-1);
180 	return (n);
181 }
182 
183 static __inline int
184 convert_ccl(FILE *fp, char * p, int width, const char *ccltab)
185 {
186 	char *p0;
187 	int n;
188 
189 	if (p == SUPPRESS_PTR) {
190 		n = 0;
191 		while (ccltab[*fp->_p]) {
192 			n++, fp->_r--, fp->_p++;
193 			if (--width == 0)
194 				break;
195 			if (fp->_r <= 0 && __srefill(fp)) {
196 				if (n == 0)
197 					return (-1);
198 				break;
199 			}
200 		}
201 	} else {
202 		p0 = p;
203 		while (ccltab[*fp->_p]) {
204 			fp->_r--;
205 			*p++ = *fp->_p++;
206 			if (--width == 0)
207 				break;
208 			if (fp->_r <= 0 && __srefill(fp)) {
209 				if (p == p0)
210 					return (-1);
211 				break;
212 			}
213 		}
214 		n = p - p0;
215 		if (n == 0)
216 			return (0);
217 		*p = 0;
218 	}
219 	return (n);
220 }
221 
222 static __inline int
223 convert_wccl(FILE *fp, wchar_t *wcp, int width, const char *ccltab,
224     locale_t locale)
225 {
226 	mbstate_t mbs;
227 	wint_t wi;
228 	int n, nread;
229 
230 	mbs = initial_mbs;
231 	n = 0;
232 	if (wcp == SUPPRESS_PTR) {
233 		while ((wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF &&
234 		    width-- != 0 && ccltab[wctob(wi)])
235 			n += nread;
236 		if (wi != WEOF)
237 			__ungetwc(wi, fp, __get_locale());
238 	} else {
239 		while ((wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF &&
240 		    width-- != 0 && ccltab[wctob(wi)]) {
241 			*wcp++ = (wchar_t)wi;
242 			n += nread;
243 		}
244 		if (wi != WEOF)
245 			__ungetwc(wi, fp, __get_locale());
246 		if (n == 0)
247 			return (0);
248 		*wcp = 0;
249 	}
250 	return (n);
251 }
252 
253 static __inline int
254 convert_string(FILE *fp, char * p, int width)
255 {
256 	char *p0;
257 	int n;
258 
259 	if (p == SUPPRESS_PTR) {
260 		n = 0;
261 		while (!isspace(*fp->_p)) {
262 			n++, fp->_r--, fp->_p++;
263 			if (--width == 0)
264 				break;
265 			if (fp->_r <= 0 && __srefill(fp))
266 				break;
267 		}
268 	} else {
269 		p0 = p;
270 		while (!isspace(*fp->_p)) {
271 			fp->_r--;
272 			*p++ = *fp->_p++;
273 			if (--width == 0)
274 				break;
275 			if (fp->_r <= 0 && __srefill(fp))
276 				break;
277 		}
278 		*p = 0;
279 		n = p - p0;
280 	}
281 	return (n);
282 }
283 
284 static __inline int
285 convert_wstring(FILE *fp, wchar_t *wcp, int width, locale_t locale)
286 {
287 	mbstate_t mbs;
288 	wint_t wi;
289 	int n, nread;
290 
291 	mbs = initial_mbs;
292 	n = 0;
293 	if (wcp == SUPPRESS_PTR) {
294 		while ((wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF &&
295 		    width-- != 0 && !iswspace(wi))
296 			n += nread;
297 		if (wi != WEOF)
298 			__ungetwc(wi, fp, __get_locale());
299 	} else {
300 		while ((wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF &&
301 		    width-- != 0 && !iswspace(wi)) {
302 			*wcp++ = (wchar_t)wi;
303 			n += nread;
304 		}
305 		if (wi != WEOF)
306 			__ungetwc(wi, fp, __get_locale());
307 		*wcp = '\0';
308 	}
309 	return (n);
310 }
311 
312 /*
313  * Read an integer, storing it in buf.  The only relevant bit in the
314  * flags argument is PFXOK.
315  *
316  * Return 0 on a match failure, and the number of characters read
317  * otherwise.
318  */
319 static __inline int
320 parseint(FILE *fp, char * __restrict buf, int width, int base, int flags)
321 {
322 	/* `basefix' is used to avoid `if' tests */
323 	static const short basefix[17] =
324 		{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
325 	char *p;
326 	int c;
327 
328 	flags |= SIGNOK | NDIGITS | NZDIGITS;
329 	for (p = buf; width; width--) {
330 		c = *fp->_p;
331 		/*
332 		 * Switch on the character; `goto ok' if we accept it
333 		 * as a part of number.
334 		 */
335 		switch (c) {
336 
337 		/*
338 		 * The digit 0 is always legal, but is special.  For
339 		 * %i conversions, if no digits (zero or nonzero) have
340 		 * been scanned (only signs), we will have base==0.
341 		 * In that case, we should set it to 8 and enable 0x
342 		 * prefixing.  Also, if we have not scanned zero
343 		 * digits before this, do not turn off prefixing
344 		 * (someone else will turn it off if we have scanned
345 		 * any nonzero digits).
346 		 */
347 		case '0':
348 			if (base == 0) {
349 				base = 8;
350 				flags |= PFXOK;
351 			}
352 			if (flags & NZDIGITS)
353 				flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
354 			else
355 				flags &= ~(SIGNOK|PFXOK|NDIGITS);
356 			goto ok;
357 
358 		/* 1 through 7 always legal */
359 		case '1': case '2': case '3':
360 		case '4': case '5': case '6': case '7':
361 			base = basefix[base];
362 			flags &= ~(SIGNOK | PFXOK | NDIGITS);
363 			goto ok;
364 
365 		/* digits 8 and 9 ok iff decimal or hex */
366 		case '8': case '9':
367 			base = basefix[base];
368 			if (base <= 8)
369 				break;	/* not legal here */
370 			flags &= ~(SIGNOK | PFXOK | NDIGITS);
371 			goto ok;
372 
373 		/* letters ok iff hex */
374 		case 'A': case 'B': case 'C':
375 		case 'D': case 'E': case 'F':
376 		case 'a': case 'b': case 'c':
377 		case 'd': case 'e': case 'f':
378 			/* no need to fix base here */
379 			if (base <= 10)
380 				break;	/* not legal here */
381 			flags &= ~(SIGNOK | PFXOK | NDIGITS);
382 			goto ok;
383 
384 		/* sign ok only as first character */
385 		case '+': case '-':
386 			if (flags & SIGNOK) {
387 				flags &= ~SIGNOK;
388 				flags |= HAVESIGN;
389 				goto ok;
390 			}
391 			break;
392 
393 		/*
394 		 * x ok iff flag still set & 2nd char (or 3rd char if
395 		 * we have a sign).
396 		 */
397 		case 'x': case 'X':
398 			if (flags & PFXOK && p ==
399 			    buf + 1 + !!(flags & HAVESIGN)) {
400 				base = 16;	/* if %i */
401 				flags &= ~PFXOK;
402 				goto ok;
403 			}
404 			break;
405 		}
406 
407 		/*
408 		 * If we got here, c is not a legal character for a
409 		 * number.  Stop accumulating digits.
410 		 */
411 		break;
412 	ok:
413 		/*
414 		 * c is legal: store it and look at the next.
415 		 */
416 		*p++ = c;
417 		if (--fp->_r > 0)
418 			fp->_p++;
419 		else if (__srefill(fp))
420 			break;		/* EOF */
421 	}
422 	/*
423 	 * If we had only a sign, it is no good; push back the sign.
424 	 * If the number ends in `x', it was [sign] '0' 'x', so push
425 	 * back the x and treat it as [sign] '0'.
426 	 */
427 	if (flags & NDIGITS) {
428 		if (p > buf)
429 			(void) __ungetc(*(u_char *)--p, fp);
430 		return (0);
431 	}
432 	c = ((u_char *)p)[-1];
433 	if (c == 'x' || c == 'X') {
434 		--p;
435 		(void) __ungetc(c, fp);
436 	}
437 	return (p - buf);
438 }
439 
440 /*
441  * __vfscanf - MT-safe version
442  */
443 int
444 __vfscanf(FILE *fp, char const *fmt0, va_list ap)
445 {
446 	int ret;
447 
448 	FLOCKFILE_CANCELSAFE(fp);
449 	ret = __svfscanf(fp, __get_locale(), fmt0, ap);
450 	FUNLOCKFILE_CANCELSAFE();
451 	return (ret);
452 }
453 int
454 vfscanf_l(FILE *fp, locale_t locale, char const *fmt0, va_list ap)
455 {
456 	int ret;
457 	FIX_LOCALE(locale);
458 
459 	FLOCKFILE_CANCELSAFE(fp);
460 	ret = __svfscanf(fp, locale, fmt0, ap);
461 	FUNLOCKFILE_CANCELSAFE();
462 	return (ret);
463 }
464 
465 /*
466  * __svfscanf - non-MT-safe version of __vfscanf
467  */
468 int
469 __svfscanf(FILE *fp, locale_t locale, const char *fmt0, va_list ap)
470 {
471 #define	GETARG(type)	((flags & SUPPRESS) ? SUPPRESS_PTR : va_arg(ap, type))
472 	const u_char *fmt = (const u_char *)fmt0;
473 	int c;			/* character from format, or conversion */
474 	size_t width;		/* field width, or 0 */
475 	int flags;		/* flags as defined above */
476 	int nassigned;		/* number of fields assigned */
477 	int nconversions;	/* number of conversions */
478 	int nr;			/* characters read by the current conversion */
479 	int nread;		/* number of characters consumed from fp */
480 	int base;		/* base argument to conversion function */
481 	char ccltab[256];	/* character class table for %[...] */
482 	char buf[BUF];		/* buffer for numeric conversions */
483 
484 	ORIENT(fp, -1);
485 
486 	nassigned = 0;
487 	nconversions = 0;
488 	nread = 0;
489 	for (;;) {
490 		c = *fmt++;
491 		if (c == 0)
492 			return (nassigned);
493 		if (isspace(c)) {
494 			while ((fp->_r > 0 || __srefill(fp) == 0) && isspace(*fp->_p))
495 				nread++, fp->_r--, fp->_p++;
496 			continue;
497 		}
498 		if (c != '%')
499 			goto literal;
500 		width = 0;
501 		flags = 0;
502 		/*
503 		 * switch on the format.  continue if done;
504 		 * break once format type is derived.
505 		 */
506 again:		c = *fmt++;
507 		switch (c) {
508 		case '%':
509 literal:
510 			if (fp->_r <= 0 && __srefill(fp))
511 				goto input_failure;
512 			if (*fp->_p != c)
513 				goto match_failure;
514 			fp->_r--, fp->_p++;
515 			nread++;
516 			continue;
517 
518 		case '*':
519 			flags |= SUPPRESS;
520 			goto again;
521 		case 'j':
522 			flags |= INTMAXT;
523 			goto again;
524 		case 'l':
525 			if (flags & LONG) {
526 				flags &= ~LONG;
527 				flags |= LONGLONG;
528 			} else
529 				flags |= LONG;
530 			goto again;
531 		case 'q':
532 			flags |= LONGLONG;	/* not quite */
533 			goto again;
534 		case 't':
535 			flags |= PTRDIFFT;
536 			goto again;
537 		case 'z':
538 			flags |= SIZET;
539 			goto again;
540 		case 'L':
541 			flags |= LONGDBL;
542 			goto again;
543 		case 'h':
544 			if (flags & SHORT) {
545 				flags &= ~SHORT;
546 				flags |= SHORTSHORT;
547 			} else
548 				flags |= SHORT;
549 			goto again;
550 
551 		case '0': case '1': case '2': case '3': case '4':
552 		case '5': case '6': case '7': case '8': case '9':
553 			width = width * 10 + c - '0';
554 			goto again;
555 
556 		/*
557 		 * Conversions.
558 		 */
559 		case 'd':
560 			c = CT_INT;
561 			base = 10;
562 			break;
563 
564 		case 'i':
565 			c = CT_INT;
566 			base = 0;
567 			break;
568 
569 		case 'o':
570 			c = CT_INT;
571 			flags |= UNSIGNED;
572 			base = 8;
573 			break;
574 
575 		case 'u':
576 			c = CT_INT;
577 			flags |= UNSIGNED;
578 			base = 10;
579 			break;
580 
581 		case 'X':
582 		case 'x':
583 			flags |= PFXOK;	/* enable 0x prefixing */
584 			c = CT_INT;
585 			flags |= UNSIGNED;
586 			base = 16;
587 			break;
588 
589 #ifndef NO_FLOATING_POINT
590 		case 'A': case 'E': case 'F': case 'G':
591 		case 'a': case 'e': case 'f': case 'g':
592 			c = CT_FLOAT;
593 			break;
594 #endif
595 
596 		case 'S':
597 			flags |= LONG;
598 			/* FALLTHROUGH */
599 		case 's':
600 			c = CT_STRING;
601 			break;
602 
603 		case '[':
604 			fmt = __sccl(ccltab, fmt);
605 			flags |= NOSKIP;
606 			c = CT_CCL;
607 			break;
608 
609 		case 'C':
610 			flags |= LONG;
611 			/* FALLTHROUGH */
612 		case 'c':
613 			flags |= NOSKIP;
614 			c = CT_CHAR;
615 			break;
616 
617 		case 'p':	/* pointer format is like hex */
618 			flags |= POINTER | PFXOK;
619 			c = CT_INT;		/* assumes sizeof(uintmax_t) */
620 			flags |= UNSIGNED;	/*      >= sizeof(uintptr_t) */
621 			base = 16;
622 			break;
623 
624 		case 'n':
625 			if (flags & SUPPRESS)	/* ??? */
626 				continue;
627 			if (flags & SHORTSHORT)
628 				*va_arg(ap, char *) = nread;
629 			else if (flags & SHORT)
630 				*va_arg(ap, short *) = nread;
631 			else if (flags & LONG)
632 				*va_arg(ap, long *) = nread;
633 			else if (flags & LONGLONG)
634 				*va_arg(ap, long long *) = nread;
635 			else if (flags & INTMAXT)
636 				*va_arg(ap, intmax_t *) = nread;
637 			else if (flags & SIZET)
638 				*va_arg(ap, size_t *) = nread;
639 			else if (flags & PTRDIFFT)
640 				*va_arg(ap, ptrdiff_t *) = nread;
641 			else
642 				*va_arg(ap, int *) = nread;
643 			continue;
644 
645 		default:
646 			goto match_failure;
647 
648 		/*
649 		 * Disgusting backwards compatibility hack.	XXX
650 		 */
651 		case '\0':	/* compat */
652 			return (EOF);
653 		}
654 
655 		/*
656 		 * We have a conversion that requires input.
657 		 */
658 		if (fp->_r <= 0 && __srefill(fp))
659 			goto input_failure;
660 
661 		/*
662 		 * Consume leading white space, except for formats
663 		 * that suppress this.
664 		 */
665 		if ((flags & NOSKIP) == 0) {
666 			while (isspace(*fp->_p)) {
667 				nread++;
668 				if (--fp->_r > 0)
669 					fp->_p++;
670 				else if (__srefill(fp))
671 					goto input_failure;
672 			}
673 			/*
674 			 * Note that there is at least one character in
675 			 * the buffer, so conversions that do not set NOSKIP
676 			 * ca no longer result in an input failure.
677 			 */
678 		}
679 
680 		/*
681 		 * Do the conversion.
682 		 */
683 		switch (c) {
684 
685 		case CT_CHAR:
686 			/* scan arbitrary characters (sets NOSKIP) */
687 			if (width == 0)
688 				width = 1;
689 			if (flags & LONG) {
690 				nr = convert_wchar(fp, GETARG(wchar_t *),
691 				    width, locale);
692 			} else {
693 				nr = convert_char(fp, GETARG(char *), width);
694 			}
695 			if (nr < 0)
696 				goto input_failure;
697 			break;
698 
699 		case CT_CCL:
700 			/* scan a (nonempty) character class (sets NOSKIP) */
701 			if (width == 0)
702 				width = (size_t)~0;	/* `infinity' */
703 			if (flags & LONG) {
704 				nr = convert_wccl(fp, GETARG(wchar_t *), width,
705 				    ccltab, locale);
706 			} else {
707 				nr = convert_ccl(fp, GETARG(char *), width,
708 				    ccltab);
709 			}
710 			if (nr <= 0) {
711 				if (nr < 0)
712 					goto input_failure;
713 				else /* nr == 0 */
714 					goto match_failure;
715 			}
716 			break;
717 
718 		case CT_STRING:
719 			/* like CCL, but zero-length string OK, & no NOSKIP */
720 			if (width == 0)
721 				width = (size_t)~0;
722 			if (flags & LONG) {
723 				nr = convert_wstring(fp, GETARG(wchar_t *),
724 				    width, locale);
725 			} else {
726 				nr = convert_string(fp, GETARG(char *), width);
727 			}
728 			if (nr < 0)
729 				goto input_failure;
730 			break;
731 
732 		case CT_INT:
733 			/* scan an integer as if by the conversion function */
734 #ifdef hardway
735 			if (width == 0 || width > sizeof(buf) - 1)
736 				width = sizeof(buf) - 1;
737 #else
738 			/* size_t is unsigned, hence this optimisation */
739 			if (--width > sizeof(buf) - 2)
740 				width = sizeof(buf) - 2;
741 			width++;
742 #endif
743 			nr = parseint(fp, buf, width, base, flags);
744 			if (nr == 0)
745 				goto match_failure;
746 			if ((flags & SUPPRESS) == 0) {
747 				uintmax_t res;
748 
749 				buf[nr] = '\0';
750 				if ((flags & UNSIGNED) == 0)
751 				    res = strtoimax_l(buf, (char **)NULL, base, locale);
752 				else
753 				    res = strtoumax_l(buf, (char **)NULL, base, locale);
754 				if (flags & POINTER)
755 					*va_arg(ap, void **) =
756 							(void *)(uintptr_t)res;
757 				else if (flags & SHORTSHORT)
758 					*va_arg(ap, char *) = res;
759 				else if (flags & SHORT)
760 					*va_arg(ap, short *) = res;
761 				else if (flags & LONG)
762 					*va_arg(ap, long *) = res;
763 				else if (flags & LONGLONG)
764 					*va_arg(ap, long long *) = res;
765 				else if (flags & INTMAXT)
766 					*va_arg(ap, intmax_t *) = res;
767 				else if (flags & PTRDIFFT)
768 					*va_arg(ap, ptrdiff_t *) = res;
769 				else if (flags & SIZET)
770 					*va_arg(ap, size_t *) = res;
771 				else
772 					*va_arg(ap, int *) = res;
773 			}
774 			break;
775 
776 #ifndef NO_FLOATING_POINT
777 		case CT_FLOAT:
778 			/* scan a floating point number as if by strtod */
779 			if (width == 0 || width > sizeof(buf) - 1)
780 				width = sizeof(buf) - 1;
781 			nr = parsefloat(fp, buf, buf + width, locale);
782 			if (nr == 0)
783 				goto match_failure;
784 			if ((flags & SUPPRESS) == 0) {
785 				if (flags & LONGDBL) {
786 					long double res = strtold_l(buf, NULL,
787 					    locale);
788 					*va_arg(ap, long double *) = res;
789 				} else if (flags & LONG) {
790 					double res = strtod_l(buf, NULL,
791 					    locale);
792 					*va_arg(ap, double *) = res;
793 				} else {
794 					float res = strtof_l(buf, NULL, locale);
795 					*va_arg(ap, float *) = res;
796 				}
797 			}
798 			break;
799 #endif /* !NO_FLOATING_POINT */
800 		}
801 		if (!(flags & SUPPRESS))
802 			nassigned++;
803 		nread += nr;
804 		nconversions++;
805 	}
806 input_failure:
807 	return (nconversions != 0 ? nassigned : EOF);
808 match_failure:
809 	return (nassigned);
810 }
811 
812 /*
813  * Fill in the given table from the scanset at the given format
814  * (just after `[').  Return a pointer to the character past the
815  * closing `]'.  The table has a 1 wherever characters should be
816  * considered part of the scanset.
817  */
818 static const u_char *
819 __sccl(char *tab, const u_char *fmt)
820 {
821 	int c, n, v, i;
822 	struct xlocale_collate *table =
823 		(struct xlocale_collate*)__get_locale()->components[XLC_COLLATE];
824 
825 	/* first `clear' the whole table */
826 	c = *fmt++;		/* first char hat => negated scanset */
827 	if (c == '^') {
828 		v = 1;		/* default => accept */
829 		c = *fmt++;	/* get new first char */
830 	} else
831 		v = 0;		/* default => reject */
832 
833 	/* XXX: Will not work if sizeof(tab*) > sizeof(char) */
834 	(void) memset(tab, v, 256);
835 
836 	if (c == 0)
837 		return (fmt - 1);/* format ended before closing ] */
838 
839 	/*
840 	 * Now set the entries corresponding to the actual scanset
841 	 * to the opposite of the above.
842 	 *
843 	 * The first character may be ']' (or '-') without being special;
844 	 * the last character may be '-'.
845 	 */
846 	v = 1 - v;
847 	for (;;) {
848 		tab[c] = v;		/* take character c */
849 doswitch:
850 		n = *fmt++;		/* and examine the next */
851 		switch (n) {
852 
853 		case 0:			/* format ended too soon */
854 			return (fmt - 1);
855 
856 		case '-':
857 			/*
858 			 * A scanset of the form
859 			 *	[01+-]
860 			 * is defined as `the digit 0, the digit 1,
861 			 * the character +, the character -', but
862 			 * the effect of a scanset such as
863 			 *	[a-zA-Z0-9]
864 			 * is implementation defined.  The V7 Unix
865 			 * scanf treats `a-z' as `the letters a through
866 			 * z', but treats `a-a' as `the letter a, the
867 			 * character -, and the letter a'.
868 			 *
869 			 * For compatibility, the `-' is not considered
870 			 * to define a range if the character following
871 			 * it is either a close bracket (required by ANSI)
872 			 * or is not numerically greater than the character
873 			 * we just stored in the table (c).
874 			 */
875 			n = *fmt;
876 			if (n == ']'
877 			    || (table->__collate_load_error ? n < c :
878 				__collate_range_cmp(n, c) < 0
879 			       )
880 			   ) {
881 				c = '-';
882 				break;	/* resume the for(;;) */
883 			}
884 			fmt++;
885 			/* fill in the range */
886 			if (table->__collate_load_error) {
887 				do {
888 					tab[++c] = v;
889 				} while (c < n);
890 			} else {
891 				for (i = 0; i < 256; i ++)
892 					if (__collate_range_cmp(c, i) <= 0 &&
893 					    __collate_range_cmp(i, n) <= 0
894 					   )
895 						tab[i] = v;
896 			}
897 #if 1	/* XXX another disgusting compatibility hack */
898 			c = n;
899 			/*
900 			 * Alas, the V7 Unix scanf also treats formats
901 			 * such as [a-c-e] as `the letters a through e'.
902 			 * This too is permitted by the standard....
903 			 */
904 			goto doswitch;
905 #else
906 			c = *fmt++;
907 			if (c == 0)
908 				return (fmt - 1);
909 			if (c == ']')
910 				return (fmt);
911 #endif
912 			break;
913 
914 		case ']':		/* end of scanset */
915 			return (fmt);
916 
917 		default:		/* just another character */
918 			c = n;
919 			break;
920 		}
921 	}
922 	/* NOTREACHED */
923 }
924 
925 #ifndef NO_FLOATING_POINT
926 static int
927 parsefloat(FILE *fp, char *buf, char *end, locale_t locale)
928 {
929 	char *commit, *p;
930 	int infnanpos = 0, decptpos = 0;
931 	enum {
932 		S_START, S_GOTSIGN, S_INF, S_NAN, S_DONE, S_MAYBEHEX,
933 		S_DIGITS, S_DECPT, S_FRAC, S_EXP, S_EXPDIGITS
934 	} state = S_START;
935 	unsigned char c;
936 	const char *decpt = localeconv_l(locale)->decimal_point;
937 	_Bool gotmantdig = 0, ishex = 0;
938 
939 	/*
940 	 * We set commit = p whenever the string we have read so far
941 	 * constitutes a valid representation of a floating point
942 	 * number by itself.  At some point, the parse will complete
943 	 * or fail, and we will ungetc() back to the last commit point.
944 	 * To ensure that the file offset gets updated properly, it is
945 	 * always necessary to read at least one character that doesn't
946 	 * match; thus, we can't short-circuit "infinity" or "nan(...)".
947 	 */
948 	commit = buf - 1;
949 	for (p = buf; p < end; ) {
950 		c = *fp->_p;
951 reswitch:
952 		switch (state) {
953 		case S_START:
954 			state = S_GOTSIGN;
955 			if (c == '-' || c == '+')
956 				break;
957 			else
958 				goto reswitch;
959 		case S_GOTSIGN:
960 			switch (c) {
961 			case '0':
962 				state = S_MAYBEHEX;
963 				commit = p;
964 				break;
965 			case 'I':
966 			case 'i':
967 				state = S_INF;
968 				break;
969 			case 'N':
970 			case 'n':
971 				state = S_NAN;
972 				break;
973 			default:
974 				state = S_DIGITS;
975 				goto reswitch;
976 			}
977 			break;
978 		case S_INF:
979 			if (infnanpos > 6 ||
980 			    (c != "nfinity"[infnanpos] &&
981 			     c != "NFINITY"[infnanpos]))
982 				goto parsedone;
983 			if (infnanpos == 1 || infnanpos == 6)
984 				commit = p;	/* inf or infinity */
985 			infnanpos++;
986 			break;
987 		case S_NAN:
988 			switch (infnanpos) {
989 			case 0:
990 				if (c != 'A' && c != 'a')
991 					goto parsedone;
992 				break;
993 			case 1:
994 				if (c != 'N' && c != 'n')
995 					goto parsedone;
996 				else
997 					commit = p;
998 				break;
999 			case 2:
1000 				if (c != '(')
1001 					goto parsedone;
1002 				break;
1003 			default:
1004 				if (c == ')') {
1005 					commit = p;
1006 					state = S_DONE;
1007 				} else if (!isalnum(c) && c != '_')
1008 					goto parsedone;
1009 				break;
1010 			}
1011 			infnanpos++;
1012 			break;
1013 		case S_DONE:
1014 			goto parsedone;
1015 		case S_MAYBEHEX:
1016 			state = S_DIGITS;
1017 			if (c == 'X' || c == 'x') {
1018 				ishex = 1;
1019 				break;
1020 			} else {	/* we saw a '0', but no 'x' */
1021 				gotmantdig = 1;
1022 				goto reswitch;
1023 			}
1024 		case S_DIGITS:
1025 			if ((ishex && isxdigit(c)) || isdigit(c)) {
1026 				gotmantdig = 1;
1027 				commit = p;
1028 				break;
1029 			} else {
1030 				state = S_DECPT;
1031 				goto reswitch;
1032 			}
1033 		case S_DECPT:
1034 			if (c == decpt[decptpos]) {
1035 				if (decpt[++decptpos] == '\0') {
1036 					/* We read the complete decpt seq. */
1037 					state = S_FRAC;
1038 					if (gotmantdig)
1039 						commit = p;
1040 				}
1041 				break;
1042 			} else if (!decptpos) {
1043 				/* We didn't read any decpt characters. */
1044 				state = S_FRAC;
1045 				goto reswitch;
1046 			} else {
1047 				/*
1048 				 * We read part of a multibyte decimal point,
1049 				 * but the rest is invalid, so bail.
1050 				 */
1051 				goto parsedone;
1052 			}
1053 		case S_FRAC:
1054 			if (((c == 'E' || c == 'e') && !ishex) ||
1055 			    ((c == 'P' || c == 'p') && ishex)) {
1056 				if (!gotmantdig)
1057 					goto parsedone;
1058 				else
1059 					state = S_EXP;
1060 			} else if ((ishex && isxdigit(c)) || isdigit(c)) {
1061 				commit = p;
1062 				gotmantdig = 1;
1063 			} else
1064 				goto parsedone;
1065 			break;
1066 		case S_EXP:
1067 			state = S_EXPDIGITS;
1068 			if (c == '-' || c == '+')
1069 				break;
1070 			else
1071 				goto reswitch;
1072 		case S_EXPDIGITS:
1073 			if (isdigit(c))
1074 				commit = p;
1075 			else
1076 				goto parsedone;
1077 			break;
1078 		default:
1079 			abort();
1080 		}
1081 		*p++ = c;
1082 		if (--fp->_r > 0)
1083 			fp->_p++;
1084 		else if (__srefill(fp))
1085 			break;	/* EOF */
1086 	}
1087 
1088 parsedone:
1089 	while (commit < --p)
1090 		__ungetc(*(u_char *)p, fp);
1091 	*++commit = '\0';
1092 	return (commit - buf);
1093 }
1094 #endif
1095