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

#include "dbm.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.
*/

/*
	This implements the mainline of the TeenyMUD database manager,
for doing bulk operations standalone on a TeenyMUD database.

*/

char *strstr_CI();
int sel_handle();
int pur_handle();
int cho_handle();
int sum_handle();

struct {
	char *name;
	int (*handler)();
} cmdtab[] = {
	{"select",sel_handle},
	{"purge",pur_handle},
	{"chown",cho_handle},
	{"summarize",sum_handle},
	{NULL,NULL}
};

/* This is the RPN expression we'll apply to the db */

static atom expr[MAXEXPR];

/* The current time, expressed as minute since the epoch. */

int currenttime;

/* Do it to it */

main(argc,argv)
int argc;
char *argv[];

{
	char cmd[1024];
	char *command,*p;
	int i;
	int ret;
	int required;
	struct timeval tv;

	if(argc != 3){
		puts("Usage: teenydbm <indexfile> <chunkfile>");
		exit(1);
	}

	(void)gettimeofday(&tv,(struct timezone *)0);
	currenttime = 60 * tv.tv_sec;

	/* Open up the database */

	initialize_cache(10240L);  /* Smallish cache. */
	open_chunkfile(argv[2]);
	read_descriptors(argv[1]);

	/* Now loop busily around until we quit. */

	while(1){
		printf("dbm>");
		get_cmd(cmd);                

		/* Strip out the first token. */

		p = cmd;
		while(isspace(*p)) p++;
		if(!*p)
			continue;
		command = p;
		while(!isspace(*p) && *p) p++;
		if(*p)
			*p++ = '\0';
		while(isspace(*p)) p++;

		/* command points at the first token, p points    */
		/* at the rest of the string, if any. Try to find */
		/* The command.   */

		if(strcmp(command,"quit") == 0)
			break;

		for(i = 0; cmdtab[i].name != NULL; i++){
			if(strcmp(cmdtab[i].name,command) == 0){
				ret = (cmdtab[i].handler)(p,expr,&required);
				if(ret == -2){
					required = 0;
					expr[0].type = STOP;
				}
				break;
			}
		}
		if(cmdtab[i].name == NULL){
			printf("No such command.\n");
		}
	}

	/* Bring the db back into synch, and quit. */

	cache_flush();
	if(write_descriptors(argv[1]) == -1){
		warning("Final db dump","write_descriptors() failed.");
	}
}


/*
	Routine to evalute an expression on a database object. Returns
1 if the object meets the conditions, 0 otherwise.

*/

static int eval_stack[512]; /* Screw counting. Make the bugger *big* */

eval_expr(obj,exp,required)
int obj;
atom *exp;
int required;

{
	int sp;
	int op1,op2;
	char *name;
	int owner;
	int flags;
#ifdef TIMESTAMPS
	int timestamp;
#endif

	/* Go and get all the required stuff for this expression */

	if(required & REQ_NAME){
		if(get_str_elt(obj,NAME,&name) == -1){
			printf("Bad db ref on object %d\n",obj);
			return(0);
		}
	}
	if(required & REQ_FLAG){
		if(get_int_elt(obj,FLAGS,&flags) == -1){
			printf("Bad db ref on object %d\n",obj);
			return(0);
		}
	}
	if(required & REQ_OWNER){
		if(get_int_elt(obj,OWNER,&owner) == -1){
			printf("Bad db ref on object %d\n",obj);
			return(0);
		}
	}

#ifdef TIMESTAMPS
	if(required & REQ_TIME){
		if(get_int_elt(obj,TIMESTAMP,&timestamp) == -1){
			printf("Bad db ref on object %d\n",obj);
			return(0);
		}
	}
#endif
	sp = 0;

	while(exp->type != STOP){
		switch(exp->type){
		case OR:
			op1 = eval_stack[--sp];
			op2 = eval_stack[--sp];
			eval_stack[sp++] = op1 || op2;
			break;
		case AND:
			op1 = eval_stack[--sp];
			op2 = eval_stack[--sp];
			eval_stack[sp++] = op1 && op2;
			break;
		case NOT:
			op1 = eval_stack[--sp];
			eval_stack[sp++] = !op1;
			break;
		case DBM_NUMBER:
			eval_stack[sp++] = (obj == (exp->data).number);
			break;
		case DBM_FLAG:
			eval_stack[sp++] = (flags & (exp->data).flag);
			break;
		case DBM_OWNER:
			eval_stack[sp++] = (owner == (exp->data).owner);
			break;
		case DBM_NAME:
			eval_stack[sp++] =
				(strstr_CI(name,((exp->data).name)) != NULL);
			break;
#ifdef TIMESTAMPS
		case DBM_TIME:
			if((exp->data).time < 0){  /* Unused < X minutes */
				eval_stack[sp++] = ( (currenttime - timestamp)
					< -((exp->data).time) );
			} else {  /* Unused > X minutes */
				eval_stack[sp++] = ((currenttime - timestamp)
					> (exp->data).time);
			}
			break;
#endif
		case DBM_TYPE:
			eval_stack[sp++] =
				((flags & TYPE_MASK) == (exp->data).flag);
			break;
		}
	exp++;
	}
	return(eval_stack[--sp]);
}

dump_expr(ex)
atom *ex;
{
	while(ex->type != STOP){
		switch(ex->type){
		case AND:
			printf("& ");
			break;
		case OR:
			printf("| ");
			break;
		case NOT:
			printf("!");
			break;
		case DBM_NUMBER:
			printf("#%d ",(ex->data).number);
			break;
		case DBM_FLAG:
			printf("flag=");
			switch((ex->data).flag){
			case WIZARD:
				putchar('W');
				break;
			case STICKY:
				putchar('S');
				break;
			case DARK:
				putchar('D');
				break;
			case LINK_OK:
				putchar('L');
				break;
			}
			putchar(' ');
			break;
		case DBM_TYPE:
			printf("type=");
			switch((ex->data).flag){
			case TYP_ROOM:
				putchar('R');
				break;
			case TYP_THING:
				putchar('T');
				break;
			case TYP_EXIT:
				putchar('E');
				break;
			case TYP_PLAYER:
				putchar('P');
				break;
			}
			putchar(' ');
			break;
		case DBM_TIME:
			printf("unused %d",(ex->data).time);
			break;
		case DBM_OWNER:
			printf("owner=%d ",(ex->data).owner);
			break;
		case DBM_NAME:
			printf("name=%s ",(ex->data).name);
			break;
		}
		ex++;
	}
	putchar('\n');
}

/*
	Read in a (potentially long) command line.
*/

get_cmd(buff)
char *buff;
{
	char *p = buff;
	char *q;

	while(1){
		gets(p);
		q = p;
		while(*q) q++;  /* Find EOLN */
		q--;
		while(isspace(*q)) q--;  /* Back up to 1st non-space */
		if(*q == '\\'){
			p = q;
		} else {
			q[1] = '\0';
			break;
		}
	}
}
/*
	My version of strstr(). It is case insensitive.
*/


char *strstr_CI(str,key)

char *str,*key;

	{
	char *curr,*holder;

	curr = key;holder = str;

	for(;(*str != '\0') && (*curr != '\0');){
		if( DOWNCASE(*str) == DOWNCASE(*curr) ){
			curr++;
		} else {
			curr = key;
			holder = str + 1;
		}
		str++;
	}

	if(*curr == '\0') {
		return(holder);
	} else {
		return(NULL);
	}
}


/*
	We need to emulate some support routines for the DB library that the
MUD normally provides. These are them:

*/

char *ty_itoa(p,n)
char *p;
int n;
{
	int i;
	char tmp[32];

	tmp[31] = '\0';
	tmp[30] = '0';
	for(i = 30;n != 0;i--){
		tmp[i] = (n % 10) + '0';
		n = n / 10;
	}
	strcpy(p,tmp + i);
	return(p+(31-i));
}

warning(p,q)
char *p,*q;
{
	printf("%s: %s\n",p,q);
}

fatal(p,q)
char *p,*q;
{
	warning(p,q);
	exit(1);
}

char *ty_malloc(n,p)
int n;
char *p;
{
	char *foo;

	foo = malloc((unsigned)n);
	if(foo == NULL){
		fatal("Out of memory",p);
	}
	return(foo);
}

ty_free(p)
char *p;
{
	free(p);
}