/*
 * Csh - a shell by PJAC
 * eval.c - the expression evaluayion routines
 */

#include "csh.h"
#include <sys/types.h>
#include <sys/stat.h>

/*
 * evaluate the argv array - given by argv
 */

#define	OROR	1
#define	ANDAND	2
#define	EQUEQU	3
#define	NOTEQU	4
#define	LESEQU	5
#define	MOREQU	6
#define	LSHIFT  7
#define RSHIFT	8

struct	m	{
	int	mval;
	char	*msval;
	char	mcmd;
	char	mlevel;
};

arg_eval(argc, argv, res)
char	**argv;
int	*res;
{
	int	val = 0;
	int	lastlchar = 0;
	int	tval;
	int	cmd;
	char	*p, *q;
	int	level;
	char	*sval, *stval;
	struct	m	*j;
	struct	m	restab[18];
	struct	cmd	T;
	/*
	 * single char operators, | is used as a seperator as well as an
	 * an op, offset into o1char == precedence
	 */
	static	char	o1char[] = "|^&||||<>||+-*/%";
	static	char	o2char[] = "|&|||=|||||<>";
	static	char	o2cmd[] =  { OROR, ANDAND, 0, 0, 0, EQUEQU,
				     0, 0, 0, 0, 0, LSHIFT, RSHIFT };
	static	char	o3char[] = "!=<=>=";
	static	char	o3cmd[] = { NOTEQU, LESEQU, MOREQU };

	j = restab;
	j->mlevel = 0;
	for(;;){
		if(--argc < 0)
			p = "";
		else
			p = *argv++;
		if(strcmp("(", p) ==  0){
			/* a bracketed expression  - call the function */
			for(tval = 0 ; tval < argc && strcmp(argv[tval], ")");)
				tval++;
			if(tval >= argc)
				return(ERR);
			if(tval){
				/* if got a command execute it */
				T.cm_parent = 0; T.cm_child = 0; T.cm_next = 0;
				T.cm_ofile = 0; T.cm_ifile = 0;
				stval = argv[tval];
				argv[tval] = 0; /* hack the last arg */
				T.cm_argv = argv;
				T.cm_argc = tval;
				val = r_tree(&T);
				argv[tval] = stval;
			}
			else
				val = 1;
			tval++;
			argc -= tval;
			argv += tval;
			val = val ? 0 : 1; /* invert the logic */
			p = val ? "1" : "0";
		}
		else if(*p == '-'){
			if(p[1] == 0){	/* a normal minus sign */
				if(!argc)
					return(ERR);
				lastlchar ^= 4;
				continue;
			}
			if(p[2] == 0 && index("rwxeozfd", p[1])){
				if(!argc)
					return(ERR);
				--argc;
				if(tst_file(*argv++, p[1]))
					p = "1";
				else
					p = "0";
				val = atoi(p);
			}
			else
				val = 0;
		}
		else { 
			if(p[1] == 0){
				if(*p == '~'){
					if(!argc)
						return(ERR);
					lastlchar ^= 1;
					continue;
				}
				else if(*p == '^'){
					if(!argc)
						return(ERR);
					lastlchar ^= 2;
					continue;
				}
			}
			if(*p >= '0' && *p <= '9')
				val = atoi(p);
			else
				val = 0;
		}
		sval = p;
		if(lastlchar){
			if(lastlchar & 1)
				val = ~val;
			if(lastlchar & 2)
				val = !val;
			if(lastlchar & 4)
				val = -val;
			lastlchar = 0;
		}
		if(--argc < 0)
			p = "XXX";
		else
			p = *argv++;
		if(p[1] == 0){
			q = index(o1char, *p);
			if(q == 0)
				return(ERR);
			cmd = *p;
			level = q - o1char + 2; /* +2 for || and && */
		}
		else if(*p == p[1] && !p[2]){
			q = index(o2char, *p);
			if(q == 0)
				return(ERR);
			level = q - o2char;
			cmd = o2cmd[level];
		}
		else {
			level = -1;
			for(q = o3char ; *q ; q+=2)
				if(*q == *p && q[1] == p[1] && !p[2])
					break;
			if(*q){
				level = (q - o3char)/2;
				cmd = o3cmd[level];
				level += 6; /* get it to the right precedence */
			}
		}
		level++;
		/*
		 * now do the ame loop
		 */
	ame:    if(j->mlevel < (char)level){	/* current operator has */
			(++j)->mcmd = cmd;	/* higher precedence */
			j->mval = val;
			j->msval = sval;
			j->mlevel = level;
			continue;
		}
		if(!j->mlevel){			/* end of expression */
			*res = val;
			if(argc > 0)
				return(ERR);
			return(OK);
		}
		tval = j->mval;
		stval = j->msval;
		switch(j->mcmd){
		case '+':
			val += tval;
			break;
		case '-':
			val = tval - val;
			break;
		case '*':
			val *= tval;
			break;
		case '/':
			val = tval / val;
			break;
		case '%':
			val = tval % val;
			break;
		case '&':
			val &= tval;
			break;
		case '|':
			val |= tval;
			break;
		case '^':
			val ^= tval;
			break;
		case '<':
			val = (tval < val);
			break;
		case '>':
			val = (tval > val);
			break;
		case NOTEQU:
			val = (strcmp(stval, sval) != 0);
			break;
		case EQUEQU:
			val = (strcmp(stval, sval) == 0);
			break;
		case OROR:
			val = (val || tval);
			break;
		case ANDAND:
			val = (val && tval);
			break;
		case LESEQU:
			val = (tval <= val);
			break;
		case MOREQU:
			val = (tval >= val);
			break;
		case LSHIFT:
			val = tval << val;
			break;
		case RSHIFT:
			val = tval >> val;
			break;
		}
		sval = val ? "1" : "0";	/* give it a value */
		j--;                    /* execute it then pop the stack and */
		goto ame;               /* deal with the next operator */
	}
	/* return(ERR); */
}

/*
 * test for file existance and satisfying condition mode
 */

tst_file(file, mode)
char	*file;
{
	struct	stat	statbuf;

	if(stat(file, &statbuf) < 0)
		return(FALSE);
	switch(mode){
	case 'r':
		if(statbuf.st_mode & S_IREAD)
			break;
		return(FALSE);
	case 'w':
		if(statbuf.st_mode & S_IWRITE)
			break;
		return(FALSE);
	case 'x':
		if(statbuf.st_mode & S_IEXEC)
			break;
		return(FALSE);
	case 'e':	/* exist */
		break;
	case 'o':	/* on msdos we always own it */
#ifndef	MSDOS
		if(getuid() != statbuf.st_uid)
			return(FALSE);
#endif
		break;
	case 'z':
		if(statbuf.st_size)
			return(FALSE);
		break;
	case 'f':
		if((statbuf.st_mode & S_IFMT) != S_IFREG)
			return(FALSE);
		break;
	case 'd':
		if((statbuf.st_mode & S_IFMT) != S_IFDIR)
			return(FALSE);
		break;
	}
	return(TRUE);
}
