/*
 * Configurable ps-like program.
 * Conditional display routines.
 *
 * Copyright (c) 2014 David I. Bell
 * Permission is granted to use, distribute, or modify this source,
 * provided that this copyright notice remains intact.
 */

#include "ips.h"
#include "expr.h"


/*
 * Function ids for expressions.
 */
#define	FUNC_MATCH	FUNCTION_BUILD(1, 2)
#define	FUNC_STRLEN	FUNCTION_BUILD(2, 1)
#define	FUNC_MIN	FUNCTION_BUILD(3, 2)
#define	FUNC_MAX	FUNCTION_BUILD(4, 2)
#define	FUNC_ABS	FUNCTION_BUILD(5, 1)
#define	FUNC_MY		FUNCTION_BUILD(6, 1)
#define	FUNC_CMP	FUNCTION_BUILD(7, 2)
#define	FUNC_STR	FUNCTION_BUILD(8, 1)


/*
 * A conservative buffer size which is guaranteed to hold the string
 * representation of a long integer value and its terminating null.
 */
#define	LONG_STR_LENGTH	(sizeof(long) * 3 + 2)


/*
 * Static variables.
 */
static	BOOL	condFlag;		/* TRUE if have condition tree */
static	TREE	condTree;		/* condition tree */
static	NODE	badNode;		/* special node for errors */
static	VALUE	badValue;		/* special value for errors */


/*
 * Local procedures.
 */
static	NODE *	ParseAlternation(TREE * tree);
static	NODE *	ParseOrOr(TREE * tree);
static	NODE *	ParseAndAnd(TREE * tree);
static	NODE *	ParseRelation(TREE * tree);
static	NODE *	ParseSum(TREE * tree);
static	NODE *	ParseProduct(TREE * tree);
static	NODE *	ParseOr(TREE * tree);
static	NODE *	ParseAnd(TREE * tree);
static	NODE *	ParseShift(TREE * tree);
static	NODE *	ParseUnary(TREE * tree);
static	NODE *	ParseTerm(TREE * tree);
static	NODE *	ParseSymbol(TREE * tree, char * str);
static	NODE *	ParseMacro(TREE * tree, char * str);
static	NODE *	ParseFunction(TREE * tree, char * str);
static	NODE *	NewNode(TREE * tree, OP op, NODE * left, NODE * right);
static	NODE *	BadNode(TREE * tree, char * message);
static	TOKEN	ParseNewToken(TREE * tree);
static	TOKEN	ParseToken(TREE * tree);
static	TOKEN	ParseNumber(TREE * tree);
static	TOKEN	BadToken(TREE * tree, char * msg);
static	VALUE	EvaluateComparison(TREE * tree, const NODE * node);
static	VALUE	EvaluateFunction(TREE * tree, const NODE * node);
static	BOOL	ConvertType(VALUE val, int type, VALUE * retval);

static	VALUE	FunctionMatch(TREE * tree, VALUE arg1, VALUE arg2);
static	VALUE	FunctionMin(TREE * tree, VALUE arg1, VALUE arg2);
static	VALUE	FunctionMax(TREE * tree, VALUE arg1, VALUE arg2);
static	VALUE	FunctionStrlen(TREE * tree, VALUE arg);
static	VALUE	FunctionAbs(TREE * tree, VALUE arg);
static	VALUE	FunctionStr(TREE * tree, VALUE arg);
static	VALUE	FunctionCmp(TREE * tree, VALUE arg1, VALUE arg2);
static	VALUE	FunctionMy(TREE * tree, const NODE *node);


/*
 * Check if the specified process should be shown based on the conditional
 * criteria.  Returns TRUE if so.  This works by possibly evaluating an
 * expression tree.
 */
BOOL
IsShownProcess(const PROC * proc)
{
	TREE *	tree;
	int	i;
	VALUE	value;

	/*
	 * If this is a thread and threads are not being shown then
	 * we don't want it.
	 */
	if (proc->isThread && !showThreads)
		return FALSE;

	/*
	 * If we are showing threads and this is a main process and
	 * there is more than 1 thread then we don't want it.
	 */
	if (showThreads && !proc->isThread && (proc->threadCount > 1))
		return FALSE;

	/*
	 * If we are showing threads and this is a thread but there is
	 * only one thread then we don't want it (since we will show
	 * the main process in that case).
	 */
	if (showThreads && proc->isThread && (proc->threadCount <= 1))
		return FALSE;

	if (myProcs && (proc->uid != myUid))
		return FALSE;

	if (noSelf && (proc->pid == myPid))
		return FALSE;

	if (pidCount)
	{
		for (i = 0; i < pidCount; i++)
		{
			if (pidList[i] == proc->pid)
				break;
		}

		if (i == pidCount)
			return FALSE;
	}

	if (userCount)
	{
		for (i = 0; i < userCount; i++)
		{
			if (userList[i] == proc->uid)
				break;
		}

		if (i == userCount)
			return FALSE;
	}

	if (groupCount)
	{
		for (i = 0; i < groupCount; i++)
		{
			if (groupList[i] == proc->gid)
				break;
		}

		if (i == groupCount)
			return FALSE;
	}

	if (programCount)
	{
		for (i = 0; i < programCount; i++)
		{
			if (strcmp(programList[i], proc->program) == 0)
				break;
		}

		if (i == programCount)
			return FALSE;
	}

	if (activeOnly && !GetIsActive(proc))
		return FALSE;

	if (!condFlag)
		return TRUE;

	tree = &condTree;
	tree->proc = proc;

	value = EvaluateNode(tree, tree->root);

	if ((value.type == VALUE_NUMBER) || (value.type == VALUE_BOOLEAN))
		return (value.intVal != 0);

	if (value.type == VALUE_STRING)
		return (*value.strVal != '\0');

	return FALSE;
}


/*
 * Get the use flags for the current condition expression.
 * This just means find all references to columns, and OR together
 * the use flags for each column.
 */
USEFLAG
GetConditionUseFlags(void)
{
	if (!condFlag)
		return USE_NONE;

	return GetNodeUseFlags(condTree.root);
}


/*
 * Recursive routine to search down all nodes to collect use flags
 * from those nodes which reference column values.
 */
USEFLAG
GetNodeUseFlags(const NODE * node)
{
	int	i;
	USEFLAG	flags;

	flags = USE_NONE;

	/*
	 * If this is a column opcode, then get the use flags for the column.
	 */
	switch (node->op)
	{
		case OP_COLUMN_BASE:
		case OP_COLUMN_SHOW:
		case OP_COLUMN_TEST:
			flags |= node->column->useFlag;
			break;

		case OP_FUNCTION:
			if (node->intVal == FUNC_MY)
				flags |= USE_SELF;

		default:
			break;
	}

	/*
	 * Now look at all the child nodes, and recurse on them.
	 */
	for (i = 0; i < CHILDS; i++)
	{
		if (node->child[i])
			flags |= GetNodeUseFlags(node->child[i]);
	}

	/*
	 * Return the final use flags.
	 */
	return flags;
}


/*
 * Recursive routine to calculate the result of an expression tree
 * concerning the specified process starting with the specified node.
 */
VALUE
EvaluateNode(TREE * tree, const NODE * node)
{
	NODE *		left;
	NODE *		right;
	int		leftType;
	int		rightType;
	int		valType;
	VALUE		value;
	VALUE		leftVal;
	VALUE		rightVal;
	const char *	cp;

	if (node == NULL)
		return badValue;

	left = node->child[0];
	right = node->child[1];

	leftType = VALUE_NONE;
	rightType = VALUE_NONE;
	valType = VALUE_NONE;

	/*
	 * First specify the required types of subTree expressions
	 * and whether we need to evaluate them.
	 */
	switch (node->op)
	{
		case OP_NOT:
			leftType = VALUE_BOOLEAN;
			valType = VALUE_BOOLEAN;
			break;

		case OP_NEGATE:
		case OP_COMPLEMENT:
			leftType = VALUE_NUMBER;
			valType = VALUE_NUMBER;
			break;

		case OP_ADD:
		case OP_SUBTRACT:
		case OP_MULTIPLY:
		case OP_DIVIDE:
		case OP_MODULO:
		case OP_AND:
		case OP_XOR:
			leftType = VALUE_NUMBER;
			rightType = VALUE_NUMBER;
			valType = VALUE_NUMBER;
			break;

		case OP_OROR:
		case OP_ANDAND:
			leftType = VALUE_BOOLEAN;
			rightType = VALUE_BOOLEAN;
			valType = VALUE_BOOLEAN;
			break;

		case OP_ALTERNATION:
			leftType = VALUE_BOOLEAN;
			break;

		case OP_STRING:
			value.type = VALUE_STRING;
			value.strVal = node->strVal;

			return value;

		case OP_NUMBER:
			value.type = VALUE_NUMBER;
			value.intVal = node->intVal;

			return value;

		case OP_COLUMN_TEST:
			value.type = VALUE_BOOLEAN;
			value.intVal =  node->column->testFunc(tree->proc);

			return value;

		case OP_COLUMN_BASE:
			node->column->evalFunc(tree->proc, &value);

			return value;

		case OP_COLUMN_SHOW:
			cp = node->column->showFunc(tree->proc);

			/*
			 * This is a bit of a hack.
			 * Copy the string unless it points to the program
			 * name, command line, or environment, in which case
			 * we know it is not temporary and so we don't need
			 * to copy it.  This also helps avoid running out of
			 * string space.
			 */
			if ((cp != tree->proc->command) &&
				(cp != tree->proc->environment) &&
				(cp != tree->proc->program))
			{
				cp = CopyTempString(cp);
			}

			/*
			 * Store the string value for the column.
			 * If this was copied, then it will be freed when
			 * we end the current display of processes.
			 */
			value.type = VALUE_STRING;
			value.strVal = cp;

			return value;

		case OP_EQUAL:
		case OP_NOTEQUAL:
		case OP_LESS:
		case OP_LESSEQUAL:
		case OP_GREATER:
		case OP_GREATEREQUAL:
			return EvaluateComparison(tree, node);

		case OP_FUNCTION:
			return EvaluateFunction(tree, node);

		default:
			return badValue;
	}

	/*
	 * Initialize the values to make the compiler happy.
	 */
	leftVal.type = VALUE_NONE;
	leftVal.intVal = 0;
	leftVal.strVal = 0;
	leftVal.column = 0;

	rightVal.type = VALUE_NONE;
	rightVal.intVal = 0;
	rightVal.strVal = 0;
	rightVal.column = 0;

	/*
	 * Evaluate the left and right subtrees if requested and then
	 * convert their value into the requested type.
	 */
	if (leftType != VALUE_NONE)
	{
		leftVal = EvaluateNode(tree, left);

		if (!ConvertType(leftVal, leftType, &leftVal))
			return badValue;
	}

	if (rightType != VALUE_NONE)
	{
		rightVal = EvaluateNode(tree, right);

		if (!ConvertType(rightVal, rightType, &rightVal))
			return badValue;
	}

	value.type = valType;

	/*
	 * Now operate on the converted values.
	 * For most of these, their input arguments have already validated.
	 */
	switch (node->op)
	{
		case OP_AND:
			value.intVal = leftVal.intVal & rightVal.intVal;
			break;

		case OP_OR:
			value.intVal = leftVal.intVal | rightVal.intVal;
			break;

		case OP_XOR:
			value.intVal = leftVal.intVal ^ rightVal.intVal;
			break;

		case OP_COMPLEMENT:
			value.intVal = ~leftVal.intVal;
			break;

		case OP_NEGATE:
			value.intVal = -leftVal.intVal;
			break;

		case OP_ADD:
			value.intVal = leftVal.intVal + rightVal.intVal;
			break;

		case OP_SUBTRACT:
			value.intVal = leftVal.intVal - rightVal.intVal;
			break;

		case OP_MULTIPLY:
			value.intVal = leftVal.intVal * rightVal.intVal;
			break;

		case OP_DIVIDE:
			if (rightVal.intVal == 0)
				return badValue;

			value.intVal = leftVal.intVal / rightVal.intVal;
			break;

		case OP_MODULO:
			if (rightVal.intVal == 0)
				return badValue;

			value.intVal = leftVal.intVal % rightVal.intVal;
			break;

		case OP_NOT:
			value.intVal = !leftVal.intVal;
			break;

		case OP_ANDAND:
			value.intVal = leftVal.intVal && rightVal.intVal;
			break;

		case OP_OROR:
			value.intVal = leftVal.intVal || rightVal.intVal;
			break;

		case OP_ALTERNATION:
			if (leftVal.intVal != 0)
				value = EvaluateNode(tree, right);
			else
				value = EvaluateNode(tree, node->child[2]);

			break;

		default:
			return badValue;
	}

	return value;
}


/*
 * Evaluate a function call.
 */
static VALUE
EvaluateFunction(TREE * tree, const NODE * node)
{
	VALUE	value;
	VALUE	arg1;
	VALUE	arg2;

	if (node->op != OP_FUNCTION)
		return badValue;

	/*
	 * Check for the magic "my" function, which acts specially.
	 * We cannot evaluate its argument before calling it since
	 * its argument is special.
	 */
	if (node->intVal == FUNC_MY)
		return FunctionMy(tree, node->child[0]);

	/*
	 * Evaluate the number of arguments indicated by the function id.
	 * Initialise the arguments to bad values in case of problems.
	 */
	arg1 = badValue;
	arg2 = badValue;

	switch (FUNCTION_ARGS(node->intVal))
	{
		case 2:	arg2 = EvaluateNode(tree, node->child[1]);
		case 1: arg1 = EvaluateNode(tree, node->child[0]);
		case 0: break;

		default:
			return badValue;
	}

	/*
	 * Call the proper function indicated by the function id.
	 */
	switch (node->intVal)
	{
		case FUNC_MATCH:
			return FunctionMatch(tree, arg1, arg2);

		case FUNC_MIN:
			return FunctionMin(tree, arg1, arg2);

		case FUNC_MAX:
			return FunctionMax(tree, arg1, arg2);

		case FUNC_STRLEN:
			return FunctionStrlen(tree, arg1);

		case FUNC_ABS:
			return FunctionAbs(tree, arg1);

		case FUNC_STR:
			return FunctionStr(tree, arg1);

		case FUNC_CMP:
			return FunctionCmp(tree, arg1, arg2);

		default:
			return badValue;
	}

	return value;
}


/*
 * Function to do a pattern match between two values interpreted as strings.
 * Returns the value of the comparison.
 */
static VALUE
FunctionMatch(TREE * tree, VALUE arg1, VALUE arg2)
{
	VALUE	value;
	char	buf1[LONG_STR_LENGTH];
	char	buf2[LONG_STR_LENGTH];

	/*
	 * Convert the input arguments to strings if they were numbers.
	 */
	if ((arg1.type == VALUE_BOOLEAN) || (arg1.type == VALUE_NUMBER))
	{
		sprintf(buf1, "%ld", arg1.intVal);
		arg1.type = VALUE_STRING;
		arg1.strVal = buf1;
	}

	if ((arg2.type == VALUE_BOOLEAN) || (arg2.type == VALUE_NUMBER))
	{
		sprintf(buf2, "%ld", arg2.intVal);
		arg2.type = VALUE_STRING;
		arg2.strVal = buf2;
	}

	/*
	 * If they are not both strings now, then return a bad value.
	 */
	if ((arg1.type != VALUE_STRING) || (arg2.type != VALUE_STRING))
		return badValue;

	/*
	 * They are both strings now so do the pattern match.
	 */
	value.type = VALUE_BOOLEAN;
	value.intVal = PatternMatch(arg1.strVal, arg2.strVal);

	return value;
}


/*
 * Function to find the minimum of two numbers.
 * This is not defined for strings.
 */
static VALUE
FunctionMin(TREE * tree, VALUE arg1, VALUE arg2)
{
	VALUE	value;

	/*
	 * Make sure the arguments are numbers.
	 */
	if (((arg1.type != VALUE_BOOLEAN) && (arg1.type != VALUE_NUMBER)) ||
		((arg2.type != VALUE_BOOLEAN) && (arg2.type != VALUE_NUMBER)))
	{
		return badValue;
	}

	value.type = VALUE_NUMBER;
	value.intVal = (arg1.intVal < arg2.intVal) ? arg1.intVal : arg2.intVal;

	return value;
}


/*
 * Function to find the maximum of two numbers.
 * This is not defined for strings.
 */
static VALUE
FunctionMax(TREE * tree, VALUE arg1, VALUE arg2)
{
	VALUE	value;

	/*
	 * Make sure the arguments are numbers.
	 */
	if (((arg1.type != VALUE_BOOLEAN) && (arg1.type != VALUE_NUMBER)) ||
		((arg2.type != VALUE_BOOLEAN) && (arg2.type != VALUE_NUMBER)))
	{
		return badValue;
	}

	value.type = VALUE_NUMBER;
	value.intVal = (arg1.intVal > arg2.intVal) ? arg1.intVal : arg2.intVal;

	return value;
}


/*
 * Function to find the absolute value of a number.
 * This is not defined for strings.
 */
static VALUE
FunctionAbs(TREE * tree, VALUE arg)
{
	VALUE	value;

	if ((arg.type != VALUE_BOOLEAN) && (arg.type != VALUE_NUMBER))
		return badValue;

	value.type = VALUE_NUMBER;
	value.intVal = (arg.intVal < 0) ? -arg.intVal : arg.intVal;

	return value;
}


/*
 * Function to convert a value to a string.
 */
static VALUE
FunctionStr(TREE * tree, VALUE arg)
{
	VALUE	value;
	char	buf[LONG_STR_LENGTH];

	if (arg.type == VALUE_STRING)
		return arg;

	if ((arg.type != VALUE_BOOLEAN) && (arg.type != VALUE_NUMBER))
		return badValue;

	value.type = VALUE_STRING;

	/*
	 * Check a few common values specially, and if it is not one
	 * of those, then allocate a temporary string for the value.
	 */
	if (arg.intVal == 0)
		value.strVal = "0";
	else if (arg.intVal == 1)
		value.strVal = "1";
	else if (arg.intVal == -1)
		value.strVal = "-1";
	else
	{
		sprintf(buf, "%ld", arg.intVal);
		value.strVal = CopyTempString(buf);
	}

	return value;
}


/*
 * Function to return the length of a string.
 */
static VALUE
FunctionStrlen(TREE * tree, VALUE arg)
{
	VALUE	value;
	char	buf[LONG_STR_LENGTH];

	/*
	 * Convert the input argument a string if it was a number.
	 */
	if ((arg.type == VALUE_BOOLEAN) || (arg.type == VALUE_NUMBER))
	{
		sprintf(buf, "%ld", arg.intVal);
		arg.type = VALUE_STRING;
		arg.strVal = buf;
	}

	/*
	 * If it is not a string now, then return a bad value.
	 */
	if ((arg.type != VALUE_STRING))
		return badValue;

	/*
	 * Return the length of the string.
	 */
	value.type = VALUE_NUMBER;
	value.intVal = strlen(arg.strVal);

	return value;
}


/*
 * Function to compare two values returning -1, 0, or 1.
 * The values can be numbers or strings.
 */
static VALUE
FunctionCmp(TREE * tree, VALUE arg1, VALUE arg2)
{
	VALUE	value;
	char	buf1[LONG_STR_LENGTH];
	char	buf2[LONG_STR_LENGTH];

	/*
	 * If both arguments are numbers then compare them that way.
	 */
	if (((arg1.type == VALUE_BOOLEAN) || (arg1.type == VALUE_NUMBER)) &&
		((arg2.type == VALUE_BOOLEAN) || (arg2.type == VALUE_NUMBER)))
	{
		value.type = VALUE_NUMBER;
		value.intVal = 0;
		value.column = 0;
		value.strVal = 0;

		if (arg1.intVal < arg2.intVal)
			value.intVal = -1;

		if (arg1.intVal > arg2.intVal)
			value.intVal = 1;

		return value;
	}

	/*
	 * Convert the input argument to strings if they were numbers.
	 */
	if ((arg1.type == VALUE_BOOLEAN) || (arg1.type == VALUE_NUMBER))
	{
		sprintf(buf1, "%ld", arg1.intVal);
		arg1.type = VALUE_STRING;
		arg1.strVal = buf1;
	}

	if ((arg2.type == VALUE_BOOLEAN) || (arg2.type == VALUE_NUMBER))
	{
		sprintf(buf2, "%ld", arg2.intVal);
		arg2.type = VALUE_STRING;
		arg2.strVal = buf2;
	}

	/*
	 * Make sure the arguments are strings now.
	 */
	if ((arg1.type != VALUE_STRING) || (arg2.type != VALUE_STRING))
		return badValue;

	/*
	 * OK, now do a string compare and normalize the result.
	 */
	value.type = VALUE_NUMBER;
	value.intVal = strcmp(arg1.strVal, arg2.strVal);
	value.column = 0;
	value.strVal = 0;

	if (value.intVal < 0)
		value.intVal = -1;

	if (value.intVal > 0)
		value.intVal = 1;

	return value;
}


/*
 * Function to return information about MY own process,
 * instead of the process currently being examined.
 * This function is special and doesn't work like the other functions.
 */
static VALUE
FunctionMy(TREE * tree, const NODE * node)
{
	const PROC *	savedProc;
	PROC *		proc;
	int		savedUid;
	int		savedGid;
	VALUE		value;

	/*
	 * Save the process pointer of the current process being used for
	 * evaluation, and replace it with a pointer to our own process.
	 * Save our effective user and group ids and replace them with our
	 * real ones.
	 */
	proc = FindProcess(myPid, NO_THREAD_ID);

	savedUid = proc->uid;
	savedGid = proc->gid;

	proc->uid = myUid;
	proc->gid = myGid;

	savedProc = tree->proc;
	tree->proc = proc;

	/*
	 * Evaluate the node (which should be a column name) using
	 * our own process information instead of the current one.
	 */
	value = EvaluateNode(tree, node);

	/*
	 * Restore our effective user and group ids, then replace the
	 * process pointer in the tree to what it should be.
	 */
	proc->uid = savedUid;
	proc->gid = savedGid;

	tree->proc = savedProc;

	return value;
}


/*
 * Perform a comparison operation between two arbitrary values.
 * Sets the resulting value to either a boolen value, or else
 * the bad value if the comparison is illegal.
 */
static VALUE
EvaluateComparison(TREE * tree, const NODE * node)
{
	int	compare;
	VALUE	leftVal;
	VALUE	rightVal;
	VALUE	value;

	leftVal = EvaluateNode(tree, node->child[0]);

	if (leftVal.type == VALUE_BAD)
		return badValue;

	rightVal = EvaluateNode(tree, node->child[1]);

	if (rightVal.type == VALUE_BAD)
		return badValue;

	if (!CompareValues(leftVal, rightVal, &compare))
		return badValue;

	/*
	 * Now set the result according to the comparison required
	 * and the result of the comparison.
	 */
	value.type = VALUE_BOOLEAN;

	switch (node->op)
	{
		case OP_EQUAL:
			value.intVal = (compare == 0);
			break;

		case OP_NOTEQUAL:
			value.intVal = (compare != 0);
			break;

		case OP_LESS:
			value.intVal = (compare < 0);
			break;

		case OP_LESSEQUAL:
			value.intVal = (compare <= 0);
			break;

		case OP_GREATER:
			value.intVal = (compare > 0);
			break;

		case OP_GREATEREQUAL:
			value.intVal = (compare >= 0);
			break;

		default:
			return badValue;
	}

	return value;
}


/*
 * Compare two values and determine their relative sizes.
 * Indirectly eturns -1 if the first value is less, 1 if the first value
 * is more, and 0 if they are the same.
 * Returns TRUE if the comparison is valid.
 */
BOOL
CompareValues(const VALUE leftVal, const VALUE rightVal, int * result)
{
	int	compare = 0;

	switch (TWOVAL(leftVal.type, rightVal.type))
	{
		case TWOVAL(VALUE_NUMBER, VALUE_NUMBER):
		case TWOVAL(VALUE_NUMBER, VALUE_BOOLEAN):
		case TWOVAL(VALUE_BOOLEAN, VALUE_NUMBER):
		case TWOVAL(VALUE_BOOLEAN, VALUE_BOOLEAN):
			if (leftVal.intVal < rightVal.intVal)
				compare = -1;

			if (leftVal.intVal > rightVal.intVal)
				compare = 1;

			break;

		case TWOVAL(VALUE_STRING, VALUE_STRING):
			compare = strcmp(leftVal.strVal, rightVal.strVal);

			break;;

		default:
			return FALSE;
	}

	*result = compare;

	return TRUE;
}


/*
 * Try to convert the specified input value into the specified type
 * value and return that value.  Returns TRUE if successful.
 */
static BOOL
ConvertType(VALUE val, int type, VALUE * retval)
{
	const char *	cp;
	long		intVal;
	BOOL		isNeg;

	/*
	 * If the type is already correct, then just return the same value.
	 */
	if (val.type == type)
	{
		*retval = val;

		return TRUE;
	}

	/*
	 * We actually need a conversion.
	 * Switch on the pair of "from" and "to" types.
	 */
	switch (TWOVAL(val.type, type))
	{
		case TWOVAL(VALUE_STRING, VALUE_BOOLEAN):
			retval->type = VALUE_BOOLEAN;
			retval->intVal = (val.strVal[0] != '\0');
			break;

		case TWOVAL(VALUE_NUMBER, VALUE_BOOLEAN):
			retval->type = VALUE_BOOLEAN;
			retval->intVal = (val.intVal != 0);
			break;

		case TWOVAL(VALUE_BOOLEAN, VALUE_NUMBER):
			retval->type = VALUE_NUMBER;
			retval->intVal = val.intVal;
			break;

		case TWOVAL(VALUE_STRING, VALUE_NUMBER):
			cp = val.strVal;

			intVal = 0;
			isNeg = FALSE;

			while (isBlank(*cp))
				cp++;

			if (*cp == '-')
			{
				cp++;
				isNeg = TRUE;
			}

			if (!isDigit(*cp))
			{
				*retval = badValue;

				return FALSE;
			}

			while (isDigit(*cp))
				intVal = intVal * 10 + *cp++ - '0';

			while (isBlank(*cp))
				cp++;

			if (*cp)
			{
				*retval = badValue;

				return FALSE;
			}

			if (isNeg)
				intVal = -intVal;

			retval->intVal = intVal;
			retval->type = VALUE_NUMBER;

			break;

		default:
			*retval = badValue;

			return FALSE;
	}

	return TRUE;
}


/*
 * Clear any condition that may be set.
 * This loses memory but that is ok because this is only done on startup.
 */
void
ClearCondition(void)
{
	condFlag = FALSE;
}


/*
 * Parse the specified condition expression into an expression tree.
 * The expression uses the C syntax mostly, and where left halfs of
 * comparisons statements are column names and right halfs are numeric
 * or implicit string constants.  Column names by themselves act as nonzero
 * or NULL.  Example: "percent-cpu || state == 'R'"
 */
BOOL
ParseCondition(const char * str)
{
	TREE	treeData;

	if (!ParseTree(&treeData, str, 0))
		return FALSE;

	condTree = treeData;
	condFlag = TRUE;

	return TRUE;
}


/*
 * Parse an expression into the specified tree.
 * This initialises the fields in the tree structure.
 * The depth shows the recursion depth of expression macros.
 */
BOOL
ParseTree(TREE * tree, const char * str, int depth)
{
	int	len;

	badValue.type = VALUE_BAD;
	badValue.strVal = "";
	badValue.intVal = 0;
	badValue.column = NULL;

	len = strlen(str);

	tree->expr = malloc(len + 1);
	tree->modExpr = malloc(len + 1);;

	if ((tree->expr == NULL) || (tree->modExpr == NULL))
	{
		fprintf(stderr, "Cannot allocate condition expression\n");

		return FALSE;
	}

	memcpy(tree->expr, str, len + 1);
	memcpy(tree->modExpr, str, len + 1);

	tree->cp = tree->expr;
	tree->failed = FALSE;
	tree->root = NULL;
	tree->token = TOKEN_BAD;
	tree->rescanToken = FALSE;
	tree->tokenStr = "";
	tree->tokenInt = 0;
	tree->depth = depth;

	/*
	 * This is the actual parse of the string into nodes.
	 */
	tree->root = ParseAlternation(tree);

	if (ParseToken(tree) != TOKEN_EOF)
	{
		if (!tree->failed)
			fprintf(stderr, "Bad expression\n");

		tree->failed = TRUE;
	}

	if (tree->failed)
		return FALSE;

	return TRUE;
}


/*
 * Parse a conditional result expression.
 * alternation = oror ? oror : oror.
 */
static NODE *
ParseAlternation(TREE * tree)
{
	NODE *	top;
	NODE *	right;
	NODE *	altRight;
	TOKEN	token;

	top = ParseOrOr(tree);

	token = ParseToken(tree);

	if (token != TOKEN_QUESTIONMARK)
	{
		tree->rescanToken = TRUE;

		return top;
	}

	right = ParseOrOr(tree);

	token = ParseToken(tree);

	if (token != TOKEN_COLON)
	{
		tree->rescanToken = TRUE;

		return BadNode(tree, "Missing colon in conditional");
	}

	altRight = ParseOrOr(tree);

	top = NewNode(tree, OP_OROR, top, right);

	top->child[2] = altRight;

	return top;
}


/*
 * Parse a list of OROR expressions.
 */
static NODE *
ParseOrOr(TREE * tree)
{
	NODE *	top;
	NODE *	right;
	TOKEN	token;

	top = ParseAndAnd(tree);

	while (TRUE)
	{
		token = ParseToken(tree);

		if (token != TOKEN_OROR)
		{
			tree->rescanToken = TRUE;

			return top;
		}

		right = ParseAndAnd(tree);

		top = NewNode(tree, OP_OROR, top, right);
	}
}


/*
 * Parse a list of ANDAND expressions.
 */
static NODE *
ParseAndAnd(TREE * tree)
{
	NODE *	top;
	NODE *	right;
	TOKEN	token;

	top = ParseRelation(tree);

	while (TRUE)
	{
		token = ParseToken(tree);

		if (token != TOKEN_ANDAND)
		{
			tree->rescanToken = TRUE;

			return top;
		}

		right = ParseRelation(tree);

		top = NewNode(tree, OP_ANDAND, top, right);
	}
}


/*
 * Parse a comparison relation between two values.
 */
static NODE *
ParseRelation(TREE * tree)
{
	NODE *	top;
	NODE *	right;
	TOKEN	token;
	OP	op;

	top = ParseSum(tree);

	token = ParseToken(tree);

	switch (token)
	{
		case TOKEN_EQUAL:
			op = OP_EQUAL;
			break;

		case TOKEN_NOTEQUAL:
			op = OP_NOTEQUAL;
			break;

		case TOKEN_LESS:
			op = OP_LESS;
			break;

		case TOKEN_LESSEQUAL:
			op = OP_LESSEQUAL;
			break;

		case TOKEN_GREATER:
			op = OP_GREATER;
			break;

		case TOKEN_GREATEREQUAL:
			op = OP_GREATEREQUAL;
			break;

		default:
			tree->rescanToken = TRUE;

			return top;
	}

	right = ParseSum(tree);

	return NewNode(tree, op, top, right);
}


/*
 * Parse the sum of products.
 */
static NODE *
ParseSum(TREE * tree)
{
	NODE *	top;
	NODE *	right;
	TOKEN	token;
	OP	op;

	top = ParseProduct(tree);

	/*
	 * Now keep collecting values to be added or subtracted.
	 */
	while (TRUE)
	{
		token = ParseToken(tree);

		switch (token)
		{
			case TOKEN_PLUS:
				op = OP_ADD;
				break;

			case TOKEN_MINUS:
				op = OP_SUBTRACT;
				break;

			default:
				tree->rescanToken = TRUE;

				return top;
		}

		right = ParseProduct(tree);
		top = NewNode(tree, op, top, right);
	}
}


/*
 * Parse the products of inclusive or.
 */
static NODE *
ParseProduct(TREE * tree)
{
	NODE *	top;
	NODE *	right;
	TOKEN	token;
	OP	op;

	top = ParseOr(tree);

	/*
	 * Now keep collecting values to be multiplied, divided, or moduloed.
	 */
	while (TRUE)
	{
		token = ParseToken(tree);

		switch (token)
		{
			case TOKEN_MULTIPLY:
				op = OP_MULTIPLY;
				break;

			case TOKEN_DIVIDE:
				op = OP_DIVIDE;
				break;

			case TOKEN_MODULO:
				op = OP_MODULO;
				break;

			default:
				tree->rescanToken = TRUE;

				return top;
		}

		right = ParseOr(tree);
		top = NewNode(tree, op, top, right);
	}
}


/*
 * Parse the OR of AND values.
 */
static NODE *
ParseOr(TREE * tree)
{
	NODE *	top;
	TOKEN	token;

	top = ParseAnd(tree);

	/*
	 * Keep collecting values to be OR'd together.
	 */
	while (TRUE)
	{
		token = ParseToken(tree);

		if (token != TOKEN_OR)
		{
			tree->rescanToken = TRUE;

			return top;
		}

		top = NewNode(tree, OP_OR, top, ParseAnd(tree));
	}
}


/*
 * Parse the AND or XOR of shift values.
 */
static NODE *
ParseAnd(TREE * tree)
{
	NODE *	top;
	NODE *	right;
	TOKEN	token;

	top = ParseShift(tree);

	/*
	 * Keep collecting values to be AND'd or XOR'd together.
	 */
	while (TRUE)
	{
		token = ParseToken(tree);

		switch (token)
		{
			case TOKEN_AND:
				right = ParseShift(tree);
				top = NewNode(tree, OP_AND, top, right);
				break;

			case TOKEN_XOR:
				right = ParseShift(tree);
				top = NewNode(tree, OP_XOR, top, right);
				break;

			default:
				tree->rescanToken = TRUE;

				return top;
		}
	}
}


/*
 * Parse the shift of unary operators.
 */
static NODE *
ParseShift(TREE * tree)
{
	NODE *	top;
	TOKEN	token;
	OP	op;

	top = ParseUnary(tree);

	token = ParseToken(tree);

	switch (token)
	{
		case TOKEN_LEFTSHIFT:
			op = OP_LEFTSHIFT;
			break;

		case TOKEN_RIGHTSHIFT:
			op = OP_RIGHTSHIFT;
			break;

		default:
			tree->rescanToken = TRUE;

			return top;
	}

	return NewNode(tree, op, top, ParseUnary(tree));
}


/*
 * Parse a unary operator on a term (MINUS, NOT, COMPLEMENT).
 */
static NODE *
ParseUnary(TREE * tree)
{
	switch (ParseToken(tree))
	{
		case TOKEN_MINUS:
			return NewNode(tree, OP_NEGATE, ParseUnary(tree), NULL);

		case TOKEN_NOT:
			return NewNode(tree, OP_NOT, ParseUnary(tree), NULL);

		case TOKEN_COMPLEMENT:
			return NewNode(tree, OP_COMPLEMENT,
				ParseUnary(tree), NULL);

		default:
			tree->rescanToken = TRUE;

			return ParseTerm(tree);
	}
}


/*
 * Parse a single term.
 */
static NODE *
ParseTerm(TREE * tree)
{
	TOKEN	token;
	NODE *	node;

	token = ParseToken(tree);

	switch (token)
	{
		case TOKEN_SYMBOL:
			return ParseSymbol(tree, tree->tokenStr);

		case TOKEN_STRING:
			node = NewNode(tree, OP_STRING, NULL, NULL);

			node->strVal = tree->tokenStr;

			return node;

		case TOKEN_NUMBER:
			node = NewNode(tree, OP_NUMBER, NULL, NULL);

			node->intVal = tree->tokenInt;

			return node;

		case TOKEN_LEFTPAREN:
			node = ParseAlternation(tree);

			token = ParseToken(tree);

			if (token == TOKEN_RIGHTPAREN)
				return node;

			if (token == TOKEN_EOF)
				return BadNode(tree, "Bad expression");

			return BadNode(tree, "Bad expression");

		case TOKEN_RIGHTPAREN:
			return BadNode(tree, "Unbalanced parenthesis");

		default:
			return BadNode(tree, "Missing term");
	}
}


/*
 * Parse a symbol name.
 * This can be a column name, a macro name, or a function name.
 * A column name can optionally be followed by qualifiers.
 */
static NODE *
ParseSymbol(TREE * tree, char * str)
{
	TOKEN	token;
	NODE *	node;
	COLUMN *column;
	OP	op;

	token = ParseToken(tree);

	/*
	 * See if the symbol name is followed by a parenthesis.
	 * If so, then this is a function call.
	 */
	if (token == TOKEN_LEFTPAREN)
	{
		tree->rescanToken = TRUE;

		return ParseFunction(tree, str);
	}

	/*
	 * See if the symbol name is upper case.
	 * If so, then it is a macro that needs expanding.
	 */
	if (isUpper(*str))
	{
		tree->rescanToken = TRUE;

		return ParseMacro(tree, str);
	}

	/*
	 * The symbol must now be a column name.
	 * See if the column is known.
	 */
	column = FindColumn(str);

	if (column == NULL)
	{
		if (!tree->failed)
		{
			fprintf(stderr, "Unknown column \"%s\"\n", str);

			tree->failed = TRUE;
		}

		return BadNode(tree, "");
	}

	/*
	 * If the column name is not followed by a period,
	 * then it means return the base value for the column.
	 */
	if (token != TOKEN_PERIOD)
	{
		tree->rescanToken = TRUE;

		node = NewNode(tree, OP_COLUMN_BASE, NULL, NULL);

		node->column = column;

		return node;
	}

	/*
	 * It was followed by a period.
	 * Then following the period must be a symbol which is a
	 * known qualifier for the column.
	 */
	token = ParseToken(tree);

	if (token != TOKEN_SYMBOL)
		return BadNode(tree, "Missing column qualifier");

	if (strcmp(tree->tokenStr, "base") == 0)
		op = OP_COLUMN_BASE;
	else if (strcmp(tree->tokenStr, "show") == 0)
		op = OP_COLUMN_SHOW;
	else if (strcmp(tree->tokenStr, "test") == 0)
		op = OP_COLUMN_TEST;
	else
	{
		return BadNode(tree, "Illegal column qualifier");
	}

	/*
	 * Return the appropriate type of column reference.
	 */
	node = NewNode(tree, op, NULL, NULL);

	node->column = column;

	return node;
}


/*
 * Parse a function call.
 * Currently, there can only be 3 or less arguments for a function.
 */
static NODE *
ParseFunction(TREE * tree, char * str)
{
	NODE *	node;
	int	func;
	int	argCount;
	int	i;

	func = 0;

	/*
	 * Look for the known function names.
	 */
	if (strcmp(str, "match") == 0)
		func = FUNC_MATCH;

	if (strcmp(str, "strlen") == 0)
		func = FUNC_STRLEN;

	if (strcmp(str, "min") == 0)
		func = FUNC_MIN;

	if (strcmp(str, "max") == 0)
		func = FUNC_MAX;

	if (strcmp(str, "abs") == 0)
		func = FUNC_ABS;

	if (strcmp(str, "str") == 0)
		func = FUNC_STR;

	if (strcmp(str, "my") == 0)
		func = FUNC_MY;

	if (strcmp(str, "cmp") == 0)
		func = FUNC_CMP;

	/*
	 * If not found, complain.
	 */
	if (func == 0)
	{
		fprintf(stderr, "Unknown function \"%s\"\n", str);

		tree->failed = TRUE;

		return BadNode(tree, "");
	}

	if (ParseToken(tree) != TOKEN_LEFTPAREN)
		return BadNode(tree, "Bad function call format\n");

	node = NewNode(tree, OP_FUNCTION, NULL, NULL);

	node->intVal = func;

	/*
	 * Collect the arguments and attach them to the function node.
	 */
	argCount = FUNCTION_ARGS(func);

	if (argCount > CHILDS)
		return BadNode(tree, "Too many function arguments");

	for (i = 0; i < argCount; i++)
	{
		if ((i > 0) && (ParseToken(tree) != TOKEN_COMMA))
		{
			tree->rescanToken = TRUE;

			return BadNode(tree,
				"Not enough arguments for function");
		}

		node->child[i] = ParseAlternation(tree);
	}

	/*
	 * Do special handling for the magic "my" function.
	 * Its argument MUST be a column name (with optional qualifier).
	 */
	if (func == FUNC_MY)
	{
		switch (node->child[0]->op)
		{
			case OP_COLUMN_BASE:
			case OP_COLUMN_SHOW:
			case OP_COLUMN_TEST:
				break;

			default:
				return BadNode(tree,
					"Function \"my\" only uses columns");
		}
	}

	/*
	 * Look for the end of the function call.
	 */
	switch (ParseToken(tree))
	{
		case TOKEN_COMMA:
			return BadNode(tree, "Too many function arguments");

		case TOKEN_RIGHTPAREN:
			return node;

		default:
			return BadNode(tree,
				"Missing right parenthesis in function call");
	}
}


/*
 * Parse a macro name.
 * This just parses and evaluates the macro expansion.
 */
static NODE *
ParseMacro(TREE * tree, char * str)
{
	TREE	subTree;
	ARGS	args;

	if (tree->depth >= MAX_EXPR_DEPTH)
		return BadNode(tree, "Too many levels of expression macros");

	if (!ExpandMacro(MACRO_TYPE_EXPRESSION, str, &args))
		return BadNode(tree, "Macro expansion failed");

	if (args.count != 1)
		return BadNode(tree, "Expression macro isn't one string");

	/*
	 * Parse the expression.  If it fails, an error message has
	 * already been given so we don't need to add another one.
	 */
	if (!ParseTree(&subTree, args.table[0], tree->depth + 1))
	{
		tree->failed = TRUE;

		return BadNode(tree, "");
	}

	return subTree.root;	
}


/*
 * Return the next token from the expression, taking into account
 * the rescanning of the current token.
 */
static TOKEN
ParseToken(TREE * tree)
{
	if (!tree->rescanToken)
		tree->token = ParseNewToken(tree);

	tree->rescanToken = FALSE;

	return tree->token;
}


/*
 * Parse a new token always, ignoring rescanning.
 */
static TOKEN
ParseNewToken(TREE * tree)
{
	int	ch;

	while (isBlank(*tree->cp))
		tree->cp++;

	tree->tokenStr = &tree->modExpr[tree->cp - tree->expr];
	tree->tokenInt = 0;

	/*
	 * Switch on the type of character.
	 */
	ch = *tree->cp++;

	switch (ch)
	{
		case '!':
			if (*tree->cp != '=')
				return TOKEN_NOT;

			tree->cp++;

			return TOKEN_NOTEQUAL;

		case '.':
			return TOKEN_PERIOD;

		case ',':
			return TOKEN_COMMA;

		case '~':
			return TOKEN_COMPLEMENT;

		case '^':
			return TOKEN_XOR;

		case '(':
			return TOKEN_LEFTPAREN;

		case ')':
			return TOKEN_RIGHTPAREN;

		case '&':
			if (*tree->cp != '&')
				return TOKEN_AND;

			tree->cp++;

			return TOKEN_ANDAND;

		case '|':
			if (*tree->cp != '|')
				return TOKEN_OR;

			tree->cp++;

			return TOKEN_OROR;

		case '+':
			return TOKEN_PLUS;

		case '-':
			return TOKEN_MINUS;

		case '*':
			return TOKEN_MULTIPLY;

		case '/':
			return TOKEN_DIVIDE;

		case '%':
			return TOKEN_MODULO;

		case '?':
			return TOKEN_QUESTIONMARK;

		case ':':
			return TOKEN_COLON;

		case '<':
			if (*tree->cp == '<')
			{
				tree->cp++;

				return TOKEN_LEFTSHIFT;
			}

			if (*tree->cp != '=')
				return TOKEN_LESS;

			tree->cp++;

			return TOKEN_LESSEQUAL;

		case '>':
			if (*tree->cp == '>')
			{
				tree->cp++;

				return TOKEN_RIGHTSHIFT;
			}

			if (*tree->cp != '=')
				return TOKEN_GREATER;

			tree->cp++;

			return TOKEN_GREATEREQUAL;

		case '=':
			if (*tree->cp == '=')
				tree->cp++;

			return TOKEN_EQUAL;

		case '\0':
			tree->cp--;

			return TOKEN_EOF;

		case '\'':
		case '"':
			tree->tokenStr++;

			while (*tree->cp != ch)
			{
				if (*tree->cp)
				{
					tree->cp++;
					continue;
				}

				return BadToken(tree, "Unterminated string");
			}

			tree->modExpr[tree->cp - tree->expr] = '\0';

			tree->cp++;

			return TOKEN_STRING;

		default:
			break;
	}

	/*
	 * Check for a numeric value.
	 */
	if (isDigit(ch))
	{
		tree->cp--;
		return ParseNumber(tree);
	}

	/*
	 * We must now have a symbol name.
	 */
	if (!isBegSymbol(ch))
		return BadToken(tree, "Illegal token");

	/*
	 * Yes, it is a symbol, so see how large it is and null terminate it.
	 */
	while (isSymbol(ch))
		ch = *tree->cp++;

	tree->cp--;
	tree->modExpr[tree->cp - tree->expr] = '\0';

	return TOKEN_SYMBOL;
}


/*
 * Parse a numeric value and return it (without any sign).
 * By default the number is decimal.  With a leading 0 it is octal,
 * while with a leading 0x it is hex.  Illegal numbers result in an
 * error message and a TOKEN_BAD return value.
 */
static TOKEN
ParseNumber(TREE * tree)
{
	long	value;
	int	ch;

	value = 0;
	ch = *tree->cp++;

	if (!isDigit(ch))
	{
		tree->cp--;

		return BadToken(tree, "Number expected");
	}

	/*
	 * If the number doesn't begin with a zero, then it is decimal.
	 */
	if (ch != '0')
	{
		while (isDigit(ch))
		{
			value = value * 10 + ch - '0';
			ch = *tree->cp++;
		}

		tree->cp--;

		if (isSymbol(ch))
			return BadToken(tree, "Bad decimal number");

		tree->tokenInt = value;

		return TOKEN_NUMBER;
	}

	/*
	 * The number is octal or hex.
	 * If the next character is not an 'x', then it is octal.
	 */
	ch = *tree->cp++;

	if ((ch != 'x') && (ch != 'X'))
	{
		while (isOctal(ch))
		{
			value = value * 8 + ch - '0';
			ch = *tree->cp++;
		}

		tree->cp--;

		if (isSymbol(ch))
			return BadToken(tree, "Bad octal number");

		tree->tokenInt = value;

		return TOKEN_NUMBER;
	}

	/*
	 * The number must now be hex.
	 */
	ch = *tree->cp++;

	while (isHex(ch))
	{
		value = value * 16;

		if (isDigit(ch))
			value += (ch - '0');
		else if (isLower(ch))
			value += (ch - 'a' + 10);
		else
			value += (ch - 'A' + 10);

		ch = *tree->cp++;
	}

	tree->cp--;

	if (isSymbol(ch))
		return BadToken(tree, "Bad hex number");

	tree->tokenInt = value;

	return TOKEN_NUMBER;
}


/*
 * Return a TOKEN_BAD token, mark the expression tree as having failed,
 * and give the associated error message if this is the first error.
 */
static TOKEN
BadToken(TREE * tree, char * msg)
{
	if (!tree->failed)
	{
		fprintf(stderr, "%s\n", msg);

		tree->failed = TRUE;
	}

	return TOKEN_BAD;
}


/*
 * Allocate a new for the tree, with the specified opcode and the specified
 * left and right children.
 */
NODE *
NewNode(TREE * tree, OP op, NODE * left, NODE * right)
{
	NODE *	node;

	node = (NODE *) malloc(sizeof(NODE));

	if (node == NULL)
		return BadNode(tree, "Cannot allocate memory");

	if (op == OP_NONE)
		tree->failed = TRUE;

	node->op = op;
	node->child[0] = left;
	node->child[1] = right;
	node->child[2] = NULL;
	node->column = NULL;
	node->strVal = "";
	node->intVal = 0;

	return node;
}


/*
 * Allocate a bad node, and give the specified error message
 * if there has not been an error yet.  This marks the expression
 * tree as having failed.  This routine can never fail.
 */
static NODE *
BadNode(TREE * tree, char * message)
{
	if (!tree->failed)
	{
		fprintf(stderr, "%s\n", message);

		tree->failed = TRUE;
	}

	badNode.op = OP_NONE;
	badNode.strVal = "";

	return &badNode;
}

/* END CODE */
