/*	$OpenBSD: sets.c,v 1.2 1996/03/04 10:37:22 mickey Exp $	*/
/*	$NetBSD: sets.c,v 1.4 1994/06/29 06:41:16 cgd Exp $	*/

/*
 * This code is such a kludge that I don't want to put my name on it.
 * It was a ridiculously fast hack and needs rewriting.
 * However it does work...
 */
#include "main.h"
#include "malloc.h"
#include "sets.h"
#include "debug.h"
#include <stdio.h>

struct Object *CurrentEvent = (struct Object *)0;
struct Object *Objtree;
struct Object dummy;
/* 
 * define a set w/ type and name
 * return a set number 
 */
#undef NULL
#define NULL (struct Object *)0

static FILE *Sfile, *Efile;
extern FILE *astringfile;
char *Noname = "Unnamed set\0";

initsets(f,s)
FILE *f, *s;
{
	static char errorstring[20];
	extern struct Object *SameState;
	Efile = f;
	Sfile = s;

	IFDEBUG(X)
		fprintf(astringfile, "char *%s_sstring[] = {\n", protocol);
	ENDDEBUG
	sprintf(errorstring, "%sERROR\0", ST_PREFIX);
	defineitem(STATESET, errorstring, (char *)0);	/* state 0 */
	SameState = (struct Object *) Malloc( sizeof (struct Object) );
	SameState->obj_kind = OBJ_ITEM;
	SameState->obj_type = STATESET;
	SameState->obj_name = "SAME";
	SameState->obj_struc = (char *)0;
	SameState->obj_number = 0;
	SameState->obj_members = (struct Object *)0;
	SameState->obj_left = (struct Object *)0;
	SameState->obj_right = (struct Object *)0;
	SameState->obj_parent = (struct Object *)0;
}

/*
 * get a set based on its type and name
 * returns address of an Object, may be set or item
 */

struct Object *lookup(type, name)
unsigned char type;
char *name;
{
	register struct Object *p = Objtree;
	int val = 1 ;

	IFDEBUG(o)
		fprintf(stdout,"lookup 0x%x,%s \n",
			type, name);
	ENDDEBUG

	while( p && val ) {
		IFDEBUG(o)
		fprintf(OUT, "lookup strcmp 0x%x,%s, 0x%x,%s\n",
			name, name, OBJ_NAME(p), OBJ_NAME(p));
		ENDDEBUG
		if( p->obj_name == (char *)0 ) {
			fprintf(stderr, "Unnamed set in table!\n");
			Exit(-1);
		}
		val =  (int) strcmp(name, OBJ_NAME(p));
		if(val < 0) {
			/* left */
			p = p->obj_left;
		} else if (val > 0) {
			/* right */
			p = p->obj_right;
		}
	}
	if( p && ( p->obj_type != type)) {
		fprintf(stdout, "lookup(0x%x,%s) found wrong obj type 0x%x\n",
			type,name, p->obj_type);
		p = NULL;
	}
	IFDEBUG(o)
		fprintf(stdout,"lookup 0x%x,%s returning 0x%x\n",type, name, p);
	ENDDEBUG
	return(p);
}

static int states_done  = 0;

end_states(f)
FILE *f;
{
	register unsigned n = Nstates;
	register int i;
	extern char Eventshiftstring[];

	states_done = 1;

	for( i = 0; ;i++) {
		if( (n >>= 1) <= 0 ) break;
	}
	Eventshift = i+1;
	IFDEBUG(d)
		fprintf(OUT, "Eventshift=%d\n", Eventshift);
	ENDDEBUG
	sprintf(Eventshiftstring, "%d\0",Eventshift);
	fprintf(f, "struct %s_event {\n\tint ev_number;\n", &protocol[0]);
	IFDEBUG(X)
		/* finish sstring[] & start estring[] */
		fprintf(astringfile, 
		"};\n\nchar *%s_estring[] = {\n", protocol);
	ENDDEBUG
}

int FirstEventAttribute = 1;

static 
insert(o) 
struct Object *o;
{
	struct Object *p = Objtree;
	struct Object **q = &Objtree; 
	int val=1;


	if (o->obj_name == (char *)0) {
		fprintf(stderr, "Internal Error: inserting unnamed object\n");
		Exit(-1);
	}
	if( o->obj_type == STATESET) {
		if( states_done )  {
			fprintf(stderr, "No states may be defined after *TRANSITIONS\n");
			Exit(-1);
		}
		o->obj_number =  Nstates++ ; 
		if(Nstates > MAXSTATES) {
			fprintf(stderr, "Too many states\n");
			Exit(-1);
		}
		fprintf(Sfile, "#define %s 0x%x\n", o->obj_name, o->obj_number);
		IFDEBUG(X)
			fprintf(astringfile, "\"%s(0x%x)\",\n", o->obj_name, o->obj_number);
		ENDDEBUG
	} else {
		/* EVENTSET */ 
		if( ! states_done )  {
			fprintf(stderr, "states must precede events\n");
			Exit(-1);
		}
		o->obj_number =  Nevents++ ;
		if(Nevents > MAXEVENTS) {
			fprintf(stderr, "Too many events\n");
			Exit(-1);
		}
		if(o->obj_struc)  {
			if( FirstEventAttribute ) {
				fprintf(Efile,  "\n\tunion{\n"); /*} */
				FirstEventAttribute = 0;
			}
			fprintf(Efile, 
			"struct %s %s%s;\n\n", o->obj_struc, EV_PREFIX,  o->obj_name);
		}
		fprintf(Efile, "#define %s 0x%x\n", o->obj_name, o->obj_number);
		IFDEBUG(X)
			fprintf(astringfile, "\"%s(0x%x)\",\n", o->obj_name, o->obj_number);
		ENDDEBUG
	}
	IFDEBUG(o)
		fprintf(OUT, "insert(%s)\n", OBJ_NAME(o) );
		if(o->obj_right != NULL) {
			fprintf(OUT, "insert: unclean Object right\n");
			exit(-1);
		}
		if(o->obj_left != NULL) {
			fprintf(OUT, "insert: unclean Object left\n");
			exit(-1);
		}
		fflush(OUT);
	ENDDEBUG

	while( val ) {
		if(p == NULL) {
			*q = o;
			o->obj_parent = (struct Object *)q;
			break;
		}
		if(!(val = strcmp(o->obj_name, p->obj_name)) ) {
			/* equal */
			fprintf(stderr, "re-inserting %s\n",o->obj_name);
			exit(-1);
		}
		if(val < 0) {
			/* left */
			q = &p->obj_left;
			p = p->obj_left;
		} else {
			/* right */
			q = &p->obj_right;
			p = p->obj_right;
		}
	}
	IFDEBUG(a)
		dumptree(Objtree,0);
	ENDDEBUG
}

delete(o) 
struct Object *o;
{
	register struct Object *p = o->obj_right; 
	register struct Object *q;
	register struct Object *newparent;
	register struct Object **np_childlink;

	IFDEBUG(T)
		fprintf(stdout, "delete(0x%x)\n", o);
		dumptree(Objtree,0);
	ENDDEBUG

	/* q <== lowest valued node of the right subtree */
	while( p ) {
		q = p;
		p = p->obj_left;
	}

	if (o->obj_parent == (struct Object *)&Objtree)  {
		newparent =  (struct Object *)&Objtree;
		np_childlink = (struct Object **)&Objtree;
	} else if(o->obj_parent->obj_left == o)  {
		newparent = o->obj_parent;
		np_childlink = &(o->obj_parent->obj_left);
	} else {
		newparent = o->obj_parent;
		np_childlink = &(o->obj_parent->obj_right);
	}
	IFDEBUG(T)
		fprintf(OUT, "newparent=0x%x\n");
	ENDDEBUG

	if (q) { /* q gets the left, parent gets the right */
		IFDEBUG(T)
			fprintf(OUT, "delete: q null\n");
		ENDDEBUG
		q->obj_left = p;
		if(p) p->obj_parent = q;
		p = o->obj_right;
	} else { /* parent(instead of q) gets the left ; there is no right  */
		IFDEBUG(T)
			fprintf(OUT, "delete: q not null\n");
		ENDDEBUG
		p = o->obj_left;
	}
	*np_childlink = p;
	if(p) 
		p->obj_parent = newparent;

	IFDEBUG(T)
		fprintf(OUT, "After deleting 0x%x\n",o);
		dumptree(Objtree,0);
	ENDDEBUG
}

struct Object *
defineset(type, adr, keep)
unsigned char type;
char *adr;
int keep;
{
	struct Object *onew;
	IFDEBUG(o)
		printf("defineset(0x%x,%s, %s)\n", type , adr, keep?"KEEP":"NO_KEEP");
	ENDDEBUG
	
	onew = (struct Object *)Malloc(sizeof (struct Object));
	bzero(onew, sizeof(struct Object));
	onew->obj_name = adr;
	onew->obj_kind = OBJ_SET;
	onew->obj_type = type;
	if(keep) 
		insert( onew );
		/* address already stashed before calling defineset */
	IFDEBUG(o)
		printf("defineset(0x%x,%s) returning 0x%x\n", type , adr, onew);
		dumptree(Objtree,0);
	ENDDEBUG
	return(onew);
}

dumpit(o, s)
char *o;
char *s;
{
	register int i;

IFDEBUG(o)
	fprintf(OUT, "object 0x%x, %s\n",o, s);
	for(i=0; i< sizeof(struct Object); i+=4) {
		fprintf(OUT, "0x%x: 0x%x 0x%x 0x%x 0x%x\n",
		*((int *)o), *o, *(o+1), *(o+2), *(o+3) );
	}
ENDDEBUG
}

defineitem(type, adr, struc)
unsigned char type;
char *adr;
char *struc;
{
	struct Object *onew;
	IFDEBUG(o)
		printf("defineitem(0x%x, %s at 0x%x, %s)\n", type, adr, adr, struc);
	ENDDEBUG
	
	if( onew = lookup( type, adr ) ) {
		fprintf(stderr, 
	"Internal error at defineitem: trying to redefine obj type 0x%x, adr %s\n",
			type, adr);
		exit(-1);
	} else {
		onew = (struct Object *)Malloc(sizeof (struct Object));
		bzero(onew, sizeof(struct Object));
		onew->obj_name = stash(adr);
		onew->obj_kind = OBJ_ITEM;
		onew->obj_type =  type;
		onew->obj_struc = struc?stash(struc):struc;
		insert( onew );
	}
	IFDEBUG(o)
		fprintf(OUT, "defineitem(0x%x, %s) returning 0x%x\n", type, adr, onew);
	ENDDEBUG
}

member(o, adr)
struct Object *o;
char *adr;
{
	struct Object *onew, *oold;
	IFDEBUG(o)
		printf("member(0x%x, %s)\n", o, adr);
	ENDDEBUG
	
	oold = lookup(  o->obj_type, adr );

	onew = (struct Object *)Malloc(sizeof (struct Object));
	if( oold == NULL ) {
		extern int lineno;

		fprintf(stderr,
		"Warning at line %d: set definition of %s causes definition of\n",
			lineno, OBJ_NAME(o));
		fprintf(stderr, "\t (previously undefined) member %s\n", adr);
		bzero(onew, sizeof(struct Object));
		onew->obj_name = stash(adr);
		onew->obj_kind = OBJ_ITEM;
		onew->obj_type = o->obj_type;
		onew->obj_members = NULL;
		insert( onew );
	} else {
		if(oold->obj_kind != OBJ_ITEM) {
			fprintf(stderr, "Sets cannot be members of sets; %s\n", adr);
			exit(-1);
		}
		bcopy(oold, onew, sizeof(struct Object));
		onew->obj_members = onew->obj_left = onew->obj_right = NULL;
	}
	onew->obj_members = o->obj_members;
	o->obj_members = onew;
}

struct Object *Lookup(type, name)
unsigned char type;
char *name;
{
	register struct Object *o = lookup(type,name);

	if(o == NULL) {
		fprintf(stderr, "Trying to use undefined %s: %s\n",
			type==STATESET?"state":"event", name);
		Exit(-1);
	}
	return(o);
}

AddCurrentEventName(x)
register char **x;
{
	register char *n = EV_PREFIX; ;
	
	if( CurrentEvent == (struct Object *)0 ) {
		fprintf(stderr, "No event named!  BARF!\n"); Exit(-1);
	}

	if( ! CurrentEvent->obj_struc ) {
		fprintf(stderr, "No attributes for current event!\n"); Exit(-1);
	}

	/* add prefix first */
	while(*n) {
		*(*x)++ = *n++;
	}

	n = CurrentEvent->obj_name;

	while(*n) {
		*(*x)++ = *n++;
	}
}

dumptree(o,i)
	register struct Object *o;
	int i;
{
	register int j;

	if(o == NULL) {
		for(j=0; j<i; j++)
			fputc(' ', stdout);
		fprintf(stdout, "%3d NULL\n", i);
	} else {
		dumptree(o->obj_left, i+1);
		for(j=0; j<i; j++) 
			fputc(' ', stdout);
		fprintf(stdout, "%3d 0x%x: %s\n", i,o, OBJ_NAME(o));
		dumptree(o->obj_right, i+1);
	}
}

dump(c,a)
{
	register int x = 8;
	int zero = 0;
#include <sys/signal.h>

	fprintf(stderr, "dump: c 0x%x, a 0x%x\n",c,a);

	x = x/zero;
	kill(0, SIGQUIT);
}

dump_trans( pred, oldstate, newstate, action, event )
struct Object *oldstate, *newstate, *event;
char *pred, *action;
{
	extern int transno;
	struct Object *o;

	fprintf(stdout, "\n%d:  ", transno);
#define dumpit(x)\
	if((x)->obj_kind == OBJ_SET) {\
		o = (x)->obj_members; fprintf( stdout, "[ " );\
		while(o) { fprintf(stdout, "%s ", o->obj_name); o = o->obj_members; }\
		fprintf( stdout, " ] ");\
	} else { fprintf(stdout, "%s ", (x)->obj_name); }

	dumpit(newstate);
	fprintf(stdout, " <== ");
	dumpit(oldstate);
	dumpit(event);
	fprintf(stdout, "\n\t\t%s\n\t\t%s\n", pred?pred:"DEFAULT", 
		action);
}