#include	"ubermud.h"
#include	"syssym.h"
#include	"externs.h"


/*
Copyright(C) 1990, Marcus J. Ranum, 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.
*/

/*
	routines for managing local function symbol tables.
*/


/*
#define FREEZEDEBUG
#define THAWDEBUG
#define SYMDEBUG
*/

/* free entries are stacked, not freed */
static	Sym	*freel = (Sym *)0;


/* system symbol table */
static	SysSym	*systable[SYSSYMWIDTH];
static	SysSym	*syssymfree = (SysSym *)0;



Sym	*
symnew(name,typ,op)
char	*name;
int	typ;
Oper	op;
{
	Sym	*ret;
	if(freel != (Sym *)0) {
		ret = freel;
		freel = freel->next;
#ifdef	SYMDEBUG
		printf("symnew return Sym holder %d\n",ret);
#endif
	} else {
		if((ret = (Sym *)malloc(sizeof(Sym))) == 0)
			return(0);
#ifdef	SYMDEBUG
		printf("symnew malloc Sym holder %d\n",ret);
#endif
	}
	ret->data = op;
	ret->typ = typ;
	ret->name = name;
	return(ret);
}


Sym	*
symadd(sym,table)
Sym	*sym;
Sym	*table;
{
	sym->next = table;
	return(sym);
}


Sym	*
symlook(str,table)
char	*str;
Sym	*table;
{
	Sym	*ret;
	for(ret = table; ret != (Sym *)0; ret = ret->next)
		if(str != 0 && !strcmp(str,ret->name))
			return(ret);
	return(0);
}



void
symfreelist(l)
Sym	*l;
{
	Sym	*p;
	
	p = l;
	while(l != 0) {
		l = l->next;
#ifdef	SYMDEBUG
		printf("symfree stack Sym holder %d\n",p);
#endif
		p->next = freel;
		freel = p;
		p = l;
	}
}



int
cornedbeefhash(s,siz)
char	*s;
int	siz;
{
	register unsigned int	c = 0;
	register unsigned int	g;
	while(*s) {
		c = (c << 1) + *s++;
		if(g = c & 0xF00000000) {
			c = c ^ (g >> 24);
			c = c ^ g;
		}
	}
	c = c % 211;
	return(c % siz);
}



syssyminit()
{
	int	x;

	for(x = 0; x < SYSSYMWIDTH; x++)
		systable[x] = (SysSym *)0;
	return(0);
}



/*
chill a system variable.
*/
sys_freeze(ele,typ,data,uid,euid)
char	*ele;
int	typ;
Oper	data;
long	uid;
long	euid;
{

	register SysSym	*sp;
	int	hv;


#ifdef	FREEZEDEBUG
	printf("freeze:elem %d.%s\n",ELENUM(ele),ELENAM(ele));
#endif

	if(*ELENAM(ele) == '_' && uid != (long)0 && euid != (long)0)
		return(ERR_PERM);

	sp = systable[(hv = cornedbeefhash(ELENAM(ele),SYSSYMWIDTH))];
	while(sp != (SysSym *)0) {
		if(!strcmp(ELENAM(ele),sp->nam))
			break;
		sp = sp->next;
	}


	/*
	if the variable is not defined, simply create it
	otherwise we need to clean up the old version and
	adjust it.
	*/
	if(sp == (SysSym *)0 && typ != TYP_NULL) {

		/* only uid#0 can create in system table */
		if(uid != (long)0 && euid != (long)0)
			return(ERR_NOTOWN);

		if(syssymfree != (SysSym *)0) {
			sp = syssymfree;
			syssymfree = sp->next;
			sp->next = (SysSym *)0;
		} else {
			sp = (SysSym *)malloc(sizeof(SysSym));
			if(sp == (SysSym *)0)
				return(ERR_OOM);
		}
		if((sp->nam = copystr(ELENAM(ele))) == 0)
			return(ERR_OOM);

		sp->next = systable[hv];
		systable[hv] = sp;
#ifdef	FREEZEDEBUG
		printf("freeze:added elem to system sym table\n");
#endif
		sp->ino.mode = PERM_DEFAULT;
		sp->ino.owner = (long)0;
	} else {
		if(sp == (SysSym *)0 && typ == TYP_NULL)
			return(0);

		/*
		free any existing stuff.
		none of this may be allocated with tmpstr,etc.
		an implicit cast between char *, Prog *, and Objlist * 
		is made here. SO SUE ME!
		*/
		if(!permitted(PERM_WRITE,sp->ino.owner,sp->ino.mode,uid,euid))
			return(ERR_PERM);

		/*  free data if there is any. - a deferred free() */
		if(sp->ino.dsiz > 0)
			tmpputonfree(sp->ino.op.c);

		/* de-assign the sucker. */
		if(typ == TYP_NULL) {
			SysSym	*jp;

			free(sp->nam);
			jp = systable[hv];
			if(jp == sp) {
				systable[hv] = sp->next;
			} else {
				while(jp != (SysSym *)0 && jp->next != sp)
					jp = jp->next;
				if(jp != (SysSym *)0)
					jp->next = sp->next;
				sp->next = syssymfree;
				syssymfree = sp;
			}
			return(0);
		}
	}

	if(opercopy(data,typ,&(sp->ino.op),&(sp->ino.typ),&(sp->ino.dsiz)))
		return(1);

	/* back-patch functions */
	if(typ == TYP_FUNC) {
		sp->ino.op.p->p_uid = sp->ino.owner;
		sp->ino.op.p->p_mode = sp->ino.mode;
	}

#ifdef	FREEZEDEBUG
	printf("installed sys \"%s\", size=%d\n",sp->nam,sp->ino.dsiz);
#endif
	return(0);
}





sys_thaw(ele,typ,data,uid,euid)
char	*ele;
int	*typ;
Oper	*data;
long	uid;
long	euid;
{
	register SysSym	*sp;
#ifdef	THAWDEBUG
	printf("thaw:elem %d.%s\n",ELENUM(ele),ELENAM(ele));
#endif

	sp = systable[cornedbeefhash(ELENAM(ele),SYSSYMWIDTH)];
	while(sp != (SysSym *)0) {
		if(!strcmp(ELENAM(ele),sp->nam))
			break;
		sp = sp->next;
	}
	if(sp == (SysSym *)0)
		return(ERR_NOTHERE);

	if(!permitted(PERM_READ,sp->ino.owner,sp->ino.mode,uid,euid))
		return(ERR_PERM);

	*typ = sp->ino.typ;
	*data = sp->ino.op;
	return(0);
}



/*
used by blt_chmod and blt_chown in store.c
*/
SysSym	*
sys_getsym(ele)
char	*ele;
{
	register SysSym	*sp;
#ifdef	THAWDEBUG
	printf("sys_getsym:elem %d.%s\n",ELENUM(ele),ELENAM(ele));
#endif

	sp = systable[cornedbeefhash(ELENAM(ele),SYSSYMWIDTH)];
	while(sp != (SysSym *)0) {
		if(!strcmp(ELENAM(ele),sp->nam))
			break;
		sp = sp->next;
	}
	return(sp);
}