/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1991, 1990 MIPS Computer Systems, Inc.      |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 252.227-7013.  |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Avenue                               |
 * |         Sunnyvale, California 94088-3650, USA             |
 * |-----------------------------------------------------------|
 */
#ident	"$Header: atof.c,v 1.13.2.1 92/06/24 13:59:43 marker Exp $"

/*LINTLIBRARY*/
/*
 *	C library - ascii to floating (atof) and string to double (strtod)
 *
 *	This version compiles both atof and strtod depending on the value
 *	of STRTOD, which is set in the file and may be overridden on the
 *	"cc" command line.  The only difference is the storage of a pointer
 *	to the character which terminated the conversion.
 */

#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <math.h>

extern char _numeric[];	/* INTERNATIONAL */

#define MAXDIGITS 17

#if MIPSEL
#  define LSW 0
#  define MSW 1
static unsigned infinity[2] = { 0x00000000, 0x7ff00000 };
#endif
#if MIPSEB
#  define MSW 0
#  define LSW 1
static unsigned infinity[2] = { 0x7ff00000, 0x00000000 };
#endif

extern double _atod ();

#if STRTOD
#define STORE_PTR	if (ptr != (char **)0) (*ptr = (char *)(s - 1))
#else
#define STORE_PTR
#endif

#if defined(__STDC__)

#if STRTOD
double strtod(const char *s, char **ptr)
#else
double atof(const char *s)
#endif

#else

#if STRTOD
double
strtod(s, ptr)
    const char *s;
    char **ptr;
#else
double
atof (s)
    const char *s;

#endif /* STRTOD */
#endif /* __STDC__ */
{
    register unsigned c;
    register unsigned negate, decimal_point;
    register char *d;
    register int exp;
    register double x;
    char digits[MAXDIGITS];

    while (c = *s++, isspace(c));

    negate = 0;
    if (c == '+') {
	c = *s++;
    }
    else if (c == '-') {
	negate = 1;
	c = *s++;
    }
    d = digits;
    decimal_point = 0;
    exp = 0;
    while (1) {
	c -= '0';
	if (c < 10) {
	    if (d == digits+MAXDIGITS) {
		/* ignore more than 17 digits, but adjust exponent */
		exp += (decimal_point ^ 1);
	    }
	    else {
		if (c == 0 && d == digits) {
		    /* ignore leading zeros */
		}
		else {
		    *d++ = c;
		}
		exp -= decimal_point;
	    }
	}
	else if (c == _numeric[0] - '0' && !decimal_point) {	/* INTERNATIONAL */
	    decimal_point = 1;
	}
	else {
	    STORE_PTR;
	    break;
	}
	c = *s++;
    }
    if (d == digits) {
	return 0.0;
    }
    if (c == 'e'-'0' || c == 'E'-'0') {
	register unsigned negate_exp = 0;
	register int e = 0;
	c = *s++;
	if (c == '+' || c == ' ') {
	    c = *s++;
	}
	else if (c == '-') {
	    negate_exp = 1;
	    c = *s++;
	}
	while (c -= '0', c < 10) {
	    e = e * 10 + c;
	    c = *s++;
	}
	if (negate_exp) {
	    e = -e;
	}
	exp += e;
	STORE_PTR;
    }
    if (d == digits) {
	return 0.0;
    }

    if (exp < -340) {
	x = 0;
	errno = ERANGE;
    }
    else if (exp > 308) {
	x = *(double *)infinity;
	errno = ERANGE;
    }
    else {
	x = _atod (digits, d - digits, exp);
    }
    if (negate) {
	x = -x;
    }
    return x;
}
