/
umud/DOC/
umud/DOC/examples/
umud/DOC/internals/
umud/DOC/wizard/
umud/MISC/
umud/MISC/dbchk/
umud/RWHO/rwhod/
/*
	Copyright (C)1991, Marcus J. Ranum. All rights reserved.
*/

#ifndef	lint
static	char	RCSid[] = "$Header: /usr/users/mjr/hacks/umud/RCS/util.c,v 1.4 91/08/29 17:24:13 mjr Exp $";
#endif

/* configure all options BEFORE including system stuff. */
#include	"config.h"


#include	<ctype.h>
#include	<varargs.h>

#include	"mud.h"
#include	"vars.h"

/*
this  file contains useful routines that do a somewhat higher-level
manipulation on objects and their attributes, etc. since attributes
are typed, for example, it's a waste of code for each player command
that gets an attribute to have to check types. there are functions
in here to get attributes of specific types, add to lists and make
sure the attribute being added to is a list, etc, etc. typically,
most player-command code, unless something really low-level is
needed, will be programmed with stuff out of this file.

most of these routines operate in a "user context" - rather than a
lower-level. for example, error messages get written to the user,
types are checked, permissions are checked, etc.
*/




/*
return nonzero if the object-ID given is well-formed.
*/
int
ut_isgoodid(v)
char	*v;
{
	if(!strcmp(v,system_object))
		return(1);

	while(1) {
		if(!isdigit(*v)) {
			/* pure numeric */
			if(*v == '\0')
				return(1);

			/* fully qualified - strip out '@' and keep looking */
			if(*v != '@')
				return(0);
			v++;

			/* hunt to end of string */
			while(*v != '\0') {
				if(!isalnum(*v) && *v != '_')
					return(0);
				v++;
			}
			return(1);
		}
		v++;
	}
}




/* make an object fully-qualified addressed. */
ut_delocaliz(o1)
char	*o1;
{
	char	*p = o1;
	char	*q;

	while(*p != '\0') {
		if(*p == '@')
			return(0);
		p++;
	}
	
	*p++ = '@';
	for(q = mud_getname(); *q != '\0';)
		*p++ = *q++;
	*p = '\0';
	return(0);
}




ut_getnum(ob,att,ret)
char	*ob;
char	*att;
int	*ret;
{
	char	*ap;

	*ret = 0;
	if((ap = ut_getatt(ob,0,typ_int,att,(char *)0)) == (char *)0)
		return(-1);
	*ret = atoi(ap);
	return(0);
}




ut_setnum(who,ob,att,val)
char	*who;
char	*ob;
char	*att;
int	val;
{
	char	nbuf[128];

	return(ut_set(who,ob,typ_int,att,itoa(val,nbuf)));
}




/*
recursively look up attribute chains. The first parameter is the name
of the object to look up, the second is the type of the attribute. the
rest are the successive names of the attributes to chain off that
attribute. so, for example:

	ut_getatt("342@here",flg,typ_str,attr_loc,attr_ply,(char *)0);

would first try to access 342@here, then would look up the location
attribute of that object, and IF AND ONLY IF that is an object id,
it will look that up, and look for the player list of it. any failure
will return (char *)0
if "flg" is set, the entire attribute (name and type) is returned.
*/
/* VARARGS3 */
char	*
ut_getatt(onam,flg,typ,va_alist)
char	*onam;
int	flg;
char	*typ;
va_dcl
{
	Obj	*op;
	char	*atp;
	char	*nxt;
	va_list	ap;

	if((op = cache_get(onam)) == (Obj *)0)
		return((char *)0);

	va_start(ap);

	if((atp = va_arg(ap,char *)) == (char *)0)
		return((char *)0);

	while(1) {
		if((atp = objattr(op,atp,(int *)0)) == (char *)0)
			return((char *)0);
		if((nxt = va_arg(ap,char *)) == (char *)0)
			break;
		if(!attistype(atp,typ_obj))
			return((char *)0);
		if((atp = attdata(atp)) == (char *)0)
			return((char *)0);
		if((op = cache_get(atp)) == (Obj *)0)
			return((char *)0);
		atp = nxt;
	}
	va_end(ap);

	if(!attistype(atp,typ))
		return((char *)0);

	if(flg)
		return(atp);
	return(attdata(atp));
}




/*
return nonzero if "who" has "flag" set
*/
ut_flagged(who,flag)
char	*who;
char	*flag;
{
	return(ut_getatt(who,0,typ_flag,flag,(char *)0) != (char *)0);
}





/*
return nonzero if "who" owns "what"
*/
ut_isobjown(who,what)
char	*who;
char	*what;
{
	Obj	*op;
	char	*ap;

	if((op = cache_get(what)) == (Obj *)0)
		return(0);

	if((ap = objattr(op,var_owner,(int *)0)) == (char *)0)
		return(1);

	if(attistype(ap,typ_list) && lstlook(attdata(ap),who))
			return(1);
	return(0);
}




/*
set an object's attributes. this requires a
context of who is running it, etc.
*/
int
ut_set(who,onam,typ,anam,val)
char	*who;
char	*onam;
char	*typ;
char	*anam;
char	*val;
{
	Obj	*op;

	if((op = cache_get(onam)) == (Obj *)0) {
		say(who,onam," does not exist.\n",(char *)0);
		return(1);
	}

	if(objsetattr(op,typ,anam,val)) {
		say(who,"error writing to #",onam,"\n",(char *)0);
		return(1);
	}

	if(cache_put(op,onam)) {
		say(who,"error modifying #",onam,"\n",(char *)0);
		return(1);
	}
	return(0);
}




/*
unset an object's attributes. this requires a
context of who is running it, etc.
*/
int
ut_unset(who,onam,anam)
char	*who;
char	*onam;
char	*anam;
{
	Obj	*op;

	if((op = cache_get(onam)) == (Obj *)0) {
		say(who,onam," does not exist.\n",(char *)0);
		return(1);
	}

	if(objunsetattr(op,anam)) {
		say(who,"error writing to #",onam,"\n",(char *)0);
		return(1);
	}

	if(cache_put(op,onam)) {
		say(who,"error modifying #",onam,"\n",(char *)0);
		return(1);
	}
	return(0);
}




/*
add an item to the object's attribute list
*/
int
ut_listadd(who,onam,anam,item)
char	*who;
char	*onam;
char	*anam;
char	*item;
{
	char	*ap;
	char	*ins;

	if((ap = ut_getatt(onam,0,typ_list,anam,(char *)0)) == (char *)0) {
		ins = item;
	} else {
		if((ins = lstadd(ap,item,(int *)0)) == (char *)0) {
			say(who,"error adding to list.\n",(char *)0);
			return(1);
		}
	}
	if(ut_set(who,onam,typ_list,anam,ins)) {
		say(who,"error modifying object #",onam,".\n",(char *)0);
		return(1);
	}

	return(0);
}




/*
drop an item from the object's attribute list
*/
int
ut_listdel(who,onam,anam,item)
char	*who;
char	*onam;
char	*anam;
char	*item;
{
	char	*ap;
	char	*ins;

	if((ap = ut_getatt(onam,0,typ_list,anam,(char *)0)) == (char *)0)
		return(0);

	if((ins = lstdel(ap,item,(int *)0)) == (char *)0) {
		say(who,"error dropping from list.\n",(char *)0);
		return(1);
	}

	/* list emptied? */
	if(ins[0] == '\0' || (ins[0] == ';' && ins[1] == '\0')) {
		ut_unset(who,onam,anam);
		return(0);
	}

	if(ut_set(who,onam,typ_list,anam,ins)) {
		say(who,"error modifying object #",onam,".\n",(char *)0);
		return(1);
	}

	return(0);
}





/* create a new object ID and object, and return the ID to the player */
ut_objnew(who,rbuf,localflag)
char	*who;
char	*rbuf;
int	localflag;
{
	Obj	*op;
	int	onum;
	char	nxbuf[MAXOID];

	rbuf[0] = '\0';

	/* make an in-memory holder */
	if((op = objnew()) == (Obj *)0) {
		say(who,"cannot create object.\n",(char *)0);
		return(1);
	}

	/* get the system high-object # */
	if(ut_getnum(system_object,var_objcnt,&onum))
		onum = 0;

	onum++;

	/* write it in the system object */
	if(ut_setnum(who,system_object,var_objcnt,onum))
		return(1);

	itoa(onum,nxbuf);

	/* if the object is not local-only, use FQON */
	if(!localflag && ut_delocaliz(nxbuf))
		return(1);

	/* make sure the object isn't there already!! */
	if(cache_check(nxbuf)) {
		say(who,"WARNING - system free object count hosed\n",(char *)0);
		return(1);
	}

	/*
	add object to cache  -  NOTE - the cache routines will take
	care of eventually freeing the Obj * DO NOT FREE IT HERE!!!!
	*/
	if(cache_put(op,nxbuf)) {
		say(who,"cannot create #",nxbuf,"\n",(char *)0);
		return(1);
	}

	/* done! */
	(void)strcpy(rbuf,nxbuf);
	return(0);
}




/*
send a copy of the messages (va_list) to everyone in the room
if "notme" is set, don't send a copy to that object
*/
/* VARARGS 2 */
void
ut_roombcast(where,notme,va_alist)
char	*where;
char	*notme;
va_dcl
{
	char	*in;
	char	*atp;
	char	nxtu[MAXOID];
	va_list	ap;

	if((in = ut_getatt(where,0,typ_list,var_ply,(char *)0)) == (char *)0)
		return;

	while((in = lstnext(in,nxtu)) != (char *)0) {
		if(notme != (char *)0 && !strcmp(nxtu,notme))
			continue;

		va_start(ap);
		while((atp = va_arg(ap,char *)) != (char *)0)
			say(nxtu,atp,(char *)0);
		va_end(ap);
	}
}




/* return the location of the thing */
char	*
ut_loc(ob)
char	*ob;
{
	char	*ret;

	if((ret = ut_getatt(ob,0,typ_obj,var_loc,(char *)0)) != (char *)0)
		return(ret);
	return("nowhere");
}




/* return the name of the thing, or the thing's objid if it is unnamed */
char	*
ut_name(ob)
char	*ob;
{
	char	*ret;

	if((ret = ut_getatt(ob,0,typ_str,var_nam,(char *)0)) != (char *)0)
		return(ret);
	return(ob);
}



int
ut_setpass(obj,pass)
char	*obj;
char	*pass;
{
	Obj	*op;
	char	pbuf[MAXOID];

	if(strlen(pass) >= MAXOID)
		return(1);

	if((op = cache_get(obj)) == (Obj *)0)
		return(1);

	rot_init(obj);
	rot_encode(pass,pbuf);

	if(objsetattr(op,typ_str,var_pass,pbuf) || cache_put(op,obj))
		return(1);
	return(0);
}

/*
Figure out where 'home' is for this object. Sometimes this means limbo.
*/

ut_home(obj,dest)
char	*obj;
char	*dest;
{
	char		*home;
	char		*mud;
	int		i;

	home = ut_getatt(obj,0,typ_obj,var_home,(char *)0);

	/* We demand that home be fully qualified */

	if(home == (char *)0 || (mud = index(home,'@')) == (char *)0)
		goto badhome;
	mud++;

	/* If it's not in cache, and belongs to this mud, extract the number */

	if(!cache_check(home) && !strcmp(mud,mud_getname())){
		for(i = 0; *home != '@' && i < MAXOID; i++)
			dest[i] = *home++;
		dest[i] = '\0';
	} else {
		strcpy(dest,home);
	}
	if(!cache_check(dest))
		goto badhome;

	return(1);

badhome:
	/* Get limbo and use that as home */

	home = ut_getatt(system_object,0,typ_obj,var_syslimbo,(char *)0);
	if(home == (char *)0)
		return(0);
	strcpy(dest,home);
	return(1);
}



/*
returns where to put an object, assuming you'll want to use a dropto
if there is one.
*/

char	*
ut_dropto(what,where,hm)
char	*what;
char	*where;
char	*hm;
{
	char	*drpto;
	int	hashome;

	/* If it's homed to 'where', NO dropto */

	if((hashome = ut_home(what,hm)) && !strcmp(hm,where))
		return(where);

	drpto = ut_getatt(where,0,typ_obj,var_dropto,(char *)0);
	if(drpto == (char *)0)
		return(where);

	/* If the dropto is to 'home'.. */

	if(!strcmp(drpto,"home"))
		return(hashome ? hm : where);

	return(drpto);
}

int
ut_listchk(onam,anam,item)
char	*onam;
char	*anam;
char	*item;
{
	return(lstlook(ut_getatt(onam,0,typ_list,anam,(char *)0),item));
}