Import some interworking support code from devo.
[official-gcc.git] / libio / iovfscanf.c
blob0631458f21ec6d9cedc562ddb51409bc10b4a5d5
1 /*
2 Copyright (C) 1993 Free Software Foundation
4 This file is part of the GNU IO Library. This library is free
5 software; you can redistribute it and/or modify it under the
6 terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 2, or (at your option)
8 any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this library; see the file COPYING. If not, write to the Free
17 Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 As a special exception, if you link this library with files
20 compiled with a GNU compiler to produce an executable, this does not cause
21 the resulting executable to be covered by the GNU General Public License.
22 This exception does not however invalidate any other reasons why
23 the executable file might be covered by the GNU General Public License. */
26 * Copyright (c) 1990 The Regents of the University of California.
27 * All rights reserved.
29 * Redistribution and use in source and binary forms are permitted
30 * provided that the above copyright notice and this paragraph are
31 * duplicated in all such forms and that any documentation,
32 * advertising materials, and other materials related to such
33 * distribution and use acknowledge that the software was developed
34 * by the University of California, Berkeley. The name of the
35 * University may not be used to endorse or promote products derived
36 * from this software without specific prior written permission.
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
38 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
39 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
42 /* Extensively hacked for GNU iostream by Per Bothner 1991, 1992, 1993.
43 Changes copyright Free Software Foundation 1992, 1993. */
45 #if defined(LIBC_SCCS) && !defined(lint)
46 static char sccsid[] = "%W% (Berkeley) %G%";
47 #endif /* LIBC_SCCS and not lint */
49 #include <libioP.h>
50 #include <ctype.h>
51 #ifdef __STDC__
52 #include <stdarg.h>
53 #else
54 #include <varargs.h>
55 #endif
57 #ifndef NO_FLOATING_POINT
58 #define FLOATING_POINT
59 #endif
61 #ifdef FLOATING_POINT
62 #include "floatio.h"
63 #define BUF (MAXEXP+MAXFRACT+3) /* 3 = sign + decimal point + NUL */
64 #else
65 #define BUF 40
66 #endif
69 * Flags used during conversion.
71 #define LONG 0x01 /* l: long or double */
72 #define LONGDBL 0x02 /* L: long double; unimplemented */
73 #define SHORT 0x04 /* h: short */
74 #define SUPPRESS 0x08 /* suppress assignment */
75 #define POINTER 0x10 /* weird %p pointer (`fake hex') */
76 #define NOSKIP 0x20 /* do not skip blanks */
77 #define WIDTH 0x40 /* width */
80 * The following are used in numeric conversions only:
81 * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point;
82 * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral.
84 #define SIGNOK 0x40 /* +/- is (still) legal */
85 #define NDIGITS 0x80 /* no digits detected */
87 #define DPTOK 0x100 /* (float) decimal point is still legal */
88 #define EXPOK 0x200 /* (float) exponent (e+3, etc) still legal */
90 #define PFXOK 0x100 /* 0x prefix is (still) legal */
91 #define NZDIGITS 0x200 /* no zero digits detected */
94 * Conversion types.
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 /* integer, i.e., strtol or strtoul */
100 #define CT_FLOAT 4 /* floating, i.e., strtod */
102 #define u_char unsigned char
103 #define u_long unsigned long
105 #ifdef __cplusplus
106 extern "C" {
107 #endif
108 extern u_long strtoul __P((const char*, char**, int));
109 extern long strtol __P((const char*, char**, int));
110 static const u_char *__sccl __P((char *tab, const u_char *fmt));
111 #ifndef _IO_USE_DTOA
112 extern double atof();
113 #endif
114 #ifdef __cplusplus
116 #endif
118 /* If errp != NULL, *errp|=1 if we see a premature EOF;
119 *errp|=2 if we an invalid character. */
122 _IO_vfscanf (fp, fmt0, ap, errp)
123 _IO_FILE *fp;
124 char const *fmt0;
125 _IO_va_list ap;
126 int *errp;
128 register const u_char *fmt = (const u_char *)fmt0;
129 register int c; /* character from format, or conversion */
130 register _IO_ssize_t width; /* field width, or 0 */
131 register char *p; /* points into all kinds of strings */
132 register int n; /* handy integer */
133 register int flags = 0; /* flags as defined above */
134 register char *p0; /* saves original value of p when necessary */
135 int nassigned; /* number of fields assigned */
136 int nread; /* number of characters consumed from fp */
137 /* Assignments to base and ccfn are just to suppress warnings from gcc.*/
138 int base = 0; /* base argument to strtol/strtoul */
139 typedef u_long (*strtoulfn) __P((const char*, char**, int));
140 strtoulfn ccfn = 0;
141 /* conversion function (strtol/strtoul) */
142 char ccltab[256]; /* character class table for %[...] */
143 char buf[BUF]; /* buffer for numeric conversions */
144 int seen_eof = 0;
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 };
150 nassigned = 0;
151 nread = 0;
152 for (;;) {
153 c = *fmt++;
154 if (c == 0)
155 goto done;
156 if (isspace(c)) {
157 for (;;) {
158 c = _IO_getc(fp);
159 if (c == EOF) {
160 seen_eof++;
161 break;
163 if (!isspace(c)) {
164 _IO_ungetc (c, fp);
165 break;
167 nread++;
169 continue;
171 if (c != '%')
172 goto literal;
173 width = 0;
174 flags = 0;
176 * switch on the format. continue if done;
177 * break once format type is derived.
179 again: c = *fmt++;
180 switch (c) {
181 case '%':
182 literal:
183 n = _IO_getc(fp);
184 if (n == EOF)
185 goto eof_failure;
186 if (n != c) {
187 _IO_ungetc (n, fp);
188 goto match_failure;
190 nread++;
191 continue;
193 case '*':
194 if (flags) goto control_failure;
195 flags = SUPPRESS;
196 goto again;
197 case 'l':
198 if (flags & ~(SUPPRESS | WIDTH)) goto control_failure;
199 flags |= LONG;
200 goto again;
201 case 'L':
202 if (flags & ~(SUPPRESS | WIDTH)) goto control_failure;
203 flags |= LONGDBL;
204 goto again;
205 case 'h':
206 if (flags & ~(SUPPRESS | WIDTH)) goto control_failure;
207 flags |= SHORT;
208 goto again;
210 case '0': case '1': case '2': case '3': case '4':
211 case '5': case '6': case '7': case '8': case '9':
212 if (flags & ~(SUPPRESS | WIDTH)) goto control_failure;
213 flags |= WIDTH;
214 width = width * 10 + c - '0';
215 goto again;
218 * Conversions.
219 * Those marked `compat' are for 4.[123]BSD compatibility.
221 * (According to ANSI, E and X formats are supposed
222 * to the same as e and x. Sorry about that.)
224 case 'D': /* compat */
225 flags |= LONG;
226 /* FALLTHROUGH */
227 case 'd':
228 c = CT_INT;
229 ccfn = (strtoulfn)strtol;
230 base = 10;
231 break;
233 case 'i':
234 c = CT_INT;
235 ccfn = (strtoulfn)strtol;
236 base = 0;
237 break;
239 case 'O': /* compat */
240 flags |= LONG;
241 /* FALLTHROUGH */
242 case 'o':
243 c = CT_INT;
244 ccfn = strtoul;
245 base = 8;
246 break;
248 case 'u':
249 c = CT_INT;
250 ccfn = strtoul;
251 base = 10;
252 break;
254 case 'X':
255 case 'x':
256 flags |= PFXOK; /* enable 0x prefixing */
257 c = CT_INT;
258 ccfn = strtoul;
259 base = 16;
260 break;
262 #ifdef FLOATING_POINT
263 case 'E': case 'F':
264 case 'e': case 'f': case 'g':
265 c = CT_FLOAT;
266 break;
267 #endif
269 case 's':
270 c = CT_STRING;
271 break;
273 case '[':
274 fmt = __sccl(ccltab, fmt);
275 flags |= NOSKIP;
276 c = CT_CCL;
277 break;
279 case 'c':
280 flags |= NOSKIP;
281 c = CT_CHAR;
282 break;
284 case 'p': /* pointer format is like hex */
285 flags |= POINTER | PFXOK;
286 c = CT_INT;
287 ccfn = strtoul;
288 base = 16;
289 break;
291 case 'n':
292 if (flags & SUPPRESS) /* ??? */
293 continue;
294 if (flags & SHORT)
295 *va_arg(ap, short *) = nread;
296 else if (flags & LONG)
297 *va_arg(ap, long *) = nread;
298 else
299 *va_arg(ap, int *) = nread;
300 continue;
303 * Disgusting backwards compatibility hacks. XXX
305 case '\0': /* compat */
306 nassigned = EOF;
307 goto done;
309 default: /* compat */
310 if (isupper(c))
311 flags |= LONG;
312 c = CT_INT;
313 ccfn = (strtoulfn)strtol;
314 base = 10;
315 break;
319 * We have a conversion that requires input.
321 if (_IO_peekc(fp) == EOF)
322 goto eof_failure;
325 * Consume leading white space, except for formats
326 * that suppress this.
328 if ((flags & NOSKIP) == 0) {
329 n = (unsigned char)*fp->_IO_read_ptr;
330 while (isspace(n)) {
331 fp->_IO_read_ptr++;
332 nread++;
333 n = _IO_peekc(fp);
334 if (n == EOF)
335 goto eof_failure;
337 /* Note that there is at least one character in
338 the buffer, so conversions that do not set NOSKIP
339 can no longer result in an input failure. */
343 * Do the conversion.
345 switch (c) {
347 case CT_CHAR:
348 /* scan arbitrary characters (sets NOSKIP) */
349 if (width == 0) /* FIXME! */
350 width = 1;
351 if (flags & SUPPRESS) {
352 _IO_size_t sum = 0;
353 for (;;) {
354 n = fp->_IO_read_end - fp->_IO_read_ptr;
355 if (n < (int)width) {
356 sum += n;
357 width -= n;
358 fp->_IO_read_ptr += n;
359 if (__underflow(fp) == EOF)
360 if (sum == 0)
361 goto eof_failure;
362 else {
363 seen_eof++;
364 break;
366 } else {
367 sum += width;
368 fp->_IO_read_ptr += width;
369 break;
372 nread += sum;
373 } else {
374 _IO_size_t r =
376 _IO_XSGETN (fp, (char*)va_arg(ap, char*), width);
377 if (r != width)
378 goto eof_failure;
379 nread += r;
380 nassigned++;
382 break;
384 case CT_CCL:
385 /* scan a (nonempty) character class (sets NOSKIP) */
386 if (width == 0)
387 width = ~0; /* `infinity' */
388 /* take only those things in the class */
389 if (flags & SUPPRESS) {
390 n = 0;
391 while (ccltab[(unsigned char)*fp->_IO_read_ptr]) {
392 n++, fp->_IO_read_ptr++;
393 if (--width == 0)
394 break;
395 if (_IO_peekc(fp) == EOF) {
396 if (n == 0)
397 goto eof_failure;
398 seen_eof++;
399 break;
402 if (n == 0)
403 goto match_failure;
404 } else {
405 p0 = p = va_arg(ap, char *);
406 while (ccltab[(unsigned char)*fp->_IO_read_ptr]) {
407 *p++ = *fp->_IO_read_ptr++;
408 if (--width == 0)
409 break;
410 if (_IO_peekc(fp) == EOF) {
411 if (p == p0)
412 goto eof_failure;
413 seen_eof++;
414 break;
417 n = p - p0;
418 if (n == 0)
419 goto match_failure;
420 *p = 0;
421 nassigned++;
423 nread += n;
424 break;
426 case CT_STRING:
427 /* like CCL, but zero-length string OK, & no NOSKIP */
428 if (width == 0)
429 width = ~0;
430 if (flags & SUPPRESS) {
431 n = 0;
432 while (!isspace((unsigned char)*fp->_IO_read_ptr)) {
433 n++, fp->_IO_read_ptr++;
434 if (--width == 0)
435 break;
436 if (_IO_peekc(fp) == EOF) {
437 seen_eof++;
438 break;
441 nread += n;
442 } else {
443 p0 = p = va_arg(ap, char *);
444 while (!isspace((unsigned char)*fp->_IO_read_ptr)) {
445 *p++ = *fp->_IO_read_ptr++;
446 if (--width == 0)
447 break;
448 if (_IO_peekc(fp) == EOF) {
449 seen_eof++;
450 break;
453 *p = 0;
454 nread += p - p0;
455 nassigned++;
457 continue;
459 case CT_INT:
460 /* scan an integer as if by strtol/strtoul */
461 if (width == 0 || width > sizeof(buf) - 1)
462 width = sizeof(buf) - 1;
463 flags |= SIGNOK | NDIGITS | NZDIGITS;
464 for (p = buf; width; width--) {
465 c = (unsigned char)*fp->_IO_read_ptr;
467 * Switch on the character; `goto ok'
468 * if we accept it as a part of number.
470 switch (c) {
473 * The digit 0 is always legal, but is
474 * special. For %i conversions, if no
475 * digits (zero or nonzero) have been
476 * scanned (only signs), we will have
477 * base==0. In that case, we should set
478 * it to 8 and enable 0x prefixing.
479 * Also, if we have not scanned zero digits
480 * before this, do not turn off prefixing
481 * (someone else will turn it off if we
482 * have scanned any nonzero digits).
484 case '0':
485 if (base == 0) {
486 base = 8;
487 flags |= PFXOK;
489 if (flags & NZDIGITS)
490 flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
491 else
492 flags &= ~(SIGNOK|PFXOK|NDIGITS);
493 goto ok;
495 /* 1 through 7 always legal */
496 case '1': case '2': case '3':
497 case '4': case '5': case '6': case '7':
498 base = basefix[base];
499 flags &= ~(SIGNOK | PFXOK | NDIGITS);
500 goto ok;
502 /* digits 8 and 9 ok iff decimal or hex */
503 case '8': case '9':
504 base = basefix[base];
505 if (base <= 8)
506 break; /* not legal here */
507 flags &= ~(SIGNOK | PFXOK | NDIGITS);
508 goto ok;
510 /* letters ok iff hex */
511 case 'A': case 'B': case 'C':
512 case 'D': case 'E': case 'F':
513 case 'a': case 'b': case 'c':
514 case 'd': case 'e': case 'f':
515 /* no need to fix base here */
516 if (base <= 10)
517 break; /* not legal here */
518 flags &= ~(SIGNOK | PFXOK | NDIGITS);
519 goto ok;
521 /* sign ok only as first character */
522 case '+': case '-':
523 if (flags & SIGNOK) {
524 flags &= ~SIGNOK;
525 goto ok;
527 break;
529 /* x ok iff flag still set & 2nd char */
530 case 'x': case 'X':
531 if (flags & PFXOK && p == buf + 1) {
532 base = 16; /* if %i */
533 flags &= ~PFXOK;
534 goto ok;
536 break;
540 * If we got here, c is not a legal character
541 * for a number. Stop accumulating digits.
543 break;
546 * c is legal: store it and look at the next.
548 *p++ = c;
549 fp->_IO_read_ptr++;
550 if (_IO_peekc(fp) == EOF) {
551 seen_eof++;
552 break; /* EOF */
556 * If we had only a sign, it is no good; push
557 * back the sign. If the number ends in `x',
558 * it was [sign] '0' 'x', so push back the x
559 * and treat it as [sign] '0'.
561 if (flags & NDIGITS) {
562 if (p > buf)
563 (void) _IO_ungetc(*(u_char *)--p, fp);
564 goto match_failure;
566 c = ((u_char *)p)[-1];
567 if (c == 'x' || c == 'X') {
568 --p;
569 (void) _IO_ungetc (c, fp);
571 if ((flags & SUPPRESS) == 0) {
572 u_long res;
574 *p = 0;
575 res = (*ccfn)(buf, (char **)NULL, base);
576 if (flags & POINTER)
577 *va_arg(ap, void **) = (void *)res;
578 else if (flags & SHORT)
579 *va_arg(ap, short *) = res;
580 else if (flags & LONG)
581 *va_arg(ap, long *) = res;
582 else
583 *va_arg(ap, int *) = res;
584 nassigned++;
586 nread += p - buf;
587 break;
589 #ifdef FLOATING_POINT
590 case CT_FLOAT:
591 /* scan a floating point number as if by strtod */
592 if (width == 0 || width > sizeof(buf) - 1)
593 width = sizeof(buf) - 1;
594 flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
595 for (p = buf; width; width--) {
596 c = (unsigned char)*fp->_IO_read_ptr;
598 * This code mimicks the integer conversion
599 * code, but is much simpler.
601 switch (c) {
603 case '0': case '1': case '2': case '3':
604 case '4': case '5': case '6': case '7':
605 case '8': case '9':
606 flags &= ~(SIGNOK | NDIGITS);
607 goto fok;
609 case '+': case '-':
610 if (flags & SIGNOK) {
611 flags &= ~SIGNOK;
612 goto fok;
614 break;
615 case '.':
616 if (flags & DPTOK) {
617 flags &= ~(SIGNOK | DPTOK);
618 goto fok;
620 break;
621 case 'e': case 'E':
622 /* no exponent without some digits */
623 if ((flags&(NDIGITS|EXPOK)) == EXPOK) {
624 flags =
625 (flags & ~(EXPOK|DPTOK)) |
626 SIGNOK | NDIGITS;
627 goto fok;
629 break;
631 break;
632 fok:
633 *p++ = c;
634 fp->_IO_read_ptr++;
635 if (_IO_peekc(fp) == EOF) {
636 seen_eof++;
637 break; /* EOF */
641 * If no digits, might be missing exponent digits
642 * (just give back the exponent) or might be missing
643 * regular digits, but had sign and/or decimal point.
645 if (flags & NDIGITS) {
646 if (flags & EXPOK) {
647 /* no digits at all */
648 while (p > buf)
649 _IO_ungetc (*(u_char *)--p, fp);
650 goto match_failure;
652 /* just a bad exponent (e and maybe sign) */
653 c = *(u_char *)--p;
654 if (c != 'e' && c != 'E') {
655 (void) _IO_ungetc (c, fp);/* sign */
656 c = *(u_char *)--p;
658 (void) _IO_ungetc (c, fp);
660 if ((flags & SUPPRESS) == 0) {
661 double res;
662 *p = 0;
663 #ifdef _IO_USE_DTOA
664 res = _IO_strtod(buf, NULL);
665 #else
666 res = atof(buf);
667 #endif
668 if (flags & LONG)
669 *va_arg(ap, double *) = res;
670 else
671 *va_arg(ap, float *) = res;
672 nassigned++;
674 nread += p - buf;
675 break;
676 #endif /* FLOATING_POINT */
679 eof_failure:
680 seen_eof++;
681 input_failure:
682 if (nassigned == 0)
683 nassigned = -1;
684 control_failure:
685 match_failure:
686 if (errp)
687 *errp |= 2;
688 done:
689 if (errp && seen_eof)
690 *errp |= 1;
691 return (nassigned);
695 * Fill in the given table from the scanset at the given format
696 * (just after `['). Return a pointer to the character past the
697 * closing `]'. The table has a 1 wherever characters should be
698 * considered part of the scanset.
700 static const u_char *
701 __sccl (tab, fmt)
702 char *tab;
703 const u_char *fmt;
705 register int c, n, v;
707 /* first `clear' the whole table */
708 c = *fmt++; /* first char hat => negated scanset */
709 if (c == '^') {
710 v = 1; /* default => accept */
711 c = *fmt++; /* get new first char */
712 } else
713 v = 0; /* default => reject */
714 /* should probably use memset here */
715 for (n = 0; n < 256; n++)
716 tab[n] = v;
717 if (c == 0)
718 return (fmt - 1);/* format ended before closing ] */
721 * Now set the entries corresponding to the actual scanset
722 * to the opposite of the above.
724 * The first character may be ']' (or '-') without being special;
725 * the last character may be '-'.
727 v = 1 - v;
728 for (;;) {
729 tab[c] = v; /* take character c */
730 doswitch:
731 n = *fmt++; /* and examine the next */
732 switch (n) {
734 case 0: /* format ended too soon */
735 return (fmt - 1);
737 case '-':
739 * A scanset of the form
740 * [01+-]
741 * is defined as `the digit 0, the digit 1,
742 * the character +, the character -', but
743 * the effect of a scanset such as
744 * [a-zA-Z0-9]
745 * is implementation defined. The V7 Unix
746 * scanf treats `a-z' as `the letters a through
747 * z', but treats `a-a' as `the letter a, the
748 * character -, and the letter a'.
750 * For compatibility, the `-' is not considerd
751 * to define a range if the character following
752 * it is either a close bracket (required by ANSI)
753 * or is not numerically greater than the character
754 * we just stored in the table (c).
756 n = *fmt;
757 if (n == ']' || n < c) {
758 c = '-';
759 break; /* resume the for(;;) */
761 fmt++;
762 do { /* fill in the range */
763 tab[++c] = v;
764 } while (c < n);
765 #if 1 /* XXX another disgusting compatibility hack */
767 * Alas, the V7 Unix scanf also treats formats
768 * such as [a-c-e] as `the letters a through e'.
769 * This too is permitted by the standard....
771 goto doswitch;
772 #else
773 c = *fmt++;
774 if (c == 0)
775 return (fmt - 1);
776 if (c == ']')
777 return (fmt);
778 #endif
779 break;
781 case ']': /* end of scanset */
782 return (fmt);
784 default: /* just another character */
785 c = n;
786 break;
789 /* NOTREACHED */