#include	"ubermud.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 that handle copying stuff in memory, and other main
memory allocation shenanigans.
*/


/*
#define	ALLOCDEBUG
*/


/*
temporary data is managed by keeping a list of allocated stuff, which
can be traversed and freed. this is not too terribly efficient, but
is not a load-bearing section of code.
*/
struct	tmplist	{
	struct	tmplist	*next;
	char	*dat;
};


/* list of currently active temporaries */
static	struct	tmplist	*tmpl;

/* list of temp list structures that have been freed and stacked */
static	struct	tmplist	*freetmpl;



/* just like malloc() but keep track of the storage */
char	*
tmpalloc(siz)
int	siz;
{
	struct	tmplist	*lp;

	if(freetmpl != (struct tmplist *)0) {
		lp = freetmpl;
#ifdef	ALLOCDEBUG
		printf("take holder %d from tmp holder list\n",lp);
#endif
		freetmpl = freetmpl->next;
	} else {
		if((lp = (struct tmplist *)malloc(sizeof(struct tmplist))) == 0)
			return(0);
	}
	lp->dat = malloc((unsigned)siz);

	/* add to the chain */
	lp->next = tmpl;
	tmpl = lp;

#ifdef	ALLOCDEBUG
	printf("tmpalloc %d bytes at %d (holder is %d)\n",siz,lp->dat,lp);
#endif
	return(lp->dat);
}




/*
put something on the temp list to free later.
this results in what is basically a deferred free() - executed
at the end of each machine run.
*/
void
tmpputonfree(p)
char	*p;
{
	struct	tmplist	*lp;

	if(freetmpl != (struct tmplist *)0) {
		lp = freetmpl;
#ifdef	ALLOCDEBUG
		printf("take holder %d from tmp holder list\n",lp);
#endif
		freetmpl = freetmpl->next;
	} else {
		if((lp = (struct tmplist *)malloc(sizeof(struct tmplist))) == 0)
			return;
	}
	lp->dat = p;
	lp->next = tmpl;
	tmpl = lp;

#ifdef	ALLOCDEBUG
	printf("tmpputonfree ? bytes at %d (holder is %d)\n",lp->dat,lp);
#endif
}




/* free all the temporaries */
void
tmpfree()
{
	register struct	tmplist	*lp = tmpl;
	register struct	tmplist	*np;

	if(tmpl == (struct tmplist *)0)
		return;

	while(lp != (struct tmplist *)0) {
		if(lp->dat != 0)
			(void)free(lp->dat);
#ifdef	ALLOCDEBUG
		printf("tmpfree at %d (holder is %d)\n",lp->dat,lp);
#endif
		np = lp->next;
		lp->next = freetmpl;
		freetmpl = lp;
#ifdef	ALLOCDEBUG
		printf("stack holder %d on tmp holder list\n",lp);
#endif
		lp = np;
	}
	tmpl = (struct tmplist *)0;
}




/* allocate and copy a string that is NOT temporary */
char	*
copystr(s)
char	*s;
{
	int	alen;
	char	*ret;
	alen = strlen(s) + 1;
	ret = malloc((unsigned)alen);
	if(ret == (char *)0)
		return((char *)0);
	bcopy(s,ret,alen);
	return(ret);
}



/* freeze a copy of a program into an already allocated buffer.  */
Prog	*
progcopyinto(p,r)
Prog	*p;
char	*r;
{
	Prog	*ret = (Prog *)r;

	/* copy the prog in at the head of the buffer */
	bcopy((char *)p,r,sizeof(Prog));

	/* next in, the program memory... */
	bcopy((char *)p->p_mem,&r[sizeof(Prog)],p->p_siz * (int)sizeof(int));
	ret->p_mem = (int *)&r[sizeof(Prog)];
#ifdef	ALLOCDEBUG
	printf("...function memory is at %d\n",ret->p_mem);
#endif

	/* last in, the program string table... */
	bcopy(p->p_str,&r[sizeof(Prog) + (p->p_siz * sizeof(int))],p->s_siz);
	ret->p_str = &r[sizeof(Prog) + (p->p_siz * sizeof(int))];
#ifdef	ALLOCDEBUG
	printf("...function string table is at %d\n",ret->p_str);
#endif
	return(ret);
}



/* freeze a copy of a program into a contiguous buffer.  */
Prog	*
progcopy(p)
Prog	*p;
{
	char	*r;

	/*
	we do some wild and crazy mallocing and pasting here !
	it should be quite portable, however.
	*/
	if((r = malloc((unsigned)(PROGSIZE(p)))) == 0)
		return(0);
	return(progcopyinto(p,r));
}




ObjList	*
listcopyinto(l,p)
ObjList	*l;
char	*p;
{
	ObjList	*ret = (ObjList *)p;

	/* copy the list header in at the head of the buffer */
	bcopy((char *)l,p,sizeof(ObjList));

	bcopy((char *)l->l_data,&p[sizeof(ObjList)],l->l_cnt * (int)sizeof(long));
	ret->l_data = (long *)&p[sizeof(ObjList)];
	return(ret);
}




ObjList	*
listcopy(l)
ObjList	*l;
{
	char	*p;

	if((p = malloc((unsigned)LISTSIZE(l))) == (char *)0)
		return(0);
	return(listcopyinto(l,p));
}




ObjList	*
listtmpcopy(l)
ObjList	*l;
{
	char	*p;

	if((p = tmpalloc(LISTSIZE(l))) == (char *)0)
		return(0);

	return(listcopyinto(l,p));
}



/*
return the # of bytes of EXTERNAL storage needed.
numerical data doesn't need any, remember ?
*/
operdatasize(typ,op)
int	typ;
Oper	op;
{
	if(typ == TYP_STR)
		return(strlen(op.c) + 1);
	if(typ == TYP_OLIST)
		return(LISTSIZE(op.ol));
	if(typ == TYP_FUNC)
		return(PROGSIZE(op.p));
	return(0);
}




/*
copy an oper, allocating memory if needed.
*/
opercopy(old,otyp,new,ntyp,nsiz)
Oper		old;
int		otyp;
Oper		*new;
int		*ntyp;
unsigned	*nsiz;
{
	*ntyp = TYP_NULL;

	switch(otyp) {
	case TYP_STR:
		if((new->c = copystr(old.c)) == (char *)0)
			return(1);
		*nsiz = strlen(old.c) + 1;
		break;

	case TYP_OLIST:
		if((new->ol = listcopy(old.ol)) == (ObjList *)0)
			return(1);
		*nsiz = LISTSIZE(old.ol);
		break;

	case TYP_FUNC:
		if((new->p = progcopy(old.p)) == (Prog *)0)
			return(1);
		*nsiz = PROGSIZE(old.p);
		break;

	default:
		*new = old;
		*nsiz = 0;
	}
	*ntyp = otyp;
	return(0);
}