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