summaryrefslogtreecommitdiff
path: root/libc/stdio
diff options
context:
space:
mode:
authorManuel Novoa III <mjn3@codepoet.org>2001-03-13 16:04:09 +0000
committerManuel Novoa III <mjn3@codepoet.org>2001-03-13 16:04:09 +0000
commit22b68ede21b6d91f801e33edc6ed5397809644f9 (patch)
tree66050e858cf1294be9cb4fb43ec27db068bb865d /libc/stdio
parente127a0921f74756c5cdc460ac9096b4d4bf61389 (diff)
New version of scanf, with floating point support.
Diffstat (limited to 'libc/stdio')
-rw-r--r--libc/stdio/scanf.c923
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