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