/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*          Copyright (c) 1985-2011 AT&T Intellectual Property          *
*          Copyright (c) 2020-2023 Contributors to ksh 93u+m           *
*                      and is licensed under the                       *
*                 Eclipse Public License, Version 2.0                  *
*                                                                      *
*                A copy of the License is available at                 *
*      https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html      *
*         (with md5 checksum 84283fa8859daf213bdda5a9f8d1be1d)         *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                  David Korn <dgk@research.att.com>                   *
*                   Phong Vo <kpv@research.att.com>                    *
*                  Martijn Dekker <martijn@inlv.org>                   *
*                                                                      *
***********************************************************************/
/*
 * AT&T Research
 * Glenn Fowler & Phong Vo
 *
 * common header and implementation for
 *
 *	strtof		strtod		strtold		_sfdscan
 *	strntof		strntod		strntold
 *
 * define these macros to instantiate an implementation:
 *
 *	S2F_function	the function name
 *	S2F_static	<0:export =0:extern >0:static
 *	S2F_type	0:float 1:double 2:long.double
 *	S2F_qualifier	1 for optional [fFlL] qualifier suffix
 *	S2F_size	1 for interface with size_t second arg
 *	S2F_scan	1 for alternate interface with these arguments:
 *				void* handle
 *				int (*getchar)(void* handle, int flag)
 *			exactly one extra (*getchar)() is done, i.e.,
 *			the caller must do the pushback
 *				flag==0		get next char
 *				flag==1		no number seen
 *			return 0 on error or EOF
 */

#include "sfhdr.h"
#include "FEATURE/float"

/*
 * the default is _sfdscan for standalone sfio compatibility
 */

#if !defined(S2F_function)
#define S2F_function	_sfdscan
#define S2F_static	1
#define S2F_type	2
#define S2F_scan	1
#ifndef elementsof
#define elementsof(a)	(sizeof(a)/sizeof(a[0]))
#endif
#endif

#if S2F_type == 2 && _ast_fltmax_double
#undef	S2F_type
#define S2F_type	1
#endif

#if S2F_type == 0
#define S2F_number	float
#define S2F_ldexp	ldexp
#define S2F_pow10	_Sffpow10
#define S2F_inf		_Sffinf
#define S2F_nan		_Sffnan
#define S2F_min		(FLT_MIN)
#define S2F_max		(FLT_MAX)
#define S2F_exp_10_min	(FLT_MIN_10_EXP)
#define S2F_exp_10_max	(FLT_MAX_10_EXP)
#define S2F_exp_2_min	(FLT_MIN_EXP)
#define S2F_exp_2_max	(FLT_MAX_EXP)
#endif
#if S2F_type == 1
#define S2F_number	double
#define S2F_ldexp	ldexp
#define S2F_pow10	_Sfdpow10
#define S2F_inf		_Sfdinf
#define S2F_nan		_Sfdnan
#define S2F_min		(DBL_MIN)
#define S2F_max		(DBL_MAX)
#define S2F_exp_10_min	(DBL_MIN_10_EXP)
#define S2F_exp_10_max	(DBL_MAX_10_EXP)
#define S2F_exp_2_min	(DBL_MIN_EXP)
#define S2F_exp_2_max	(DBL_MAX_EXP)
#endif
#if S2F_type == 2
#define S2F_number	long double
#define S2F_ldexp	ldexpl
#define S2F_pow10	_Sflpow10
#define S2F_inf		_Sflinf
#define S2F_nan		_Sflnan
#define S2F_min		(LDBL_MIN)
#define S2F_max		(LDBL_MAX)
#define S2F_exp_10_min	(LDBL_MIN_10_EXP)
#define S2F_exp_10_max	(LDBL_MAX_10_EXP)
#define S2F_exp_2_min	(LDBL_MIN_EXP)
#define S2F_exp_2_max	(LDBL_MAX_EXP)
#endif

#if -S2F_exp_10_min < S2F_exp_10_max
#define S2F_exp_10_abs	(-S2F_exp_10_min)
#else
#define S2F_exp_10_abs	S2F_exp_10_max
#endif

#define S2F_batch	_ast_flt_unsigned_max_t

#undef	ERR		/* who co-opted this namespace? */

#if S2F_scan

typedef int (*S2F_get_f)(void*, int);

#define ERR(e)
#define GET(p)		(*get)(p,0)
#define NON(p)		(*get)(p,1)
#define PUT(p)
#define REV(p,t,b)
#define SET(p,t,b)

#else

#define ERR(e)		(errno=(e))
#define NON(p)

#if S2F_size
#define GET(p)		(((p)<(z))?(*p++):(back=0))
#define PUT(p)		(end?(*end=(char*)p-back):NULL)
#define REV(p,t,b)	(p=t,back=b)
#define SET(p,t,b)	(t=p,b=back)
#else
#define GET(p)		(*p++)
#define PUT(p)		(end?(*end=(char*)p-1):NULL)
#define REV(p,t,b)	(p=t)
#define SET(p,t,b)	(t=p)
#endif

#endif

typedef struct S2F_part_s
{
	S2F_batch	batch;
	int		digits;
} S2F_part_t;

#if !defined(ERANGE)
#define ERANGE		EINVAL
#endif

#if S2F_static > 0
static
#elif S2F_static < 0 || !defined(S2F_static)
extern
#endif
S2F_number
#if S2F_scan
S2F_function(void* s, S2F_get_f get)
#elif S2F_size
S2F_function(const char* str, size_t size, char** end)
#else
S2F_function(const char* str, char** end)
#endif
{
#if !S2F_scan
	unsigned char*	s = (unsigned char*)str;
#if S2F_size
	unsigned char*	z = s + size;
	int		back = 1;
	int		b;
#endif
	unsigned char*		t;
#endif
	S2F_batch	n;
	int		c;
	int		digits;
	int		m;
	unsigned char*	cv;
	int		negative;
	int		enegative;
	int		fraction;
	int		decimal = 0;
	int		thousand = 0;
	int		part = 0;
	int		back_part = 0;
	S2F_batch	back_n = 0;
	S2F_number	v;
	S2F_number	p;
	S2F_part_t	parts[16];

	/*
	 * radix char and thousands separator are locale specific
	 */

	SFSETLOCALE(&decimal, &thousand);
	SFCVINIT();

	/*
	 * skip initial blanks
	 */

	do c = GET(s); while (isspace(c));
	SET(s, t, b);

	/*
	 * get the sign
	 */

	if ((negative = (c == '-')) || c == '+')
		c = GET(s);

	/*
	 * drop leading 0's
	 */

	digits = 0;
	fraction = -1;
	if (c == '0')
	{
		c = GET(s);
		if (c == 'x' || c == 'X')
		{
			/*
			 * hex floating point -- easy
			 */

			cv = _Sfcv36;
			v = 0;
			for (;;)
			{
				c = GET(s);
				if ((part = cv[c]) < 16)
				{
					digits++;
					v *= 16;
					v += part;
				}
				else if (c == decimal)
				{
					decimal = -1;
					fraction = digits;
				}
				else
					break;
			}
			m = 0;
			if (c == 'p' || c == 'P')
			{
				c = GET(s);
				if ((enegative = c == '-') || c == '+')
					c = GET(s);
				while (c >= '0' && c <= '9')
				{
					m = (m << 3) + (m << 1) + (c - '0');
					c = GET(s);
				}
				if (enegative)
					m = -m;
			}

#if S2F_qualifier

			/*
			 * consume the optional suffix
			 */

			switch (c)
			{
			case 'f':
			case 'F':
			case 'l':
			case 'L':
				c = GET(s);
				break;
			}
#endif
			PUT(s);
			if (v == 0)
				return negative ? -v : v;
			if (fraction >= 0)
				m -= 4 * (digits - fraction);
			if (m < S2F_exp_2_min)
			{
				if ((m -= S2F_exp_2_min) < S2F_exp_2_min)
				{
					ERR(ERANGE);
					return 0;
				}
				v = S2F_ldexp(v, S2F_exp_2_min);
			}
			else if (m > S2F_exp_2_max)
			{
				ERR(ERANGE);
				return negative ? -S2F_inf : S2F_inf;
			}
			v = S2F_ldexp(v, m);
			goto check;
		}
		while (c == '0')
			c = GET(s);
	}
	else if (c == decimal)
	{
		decimal = -1;
		fraction = 0;
		for (;;)
		{
			c = GET(s);
			if (c != '0')
				break;
			digits++;
		}
	}
	else if (c == 'i' || c == 'I')
	{
		if ((c = GET(s)) != 'n' && c != 'N' ||
		    (c = GET(s)) != 'f' && c != 'F')
		{
			REV(s, t, b);
			PUT(s);
			return 0;
		}
		c = GET(s);
		SET(s, t, b);
		if (((c)          == 'i' || c == 'I') &&
		    ((c = GET(s)) == 'n' || c == 'N') &&
		    ((c = GET(s)) == 'i' || c == 'I') &&
		    ((c = GET(s)) == 't' || c == 'T') &&
		    ((c = GET(s)) == 'y' || c == 'Y'))
		{
			c = GET(s);
			SET(s, t, b);
		}
		REV(s, t, b);
		PUT(s);
		return negative ? -S2F_inf : S2F_inf;
	}
	else if (c == 'n' || c == 'N')
	{
		if ((c = GET(s)) != 'a' && c != 'A' ||
		    (c = GET(s)) != 'n' && c != 'N')
		{
			REV(s, t, b);
			PUT(s);
			return 0;
		}
		do c = GET(s); while (c && !isspace(c));
		PUT(s);
		return negative ? -S2F_nan : S2F_nan;
	}
	else if (c < '1' || c > '9')
	{
		REV(s, t, b);
		PUT(s);
		NON(s);
		return 0;
	}

	/*
	 * consume the integral and fractional parts
	 */

	n = 0;
	m = 0;
	for (;;)
	{
		if (c >= '0' && c <= '9')
		{
			digits++;
			n = (n << 3) + (n << 1) + (c - '0');
			if (n >= ((~((S2F_batch)0)) / 10) && part < elementsof(parts))
			{
				parts[part].batch = n;
				n = 0;
				parts[part].digits = digits;
				part++;
			}
		}
		else if (m && (digits - m) != 3)
			break;
		else if (c == decimal)
		{
			decimal = -1;
			thousand = -1;
			m = 0;
			fraction = digits;
		}
		else if (c != thousand || (c == thousand && decimal == -1))
			break;
		else if (!(m = digits))
		{
			SET(s, t, b);
			break;
		}
		else
		{
			SET(s, t, b);
			back_n = n;
			back_part = part;
		}
		c = GET(s);
	}
	if (m && (digits - m) != 3)
	{
		REV(s, t, b);
		n = back_n;
		part = back_part;
	}

	/*
	 * don't forget the last part
	 */

	if (n && part < elementsof(parts))
	{
		parts[part].batch = n;
		parts[part].digits = digits;
		part++;
	}

	/*
	 * consume the exponent
	 */

	if (fraction >= 0)
		digits = fraction;
	if (c == 'e' || c == 'E')
	{
		c = GET(s);
		if ((enegative = (c == '-')) || c == '+')
			c = GET(s);
		n = 0;
		while (c >= '0' && c <= '9')
		{
			n = (n << 3) + (n << 1) + (c - '0');
			c = GET(s);
		}
		if (enegative)
			digits -= n;
		else
			digits += n;
	}

#if S2F_qualifier

	/*
	 * consume the optional suffix
	 */

	switch (c)
	{
	case 'f':
	case 'F':
	case 'l':
	case 'L':
		c = GET(s);
		break;
	}
#endif
	PUT(s);

	/*
	 * adjust for at most one multiply per part
	 * and at most one divide overall
	 */

	v = 0;
	if (!part)
		return negative ? -v : v;
	else if ((m = parts[part-1].digits - digits) > 0)
		digits += m;
	else
		m = 0;

	/*
	 * combine the parts
	 */

	while (part--)
	{
		p = parts[part].batch;
		c = digits - parts[part].digits;
		if (c > S2F_exp_10_max)
		{
			ERR(ERANGE);
			return negative ? -S2F_inf : S2F_inf;
		}
		if (c > 0)
		{
#if _ast_mpy_overflow_fpe
			if ((S2F_max / p) < S2F_pow10[c])
			{
				ERR(ERANGE);
				return negative ? -S2F_inf : S2F_inf;
			}
#endif
			p *= S2F_pow10[c];
		}
		v += p;
	}
	if (m)
	{
		while (m > S2F_exp_10_max)
		{
			m -= S2F_exp_10_max;
			v /= S2F_pow10[S2F_exp_10_max];
		}
#if _ast_div_underflow_fpe
		if ((S2F_min * p) > S2F_pow10[c])
		{
			ERR(ERANGE);
			return negative ? -S2F_inf : S2F_inf;
		}
#endif
		v /= S2F_pow10[m];
	}

	/*
	 * check the range
	 */

 check:
	if (v < S2F_min)
	{
		ERR(ERANGE);
		v = 0;
	}
	else if (v > S2F_max)
	{
		ERR(ERANGE);
		v = S2F_inf;
	}

	/*
	 * done
	 */

	return negative ? -v : v;
}
