OSDN Git Service

New version of scanf, with floating point support.
authorManuel Novoa III <mjn3@codepoet.org>
Tue, 13 Mar 2001 16:04:09 +0000 (16:04 -0000)
committerManuel Novoa III <mjn3@codepoet.org>
Tue, 13 Mar 2001 16:04:09 +0000 (16:04 -0000)
libc/stdio/scanf.c

index 9e7ccef..7070727 100644 (file)
@@ -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