/*
**      types.c - Lite type handling routines
**
**
** Copyright (c) 1996,97  Hughes Technologies Pty Ltd
**
** Permission to use, copy, and distribute for non-commercial purposes,
** is hereby granted without fee, providing that the above copyright
** notice appear in all copies and that both the copyright notice and this
** permission notice appear in supporting documentation.
**
** This software is provided "as is" without any expressed or implied warranty.
**
**
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>

#include "lite.h"
#include "y.tab.h"
#include <regexp/regexp.h>

extern	char	*errMsg;

#define	REG	register


/************************************************************************
** Predeclaration of type functions
*/

int	intPack();
int	intUnpack();
int	intCompare();
int	intMath();
int	intCheck();

int	uintUnpack();
int	uintCompare();
int	uintMath();

int	charPack();
int	charUnpack();
int	charCompare();
int	charMath();
int	charCheck();

int	realPack();
int	realUnpack();
int	realCompare();
int	realMath();
int	realCheck();

int	moneyPack();
int	moneyUnpack();



/************************************************************************
** Type table
**
** NOTE : The first 3 entires in the table _MUST_NOT_ be modified.  If
** the order of the first 3 entries is changed then things will break.
** The order must be 'real', 'int', 'char'.  If you wish to add your
** own types they must be defined _after_ the definition of 'char'
*/


#define MAX_TYPE_INDEX	4

static type_t	types[] = {
	{ "real", 8, realPack, realUnpack, NULL, NULL, 
		realCompare, realMath, realCheck},
	{ "int", 4, intPack, intUnpack, NULL, NULL, 
		intCompare, intMath, intCheck},
	{ "char", -1, charPack, charUnpack, NULL, NULL, 
		charCompare, charMath, charCheck},
	{ "uint", 4, intPack, uintUnpack, NULL, NULL, 
		uintCompare, uintMath, intCheck},
	{ "money", 8, moneyPack, moneyUnpack, NULL, NULL, 
		realCompare, realMath, realCheck},
	{ NULL, 0, NULL, NULL, NULL }
};




/************************************************************************
** Type definition functions
*/

type_t *typeGetType(index)
	int	index;
{
	if (index < 0 || index > MAX_TYPE_INDEX)
		return(NULL);
	return(&(types[index]));
}


int typeGetTypeIndex(name)
	char	*name;
{
	int	index = 0;

	while(types[index].name)
	{
		if (strcmp(types[index].name,name) == 0)
			return(index);
		index++;
	}
	return(-1);
}


int typeDetermineType(val)
	char	*val;
{
	int	index;

	index = 0;
	while(types[index].name)
	{
		if ( (*types[index].check)(val) == 0)
			return(index);
		index++;
	}
	return(-1);
}




/************************************************************************
** Integer functions
*/



int my_atoi(val)
	char	*val;
{
	int  	res,
		neg;
	char	*cp;

	neg = res = 0;
	cp = val;
	if (*cp == '-')
	{
		neg++;
		cp++;
	}
	while(isdigit(*cp))
	{
		res *= 10;
		res += *cp - '0';
		cp++;
	}
	if (neg)
		res = 0 - res;
	return(res);
}


int oct2dec(val)
        char    *val;
{
        int     decval,
                octval;
        char    *cd;

        cd = val;
        decval = 0;
        while(*cd)
        {
                decval *= 8;
                octval = *cd - '0';
                if (octval > 7)
                {
                        runError("Bad octal literal value.");
                        exit(-1);
                }
                decval += octval;
                cd++;
        }
        return(decval);
}



int hex2dec(val)
        char    *val;
{
        int     decval;
        char    *cd;

        cd = val;
        decval = 0;
        while(*cd)
        {
                decval *= 16;
                if (*cd >= 'a')
                        decval += *cd - 'a' + 10;
                else
                if (*cd >= 'A')
                        decval += *cd - 'A' + 10;
                else
                        decval += *cd - '0';
                cd++;
        }
        return(decval);
}



int intPack(value, ptr, len, lit)
	char	*value;
	u_char	**ptr;
	int	len;
	char	lit;
{
	int	packVal;

	/*
	** Pack the value into the buffer.  Do base conversions if this
	** is a literal value (i.e. not a value from a variable)
	*/

	if (lit)
	{
		if (*value == '0')
		{
			if (*(value+1) == 'x')
				packVal = hex2dec(value);
			else
				packVal = oct2dec(value);
		}
		else
		{
			packVal = my_atoi(value);
		}
	}
	else
		packVal = my_atoi(value);
	
	*ptr = (u_char *)malloc(4);
	bcopy4(&packVal,*ptr);
	return(4);
}



int intUnpack(value, ptr, len)
	char	**value;
	u_char	*ptr;
	int	len;
{
	int	val;
	char	valBuf[10];

	bcopy4(ptr,&val);
	sprintf(valBuf,"%d",val);
	*value = (char *)strdup(valBuf);
	return(strlen(*value));
}



int uintUnpack(value, ptr, len)
	char	**value;
	u_char	*ptr;
	int	len;
{
	int	val;
	char	valBuf[10];

	bcopy4(ptr,&val);
	sprintf(valBuf,"%u",val);
	*value = (char *)strdup(valBuf);
	return(strlen(*value));
}




int intCompare(v1, v2, op, len1, len2, res)
	u_char	*v1,
		*v2;
	int	op,
		len1,
		len2,
		*res;
{
	int	int1,
		int2;

	bcopy4(v1,&int1);
	bcopy4(v2,&int2);
	switch(op)
	{
		case GE:
			*res = (int1 >= int2);
			break;
		case LE:
			*res = (int1 <= int2);
			break;
		case NE:
			*res = (int1 != int2);
			break;
		case EQ:
			*res = (int1 == int2);
			break;
		case GT:
			*res = (int1 > int2);
			break;
		case LT:
			*res = (int1 < int2);
			break;
		case RE:
			return(-1);
			break;
	}
	return(0);
}



int intMath(v1,v2,res,op,len1,len2)
	u_char	*v1,
		*v2,
		**res;
	int	len1,
		len2;
{
	int	val1,
		val2,
		resVal;

	bcopy4(v1,&val1);
	bcopy4(v2,&val2);
	switch(op)
	{
		case OP_ADD:
			resVal = val1+val2;
			break;
		case OP_SUB:
			resVal = val1-val2;
			break;
		case OP_MUL:
			resVal = val1*val2;
			break;
		case OP_DIV:
			if (val2 == 0)
			{
                		runError("Divide by zero");
				exit(1);
			}
			resVal = val1/val2;
			break;
		case OP_AND:
			resVal = val1&val2;
			break;
		case OP_OR:
			resVal = val1|val2;
			break;
		case OP_MOD:
			if (val2 == 0)
			{
                		runError("Divide by zero");
				exit(1);
			}
			resVal = val1%val2;
			break;
	}
	*res = (u_char *)malloc(4);
	bcopy4(&resVal,*res);
	return(4);
}


int uintCompare(v1, v2, op, len1, len2, res)
	u_char	*v1,
		*v2;
	int	op,
		len1,
		len2,
		*res;
{
	u_int	int1,
		int2;

	bcopy4(v1,&int1);
	bcopy4(v2,&int2);
	switch(op)
	{
		case GE:
			*res = (int1 >= int2);
			break;
		case LE:
			*res = (int1 <= int2);
			break;
		case NE:
			*res = (int1 != int2);
			break;
		case EQ:
			*res = (int1 == int2);
			break;
		case GT:
			*res = (int1 > int2);
			break;
		case LT:
			*res = (int1 < int2);
			break;
		case RE:
			return(-1);
			break;
	}
	return(0);
}



int uintMath(v1,v2,res,op,len1,len2)
	u_char	*v1,
		*v2,
		**res;
	int	len1,
		len2;
{
	u_int	val1,
		val2,
		resVal;

	bcopy4(v1,&val1);
	bcopy4(v2,&val2);
	switch(op)
	{
		case OP_ADD:
			resVal = val1+val2;
			break;
		case OP_SUB:
			resVal = val1-val2;
			break;
		case OP_MUL:
			resVal = val1*val2;
			break;
		case OP_DIV:
			if (val2 == 0)
			{
                		runError("Divide by zero");
				exit(1);
			}
			resVal = val1/val2;
			break;
		case OP_AND:
			resVal = val1&val2;
			break;
		case OP_OR:
			resVal = val1|val2;
			break;
		case OP_MOD:
			if (val2 == 0)
			{
                		runError("Divide by zero");
				exit(1);
			}
			resVal = val1%val2;
			break;
	}
	*res = (u_char *)malloc(4);
	bcopy4(&resVal,*res);
	return(4);
}



int intCheck(str)
	char	*str;
{
	int	val;
	char	*cp;

	cp = str;

	/*	
	** First thing, check if it's got a dot.  If it's got a dot then
	** it was rejected by realCheck.  Probably an IP address
	*/
	if ((char *)index(str,'.') != NULL)
	{
		return(-1);
	}
	if (*cp == '-')
		cp++;
	
	/* Hex number */
	if (*cp == '0' && *(cp+1) == 'x')
	{
		cp += 2;
		while (*cp)
		{
			if ( isdigit(*cp) || 
			     ( *cp >= 'A' && *cp <= 'F') ||
			     ( *cp >= 'a' && *cp <= 'A'))
			{
				cp++;
				continue;
			}	
			return(-1);
		}
	}
	
	if (sscanf(cp,"%d",&val) == 1)
	{
		return(0);
	}
	return(-1);
}



int uintCheck(str)
	char	*str;
{
	int	val;
	char	*cp;

	cp = str;

	/*	
	** First thing, check if it's got a dot.  If it's got a dot then
	** it was rejected by realCheck.  Probably an IP address
	*/
	if ((char *)index(str,'.') != NULL)
	{
		return(-1);
	}
	
	/* Hex number */
	if (*cp == '0' && *(cp+1) == 'x')
	{
		cp += 2;
		while (*cp)
		{
			if ( isdigit(*cp) || 
			     ( *cp >= 'A' && *cp <= 'F') ||
			     ( *cp >= 'a' && *cp <= 'A'))
			{
				cp++;
				continue;
			}	
			return(-1);
		}
	}
	
	if (sscanf(cp,"%d",&val) == 1)
	{
		return(0);
	}
	return(-1);
}



/************************************************************************
** Real functions
*/

int realPack(value, ptr, len, lit)
	char	*value;
	u_char	**ptr;
	int	len;
	char	lit;
{
	double	packVal;

	/*
	** Ensure it's a reasonable value
	*/
	if (sscanf(value,"%lf",&packVal) != 1)
	{
		return(-1);
	}

	/*
	** OK, pack the value into the buffer
	*/
	*ptr = (u_char *)malloc(8);
	bcopy8(&packVal,*ptr);
	return(8);
}



int realUnpack(value, ptr, len)
	char	**value;
	u_char	*ptr;
	int	len;
{
	double	val;
	char	valBuf[50];

	bcopy8(ptr,&val);
	sprintf(valBuf,"%.16g",val);
	*value = (char *)strdup(valBuf);
	return(strlen(*value));
}




int realCompare(v1, v2, op, len1, len2, res)
	u_char	*v1,
		*v2;
	int	op,
		len1,
		len2,
		*res;
{
	double	real1,
		real2;

	bcopy8(v1,&real1);
	bcopy8(v2,&real2);
	switch(op)
	{
		case GE:
			*res = (real1 >= real2);
			break;
		case LE:
			*res = (real1 <= real2);
			break;
		case NE:
			*res = (real1 != real2);
			break;
		case EQ:
			*res = (real1 == real2);
			break;
		case GT:
			*res = (real1 > real2);
			break;
		case LT:
			*res = (real1 < real2);
			break;
		case RE:
			return(-1);
			break;
	}
	return(0);
}




int realMath(v1,v2,res,op,len1,len2)
	u_char	*v1,
		*v2,
		**res;
	int	len1,
		len2;
{
	double	val1,
		val2,
		resVal;

	bcopy8(v1,&val1);
	bcopy8(v2,&val2);
	switch(op)
	{
		case OP_ADD:
			resVal = val1+val2;
			break;
		case OP_SUB:
			resVal = val1-val2;
			break;
		case OP_MUL:
			resVal = val1*val2;
			break;
		case OP_DIV:
			if (val2 == 0)
			{
                		runError("Divide by zero");
				exit(1);
			}
			resVal = val1/val2;
			break;
		case OP_AND:
		case OP_OR:
		case OP_MOD:
			return(-1);
			break;
	}
	*res = (u_char *)malloc(8);
	bcopy8(&resVal,*res);
	return(8);
}




int realCheck(str)
	char	*str;
{
	double	val;
	char	*cp;

	/*
	** If there's no '.' the we'll treat it as an integer value
	*/
	cp = (char *)index(str,'.');
	if (!cp)
	{
		return(-1);
	}
	if ((char *)index(cp+1,'.') != NULL)
	{
		/* multiple dots.  Probably an IP address */
		return(-1);
	}
	
	if (sscanf(str,"%lf",&val) == 1)
	{
		return(0);
	}
	return(-1);
}


int moneyPack(value, ptr, len, lit)
	char	*value;
	u_char	**ptr;
	int	len;
	char	lit;
{
	double	packVal;

	/*
	** Ensure it's a reasonable value
	*/
	if (sscanf(value,"%lf",&packVal) != 1)
	{
		return(-1);
	}

	/*
	** OK, pack the value into the buffer
	*/
	*ptr = (u_char *)malloc(8);
	bcopy8(&packVal,*ptr);
	return(8);
}



int moneyUnpack(value, ptr, len)
	char	**value;
	u_char	*ptr;
	int	len;
{
	double	val;
	char	valBuf[50];

	bcopy8(ptr,&val);
	sprintf(valBuf,"%.2f",val);
	*value = (char *)strdup(valBuf);
	return(strlen(*value));
}






/************************************************************************
** Char functions
*/

int charPack(value, ptr, len, lit)
	char	*value;
	u_char	**ptr;
	int	len;
	char	lit;
{
	*ptr = (u_char *)malloc(len+1);
	bcopy(value,*ptr,len);
	*(*ptr+len) = 0;
	return(len+1);
}


int charUnpack(value, ptr, len)
	char	**value;
	u_char	*ptr;
	int	len;
{
	*value = (char *)malloc(len+1);
	bcopy(ptr,*value,len);
	*(*value+len) = 0;
	return(len);
}



void regerror(msg)
	char	*msg;
{
	char	buf[160];

	sprintf(buf,"Regular expression error - %s",msg);
	runError(buf);
	exit(1);
}


int charCompare(v1, v2, op, len1, len2, res)
	u_char	*v1,
		*v2;
	int	op,
		len1,
		len2,
		*res;
{
	char	*val1,
		*val2;

	val1 = (char *)malloc(len1+1);
	val2 = (char *)malloc(len2+1);
	bcopy(v1,val1,len1);
	bcopy(v2,val2,len2);
	*(val1+len1) = 0;
	*(val2+len2) = 0;

	switch(op)
	{
		case GE:
			*res = (strcmp(val1,val2) >= 0);
			break;
		case LE:
			*res = (strcmp(val1,val2) <= 0);
			break;
		case NE:
			*res = (strcmp(val1,val2) != 0);
			break;
		case EQ:
			*res = (strcmp(val1,val2) == 0);
			break;
		case GT:
			*res = (strcmp(val1,val2) > 0);
			break;
		case LT:
			*res = (strcmp(val1,val2) < 0);
			break;
		case RE:
		{
			regexp	*reg;
			reg = regcomp(val2);
			*res = regexec(reg,val1);
			(void)free(reg);
			break;
		}
	}
	free(val1);
	free(val2);
	return(0);
}


int charMath(v1,v2,res,op,len1,len2)
	u_char	*v1,
		*v2,
		**res;
	int	len1,
		len2;
{
	char 	*cp;
	if (op != OP_ADD)
	{
		return(-1);
	}
	*res = (u_char*)malloc(len1+len2+1);
	cp = (char *)*res;
	bzero(cp,len1+len2+1);
	strncpy(cp,v1,len1);
	strncat(cp,v2,len2);
	return(strlen(cp));
}

int charCheck(str)
	char	*str;
{
	/* Anything matches the char type */
	return(0);
}
