Update contrib.
3 * Portions Copyright (c) 1990-2004 Nokia Corporation and/or its subsidiary(-ies).
7 /* No user fns here. Pesch 15apr92. */
10 * Copyright (c) 1990 The Regents of the University of California.
11 * All rights reserved.
13 * Redistribution and use in source and binary forms are permitted
14 * provided that the above copyright notice and this paragraph are
15 * duplicated in all such forms and that any documentation,
16 * advertising materials, and other materials related to such
17 * distribution and use acknowledge that the software was developed
18 * by the University of California, Berkeley. The name of the
19 * University may not be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
34 #define BUF (MAXEXP+MAXFRACT+3) /* 3 = sign + decimal point + NUL */
37 * Flags used during conversion.
40 #define LONG 0x01 /* l: long or double */
41 #define LONGDBL 0x02 /* L: long double; unimplemented */
42 #define SHORT 0x04 /* h: short */
43 #define SUPPRESS 0x08 /* suppress assignment */
44 #define POINTER 0x10 /* weird %p pointer (`fake hex') */
45 #define NOSKIP 0x20 /* do not skip blanks */
48 * The following are used in numeric conversions only:
49 * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point;
50 * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral.
53 #define SIGNOK 0x40 /* +/- is (still) legal */
54 #define NDIGITS 0x80 /* no digits detected */
56 #define DPTOK 0x100 /* (float) decimal point is still legal */
57 #define EXPOK 0x200 /* (float) exponent (e+3, etc) still legal */
59 #define PFXOK 0x100 /* 0x prefix is (still) legal */
60 #define NZDIGITS 0x200 /* no zero digits detected */
66 #define CT_CHAR 0 /* %c conversion */
67 #define CT_CCL 1 /* %[...] conversion */
68 #define CT_STRING 2 /* %s conversion */
69 #define CT_INT 3 /* integer, i.e., strtol or strtoul */
70 #define CT_FLOAT 4 /* floating, i.e., strtod */
73 #define u_long unsigned long
75 /*static*/ u_char *__sccl (register char *tab,register u_char *fmt);
81 #define BufferEmpty (fp->_r <= 0 && __srefill(fp))
84 __svfscanf (register FILE *fp,char const *fmt0, va_list ap)
86 register u_char *fmt = (u_char *) fmt0;
87 register int c; /* character from format, or conversion */
88 register size_t width; /* field width, or 0 */
89 register char *p; /* points into all kinds of strings */
90 register int n; /* handy integer */
91 register int flags; /* flags as defined above */
92 register char *p0; /* saves original value of p when necessary */
93 int nassigned; /* number of fields assigned */
94 int nread; /* number of characters consumed from fp */
95 int base = 0; /* base argument to strtol/strtoul */
97 u_long (*ccfn) (const char *_n, char **_end_PTR, int _base) = 0; /* conversion function (strtol/strtoul) */
98 char ccltab[256]; /* character class table for %[...] */
99 char buf[BUF]; /* buffer for numeric conversions */
107 /* `basefix' is used to avoid `if' tests in the integer scanner */
108 static const short basefix[17] =
109 {10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
124 if (!isspace (*fp->_p))
126 nread++, fp->_r--, fp->_p++;
136 * switch on the format. continue if done; break once format
162 /* not supported flags |= LONGDBL; */
178 width = width * 10 + c - '0';
182 * Conversions. Those marked `compat' are for
183 * 4.[123]BSD compatibility.
185 * (According to ANSI, E and X formats are supposed to
186 * the same as e and x. Sorry about that.)
189 case 'D': /* compat */
194 ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtol;
200 ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtol;
204 case 'O': /* compat */
209 ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtoul;
215 ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtoul;
219 case 'X': /* compat XXX */
221 flags |= PFXOK; /* enable 0x prefixing */
223 ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtoul;
227 case 'E': /* compat XXX */
228 case 'G': /* compat XXX */
229 /* ANSI says that E,G and X behave the same way as e,g,x */
242 fmt = __sccl (ccltab, fmt);
252 case 'p': /* pointer format is like hex */
253 flags |= POINTER | PFXOK;
255 ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtoul;
260 if (flags & SUPPRESS) /* ??? */
264 sp = va_arg (ap, short *);
267 else if (flags & LONG)
269 lp = va_arg (ap, long *);
274 ip = va_arg (ap, int *);
280 * Disgusting backwards compatibility hacks. XXX
282 case '\0': /* compat */
285 default: /* compat */
289 ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtol;
295 * We have a conversion that requires input.
301 * Consume leading white space, except for formats that
304 if ((flags & NOSKIP) == 0)
306 while (isspace (*fp->_p))
316 * Note that there is at least one character in the
317 * buffer, so conversions that do not set NOSKIP ca
318 * no longer result in an input failure.
329 /* scan arbitrary characters (sets NOSKIP) */
332 if (flags & SUPPRESS)
338 if ((n = fp->_r) < (int)width)
362 size_t r = fread ((void*) va_arg (ap, char *), 1, width, fp);
372 /* scan a (nonempty) character class (sets NOSKIP) */
374 width = (unsigned long)(~0); /* `infinity' */
375 /* take only those things in the class */
376 if (flags & SUPPRESS)
379 while (ccltab[*fp->_p])
381 n++, fp->_r--, fp->_p++;
396 p0 = p = va_arg (ap, char *);
397 while (ccltab[*fp->_p])
420 /* like CCL, but zero-length string OK, & no NOSKIP */
422 width = (unsigned long)(~0);
423 if (flags & SUPPRESS)
426 while (!isspace (*fp->_p))
428 n++, fp->_r--, fp->_p++;
438 p0 = p = va_arg (ap, char *);
439 while (!isspace (*fp->_p))
455 /* scan an integer as if by strtol/strtoul */
457 if (width == 0 || width > sizeof (buf) - 1)
458 width = sizeof (buf) - 1;
460 /* size_t is unsigned, hence this optimisation */
461 if (--width > sizeof (buf) - 2)
462 width = sizeof (buf) - 2;
465 flags |= SIGNOK | NDIGITS | NZDIGITS;
466 for (p = buf; width; width--)
470 * Switch on the character; `goto ok' if we
471 * accept it as a part of number.
476 * The digit 0 is always legal, but is special.
477 * For %i conversions, if no digits (zero or nonzero)
478 * have been scanned (only signs), we will have base==0.
479 * In that case, we should set it to 8 and enable 0x
480 * prefixing. Also, if we have not scanned zero digits
481 * before this, do not turn off prefixing (someone else
482 * will turn it off if we have scanned any nonzero digits).
490 if (flags & NZDIGITS)
491 flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
493 flags &= ~(SIGNOK | PFXOK | NDIGITS);
496 /* 1 through 7 always legal */
504 base = basefix[base];
505 flags &= ~(SIGNOK | PFXOK | NDIGITS);
508 /* digits 8 and 9 ok iff decimal or hex */
511 base = basefix[base];
513 break; /* not legal here */
514 flags &= ~(SIGNOK | PFXOK | NDIGITS);
517 /* letters ok iff hex */
530 /* no need to fix base here */
532 break; /* not legal here */
533 flags &= ~(SIGNOK | PFXOK | NDIGITS);
536 /* sign ok only as first character */
546 /* x ok iff flag still set & 2nd char */
549 if (flags & PFXOK && p == buf + 1)
551 base = 16;/* if %i */
559 * If we got here, c is not a legal character
560 * for a number. Stop accumulating digits.
565 * c is legal: store it and look at the next.
575 * If we had only a sign, it is no good; push back the sign.
576 * If the number ends in `x', it was [sign] '0' 'x', so push back
577 * the x and treat it as [sign] '0'.
582 (void) ungetc (*(u_char *)-- p, fp);
585 c = ((u_char *) p)[-1];
586 if (c == 'x' || c == 'X')
589 /*(void)*/ ungetc (c, fp);
591 if ((flags & SUPPRESS) == 0)
596 res = (*ccfn) (buf, (char **) NULL, base);
598 *(va_arg (ap, void* *)) = (void*) res;
599 else if (flags & SHORT)
601 sp = va_arg (ap, short *);
604 else if (flags & LONG)
606 lp = va_arg (ap, long *);
611 ip = va_arg (ap, int *);
620 /* scan a floating point number as if by strtod */
622 if (width == 0 || width > sizeof (buf) - 1)
623 width = sizeof (buf) - 1;
625 /* size_t is unsigned, hence this optimisation */
626 if (--width > sizeof (buf) - 2)
627 width = sizeof (buf) - 2;
630 flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
631 for (p = buf; width; width--)
635 * This code mimicks the integer conversion
636 * code, but is much simpler.
651 flags &= ~(SIGNOK | NDIGITS);
665 flags &= ~(SIGNOK | DPTOK);
671 /* no exponent without some digits */
672 if ((flags & (NDIGITS | EXPOK)) == EXPOK)
675 (flags & ~(EXPOK | DPTOK)) |
691 * If no digits, might be missing exponent digits
692 * (just give back the exponent) or might be missing
693 * regular digits, but had sign and/or decimal point.
699 /* no digits at all */
701 ungetc (*(u_char *)-- p, fp);
704 /* just a bad exponent (e and maybe sign) */
706 if (c != 'e' && c != 'E')
708 (void) ungetc (c, fp); /* sign */
711 (void) ungetc (c, fp);
713 if ((flags & SUPPRESS) == 0)
721 dp = va_arg (ap, double *);
726 flp = va_arg (ap, float *);
736 return nassigned ? nassigned : -1;
742 * Fill in the given table from the scanset at the given format
743 * (just after `['). Return a pointer to the character past the
744 * closing `]'. The table has a 1 wherever characters should be
745 * considered part of the scanset.
750 __sccl (register char *tab,register u_char *fmt)
752 register int c, n, v;
754 /* first `clear' the whole table */
755 c = *fmt++; /* first char hat => negated scanset */
758 v = 1; /* default => accept */
759 c = *fmt++; /* get new first char */
762 v = 0; /* default => reject */
763 /* should probably use memset here */
764 for (n = 0; n < 256; n++)
767 return fmt - 1; /* format ended before closing ] */
770 * Now set the entries corresponding to the actual scanset to the
771 * opposite of the above.
773 * The first character may be ']' (or '-') without being special; the
774 * last character may be '-'.
780 tab[c] = (char)v; /* take character c */
782 n = *fmt++; /* and examine the next */
786 case 0: /* format ended too soon */
791 * A scanset of the form [01+-] is defined as `the digit 0, the
792 * digit 1, the character +, the character -', but the effect of a
793 * scanset such as [a-zA-Z0-9] is implementation defined. The V7
794 * Unix scanf treats `a-z' as `the letters a through z', but treats
795 * `a-a' as `the letter a, the character -, and the letter a'.
797 * For compatibility, the `-' is not considerd to define a range if
798 * the character following it is either a close bracket (required by
799 * ANSI) or is not numerically greater than the character we just
800 * stored in the table (c).
803 if (n == ']' || n < c)
806 break; /* resume the for(;;) */
810 { /* fill in the range */
814 #if 1 /* XXX another disgusting compatibility hack */
816 * Alas, the V7 Unix scanf also treats formats such
817 * as [a-c-e] as `the letters a through e'. This too
818 * is permitted by the standard....
831 case ']': /* end of scanset */
834 default: /* just another character */