Browse Source

New version of scanf, with floating point support.

Manuel Novoa III 24 years ago
parent
commit
22b68ede21
1 changed files with 545 additions and 378 deletions
  1. 545 378
      libc/stdio/scanf.c

+ 545 - 378
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