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