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

/* configure all options BEFORE including system stuff. */
#include        "config.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 (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. */
int ut_delocaliz (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);
}




int ut_getnum (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);
}




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

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




/*
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.
if 'typ' is a pointer to zero, type checking is disabled.
*/
/* VARARGS3 */
char *ut_getatt (char *onam, int flg, char *typ, ...)
{
  Obj *op;
  char *atp;
  char *nxt;
  va_list ap;

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

  va_start (ap, typ);

  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 (typ != (char *) 0 && !attistype (atp, typ))
    return ((char *) 0);

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




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





/*
return nonzero if "who" owns "what"
*/
int ut_isobjown (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 (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 (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 (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 (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 */
int ut_objnew (char *who, char *rbuf)
{
  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);

  (void) itoa (onum, nxbuf, 10);

  /* if the object is not local-only, use FQON */
  if (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 (char *where, char *notme, ...)
{
  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, sizeof (nxtu))) != (char *) 0) {
    if (notme != (char *) 0 && !strcmp (nxtu, notme))
      continue;

    va_start (ap, notme);
    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 (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 (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 (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.
*/

int ut_home (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 (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 (char *onam, char *anam, char *item)
{
  return (lstlook (ut_getatt (onam, 0, typ_list, anam, (char *) 0), item));
}



#ifdef  COMBAT
int ut_home_player (char *aswho, char *goob, char *where)
{
  char hm[MAXOID];
  char *list;
  char next[MAXOID];
  char ohom[MAXOID];

  /* Where's home? */
  if (!ut_home (goob, hm)) {
    say (aswho, "I can't figure out how to home ", goob, ".\n", (char *) 0);
    goto fail;
  }
  /* Get it out of here. */
  if (ut_listdel (aswho, where, var_ply, goob)) {
    say (aswho, "Can't send ", goob, " home.\n", (char *) 0);
    goto fail;
  }
  /* And in to there. */
  if (ut_listadd (aswho, hm, var_ply, goob)) {
    say (aswho, "Can't send ", goob, "home.\n", (char *) 0);
    goto fail;
  }
  /* and change its location */
  if (ut_set (aswho, goob, typ_obj, var_loc, hm)) {
    say (aswho, "Can't send ", goob, "home.\n", (char *) 0);
    goto fail;
  }
  /* deal with inventories. (On all non-wiz players)  */
  if (ut_flagged (goob, var_wiz))
    return (0);

  list = ut_getatt (goob, 0, typ_list, var_cont, (char *) 0);
  if (list != (char *) 0) {
    while ((list = lstnext (list, next, sizeof (next))) != (char *) 0) {
      if (!ut_home (next, ohom))
        continue;
      /* Ignore return values.  There's *nothing* we can do */
      (void) ut_listdel (aswho, goob, var_cont, next);
      (void) ut_listadd (aswho, ohom, var_cont, next);
      (void) ut_set (aswho, next, typ_obj, var_loc, ohom);
    }
  }
  list = ut_getatt (goob, 0, typ_list, var_wearing, (char *) 0);
  if (list != (char *) 0) {
    while ((list = lstnext (list, next, sizeof (next))) != (char *) 0) {
      if (!ut_home (next, ohom))
        continue;
      /* Ignore return values.  There's *nothing* we can do */
      (void) ut_listdel (aswho, goob, var_cont, next);
      (void) ut_listadd (aswho, ohom, var_cont, next);
      (void) ut_set (aswho, next, typ_obj, var_loc, ohom);
    }
  }
  list = ut_getatt (goob, 0, typ_obj, var_using, (char *) 0);
  if (list != (char *) 0 && ut_home (list, ohom) && strcmp (ohom, goob)) {
    (void) ut_listadd (aswho, ohom, var_cont, list);
    (void) ut_set (aswho, list, typ_obj, var_loc, ohom);
    (void) ut_unset (aswho, goob, var_using);
  }
#ifdef COMBAT
  list = ut_getatt (goob, 0, typ_obj, var_weapon, (char *) 0);
  if (list != (char *) 0 && ut_home (list, ohom) && strcmp (ohom, goob)) {
    (void) ut_listadd (aswho, ohom, var_cont, list);
    (void) ut_set (aswho, list, typ_obj, var_loc, ohom);
    (void) ut_unset (aswho, goob, var_using);
  }
#endif
  return (0);

fail:
  (void) ut_unset (aswho, goob, var_loc);       /* Whee!!!! */
  return (1);
}
#endif