diff options
author | Manuel Novoa III <mjn3@codepoet.org> | 2001-03-13 16:04:09 +0000 |
---|---|---|
committer | Manuel Novoa III <mjn3@codepoet.org> | 2001-03-13 16:04:09 +0000 |
commit | 22b68ede21b6d91f801e33edc6ed5397809644f9 (patch) | |
tree | 66050e858cf1294be9cb4fb43ec27db068bb865d /libc/stdio | |
parent | e127a0921f74756c5cdc460ac9096b4d4bf61389 (diff) |
New version of scanf, with floating point support.
Diffstat (limited to 'libc/stdio')
-rw-r--r-- | libc/stdio/scanf.c | 923 |
1 files changed, 545 insertions, 378 deletions
diff --git a/libc/stdio/scanf.c b/libc/stdio/scanf.c index 9e7ccef3c..7070727d9 100644 --- a/libc/stdio/scanf.c +++ b/libc/stdio/scanf.c @@ -1,3 +1,25 @@ + +/* + * Modified by Manuel Novoa III Mar 13, 2001 + * + * The vfscanf routine was completely rewritten to add features and remove + * bugs. The function __strtold, based on my strtod code in stdlib, was + * added to provide floating point support for the scanf functions. + * + * So far they pass the test cases from glibc-2.1.3, except in two instances. + * In one case, the test appears to be broken. The other case is something + * I need to research further. This version of scanf assumes it can only + * peek one character ahead. Apparently, glibc looks further. The difference + * can be seen when parsing a floating point value in the character + * sequence "100ergs". glibc is able to back up before the 'e' and return + * a value of 100, whereas this scanf reports a bad match with the stream + * pointer at 'r'. A similar situation can also happen when parsing hex + * values prefixed by 0x or 0X; a failure would occur for "0xg". In order to + * fix this, I need to rework the "ungetc" machinery in stdio.c again. + * I do have one reference though, that seems to imply scanf has a single + * character of lookahead. + */ + #include <stdlib.h> #include <unistd.h> #include <stdio.h> @@ -94,431 +116,576 @@ int vsscanf(__const char *sp, __const char *fmt, va_list ap) #ifdef L_vfscanf -#if FLOATS -int _vfscanf_fp_ref = 1; -#else -int _vfscanf_fp_ref = 0; -#endif - -/* #define skip() do{c=getc(fp); if (c<1) goto done;}while(isspace(c))*/ +#include <assert.h> +#include <ctype.h> +#include <limits.h> -#define skip() while(isspace(c)) { if ((c=getc(fp))<1) goto done; } - -#if FLOATS -/* fp scan actions */ -#define F_NADA 0 /* just change state */ -#define F_SIGN 1 /* set sign */ -#define F_ESIGN 2 /* set exponent's sign */ -#define F_INT 3 /* adjust integer part */ -#define F_FRAC 4 /* adjust fraction part */ -#define F_EXP 5 /* adjust exponent part */ -#define F_QUIT 6 - -#define NSTATE 8 -#define FS_INIT 0 /* initial state */ -#define FS_SIGNED 1 /* saw sign */ -#define FS_DIGS 2 /* saw digits, no . */ -#define FS_DOT 3 /* saw ., no digits */ -#define FS_DD 4 /* saw digits and . */ -#define FS_E 5 /* saw 'e' */ -#define FS_ESIGN 6 /* saw exp's sign */ -#define FS_EDIGS 7 /* saw exp's digits */ - -#define FC_DIG 0 -#define FC_DOT 1 -#define FC_E 2 -#define FC_SIGN 3 - -/* given transition,state do what action? */ -int fp_do[][NSTATE] = { - {F_INT, F_INT, F_INT, - F_FRAC, F_FRAC, - F_EXP, F_EXP, F_EXP}, /* see digit */ - {F_NADA, F_NADA, F_NADA, - F_QUIT, F_QUIT, F_QUIT, F_QUIT, F_QUIT}, /* see '.' */ - {F_QUIT, F_QUIT, - F_NADA, F_QUIT, F_NADA, - F_QUIT, F_QUIT, F_QUIT}, /* see e/E */ - {F_SIGN, F_QUIT, F_QUIT, F_QUIT, F_QUIT, - F_ESIGN, F_QUIT, F_QUIT}, /* see sign */ -}; +static int valid_digit(char c, char base) +{ + if (base == 16) { + return isxdigit(c); + } else { + return (isdigit(c) && (c < '0' + base)); + } +} -/* given transition,state what is new state? */ -int fp_ns[][NSTATE] = { - {FS_DIGS, FS_DIGS, FS_DIGS, - FS_DD, FS_DD, - FS_EDIGS, FS_EDIGS, FS_EDIGS}, /* see digit */ - {FS_DOT, FS_DOT, FS_DD, - }, /* see '.' */ - {0, 0, - FS_E, 0, FS_E, - }, /* see e/E */ - {FS_SIGNED, 0, 0, 0, 0, - FS_ESIGN, 0, 0}, /* see sign */ -}; +extern unsigned long long +_strto_ll(const char *str, char **endptr, int base, int uflag); -/* which states are valid terminators? */ -int fp_sval[NSTATE] = { - 0, 0, 1, 0, 1, 0, 0, 1 -}; -#endif +extern unsigned long +_strto_l(const char *str, char **endptr, int base, int uflag); -int vfscanf(fp, fmt, ap) -register FILE *fp; -register const char *fmt; -va_list ap; +/* #define skip() do{c=getc(fp); if (c<1) goto done;}while(isspace(c))*/ -{ -#if WANT_LONG_LONG - long long n; +#if WANT_LONG_LONG || WANT_LONG_LONG_ERROR +static const char qual[] = "hl" /* "jtz" */ "Lq"; +/* char = -2, short = -1, int = 0, long = 1, long long = 2 */ +static const char qsz[] = { -1, 1, 2, 2 }; #else - register long n; -#endif - register int c, width, lval, cnt = 0; - int store, neg, base, wide1, endnull, rngflag, c2; - register unsigned char *p; - unsigned char delim[128], digits[17], *q; - -#if FLOATS - long frac, expo; - int eneg, fraclen, fstate, trans; - double fx, fp_scan(); +static const char qual[] = "hl" /* "jtz" */; +static const char qsz[] = { -1, 1, }; #endif - if (!*fmt) - return (0); - - c = getc(fp); - while (c > 0) { - store = 0; - if (*fmt == '%') { - n = 0; - width = -1; - wide1 = 1; - base = 10; - lval = (sizeof(long) == sizeof(int)); - - store = 1; - endnull = 1; - neg = -1; - - strcpy(delim, "\011\012\013\014\015 "); - strcpy(digits, "0123456789ABCDEF"); - - if (*++fmt == '*') { - endnull = store = 0; - ++fmt; - } - - while (isdigit(*fmt)) { /* width digit(s) */ - if (width == -1) - width = 0; - wide1 = width = (width * 10) + (*fmt - '0'); - ++fmt; - } - --fmt; - fmtnxt: - ++fmt; - switch (tolower(*fmt)) { /* tolower() is a MACRO! */ - case '*': - endnull = store = 0; - goto fmtnxt; - case 'l': /* long data */ - lval = 1; -#if WANT_LONG_LONG - if (*fmt == 'L') { /* long long data */ - lval = 2; - } +#if WANT_DOUBLE || WANT_DOUBLE_ERROR + /*01234567890123456 */ +static const char spec[] = "%n[csoupxXidfeEgG"; +#else +static const char spec[] = "%n[csoupxXid"; #endif - goto fmtnxt; - case 'h': /* short data */ - lval = 0; - goto fmtnxt; +/* radix[i] <-> spec[i+5] o u p x X i d */ +static const char radix[] = { 8, 10, 16, 16, 16, 0, 10 }; + +struct scan_cookie { + FILE *fp; + int nread; + int width; + int ungot_char; + int ungot_flag; +}; - case 'i': /* any-base numeric */ - base = 0; - goto numfmt; +static int __strtold(long double *ld, struct scan_cookie *sc); - case 'b': /* unsigned binary */ - base = 2; - goto numfmt; +static void init_scan_cookie(struct scan_cookie *sc, FILE *fp) +{ + sc->fp = fp; + sc->nread = 0; + sc->ungot_flag = 0; + if ((sc->ungot_char = getc(fp)) > 0) { /* not EOF or EOS */ + sc->ungot_flag = 1; + } +} - case 'o': /* unsigned octal */ - base = 8; - goto numfmt; +static int scan_getc_nw(struct scan_cookie *sc) +{ + if (sc->ungot_flag == 0) { + sc->ungot_char = getc(sc->fp); + } else { + sc->ungot_flag = 0; + } + if (sc->ungot_char > 0) { + ++sc->nread; + } + return sc->ungot_char; +} - case 'x': /* unsigned hexadecimal */ - base = 16; - goto numfmt; +static int scan_getc(struct scan_cookie *sc) +{ + if (sc->ungot_flag == 0) { + sc->ungot_char = getc(sc->fp); + } + if (--sc->width < 0) { + sc->ungot_flag = 1; + return 0; + } + sc->ungot_flag = 0; + if (sc->ungot_char > 0) { + ++sc->nread; + } + return sc->ungot_char; +} - case 'd': /* SIGNED decimal */ - neg = 0; - /* FALL-THRU */ +static void scan_ungetc(struct scan_cookie *sc) +{ + if (sc->ungot_flag != 0) { + assert(sc->width < 0); + return; + } + sc->ungot_flag = 1; + if (sc->ungot_char > 0) { /* not EOF or EOS */ + --sc->nread; + } +} - case 'u': /* unsigned decimal */ - numfmt:skip(); +static void kill_scan_cookie(struct scan_cookie *sc) +{ + if (sc->ungot_flag) { + ungetc(sc->ungot_char,sc->fp); + } +} -#if 0 - if (isupper(*fmt)) - lval = 1; +int vfscanf(fp, format, ap) +FILE *fp; +const char *format; +va_list ap; +{ +#if WANT_LONG_LONG +#define STRTO_L_(s,e,b,u) _strto_ll(s,e,b,u) +#define MAX_DIGITS 64 +#define UV_TYPE unsigned long long +#define V_TYPE long long +#else +#define STRTO_L_(s,e,b,u) _strto_l(s,e,b,u) +#define MAX_DIGITS 32 +#define UV_TYPE unsigned long +#define V_TYPE long #endif - - if (!base) { - base = 10; - neg = 0; - if (c == '%') { - base = 2; - goto skip1; - } else if (c == '0') { - c = getc(fp); - if (c < 1) - goto savnum; - if ((c != 'x') - && (c != 'X')) { - base = 8; - digits[8] = '\0'; - goto zeroin; - } - base = 16; - goto skip1; +#if WANT_DOUBLE + long double ld; +#endif + UV_TYPE uv; + struct scan_cookie sc; + unsigned const char *fmt; + const char *p; + unsigned char *b; + void *vp; + int cc, i, cnt; + signed char lval; + unsigned char store, usflag, base, invert, r0, r1; + unsigned char buf[MAX_DIGITS+2]; + unsigned char scanset[UCHAR_MAX + 1]; + + init_scan_cookie(&sc,fp); + + fmt = (unsigned const char *) format; + cnt = 0; + + while (*fmt) { + store = 1; + lval = 0; + sc.width = INT_MAX; + if (*fmt == '%') { /* Conversion specification. */ + ++fmt; + if (*fmt == '*') { /* Suppress assignment. */ + store = 0; + ++fmt; + } + for (i = 0 ; isdigit(*fmt) ; sc.width = i) { + i = (i * 10) + (*fmt++ - '0'); /* Get specified width. */ + } + for (i = 0 ; i < sizeof(qual) ; i++) { /* Optional qualifier. */ + if (qual[i] == *fmt) { + ++fmt; + lval += qsz[i]; + if ((i < 2) && (qual[i] == *fmt)) { /* Double h or l. */ + ++fmt; + lval += qsz[i]; } + break; } - - if ((neg == 0) && (base == 10) - && ((neg = (c == '-')) || (c == '+'))) { - skip1: - c = getc(fp); - if (c < 1) - goto done; + } + for (p = spec ; *p ; p++) { /* Process format specifier. */ + if (*fmt != *p) continue; + if (p-spec < 1) { /* % - match a '%'*/ + goto matchchar; } - - digits[base] = '\0'; - p = ((unsigned char *) - strchr(digits, toupper(c))); - - if ((!c || !p) && width) + if (p-spec < 2) { /* n - store number of chars read */ + *(va_arg(ap, int *)) = sc.nread; + scan_getc_nw(&sc); + goto nextfmt; + } + if (p-spec > 3) { /* skip white space if not c or [ */ + while (isspace(scan_getc_nw(&sc))) + {} + scan_ungetc(&sc); + } + if (p-spec < 5) { /* [,c,s - string conversions */ + if ((*p == 'c') && (sc.width == INT_MAX)) { + sc.width = 1; + } + invert = 0; + for (i=0 ; i<= UCHAR_MAX ; i++) { + scanset[i] = ((*p == 's') ? (isspace(i) == 0) : 0); + } + if (*p == '[') { /* need to build a scanset */ + if (*++fmt == '^') { + invert = 1; + ++fmt; + } + if (*fmt == ']') { + scanset[(int)']'] = 1; + ++fmt; + } + r0 = 0; + while (*fmt && *fmt !=']') { /* build scanset */ + if ((*fmt == '-') && r0 && (fmt[1] != ']')) { + /* range */ + ++fmt; + if (*fmt < r0) { + r1 = r0; + r0 = *fmt; + } else { + r1 = *fmt; + } + for (i=r0 ; i<= r1 ; i++) { + scanset[i] = 1; + } + r0 = 0; + } else { + r0 = *fmt; + scanset[r0] = 1; + } + ++fmt; + } + if (!*fmt) { /* format string exhausted! */ + goto done; + } + } + /* ok -- back to common work */ + if (sc.width <= 0) { + goto done; + } + if (store) { + b = va_arg(ap, unsigned char *); + } else { + b = buf; + } + i = 0; + cc = scan_getc(&sc); + while ((cc>0) && (scanset[cc] != invert)) { + i = store; /* yes, we stored something */ + *b = cc; + b += store; + cc = scan_getc(&sc); + } + if (*p != 'c') { /* nul-terminate the stored string */ + *b = 0; + cnt += i; + goto nextfmt; + } else if (sc.width < 0) { /* case 'c' */ + cnt += store; + goto nextfmt; + } + scan_ungetc(&sc); goto done; - - while (p && width-- && c) { - n = (n * base) + (p - digits); - c = getc(fp); - zeroin: - p = ((unsigned char *) - strchr(digits, toupper(c))); } - savnum: - if (store) { - if (neg == 1) - n = -n; + if (p-spec < 12) { /* o,u,p,x,X,i,d - (un)signed integer */ + if (*p == 'p') { + /* assume pointer same size as int or long. */ + lval = (sizeof(char *) == sizeof(long)); + } + usflag = ((p-spec) < 10); /* (1)0 if (un)signed */ + base = radix[(int)(p-spec) - 5]; + b = buf; + if (sc.width <= 0) { + goto done; + } + cc = scan_getc(&sc); + if ((cc == '+') || (cc == '-')) { /* Handle leading sign.*/ + *b++ = cc; + cc = scan_getc(&sc); + } + if (cc == '0') { /* Possibly set base and handle prefix. */ + if ((base == 0) || (base == 16)) { + cc = scan_getc(&sc); + if ((cc == 'x') || (cc == 'X')) { + /* We're committed to base 16 now. */ + base = 16; + cc = scan_getc(&sc); + } else { /* oops... back up */ + scan_ungetc(&sc); + cc = '0'; + if (base == 0) { + base = 8; + } + } + } + } + /* At this point, we're ready to start reading digits. */ + if (cc == '0') { + *b++ = cc; /* Store first leading 0 */ + do { /* but ignore others. */ + cc = scan_getc(&sc); + } while (cc == '0'); + } + while (valid_digit(cc,base)) { /* Now for nonzero digits.*/ + if (b - buf < MAX_DIGITS) { + *b++ = cc; + } + cc = scan_getc(&sc); + } + *b = 0; /* null-terminate */ + if ((b == buf) || (*--b == '+') || (*b == '-')) { + scan_ungetc(&sc); + goto done; /* No digits! */ + } + if (store) { + if (*buf == '-') { + usflag = 0; + } + uv = STRTO_L_(buf, NULL, base, usflag); + vp = va_arg(ap, void *); + switch (lval) { + case 2: /* If no long long, treat as long . */ #if WANT_LONG_LONG - if (lval == 2) - *va_arg(ap, long long *) = n; - - else + *((unsigned long long *)vp) = uv; + break; #endif - if (lval == 1) - *va_arg(ap, long *) = n; - - else - *va_arg(ap, short *) = n; - - ++cnt; - } - break; - -#if FLOATS - case 'e': /* float */ - case 'f': - case 'g': - skip(); - fprintf(stderr, "LIBM:SCANF"); -#if 0 - if (isupper(*fmt)) - lval = 1; + case 1: +#if ULONG_MAX == UINT_MAX + case 0: /* int and long int are the same */ #endif - - fstate = FS_INIT; - neg = 0; - eneg = 0; - n = 0; - frac = 0; - expo = 0; - fraclen = 0; - - while (c && width--) { - if (c >= '0' && c <= '9') - trans = FC_DIG; - else if (c == '.') - trans = FC_DOT; - else if (c == '+' || c == '-') - trans = FC_SIGN; - else if (tolower(c) == 'e') - trans = FC_E; - else - goto fdone; - - switch (fp_do[trans][fstate]) { - case F_SIGN: - neg = (c == '-'); - break; - case F_ESIGN: - eneg = (c == '-'); - break; - case F_INT: - n = 10 * n + (c - '0'); - break; - case F_FRAC: - frac = 10 * frac + (c - '0'); - fraclen++; - break; - case F_EXP: - expo = 10 * expo + (c - '0'); - break; - case F_QUIT: - goto fdone; +#if WANT_LONG_LONG + if (usflag) { + if (uv > ULONG_MAX) { + uv = ULONG_MAX; + } + } else if (((V_TYPE)uv) > LONG_MAX) { + uv = LONG_MAX; + } else if (((V_TYPE)uv) < LONG_MIN) { + uv = (UV_TYPE) LONG_MIN; + } +#endif + *((unsigned long *)vp) = (unsigned long)uv; + break; +#if ULONG_MAX != UINT_MAX + case 0: /* int and long int are different */ + if (usflag) { + if (uv > UINT_MAX) { + uv = UINT_MAX; + } + } else if (((V_TYPE)uv) > INT_MAX) { + uv = INT_MAX; + } else if (((V_TYPE)uv) < INT_MIN) { + uv = (UV_TYPE) INT_MIN; + } + *((unsigned int *)vp) = (unsigned int)uv; + break; +#endif + case -1: + if (usflag) { + if (uv > USHRT_MAX) { + uv = USHRT_MAX; + } + } else if (((V_TYPE)uv) > SHRT_MAX) { + uv = SHRT_MAX; + } else if (((V_TYPE)uv) < SHRT_MIN) { + uv = (UV_TYPE) SHRT_MIN; + } + *((unsigned short *)vp) = (unsigned short)uv; + break; + case -2: + if (usflag) { + if (uv > UCHAR_MAX) { + uv = UCHAR_MAX; + } + } else if (((V_TYPE)uv) > CHAR_MAX) { + uv = CHAR_MAX; + } else if (((V_TYPE)uv) < CHAR_MIN) { + uv = (UV_TYPE) CHAR_MIN; + } + *((unsigned char *)vp) = (unsigned char) uv; + break; + default: + assert(0); + } + ++cnt; } - fstate = fp_ns[trans][fstate]; - c = getc(fp); + goto nextfmt; } - - fdone: - if (!fp_sval[fstate]) - goto done; - if (store) { - fx = fp_scan(neg, eneg, n, frac, expo, fraclen); - if (lval) - *va_arg(ap, double *) = fx; - - else - *va_arg(ap, float *) = fx; - - ++cnt; +#if WANT_DOUBLE + else { /* floating point */ + if (sc.width <= 0) { + goto done; + } + if (__strtold(&ld, &sc)) { /* Success! */ + if (store) { + vp = va_arg(ap, void *); + switch (lval) { + case 2: + *((long double *)vp) = ld; + break; + case 1: + *((double *)vp) = (double) ld; + break; + case 0: + *((float *)vp) = (float) ld; + break; + default: /* Illegal qualifier! */ + assert(0); + goto done; + } + ++cnt; + } + goto nextfmt; + } } - break; #else - case 'e': /* float */ - case 'f': - case 'g': - fprintf(stderr, "LIBC:SCANF"); - exit(-1); + assert(0); #endif + goto done; + } + /* Unrecognized specifier! */ + goto done; + } if (isspace(*fmt)) { /* Consume all whitespace. */ + while (isspace(scan_getc_nw(&sc))) + {} + } else { /* Match the current fmt char. */ + matchchar: + if (scan_getc_nw(&sc) != *fmt) { + goto done; + } + scan_getc_nw(&sc); + } + nextfmt: + scan_ungetc(&sc); + ++fmt; + } - case 'c': /* character data */ - width = wide1; - lval = endnull = 0; - delim[0] = '\0'; - goto strproc; - - case '[': /* string w/ delimiter set */ + done: /* end of scan */ + kill_scan_cookie(&sc); - /* get delimiters */ - p = delim; + if ((sc.ungot_char <= 0) && (cnt == 0) && (*fmt)) { + return (EOF); + } - if (*++fmt == '^') { - fmt++; - lval = 0; - } else - lval = 1; + return (cnt); +} - rngflag = 2; - if ((*fmt == ']') || (*fmt == '-')) { - *p++ = *fmt++; - rngflag = 0; - } +/*****************************************************************************/ +#if WANT_DOUBLE - while (*fmt != ']') { - if (*fmt == '\0') - goto done; - switch (rngflag) { - case 1: - c2 = *(p - 2); - if (c2 <= *fmt) { - p -= 2; - while (c2 < *fmt) - *p++ = c2++; - rngflag = 2; - break; - } - /* fall thru intentional */ +#include <float.h> - case 0: - rngflag = (*fmt == '-'); - break; +#define MAX_SIG_DIGITS 20 +#define MAX_IGNORED_DIGITS 2000 +#define MAX_ALLOWED_EXP (MAX_SIG_DIGITS + MAX_IGNORED_DIGITS + LDBL_MAX_10_EXP) - case 2: - rngflag = 0; - } +#if LDBL_DIG > MAX_SIG_DIGITS +#error need to adjust MAX_SIG_DIGITS +#endif - *p++ = *fmt++; - } +#include <limits.h> +#if MAX_ALLOWED_EXP > INT_MAX +#error size assumption violated for MAX_ALLOWED_EXP +#endif - *p = '\0'; - goto strproc; - - case 's': /* string data */ - lval = 0; - skip(); - strproc: - /* process string */ - p = va_arg(ap, unsigned char *); - - /* if the 1st char fails, match fails */ - if (width) { - q = ((unsigned char *) - strchr(delim, c)); - if ((c < 1) || lval == (q == 0)) { - if (endnull) - *p = '\0'; - goto done; - } - } +int __strtold(long double *ld, struct scan_cookie *sc) +{ + long double number; + long double p10; + int exponent_power; + int exponent_temp; + int negative; + int num_digits; + int since_decimal; + int c; + + c = scan_getc(sc); /* Decrements width. */ + + negative = 0; + switch(c) { /* Handle optional sign. */ + case '-': negative = 1; /* Fall through to get next char. */ + case '+': c = scan_getc(sc); + } + + number = 0.; + num_digits = -1; + exponent_power = 0; + since_decimal = INT_MIN; + + LOOP: + while (isdigit(c)) { /* Process string of digits. */ + ++since_decimal; + if (num_digits < 0) { /* First time through? */ + ++num_digits; /* We've now seen a digit. */ + } + if (num_digits || (c != '0')) { /* had/have nonzero */ + ++num_digits; + if (num_digits <= MAX_SIG_DIGITS) { /* Is digit significant? */ + number = number * 10. + (c - '0'); + } + } + c = scan_getc(sc); + } + + if ((c == '.') && (since_decimal < 0)) { /* If no previous decimal pt, */ + since_decimal = 0; /* save position of decimal point */ + c = scan_getc(sc); /* and process rest of digits */ + goto LOOP; + } + + if (num_digits<0) { /* Must have at least one digit. */ + goto FAIL; + } + + if (num_digits > MAX_SIG_DIGITS) { /* Adjust exp for skipped digits. */ + exponent_power += num_digits - MAX_SIG_DIGITS; + } + + if (since_decimal >= 0) { /* Adjust exponent for decimal point. */ + exponent_power -= since_decimal; + } + + if (negative) { /* Correct for sign. */ + number = -number; + negative = 0; /* Reset for exponent processing below. */ + } + + /* Process an exponent string. */ + if (c == 'e' || c == 'E') { + c = scan_getc(sc); + switch(c) { /* Handle optional sign. */ + case '-': negative = 1; /* Fall through to get next char. */ + case '+': c = scan_getc(sc); + } - for (;;) { /* FOREVER */ - if (store) - *p++ = c; - if (((c = getc(fp)) < 1) || (--width == 0)) - break; + num_digits = 0; + exponent_temp = 0; + while (isdigit(c)) { /* Process string of digits. */ + if (exponent_temp < MAX_ALLOWED_EXP) { /* overflow check */ + exponent_temp = exponent_temp * 10 + (c - '0'); + } + c = scan_getc(sc); + ++num_digits; + } - q = ((unsigned char *) - strchr(delim, c)); - if (lval == (q == 0)) - break; - } + if (num_digits == 0) { /* Were there no exp digits? */ + goto FAIL; + } /* else */ + if (negative) { + exponent_power -= exponent_temp; + } else { + exponent_power += exponent_temp; + } + } - if (store) { - if (endnull) - *p = '\0'; - ++cnt; - } - break; + if (number != 0.) { + /* Now scale the result. */ + exponent_temp = exponent_power; + p10 = 10.; - case '\0': /* early EOS */ - --fmt; - /* FALL THRU */ + if (exponent_temp < 0) { + exponent_temp = -exponent_temp; + } - default: - goto cmatch; + while (exponent_temp) { + if (exponent_temp & 1) { + if (exponent_power < 0) { + number /= p10; + } else { + number *= p10; + } } - } else if (isspace(*fmt)) { /* skip whitespace */ - skip(); - } else { /* normal match char */ - cmatch: - if (c != *fmt) - break; - c = getc(fp); + exponent_temp >>= 1; + p10 *= p10; } - - if (!*++fmt) - break; } + *ld = number; + return 1; - done: /* end of scan */ - if ((c == EOF) && (cnt == 0)) - return (EOF); - - if (c != EOF) - ungetc(c, fp); - return (cnt); + FAIL: + scan_ungetc(sc); + return 0; } - +#endif /* WANT_DOUBLE */ #endif |