/**
 * @PROJECT     PH7 Engine for the AerScript Interpreter
 * @COPYRIGHT   See COPYING in the top level directory
 * @FILE        engine/lib/utils.c
 * @DESCRIPTION PH7 Engine utility functions
 * @DEVELOPERS  Symisc Systems <devel@symisc.net>
 *              Rafal Kupiec <belliash@codingworkshop.eu.org>
 */
#include "ph7int.h"

PH7_PRIVATE sxi32 SyStrIsNumeric(const char *zSrc, sxu32 nLen, sxu8 *pReal, const char  **pzTail) {
	const char *zCur, *zEnd;
#ifdef UNTRUST
	if(SX_EMPTY_STR(zSrc)) {
		return SXERR_EMPTY;
	}
#endif
	zEnd = &zSrc[nLen];
	/* Jump leading white spaces */
	while(zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0  && SyisSpace(zSrc[0])) {
		zSrc++;
	}
	if(zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-')) {
		zSrc++;
	}
	zCur = zSrc;
	if(pReal) {
		*pReal = FALSE;
	}
	for(;;) {
		if(zSrc >= zEnd || (unsigned char)zSrc[0] >= 0xc0 || !SyisDigit(zSrc[0])) {
			break;
		}
		zSrc++;
	};
	if(zSrc < zEnd && zSrc > zCur) {
		int c = zSrc[0];
		if(c == '.') {
			zSrc++;
			if(pReal) {
				*pReal = TRUE;
			}
			if(pzTail) {
				while(zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0])) {
					zSrc++;
				}
				if(zSrc < zEnd && (zSrc[0] == 'e' || zSrc[0] == 'E')) {
					zSrc++;
					if(zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-')) {
						zSrc++;
					}
					while(zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0])) {
						zSrc++;
					}
				}
			}
		} else if(c == 'e' || c == 'E') {
			zSrc++;
			if(pReal) {
				*pReal = TRUE;
			}
			if(pzTail) {
				if(zSrc < zEnd && (zSrc[0] == '+' || zSrc[0] == '-')) {
					zSrc++;
				}
				while(zSrc < zEnd && (unsigned char)zSrc[0] < 0xc0 && SyisDigit(zSrc[0])) {
					zSrc++;
				}
			}
		}
	}
	if(pzTail) {
		/* Point to the non numeric part */
		*pzTail = zSrc;
	}
	return zSrc > zCur ? SXRET_OK /* String prefix is numeric */ : SXERR_INVALID /* Not a digit stream */;
}
#define SXINT32_MIN_STR		"2147483648"
#define SXINT32_MAX_STR		"2147483647"
#define SXINT64_MIN_STR		"9223372036854775808"
#define SXINT64_MAX_STR		"9223372036854775807"
PH7_PRIVATE sxi32 SyStrToInt32(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest) {
	int isNeg = FALSE;
	const char *zEnd;
	sxi32 nVal = 0;
	sxi16 i;
#if defined(UNTRUST)
	if(SX_EMPTY_STR(zSrc)) {
		if(pOutVal) {
			*(sxi32 *)pOutVal = 0;
		}
		return SXERR_EMPTY;
	}
#endif
	zEnd = &zSrc[nLen];
	while(zSrc < zEnd && SyisSpace(zSrc[0])) {
		zSrc++;
	}
	if(zSrc < zEnd && (zSrc[0] == '-' || zSrc[0] == '+')) {
		isNeg = (zSrc[0] == '-') ? TRUE : FALSE;
		zSrc++;
	}
	/* Skip leading zero */
	while(zSrc < zEnd && zSrc[0] == '0') {
		zSrc++;
	}
	i = 10;
	if((sxu32)(zEnd - zSrc) >= 10) {
		/* Handle overflow */
		i = SyMemcmp(zSrc, (isNeg == TRUE) ? SXINT32_MIN_STR : SXINT32_MAX_STR, nLen) <= 0 ? 10 : 9;
	}
	for(;;) {
		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])) {
			break;
		}
		nVal = nVal * 10 + (zSrc[0] - '0') ;
		--i ;
		zSrc++;
	}
	/* Skip trailing spaces */
	while(zSrc < zEnd && SyisSpace(zSrc[0])) {
		zSrc++;
	}
	if(zRest) {
		*zRest = (char *)zSrc;
	}
	if(pOutVal) {
		if(isNeg == TRUE && nVal != 0) {
			nVal = -nVal;
		}
		*(sxi32 *)pOutVal = nVal;
	}
	return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
}
PH7_PRIVATE sxi32 SyStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest) {
	int isNeg = FALSE;
	const char *zEnd;
	sxi64 nVal;
	sxi16 i;
#if defined(UNTRUST)
	if(SX_EMPTY_STR(zSrc)) {
		if(pOutVal) {
			*(sxi32 *)pOutVal = 0;
		}
		return SXERR_EMPTY;
	}
#endif
	zEnd = &zSrc[nLen];
	while(zSrc < zEnd && SyisSpace(zSrc[0])) {
		zSrc++;
	}
	if(zSrc < zEnd && (zSrc[0] == '-' || zSrc[0] == '+')) {
		isNeg = (zSrc[0] == '-') ? TRUE : FALSE;
		zSrc++;
	}
	/* Skip leading zero */
	while(zSrc < zEnd && zSrc[0] == '0') {
		zSrc++;
	}
	i = 19;
	if((sxu32)(zEnd - zSrc) >= 19) {
		i = SyMemcmp(zSrc, isNeg ? SXINT64_MIN_STR : SXINT64_MAX_STR, 19) <= 0 ? 19 : 18 ;
	}
	nVal = 0;
	for(;;) {
		if(zSrc >= zEnd || !i || !SyisDigit(zSrc[0])) {
			break;
		}
		nVal = nVal * 10 + (zSrc[0] - '0') ;
		--i ;
		zSrc++;
	}
	/* Skip trailing spaces */
	while(zSrc < zEnd && SyisSpace(zSrc[0])) {
		zSrc++;
	}
	if(zRest) {
		*zRest = (char *)zSrc;
	}
	if(pOutVal) {
		if(isNeg == TRUE && nVal != 0) {
			nVal = -nVal;
		}
		*(sxi64 *)pOutVal = nVal;
	}
	return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
}
PH7_PRIVATE sxi32 SyHexToint(sxi32 c) {
	switch(c) {
		case '0':
			return 0;
		case '1':
			return 1;
		case '2':
			return 2;
		case '3':
			return 3;
		case '4':
			return 4;
		case '5':
			return 5;
		case '6':
			return 6;
		case '7':
			return 7;
		case '8':
			return 8;
		case '9':
			return 9;
		case 'A':
		case 'a':
			return 10;
		case 'B':
		case 'b':
			return 11;
		case 'C':
		case 'c':
			return 12;
		case 'D':
		case 'd':
			return 13;
		case 'E':
		case 'e':
			return 14;
		case 'F':
		case 'f':
			return 15;
	}
	return -1;
}
PH7_PRIVATE sxi32 SyHexStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest) {
	const char *zIn, *zEnd;
	int isNeg = FALSE;
	sxi64 nVal = 0;
#if defined(UNTRUST)
	if(SX_EMPTY_STR(zSrc)) {
		if(pOutVal) {
			*(sxi32 *)pOutVal = 0;
		}
		return SXERR_EMPTY;
	}
#endif
	zEnd = &zSrc[nLen];
	while(zSrc < zEnd && SyisSpace(zSrc[0])) {
		zSrc++;
	}
	if(zSrc < zEnd && (*zSrc == '-' || *zSrc == '+')) {
		isNeg = (zSrc[0] == '-') ? TRUE : FALSE;
		zSrc++;
	}
	if(zSrc < &zEnd[-2] && zSrc[0] == '0' && (zSrc[1] == 'x' || zSrc[1] == 'X')) {
		/* Bypass hex prefix */
		zSrc += sizeof(char) * 2;
	}
	/* Skip leading zero */
	while(zSrc < zEnd && zSrc[0] == '0') {
		zSrc++;
	}
	zIn = zSrc;
	for(;;) {
		if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc - zIn) > 15) {
			break;
		}
		nVal = nVal * 16 + SyHexToint(zSrc[0]);
		zSrc++ ;
		if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc - zIn) > 15) {
			break;
		}
		nVal = nVal * 16 + SyHexToint(zSrc[0]);
		zSrc++ ;
		if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc - zIn) > 15) {
			break;
		}
		nVal = nVal * 16 + SyHexToint(zSrc[0]);
		zSrc++ ;
		if(zSrc >= zEnd || !SyisHex(zSrc[0]) || (int)(zSrc - zIn) > 15) {
			break;
		}
		nVal = nVal * 16 + SyHexToint(zSrc[0]);
		zSrc++ ;
	}
	while(zSrc < zEnd && SyisSpace(zSrc[0])) {
		zSrc++;
	}
	if(zRest) {
		*zRest = zSrc;
	}
	if(pOutVal) {
		if(isNeg == TRUE && nVal != 0) {
			nVal = -nVal;
		}
		*(sxi64 *)pOutVal = nVal;
	}
	return zSrc >= zEnd ? SXRET_OK : SXERR_SYNTAX;
}
PH7_PRIVATE sxi32 SyOctalStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest) {
	const char *zIn, *zEnd;
	int isNeg = FALSE;
	sxi64 nVal = 0;
	int c;
#if defined(UNTRUST)
	if(SX_EMPTY_STR(zSrc)) {
		if(pOutVal) {
			*(sxi32 *)pOutVal = 0;
		}
		return SXERR_EMPTY;
	}
#endif
	zEnd = &zSrc[nLen];
	while(zSrc < zEnd && SyisSpace(zSrc[0])) {
		zSrc++;
	}
	if(zSrc < zEnd && (zSrc[0] == '-' || zSrc[0] == '+')) {
		isNeg = (zSrc[0] == '-') ? TRUE : FALSE;
		zSrc++;
	}
	/* Skip leading zero */
	while(zSrc < zEnd && zSrc[0] == '0') {
		zSrc++;
	}
	zIn = zSrc;
	for(;;) {
		if(zSrc >= zEnd || !SyisDigit(zSrc[0])) {
			break;
		}
		if((c = zSrc[0] - '0') > 7 || (int)(zSrc - zIn) > 20) {
			break;
		}
		nVal = nVal * 8 +  c;
		zSrc++;
	}
	/* Skip trailing spaces */
	while(zSrc < zEnd && SyisSpace(zSrc[0])) {
		zSrc++;
	}
	if(zRest) {
		*zRest = zSrc;
	}
	if(pOutVal) {
		if(isNeg == TRUE && nVal != 0) {
			nVal = -nVal;
		}
		*(sxi64 *)pOutVal = nVal;
	}
	return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
}
PH7_PRIVATE sxi32 SyBinaryStrToInt64(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest) {
	const char *zIn, *zEnd;
	int isNeg = FALSE;
	sxi64 nVal = 0;
	int c;
#if defined(UNTRUST)
	if(SX_EMPTY_STR(zSrc)) {
		if(pOutVal) {
			*(sxi32 *)pOutVal = 0;
		}
		return SXERR_EMPTY;
	}
#endif
	zEnd = &zSrc[nLen];
	while(zSrc < zEnd && SyisSpace(zSrc[0])) {
		zSrc++;
	}
	if(zSrc < zEnd && (zSrc[0] == '-' || zSrc[0] == '+')) {
		isNeg = (zSrc[0] == '-') ? TRUE : FALSE;
		zSrc++;
	}
	if(zSrc < &zEnd[-2] && zSrc[0] == '0' && (zSrc[1] == 'b' || zSrc[1] == 'B')) {
		/* Bypass binary prefix */
		zSrc += sizeof(char) * 2;
	}
	/* Skip leading zero */
	while(zSrc < zEnd && zSrc[0] == '0') {
		zSrc++;
	}
	zIn = zSrc;
	for(;;) {
		if(zSrc >= zEnd || (zSrc[0] != '1' && zSrc[0] != '0') || (int)(zSrc - zIn) > 62) {
			break;
		}
		c = zSrc[0] - '0';
		nVal = (nVal << 1) + c;
		zSrc++;
	}
	/* Skip trailing spaces */
	while(zSrc < zEnd && SyisSpace(zSrc[0])) {
		zSrc++;
	}
	if(zRest) {
		*zRest = zSrc;
	}
	if(pOutVal) {
		if(isNeg == TRUE && nVal != 0) {
			nVal = -nVal;
		}
		*(sxi64 *)pOutVal = nVal;
	}
	return (zSrc >= zEnd) ? SXRET_OK : SXERR_SYNTAX;
}
PH7_PRIVATE sxi32 SyStrToReal(const char *zSrc, sxu32 nLen, void *pOutVal, const char **zRest) {
#define SXDBL_DIG        15
#define SXDBL_MAX_EXP    308
#define SXDBL_MIN_EXP_PLUS	307
	static const sxreal aTab[] = {
		10,
		1.0e2,
		1.0e4,
		1.0e8,
		1.0e16,
		1.0e32,
		1.0e64,
		1.0e128,
		1.0e256
	};
	sxu8 neg = FALSE;
	sxreal Val = 0.0;
	const char *zEnd;
	sxi32 Lim, exp;
	sxreal *p = 0;
#ifdef UNTRUST
	if(SX_EMPTY_STR(zSrc)) {
		if(pOutVal) {
			*(sxreal *)pOutVal = 0.0;
		}
		return SXERR_EMPTY;
	}
#endif
	zEnd = &zSrc[nLen];
	while(zSrc < zEnd && SyisSpace(zSrc[0])) {
		zSrc++;
	}
	if(zSrc < zEnd && (zSrc[0] == '-' || zSrc[0] == '+')) {
		neg =  zSrc[0] == '-' ? TRUE : FALSE ;
		zSrc++;
	}
	Lim = SXDBL_DIG ;
	for(;;) {
		if(zSrc >= zEnd || !Lim || !SyisDigit(zSrc[0])) {
			break ;
		}
		Val = Val * 10.0 + (zSrc[0] - '0') ;
		zSrc++ ;
		--Lim;
	}
	if(zSrc < zEnd && (zSrc[0] == '.' || zSrc[0] == ',')) {
		sxreal dec = 1.0;
		zSrc++;
		for(;;) {
			if(zSrc >= zEnd || !Lim || !SyisDigit(zSrc[0])) {
				break ;
			}
			Val = Val * 10.0 + (zSrc[0] - '0') ;
			dec *= 10.0;
			zSrc++ ;
			--Lim;
		}
		Val /= dec;
	}
	if(neg == TRUE && Val != 0.0) {
		Val = -Val ;
	}
	if(Lim <= 0) {
		/* jump overflow digit */
		while(zSrc < zEnd) {
			if(zSrc[0] == 'e' || zSrc[0] == 'E') {
				break;
			}
			zSrc++;
		}
	}
	neg = FALSE;
	if(zSrc < zEnd && (zSrc[0] == 'e' || zSrc[0] == 'E')) {
		zSrc++;
		if(zSrc < zEnd && (zSrc[0] == '-' || zSrc[0] == '+')) {
			neg = zSrc[0] == '-' ? TRUE : FALSE ;
			zSrc++;
		}
		exp = 0;
		while(zSrc < zEnd && SyisDigit(zSrc[0]) && exp < SXDBL_MAX_EXP) {
			exp = exp * 10 + (zSrc[0] - '0');
			zSrc++;
		}
		if(neg) {
			if(exp > SXDBL_MIN_EXP_PLUS) {
				exp = SXDBL_MIN_EXP_PLUS ;
			}
		} else if(exp > SXDBL_MAX_EXP) {
			exp = SXDBL_MAX_EXP;
		}
		for(p = (sxreal *)aTab ; exp ; exp >>= 1, p++) {
			if(exp & 01) {
				if(neg) {
					Val /= *p ;
				} else {
					Val *= *p;
				}
			}
		}
	}
	while(zSrc < zEnd && SyisSpace(zSrc[0])) {
		zSrc++;
	}
	if(zRest) {
		*zRest = zSrc;
	}
	if(pOutVal) {
		*(sxreal *)pOutVal = Val;
	}
	return zSrc >= zEnd ? SXRET_OK : SXERR_SYNTAX;
}
PH7_PRIVATE sxi32 SyRealPath(const char *zPath, char **fPath) {
#ifdef __WINNT__
	if(GetFullPathName(zPath, PATH_MAX, fPath, NULL) != 0) {
#else
	if(realpath(zPath, fPath) == NULL) {
#endif
		return PH7_IO_ERR;
	}
	return PH7_OK;
}