/
teeny/db/
teeny/dbm/
teeny/doc/
teeny/includes/
#include <stdio.h>
#include <strings.h>
#include <ctype.h>

#include "teeny.h"

/*
Copyright(C) 1990, Andrew Molitor, All Rights Reserved.
This software may be freely used, modified, and redistributed,
as long as this copyright message is left intact, and this
software is not used to develop any commercial product, or used
in any product that is provided on a pay-for-use basis.

No warranties whatsoever. This is not guaranteed to compile, nor,
in the event that it does compile to binaries, are these binaries
guaranteed to perform any function whatsoever.
*/

/*
	Routines for parsing, evaluating and displaying boolean expressions.

*/


/* Opcodes for our little Boolean expression evaluator. */

#define STOP -1
#define AND -2
#define OR -3
#define NOT -4
#define OPEN -5
#define CLOSE -6
#define BADREF -7

char *get_tok();
char *to_infix();

int stack[64]; /* Expressions can't be too big.*/
static int work[256];

char toocomplex[] = "Lock is too complex\n";
char badlock[] = "I don't understand that lock.\n";

/*
	We use these structs, an array of them, to build parse trees from our
internal RPN locks so we can write them out in infix.

*/

struct tree {
	int dat;
	short left,right;  /* Array offsets */
};

/*
	Parses a boolean expression, stuffs it into a hunk of memory, and
returns a pointer to said hunk. Returns NULL if it can't cope.

*/

int *bool_parse(player,str)
int player;
char *str;
{
	int wp,sp;
	int tok,*thelock;

	sp = 0;
	wp = 1;

	do {
		str = get_tok(player,str,&tok);

		if(tok == BADREF){
			notify_player(player,badlock);
			return(NULL);
		}

		switch(tok){
		case OPEN:
			if(sp < 63){
				stack[sp++] = OPEN;
			} else {
				notify_player(player,toocomplex);
				return(NULL);
			}
			break;
		case CLOSE:
			while(sp > 0 && stack[sp-1] != OPEN && wp < 255){
				work[wp++] = stack[--sp];
			}
			if(sp == 0 || stack[sp-1] != OPEN){
				notify_player(player,
					"Too complex, or unbalanced parens.\n");
				return(NULL);
			}
			sp--;
			break;
		case AND:
			while(sp > 0 && stack[sp-1] == NOT && wp < 255){
				work[wp++] = stack[--sp];
			}
			if(wp == 255){
				notify_player(player,toocomplex);
				return(NULL);
			}
			if(sp < 63){
				stack[sp++] = AND;
			} else {
				notify_player(player,toocomplex);
				return(NULL);
			}
			break;
		case OR:
			while(sp > 0 && (stack[sp-1] == NOT
					|| stack[sp-1] == AND) && wp < 255){
				work[wp++] = stack[--sp];
			}
			if(wp == 255){
				notify_player(player,toocomplex);
				return(NULL);
			}
			if(sp < 63){
				stack[sp++] = OR;
			} else {
				notify_player(player,toocomplex);
				return(NULL);
			}
			break;
		case NOT:
			if(sp < 63){
				stack[sp++] = NOT;
			} else {
				notify_player(player,toocomplex);
				return(NULL);
			}
			break;
		case STOP:
			while(sp > 0 && wp < 255){
				if(stack[sp-1] != AND && stack[sp-1] != OR
						&& stack[sp-1] != NOT){
					notify_player(player,badlock);
					return(NULL);
				}
				work[wp++] = stack[--sp];
			}
			if(wp == 255){
				notify_player(player,toocomplex);
				return(NULL);
			}
			work[wp++] = STOP;
			break;
		default:
			work[wp++] = tok;
			break;
		}
	} while(tok != STOP);

	/* Stow it away somewhere. */

	thelock = (int *) ty_malloc(sizeof(int) * wp, "bool_parse");
	thelock[0] = --wp;
	while(wp > 0){
		thelock[wp] = work[wp];
		wp--;
	}
	return(thelock);
}

/*
	Grabs the next token out a string, and returns a poointer to the next
thing in the string.

*/


char *get_tok(player,p,tok)
char *p;
int *tok;

{
	char *q,ch;
	int obj;

	/* Skip white-boy space, Marcus-like */

	while(isspace(*p) && *p) p++;

	switch(*p){
	case '&':
		*tok = AND;
		break;
	case '|':
		*tok = OR;
		break;
	case '!':
		*tok = NOT;
		break;
	case '(':
		*tok = OPEN;
		break;
	case ')':
		 *tok = CLOSE;
		break;
	case '\0':
		*tok = STOP;
		break;
	default:
		/* Snarf out an object name */

		q = p;
		while(*p != '&' && *p != '|' && *p != '!' && *p != ')' && *p){
			p++;
		}
		p--;
		while(isspace(*p)) p--;
		ch = p[1];
		p[1] = '\0';
		obj = resolve_object(player,q,1); /* Allow *<playername> */
		p[1] = ch;

		if(obj == -1){
			*tok = BADREF;
		} else {
			*tok = obj;
		}
	}
	/* p points at the last char of the token. */

	p++;
	return(p);
}

/*
	Writes a boolean expression into a sized buffer.
*/

bool_display(player,exp,buff,buffsiz)
int player;
int *exp;
char *buff;
int buffsiz;
{
	struct tree trees[64];
	int tp;
	char *p;
	int sp;
	int count;

	if(exp == NULL){
		strcpy(buff,"UNLOCKED");
		return(0);
	}
	tp = sp = 0;
	count = *exp++;

	while(*exp != STOP && count){
		switch(*exp){
		case AND:
		case OR:
			if(sp < 2 || tp > 63){
				warning("bool_display","lock bad or too big");
				return(-1);
			}
			trees[tp].dat = *exp;
			trees[tp].left = (short) (stack[--sp]);
			trees[tp].right = (short) (stack[--sp]);
			stack[sp++] = tp++;
			break;
		case NOT:
			if(sp < 1 || tp > 63){
				warning("bool_display","lock bad or too big");
				return(-1);
			}
			trees[tp].dat = NOT;
			trees[tp].right = (short) (stack[--sp]);
			stack[sp++] = tp++;
			break;
		default:
			if(sp > 255 || tp > 63){
				warning("bool_display","lock bad or too big");
				return(-1);
			}
			trees[tp].dat = *exp;
			if(sp < 63){
				stack[sp++] = tp++;
			} else {
				warning("to_infix","Internal lock too complex!");
				return(-1);
			}
			break;
		}
		exp++;
	}

	/* OK. We've built this parse tree thing. Now traverse the tree. */

	if(sp != 1){
		warning("bool_display","bad internal boolean expression");
		return(-1);
	}
	p = to_infix(player,trees,tp-1,buff,buffsiz);
	*p = '\0';
	return(0);
}

/*
	Convert a tree to infix. Recursive as hell.
*/

char *to_infix(player,trees,t,b,s)
int player;
struct tree *trees;
int t;
char *b;
int s;

{
	char *p,ret;

	if(s < 5){
		return(b);
	}
	switch(trees[t].dat){
		case AND:
			*b++ = '(';
			p = to_infix(player,trees,trees[t].left,b,s);
			if(s - (p-b) < 6){
				return(p);
			}
			*p++ = ' ';
			*p++ = '&';
			*p++ = ' ';
			p = to_infix(player,trees,trees[t].right,p, s - (p-b));
			*p++ = ')';
			break;
		case OR:
			*b++ = '(';
			p = to_infix(player,trees,trees[t].left,b,s);
			if(s - (p-b) < 5){
				return(p);
			}
			*p++ = ' ';
			*p++ = '|';
			*p++ = ' ';
			p = to_infix(player,trees,trees[t].right,p, s - (p-b));
			*p++ = ')';
			break;
		case NOT:
			if(s < 4){
				return(p);
			}
			*b++ = '!';
			*b++ = '(';
			p = to_infix(player,trees,trees[t].right,b, s - 2);
			*p++ = ')';
			break;
		default:
			if(s < 9){
				return(b);
			}
			ret = stuff_name(player,trees[t].dat,b,s-1);
			if(ret == -1){
				strcpy(b,"<unknown>");
				p = b + 9;
			} else {
				p = b + ret;
			}
			break;
	}
	return(p);
}

/*
	Returns 1 if thing is locked against player, 0 if not.
*/


islocked(player,thing)
int player;
int thing;
{
	int *exp;
	int sp,op1,op2;

	if(get_lock_elt(thing,LOCK,&exp) == -1){
		warning("locked","bad lock reference");
		return(0);
	}
	if(exp == NULL){	/* No lock */
		return(0);
	}

	/* Run down the expression until you hit a STOP */

	exp++; /* Skip the count field */
	sp = 0;
	while(*exp != STOP){
		if(*exp < 0){
			switch(*exp){
			case NOT:
				op1 = stack[--sp];
				stack[sp++] = !op1;
				break;
			case AND:
				op1 = stack[--sp];
				op2 = stack[--sp];
				stack[sp++] = op1 && op2;
				break;
			case OR:
				op1 = stack[--sp];
				op2 = stack[--sp];
				stack[sp++] = op1 || op2;
				break;
			}
		} else {
			/* See if the player has this object */

			if(sp < 63){
				stack[sp++] =
					(player_has(player,*exp) ? TRUE : FALSE);
			} else {
				warning("islocked","Internal lock too complex!");
				return(1);
			}
		}
	exp++;
	}

	if(sp != 1){
		warning("locked","bad internal boolean expression");
		return(0);
	}
	return(!stack[0]);
}


/*
	Basic tester, sees if a player is or has a specific object.
*/

int player_has(player,obj)
int player,obj;
{
	int current;

	if(player == obj){
		return(1);
	}

	/* Search the list */

	if(get_int_elt(player,CONTENTS,&current) == -1){
		warning("player_has","bad contents ref on player");
		return(0);
	}

	while(current != -1){
		if(current == obj){
			return(1);
		}
		if(get_int_elt(current,NEXT,&current) == -1){
			warning("player_has","bad NEXT ref");
			return(0);
		}
	}

	/* Didn't have it, I guess... */

	return(0);
}