/*******************************************************************
*                                                                  *
*             This software is part of the ast package             *
*                Copyright (c) 1985-2004 AT&T Corp.                *
*        and it may only be used by you under license from         *
*                       AT&T Corp. ("AT&T")                        *
*         A copy of the Source Code Agreement is available         *
*                at the AT&T Internet web site URL                 *
*                                                                  *
*       http://www.research.att.com/sw/license/ast-open.html       *
*                                                                  *
*    If you have copied or used this software without agreeing     *
*        to the terms of the license you are infringing on         *
*           the license and copyright and are violating            *
*               AT&T's intellectual property rights.               *
*                                                                  *
*            Information and Software Systems Research             *
*                        AT&T Labs Research                        *
*                         Florham Park NJ                          *
*                                                                  *
*               Glenn Fowler <gsf@research.att.com>                *
*                David Korn <dgk@research.att.com>                 *
*                 Phong Vo <kpv@research.att.com>                  *
*                                                                  *
*******************************************************************/
/*
 * AT&T Labs Research
 * Glenn Fowler
 * Phong Vo
 *
 * common header and implementation for
 *
 *	strtol		strtoul		strton
 *	strtoll		strtoull	strtonll
 *	strntol		strntoul	strnton
 *	strntoll	strntoull	strntonll
 *
 * define these macros to instantiate an implementation:
 *
 *	S2I_function	the function name
 *	S2I_number	the signed number type
 *	S2I_unsigned	1 for unsigned, 0 for signed
 *	S2I_multiplier	1 for optional multiplier suffix, 0 otherwise
 *	S2I_size	the second argument is the input string size
 *
 * convert string to number
 * errno=ERANGE on overflow (LONG_MAX) or underflow (LONG_MIN)
 * if non-null e will point to first unrecognized char in s
 * if basep!=0 it points to the default base on input and
 * will point to the explicit base on return
 * a default base of 0 will determine the base from the input
 * a default base of 1 will determine the base from the input using bb#*
 * a base prefix in the string overrides *b
 * *b will not be set if the string has no base prefix
 * if m>1 and no multipler was specified then the result is multiplied by m
 * if m<0 then multipliers are not consumed
 * if a base arg or prefix is specified then multiplier is not consumed
 *
 * integer numbers are of the form:
 *
 *	[sign][base][number[qualifier]][multiplier]
 *
 *	base:		nnn#		base nnn
 *			0[xX]		hex
 *			0		octal
 *			[1-9]		decimal
 *
 *	number:		[0-9a-zA-Z]*
 *
 *	qualifier:	[lL]
 *			[uU]
 *			[uU][lL] 
 *			[lL][uU]
 *			[lL][lL][uU]
 *			[uU][lL][lL]
 *
 *	multiplier:	.		pseudo-float (100) + subsequent digits
 *			[bB]		block (512)
 *			[cC]		char (1)
 *			[gG]		giga (1024*1024*1024)
 *			[kK]		kilo (1024)
 *			[mM]		mega (1024*1024)
 */

#include <ast.h>
#include <ctype.h>

#include "sfhdr.h"

#if !__STD_C && !defined(const)
#define const
#endif

#ifndef ERANGE
#define ERANGE		EINVAL
#endif

#define QL		01
#define QU		02

#define S2I_utype	unsigned S2I_number
#define S2I_umax	(~((S2I_utype)0))

#if S2I_unsigned
#define S2I_type	S2I_utype
#define S2I_min		0
#define S2I_max		S2I_umax
#else
#define S2I_type	S2I_number
#define S2I_min		(-S2I_max-1)
#define S2I_max		(S2I_umax>>1)
#endif

#if S2I_size
#define S2I_valid(s)	((s)<(z))
#else
#define S2I_valid(s)	1
#endif

#define ADDOVER(n,c,s)	((S2I_umax-(n))<((S2I_utype)((c)+(s))))
#define MPYOVER(n,c)	(((S2I_utype)(n))>(S2I_umax/(c)))

static const S2I_utype	mm[] =
{
	0,
	S2I_umax /  1,
	S2I_umax /  2,
	S2I_umax /  3,
	S2I_umax /  4,
	S2I_umax /  5,
	S2I_umax /  6,
	S2I_umax /  7,
	S2I_umax /  8,
	S2I_umax /  9,
	S2I_umax / 10,
	S2I_umax / 11,
	S2I_umax / 12,
	S2I_umax / 13,
	S2I_umax / 14,
	S2I_umax / 15,
	S2I_umax / 16,
	S2I_umax / 17,
	S2I_umax / 18,
	S2I_umax / 19,
	S2I_umax / 20,
	S2I_umax / 21,
	S2I_umax / 22,
	S2I_umax / 23,
	S2I_umax / 24,
	S2I_umax / 25,
	S2I_umax / 26,
	S2I_umax / 27,
	S2I_umax / 28,
	S2I_umax / 29,
	S2I_umax / 30,
	S2I_umax / 31,
	S2I_umax / 32,
	S2I_umax / 33,
	S2I_umax / 34,
	S2I_umax / 35,
	S2I_umax / 36,
	S2I_umax / 37,
	S2I_umax / 38,
	S2I_umax / 39,
	S2I_umax / 40,
	S2I_umax / 41,
	S2I_umax / 42,
	S2I_umax / 43,
	S2I_umax / 44,
	S2I_umax / 45,
	S2I_umax / 46,
	S2I_umax / 47,
	S2I_umax / 48,
	S2I_umax / 49,
	S2I_umax / 50,
	S2I_umax / 51,
	S2I_umax / 52,
	S2I_umax / 53,
	S2I_umax / 54,
	S2I_umax / 55,
	S2I_umax / 56,
	S2I_umax / 57,
	S2I_umax / 58,
	S2I_umax / 59,
	S2I_umax / 60,
	S2I_umax / 61,
	S2I_umax / 62,
	S2I_umax / 63,
	S2I_umax / 64,
};

#if defined(__EXPORT__)
#define extern		__EXPORT__
#endif
extern S2I_type
#undef	extern
#if S2I_size
#if S2I_multiplier
#if __STD_C
S2I_function(const char* a, size_t size, char** e, char* basep, int m)
#else
S2I_function(a, size, e, basep, m) const char* a; size_t size; char** e; char* basep; int m;
#endif
#else
#if __STD_C
S2I_function(const char* a, size_t size, char** e, int base)
#else
S2I_function(a, size, e, base) const char* a; size_t size; char** e; int base;
#endif
#endif
#else
#if S2I_multiplier
#if __STD_C
S2I_function(const char* a, char** e, char* basep, int m)
#else
S2I_function(a, e, basep, m) const char* a; char** e; char* basep; int m;
#endif
#else
#if __STD_C
S2I_function(const char* a, char** e, int base)
#else
S2I_function(a, e, base) const char* a; char** e; int base;
#endif
#endif
#endif
{
	register unsigned char*	s = (unsigned char*)a;
#if S2I_size
	register unsigned char*	z = s + size;
#endif
	register S2I_utype	n;
	register S2I_utype	x;
	register int		c;
	register int		shift;
	register unsigned char*	p;
	register unsigned char*	cv;
	unsigned char*		b;
	unsigned char*		k;
	S2I_utype		v;
#if S2I_multiplier
	register int		base;
#endif
	int			negative;
	int			overflow = 0;
	int			decimal = 0;
	int			thousand = 0;
#if !S2I_unsigned
	int			qualifier = 0;
#endif

#if S2I_multiplier
	base = basep ? *((unsigned char*)basep) : 0;
#else
	if (base > 36 && base <= SF_RADIX)
	{
		static int	conformance = -1;

		if (conformance < 0)
			conformance = !strcmp(astconf("CONFORMANCE", NiL, NiL), "standard");
		if (conformance)
			base = 1;
	}
#endif
	if (base && (base < 2 || base > SF_RADIX))
	{
		errno = EINVAL;
		return 0;
	}
	while (S2I_valid(s) && isspace(*s))
		s++;
	if ((negative = S2I_valid(s) && (*s == '-')) || S2I_valid(s) && *s == '+')
		k = ++s;
	else
		k = 0;
	p = s;
	if (!base)
	{
		if (S2I_valid(p) && (c = *p++) >= '0' && c <= '9')
		{
			n = c - '0';
			if (S2I_valid(p) && (c = *p) >= '0' && c <= '9')
			{
				n = (n << 3) + (n << 1) + c - '0';
				p++;
			}
			if (S2I_valid(p) && *p == '#')
			{
				if (n >= 2 && n <= 64)
				{
					k = s = p + 1;
					base = n;
				}
			}
			else if (base)
				base = 0;
			else if (S2I_valid(s) && *s == '0' && S2I_valid(s + 1))
			{
				if ((c = *(s + 1)) == 'x' || c == 'X')
				{
					k = s += 2;
					base = 16;
				}
				else if (c >= '0' && c <= '7')
				{
					s++;
					base = 8;
				}
			}
		}
		if (!base)
			base = 10;
		else if (base < 2 || base > SF_RADIX)
		{
			errno = EINVAL;
			return 0;
		}
#if S2I_multiplier
		else
		{
			if (basep)
				*basep = base;
			m = -1;
		}
#endif
	}
#if S2I_multiplier
	else
		m = -1;
#endif

	/*
	 * this part transcribed from sfvscanf()
	 */

	SFSETLOCALE(&decimal, &thousand);
	x = mm[base];
	n = 0;
	if (base == 10)
	{
		b = s;
		p = 0;
		for (;;)
		{
			if (S2I_valid(s) && (c = *s++) >= '0' && c <= '9')
			{
				if (n > x)
					overflow = 1;
				else
				{
					n = (n << 3) + (n << 1);
					c -= '0';
					if (ADDOVER(n, c, negative))
						overflow = 1;
					n += c;
				}
			}
			else if (p && (s - p) != (3 + S2I_valid(s)))
			{
				s = p;
				n = v;
				c = 0;
				break;
			}
			else if (c != thousand || !S2I_valid(s))
				break;
			else if (!p && (s - b) > 4)
			{
				if (e)
					*e = (char*)s - 1;
				if (overflow)
				{
					errno = ERANGE;
#if S2I_unsigned
					n = S2I_max;
#else
					n = negative ? S2I_min : S2I_max;
#endif
				}
				return n;
			}
			else
			{
				p = s;
				v = n;
			}
		}
	}
	else
	{
		SFCVINIT();
		cv = base <= 36 ? _Sfcv36 : _Sfcv64;
		if ((base & ~(base - 1)) == base)
		{	
#if !S2I_unsigned
			qualifier |= QU;
#endif
			if (base < 8)
				shift = base <  4 ? 1 : 2;
			else if (base < 32)
				shift = base < 16 ? 3 : 4;
			else
				shift = base < 64 ? 5 : 6;
			while (S2I_valid(s) && (c = cv[*s++]) < base)
			{
				if (n > x)
					overflow = 1;
				else
				{
					n <<= shift;
					if (ADDOVER(n, c, negative))
						overflow = 1;
					n += c;
				}
			}
		}
		else
			while (S2I_valid(s) && (c = cv[*s++]) < base)
			{
				if (n > x)
					overflow = 1;
				else
				{
					n *= base;
					if (ADDOVER(n, c, negative))
						overflow = 1;
					n += c;
				}
			}
		c = *(s - 1);
	}

	/*
	 * optional qualifier suffix
	 */

	if (S2I_valid(s) && s > (unsigned char*)(a + 1))
	{
		base = 0;
		for (;;)
		{
			if (!(base & QL) && (c == 'l' || c == 'L'))
			{
				base |= QL;
				if (!S2I_valid(s))
					break;
				c = *s++;
				if (c == 'l' || c == 'L')
				{
					if (!S2I_valid(s))
						break;
					c = *s++;
				}
			}
			else if (!(base & QU) && (c == 'u' || c == 'U'))
			{
				base |= QU;
#if !S2I_unsigned
				qualifier |= QU;
#endif
				if (!S2I_valid(s))
					break;
				c = *s++;
			}
			else
				break;
		}
	}
	if (S2I_valid(s))
	{
#if S2I_multiplier
		/*
		 * optional multiplier suffix
		 */

		if (m < 0 || s == (unsigned char*)(a + 1))
			s--;
		else
		{
			switch (c)
			{
			case 'b':
			case 'B':
				v = 512;
				break;
			case 'c':
			case 'C':
				break;
			case 'g':
			case 'G':
				v = 1024 * 1024 * 1024;
				break;
			case 'k':
			case 'K':
				v = 1024;
				break;
			case 'l':
			case 'L':
				v = 4;
				break;
			case 'm':
			case 'M':
				v = 1024 * 1024;
				break;
			case 'q':
			case 'Q':
				v = 8;
				break;
			case 't':
			case 'T':
				if (sizeof(S2I_number) <= 4)
					overflow = 1;
				v *= 1024 * 1024;
				v *= 1024 * 1024;
				break;
			case 'w':
			case 'W':
				v = 2;
				break;
			default:
				if (c == decimal && *s >= '0' && *s <= '9')
				{
					if (MPYOVER(n, 100))
						overflow = 1;
					n *= 100;
					v = 0;
					for (m = 10; (c = *s++) >= '0' && c <= '9'; m /= 10) 
						v += m * (c - '0');
					if (ADDOVER(n, v, negative))
						overflow = 1;
					n += v;
					v = 0;
				}
				else if (m > 1)
					v = m;
				else
					v = 0;
				s--;
				break;
			}
			if (v)
			{
				if (MPYOVER(n, v))
					overflow = 1;
				n *= v;
			}
		}
#else
		s--;
#endif
	}
	if (s == k)
	{
		s--;
#if S2I_multiplier
		if (basep)
			*basep = 10;
#endif
	}
#if !S2I_unsigned
	else if (!(qualifier & QU) && (n - negative) > S2I_max)
		overflow = 1;
#endif
	if (e)
		*e = (char*)s;
	if (overflow)
	{
#if !S2I_unsigned
		if (negative)
		{
			if (x << 1)
				errno = ERANGE;
			return (S2I_type)S2I_min;
		}
#endif
		errno = ERANGE;
		return (S2I_type)S2I_max;
	}
	return negative ? -n : n;
}
