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