dmuck0.15-beta/docs/muf/
dmuck0.15-beta/game/
dmuck0.15-beta/game/logs/
dmuck0.15-beta/game/muf/
dmuck0.15-beta/game/muf/text/
#include "params.h"   /* Its nice to know params.h #includes config.h */

/*  This is the hook into all of the MUSH functions and subroutines.  By no
stretch of the imagination is any of this code efficient.  It does however get
the job done and thats what counts unless your counting CPU cycles.  This 
is a BETA version.  It may contain fatal bugs or severe security leaks.  If 
you find any such bugs, PLEASE contact howard@af.itd.com  Don't just tell
me "Hey, there is a bug in such and such."  Send me a dbx trace or better yet
the fix.  PLEASE go read the copyright notice contained in mush.h  --Howard  */


int mush_interp;

#ifdef MUSH           /* Are we running any MUSH type functions? */

#include "copyright.h"
#include "db.h"
#include "interface.h"
#include "externs.h"
#include "mush.h"
#include <ctype.h>
#include <varargs.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>

extern char *uppercase, *lowercase;
#define DOWNCASE(x) (lowercase[x])

typedef struct atr_ntab_hashentry ATRNHASH;

struct atr_ntab_hashentry {
  ATRNHASH *next;
  char *name;
  char *ap;
};

extern char *aname_hash_lookup(); /* for attrib hash stuff, in atr_tab.c */

/* hash stuff. Based on K&R */
struct atr_entry {
  struct atr_entry *next;
  char *name;
};

static struct atr_entry *atr_hashtab[ATR_HASH_SIZE];
static ATRNHASH *atr_name_hashtab[ATR_HASH_SIZE];

char *atr_match(char *string)
{
  return ((char *) aname_hash_lookup(string));
}

char *aname_hash_lookup(char *name)
{
  /* given an attribute name, look it up in the complete attribute table
   * (real names plus aliases), and return the appropriate real attribute.
   */

  ATRNHASH *p;

  for (p = atr_name_hashtab[hash_fn(name, ATR_HASH_MASK)];
       p != NULL; p = p->next)
    if (!strcmp(name, p->name))
      return (char *)p->ap;

  return (char *)NULL;
}

/*---------------------------------------------------------------------------
 * Command checking for $ and ^, and hash stuff for attrib inheritance checks
 */

struct atr_entry *atr_hash_lookup(char *s)
{
  struct atr_entry *ap;

  for (ap = atr_hashtab[hash_fn(s, ATR_HASH_MASK)];
       ap != NULL; ap = ap->next)
    if (!strcmp(s, ap->name))
      return ap;
  return NULL;			/* not found */
}

struct atr_entry *atr_hash_insert(char *name)
{
  struct atr_entry *ap;
  unsigned hashval;

  /* can assume that it's not going to be inserted unless it doesn't
   * exist, since atr_hash_lookup is called before.
   */

  ap = (struct atr_entry *) malloc(sizeof(struct atr_entry));
  if ((ap == NULL) || ((ap->name = (char *) strdup(name)) == NULL))
    return NULL;
  hashval = hash_fn(name, ATR_HASH_MASK);
  ap->next = atr_hashtab[hashval];
  atr_hashtab[hashval] = ap;
  return ap;
}

void atr_hash_free()
{
  struct atr_entry *ap;
  struct atr_entry *temp;
  int i;

  temp = NULL;

  for (i = 0; i < ATR_HASH_SIZE; i++) {
    for (ap = atr_hashtab[i]; ap != NULL; ap = temp) {
      temp = ap->next;
      free(ap->name);
      free(ap);
    }
    atr_hashtab[i] = NULL;
  }
}

int Hearer(dbref thing)
{
  propdir *ptr;
 
  if ((Typeof(thing) == TYPE_PLAYER && Connected(thing)) || Puppet(thing))
    return (1);
  for (ptr = DBFETCHPROP(thing); ptr; ptr = ptr->next) {
    if(ptr && !strcmp(ptr->name, "LISTEN"))
      return 1;
  }
  return (0);
}
 
int Commer(dbref thing)
{
  propdir *ptr;
 
  for (ptr = DBFETCHPROP(thing); ptr; ptr = ptr->next) {
    if(ptr) 
      if(*ptr->name == '$') return (1);
  }
  return (0);
}
 
int Listener(dbref thing)
{
  propdir *ptr;

  for (ptr = DBFETCHPROP(thing); ptr; ptr = ptr->next) {
    if(ptr->data && (!strcmp(ptr->name, "LISTEN") || *ptr->data== '^'))
      return 1;
  }
  return (0);
}

int giveto(dbref who, int cost)
{
  if(Wizard(who)) return 1;
  else DBSTORE(who, pennies, DBFETCH(who)->pennies + cost);
  DBDIRTY(who);
  return 0;
}

int check_mushact(dbref player, char *command)
{
  dbref first;

  if(o_player_user_functions) {       /* Check the player */
      if(!Nocommand(player))
        if (atr_comm_match(player, player, '$', ':', command, 0))
            return 1;
  }

  /* Check player contents */
  for(first=DBFETCH(player)->contents;first!=NOTHING;first=DBFETCH(first)->next)
    if(Typeof(first) == TYPE_THING && !Nocommand(first))  
        if (atr_comm_match(first, player, '$', ':', command, 0))
            return 1;

  /* Check objects in same location */
  first = DBFETCH(player)->location;
  if(first != NOTHING && !Nocommand(first)) {
    for(first = DBFETCH(DBFETCH(player)->location)->contents; first!=NOTHING;
        first = DBFETCH(first)->next) {
      if(Typeof(first) == TYPE_THING && !Nocommand(first))
        if(atr_comm_match(first, player, '$', ':', command, 0))
           return 1;
    }
   }

    /* Check any global objects */
  if(!Nocommand(MASTER_ROOM))
    for(first = DBFETCH(MASTER_ROOM)->contents; first!=NOTHING;
        first = DBFETCH(first)->next)
      if(Typeof(first) == TYPE_THING && !Nocommand(first)) 
        if(atr_comm_match(first, player, '$', ':', command, 0))
         return 1;

  return 0;
}

int my_random(int x)
{
#ifndef HPUX
   extern int rand(void);
   int result;
   result = (int) ( ((float)x * (float)rand())/2147477000.0);
   return result;
#else
   return (rand() % x);
#endif
}

int getrandom(int x)
{
   int temprandoms[20];
   int i;
   if (x < 1)
      x = 1;
   for (i = 0; i < 20; i++) 
      temprandoms[i] = my_random(x);
   i = my_random(20);

   return temprandoms[i];
}

int nearby(dbref obj1, dbref obj2)
{
 if ((obj1 == NOTHING) || (obj2 == NOTHING)) return -1;
 if ((Typeof(obj1) != TYPE_THING) && (Typeof(obj1) != TYPE_PLAYER)) return -1;
 if ((Typeof(obj2) != TYPE_THING) && (Typeof(obj2) != TYPE_PLAYER)) return -1;

  if (DBFETCH(obj1)->location == DBFETCH(obj2)->location) return 1;
  else if (DBFETCH(obj1)->location == obj2) return 1;
  else if (DBFETCH(obj2)->location == obj1) return 1;
  else return 0;
}

dbref short_page(char *match)
{
  struct descriptor_data *d;
  dbref who1 = NOTHING;
  int count = 0;

  for(d = descriptor_list; d; d = d->next) {
    if(d->connected) {
      if(match && !string_prefix(unparse_name(d->player), match))
        continue;
      if(!string_compare(unparse_name(d->player), match)) {
	count = 1;
	who1 = d->player;
	break;
      }
      who1 = d->player;
      count++;
    }
  }

  if(count > 1)
    return AMBIGUOUS;
  else if (count == 0)
    return NOTHING;
 
  return who1;
}

char **argv_hack(player, cause, arg, fargs, eflags)
     dbref player;
     dbref cause;
     char *arg;
     char *fargs[];
     int eflags;
{
  char *xargs[MAX_MUSH_ARG];
  int i;

  if (!arg || !*arg) {		/* make sure there's something to parse */
    for (i = 0; i < MAX_MUSH_ARG; i++)
      fargs[i] = NULL;
    return fargs;
  }

  parse_arglist(player, cause, arg, '\0', eflags, xargs, MAX_MUSH_ARG);

  /* because somebody braindamaged decided, way back when, that
   * argv command lists were going to start at argv[1], we have
   * to do a thoroughly inelegant copy.
   */

  for (i = 0; i < MAX_MUSH_ARG - 1; i++) {
      fargs[i + 1] = xargs[i];
  }

  if (xargs[MAX_MUSH_ARG - 1]) {	/* extra we can't use */
    free(xargs[MAX_MUSH_ARG - 1]);
  }
  return fargs;
}

/*
 * use this only in mush_parse
 */
#define Matched(string) { if(!string_prefix((string), command)) goto bad; }
#define arg1      buf1 = exec(cause, buf2, player, EV_STRIP | EV_FCHECK)
#define arg2      saveptr = exec(cause, arg, player, EV_STRIP | EV_FCHECK)
#define argu      saveptr = exec(cause, buff3, player, EV_STRIP | EV_FCHECK)
#define argv      argv_hack(player, cause, arg, fargs, EV_EVAL | EV_STRIP)
#define vargs     argv_hack(player, cause, arg, fargs, EV_STRIP)

int mush_parse(dbref player, char *command, dbref cause)
{
  int a;
  char *q, *p;			/* utility */
  char *fargs[MAX_MUSH_ARG];
  char buff3[BUFFER_LEN];
  char unp[BUFFER_LEN];		/* unparsed command */


  /* general form command arg0=arg1,arg2...arg10 */
  int flag = 1;
  char *slashp;
  char tchar;
  char *buf1 = NULL;
  char *buf2 = NULL;
  char *arg = NULL;
  char *saveptr = NULL;
  char *cptr = NULL;


  for (a = 0; a < MAX_MUSH_ARG; a++)
    fargs[a] = NULL;
 
  /* clear our local registers */
  for (a = 0; a < 10; a++)
    *(rptr[a]) = '\0';

  /* eat leading whitespace */
  while (*command && isspace(*command))
    command++;
  /* eat extra white space */
  q = p = command;
  while (*p) {
    /* scan over word */
    while (*p && !isspace(*p))
      *q++ = *p++;
    /* smash spaces */
    while (*p && isspace(*++p)) ;
    if (*p)
      *q++ = ' ';		/* add a space to separate next word */
  }
  /* terminate */
  *q = '\0';

    /* parse arguments */
    strcpy(unp, command);
 
    /* split command from arguments */
    /* move over command word */
    for (arg = command; *arg && !isspace(*arg); arg++)
      ;
    /* truncate command */
    if (*arg)
      *arg++ = '\0';
 
    /* grab switches and strip them off, truncating command */
    slashp = (char *) index(command, '/');
    if (slashp)
      *slashp++ = '\0';
 
    /* move over spaces */
    while (*arg && isspace(*arg))
      arg++;

    strcpy(buff3, arg);		/* save it for news */
    buf2 = parse_to(&arg, '=', 0);

    /* Don't choke when no '=' was specified */
    if (!arg || (arg && !*arg)) {
      arg = &tchar;
      *arg = '\0';
    }

    /* Just as a reminder of what all these variables mean:
     *
     * arg1, arg2, argu, and argv are all macros. They evaluate to
     * pronoun-substituted versions of the left hand side of the = sign,
     * right hand side of the = sign, the full expression regardless of
     * = sign, and the parsed version of an argument list. 
     * vargs is a macro which makes an argument vector without parsing it.
     *
     * arg is the unparsed right-hand side of the equals sign.
     * buff3 is an unparsed version of the argument to the command.
     * unp is the full unparsed command.
     *
     */

    /* now scan all the commands */
    switch (command[0]) {
      case '@':
	switch (command[1]) {
  	  case '@':
	    /* dummy statement, '@@' is the comment signal.
	     * since it's a comment, just ignore it.
	     */
	    break;
	  case 'a':
	  case 'A':
            if(!strcasecmp(command,"@allhalt")) {
	      do_allhalt(player);
	      break;
	    } else
		goto bad;
	    break;
	  case 'c':
	  case 'C':
	    /* chown, create */
	    switch (command[2]) {
  	      case 'p':
	      case 'P':
	        Matched("@cpattr");
                do_cpattr(player, arg1, argv);
	        break;
	      case 'l':
	      case 'L':
		Matched("@clone");
		do_clone(player, arg1);
		break;
	      case 'o':
	      case 'O':
	        Matched("@config");
	        if (!slashp)
		  do_config(player, 0);
	        else IfSwitch("globals")
		  do_config(player, 1);
	        else IfSwitch("defaults")
		  do_config(player, 0);
	        else IfSwitch("costs")
		  do_config(player, 2);
	        else IfSwitch("functions")
		  do_list_functions(player);
	        else IfSwitch("flags")
		  do_list_flags(player, arg1, arg2, unp);
	        else IfSwitch("commands")
		  do_list_commands(player, unp, unp, unp);
	        else
		  goto bad;
	        break;
	      default:
		goto bad;
	    }
	    break;
	  case 'd':
	  case 'D':
	    /* daytime, dbck,  dig, or dump */
	    switch (command[2]) {
	      case 'E':
	      case 'e':
		switch (command[3]) {
		case 'c':
		case 'C':
		  Matched("@decompile");
		  do_decompile(player, arg1);
		  break;
		default:
		  goto bad;
		}
		break;
	      case 'o':
	      case 'O':
		switch (command[3]) {
		case 'l':
		case 'L':
		  Matched("@dolist");
		  do_dolist(player, arg1, arg, cause, 0);
		  break;
		default:
		  goto bad;
		}
		break;
	      case 'r':
	      case 'R':
		Matched("@drain");
		do_notify(player, cause, 2, arg1, arg2);
		break;
	      default:
		goto bad;
	    }
	    break;
	  case 'E':
	  case 'e':
	    switch (command[2]) {
	     case 'l':
	     case 'L':
		Matched("@elock");
		do_mush_lock(player, arg1, arg2, ENTERLOCK);
		break;
	      case 'm':
	      case 'M':
		Matched("@emit");
		if (!slashp)
		  do_emit(player, argu);
		else IfSwitch("room")
		  do_lemit(player, argu);
		else
		  goto bad;
		break;
	      case 'u':
	      case 'U':
		Matched("@eunlock");
		do_mush_unlock(player, arg1, ENTERLOCK);
		break;
	      default:
		goto bad;
	    }
	    break;
	  case 'F':
	  case 'f':
	    /* find, or force */
	    switch (command[2]) {
	      case 'u':
	      case 'U':
		Matched("@function");
		do_function(player, arg1, argv);
		break;
	      default:
		goto bad;
	    }
	    break;
	  case 'g':
	  case 'G':
	    switch (command[2]) {
	    case 'e':
	    case 'E':
	      Matched("@gedit");
	      do_gedit(player, arg1, vargs);
	      break;
	    case 'r':
	    case 'R':
	      Matched("@grep");
	      if (!slashp)
		do_grep(player, arg1, arg, 0);
	      else IfSwitch("list")
		do_grep(player, arg1, arg, 0);
	      else IfSwitch("print")
		do_grep(player, arg1, arg, 1);
	      else
		goto bad;
	      break;
	    default:
	      goto bad;
	    }
	    break;
	  case 'h':
	  case 'H':
	    /* halt */
	    Matched("@halt");
	    if (!slashp)
	      do_halt1(player, arg1, arg2);
	    else IfSwitch("all")
	      do_allhalt(player);
	    else 
	      goto bad;
	    break;
	  case 'k':
	  case 'K':
	    Matched("@kick");
	    do_kick(player, arg1);
	    break;
	  case 'l':
	  case 'L':
	    /* lock or link */
	    switch (command[2]) {
	      case 'e':
	      case 'E':
		Matched ("@lemit");
		do_lemit(player,arg1);
		break;
	      case 'i':
	      case 'I':
		Matched ("@listen");
                do_mush_prop_set(player, arg1, arg, "@listen");
		break;
	      case 'o':
	      case 'O':
		Matched("@lock");
		if (!slashp)
		  do_mush_lock(player, arg1, arg2, BASICLOCK);
		else IfSwitch("enter")
		  do_mush_lock(player, arg1, arg2, ENTERLOCK);
		else IfSwitch("tport")
		  do_mush_lock(player, arg1, arg2, ENTERLOCK);
		else IfSwitch("use")
		  do_mush_lock(player, arg1, arg2, USELOCK);
		else
		  goto bad;
		break;
	      default:
		goto bad;
	      }
	    break;
	  case 'm':
	  case 'M':
	    /* @mail, @map, @motd */
	    switch (command[2]) {
	    case 'a':
	    case 'A':
		Matched("@map");
		do_dolist(player, arg1, arg, cause, 1);
		break;
	  case 'e':
	  case 'E':
	  case '\0':
	    Matched("@mexamine");
	    if (!slashp)
	      do_mush_examine(player, arg1, 0);
	    else IfSwitch("brief")
	      do_mush_examine(player, arg1, 1);
	    else IfSwitch("debug")
	      do_debug_examine(player, arg1);
	    else
	      goto bad;
	    break;
	      case 'p':
	      case 'P':
		Matched("@mps");
	        if (!slashp)
		  do_queue(player, arg1);
	        else IfSwitch("all")
		  do_queue(player, "all");
  	        else IfSwitch("summary")
		  do_queue(player, "count");
	        else
		  goto bad;
		break;
	    default:
	      goto bad;
	    }
	    break;
	  case 'n':
	  case 'N':
	    /* @name, @newpassword */
	    switch (command[2]) {
	      case 'o':
	      case 'O':
		Matched("@notify");
		if (!slashp)
		  do_notify(player, cause, 0, arg1, arg2);
		else IfSwitch("all")
		  do_notify(player, cause, 1, arg1, arg2);
		else
		  goto bad;
		break;
	      default:
		goto bad;
	    }
	    break;
	  case 'o':
	  case 'O':
	    /* @oemit, @open */
	    switch (command[2]) {
	      case 'e':
	      case 'E':
		Matched("@oemit");
		do_oemit(player, arg1, arg2);
		break;
	      default:
		goto bad;
	    }
	    break;
	  case 'p':
	  case 'P':
	    switch (command[2]) {
	      case 'a':
	      case 'A':
	      switch (command[3]) {
	      case 'r':
	      case 'R':
		Matched("@parent");
		do_parent(player, arg1, arg2);
		break;
	      default:
		goto bad;
	      }
	      break;
              case 'E':
              case 'e':
                Matched("@pemit");
  	        if (!slashp)
		  do_pemit(player, arg1, arg2, 1);
	        else IfSwitch("noisy")
		  do_pemit(player, arg1, arg2, 0);
	        else IfSwitch("silent")
		  do_pemit(player, arg1, arg2, 1);
	        else IfSwitch("contents")
		  do_remit(player, arg1, arg2);
	        else
		  goto bad;
                break;
	      case 'O':
	      case 'o':
		switch (command[3]) {
		case 'o':
		case 'O':
		  if (strcmp(command, "@poor"))
		    goto bad;
		  do_poor(player, arg1);
		  break;
		case 'w':
		case 'W':
		  notify(player, player, "This server doesn't support Powers.");
		  break;
		default:
		  goto bad;
		}
		break;
	      default:
		goto bad;
	      }
	    break;
          case 'r':
          case 'R':
	    switch (command[2]) {
	    case 'e':
	    case 'E':
	      switch (command[3]) {
	      case 'm':
	      case 'M':
		Matched("@remit");
		do_remit(player, arg1, arg2);
		break;
	      default:
		goto bad;
	      }
	      break;
	    default:
	      goto bad;
	    }
	    break;
	  case 's':
	  case 'S':
	    /* set, shutdown, success */
	    switch (command[2]) {
  	      case 'c':
	      case 'C':
		Matched("@scan");
		if (!slashp)
		  do_scan(player, arg1, CHECK_INVENTORY | CHECK_NEIGHBORS | 
			  CHECK_SELF | CHECK_HERE | CHECK_ZONE | CHECK_GLOBAL);
		else IfSwitch("room")
		  do_scan(player, arg1, CHECK_NEIGHBORS | CHECK_HERE);
		else IfSwitch("self")
		  do_scan(player, arg1, CHECK_INVENTORY | CHECK_SELF);
		else IfSwitch("zone")
		  do_scan(player, arg1, CHECK_ZONE);
		else IfSwitch("globals")
		  do_scan(player, arg1, CHECK_GLOBAL);
		else
		  goto bad;
		break;
	      case 'e':
	      case 'E':
		/* patched to add 'search' command */
		switch (command[3]) {
		  case 'a':
		  case 'A':
		    Matched("@search");
		    do_search(player, arg1, vargs);
		    break;
		  case 'l':
		  case 'L':
		    Matched("@select");
		    do_switch(player, arg1, vargs, cause, 1);
		    break;
		  default:
		    goto bad;
		}
		break;
	      case 'w':
	      case 'W':
		switch (command[3]) {
		  case 'e':
		  case 'E':
		    Matched("@sweep");
		    if (!slashp)
		      do_sweep(player, arg1);
		    else IfSwitch("connected")
		      do_sweep(player, "connected");
		    else IfSwitch("here")
		      do_sweep(player, "here");
		    else IfSwitch("inventory")
		      do_sweep(player, "inventory");
		    else IfSwitch("exits")
		      do_sweep(player, "exits");
		    else
		      goto bad;
		    break;
		  case 'i':
		  case 'I':
		    Matched("@switch");
		    if (!slashp)
		      do_switch(player, arg1, vargs, cause, 0);
		    else IfSwitch("first")
		      do_switch(player, arg1, vargs, cause, 1);
		    else IfSwitch("all")
		      do_switch(player, arg1, vargs, cause, 0);
		    else
		      goto bad;
		    break;
		  default:
		    goto bad;
		}
		break;
	      default:
		goto bad;
	    }
	    break;
	  case 't':
	  case 'T':
	    switch (command[2]) {
	      case 'r':
	      case 'R':
		Matched("@trigger");
		do_trigger(player, arg1, argv);
		break;
	      default:
		goto bad;
	    }
	    break;
	    case 'u':
	    case 'U':
	    switch (command[2]) {
	       case 'l':
	       case 'L':
	      Matched("@ulock");
	      do_mush_lock(player, arg1, arg2, USELOCK);
	      break;
	      case 'n':
	      case 'N':
		Matched("@unlock");
		  if (!slashp)
		    do_mush_unlock(player, arg1, BASICLOCK);
		  else IfSwitch("enter")
		    do_mush_unlock(player, arg1, ENTERLOCK);
		  else IfSwitch("tport")
		    do_mush_unlock(player, arg1, ENTERLOCK);
		  else IfSwitch("use")
		    do_mush_unlock(player, arg1, USELOCK);
		  else 
		    goto bad;
		break;
	    case 'u':
	    case 'U':
	      Matched("@uunlock");
	      do_mush_unlock(player, arg1, USELOCK);
	      break;
	      default:
		goto bad;
	    }
	    break;
          case 'v':
	  case 'V':
	      Matched("@verb");
	      do_verb(player, cause, arg1, argv);
/*	      notify(player, player, "This command is broke!"); */
	      break;
	  case 'w':
	  case 'W':
	    switch (command[2]) {
	      case 'a':
	      case 'A':
		if (string_prefix("@wait", command)) {
		  do_wait(player, cause, arg1, arg2);
		} else
		  goto bad;
		break;
	      case 'h':
	      case 'H':
		Matched("@whereis");
		do_whereis(player, arg1);
		break;
	      case 'i':
	      case 'I':
		if (string_prefix("@wipe", command)) {
		  do_wipe(player, arg1);
		} else
		  goto bad;
		break;
	      default:
		goto bad;
	    }
	    break;
	  case 'z':
	  case 'Z':
	    Matched("@zemit");
	    notify(player, player, "This server doesn't support Zones.");
	    break;
	  default:
	    goto bad;
	}
	break;
      case 'b':
      case 'B':
	Matched("brief");
	do_mush_examine(player, arg1, 1);
	break;
      case 'e':
      case 'E':
	    Matched("enter");
	    do_enter(player, arg1);
	    break;
      case 'l':
      case 'L':
	    Matched("leave");
	    do_leave(player);
	    break;
	  case 'm':
	  case 'M':
	  case '\0':
	    Matched("mexamine");
	    if (!slashp)
	      do_mush_examine(player, arg1, 0);
	    else IfSwitch("brief")
	      do_mush_examine(player, arg1, 1);
	    else IfSwitch("debug")
	      do_debug_examine(player, arg1);
	    else
	      goto bad;
            break;
      case 's':
      case 'S':
	    Matched("slay");
	    do_kill(player, arg1, arg2, unp);
	    break;
      case 't':
      case 'T':
	    Matched("think");
	    do_think(player, argu);
	break;
      case 'u':
      case 'U':
	Matched("use");
	do_use(player, arg1);
	break;
      default:
        cptr = exec(cause, unp, player, EV_EVAL | EV_FCHECK | EV_STRIP);

        if(check_enter_leave(player, cptr)) {
           if (cptr) free(cptr);
           return 1;
         }

        if(check_mushact(player, cptr)) {
           if (cptr) free(cptr);
           return 1;
         }

        goto bad;
    }

  if (cptr) free(cptr);      /* Free up all memory allocated */
  if (buf1) free(buf1);
  if (saveptr) free(saveptr);

  for (a = 0; a < MAX_MUSH_ARG; a++)        /* argv */
    if (fargs[a]) free((char *) fargs[a]);

 return flag;

bad: 

  flag = 0;
  if (cptr) free(cptr);      /* Free up all memory allocated */
  if (buf1) free(buf1);
  if (saveptr) free(saveptr);

  for (a = 0; a < MAX_MUSH_ARG; a++)       /* argv */
    if (fargs[a]) free((char *) fargs[a]);

 return flag;
}

/* now undef everything that needs to be */
#undef Matched
#undef argu
#undef argv
#undef vargs
#undef arg1
#undef arg2
#undef argu

int check_alias(char *command, char *list)
{
  /* check if a string matches part of a semi-colon separated list */
  char *p;
  while (*list) {
    for (p = command; (*p && DOWNCASE(*p) == DOWNCASE(*list) 
                       && *list != EXIT_DELIMITER);
         p++, list++)
    ;
  if (*p == '\0') {
    while (isspace(*list))
      list++;
    if (*list == '\0' || *list == EXIT_DELIMITER)
      return 1;                 /* word matched */
  }
  /* didn't match. check next word in list */
  while (*list && *list++ != EXIT_DELIMITER)
    ;
  while (isspace(*list))
    list++;
  }
  /* reached the end of the list without matching anything */
  return 0;
}

int alias_list_check(dbref thing, char *command, char *type)
{
  char *a;
  char alias[BUFFER_LEN];
 
  while (thing != NOTHING) {
    a = atr_get_noparent(thing, type);
    if (a) {
      strcpy(alias, a);
      if (check_alias(command, alias) != 0)
        return thing;           /* matched an alias */
    }
    thing = DBFETCH(thing)->next;
  }
  return -1;
}

int loc_alias_check(dbref loc, char *command, char *type)
{
  char *a;
  char alias[BUFFER_LEN];
  a = atr_get_noparent(loc, type);
  if (a) {
    strcpy(alias, a);
    return (check_alias(command, alias));
  } else
    return 0;
}

int check_enter_leave(dbref player, char *command)
{
 int i;
 char temp[BUFFER_LEN];
 /* try matching enter aliases */
          if (Location(player) != NOTHING &&
              (i = alias_list_check(
                   DBFETCH(DBFETCH(player)->location)->contents,
                                    command, "EALIAS")) != -1) {

            sprintf(temp, "#%d", i);
            do_enter(player, temp);
            return 1;
          }
          /* if that didn't work, try matching leave aliases */
          if ((Typeof(Location(player)) != TYPE_ROOM) &&
              (loc_alias_check(Location(player), command, "LALIAS"))) {
            do_leave(player);
            return 1;
          }
 return 0;
}
 
void do_mush_prop_set(__DO_PROTO)
{
  dbref thing;
  char buff[BUFFER_LEN];
  
  if((thing = match_controlled(player, arg1)) == NOTHING) return;

#ifdef TIMESTAMPS
  DBSTORE(thing, time_modified, time(NULL));
#endif

  if(*arg2) {
     if (add_property(thing, argall+1, arg2, 
         default_perms(argall+1),
         access_rights(player, thing, NOTHING))) {
            notify(player, player, "Property set.");
         if(strn_cmp("listen", argall+1)) {
           sprintf(buff, "%s grows ears and can now hear.", 
                  unparse_name(thing));
	  notify_except(thing, DBFETCH(thing)->location, thing, buff);
        }
      }
      else
        notify(player, player, "Permission Denied.");
    } else {
       switch(remove_property(thing, argall+1, 
              access_rights(player, thing, NOTHING))) {
         case 0:
             notify(player, player, "Property removed.");
             if(strn_cmp("listen", argall+1)) {
                 sprintf(buff, "%s loses its ears and becomes deaf.", 
                        unparse_name(thing));
                 notify_except(thing, DBFETCH(thing)->location, thing, buff);
                }
             break;
         case 1:
             notify(player, player, "No such property to remove.");
             break;
         case 2:
             notify(player, player, "Permission Denied.");
             break;
         default:
             fprintf(stderr, 
                    "Some type of error occured in do_mush_prop_set()\n");
             return;
          }
    }
}

void bind_and_queue(dbref player, dbref cause, char *action, char *arg)
{
  char *repl, *command;

  repl = replace_string("##", arg, action);
  command = strip_braces(repl);

  if (repl) free(repl);
  parse_que(player, command, cause);
  if (command) free(command);
}

void do_dolist(dbref player, char *list, char *command, dbref cause, int flag)
{
  char *curr, *objstring;
  char outbuf[BUFFER_LEN];
  char *ptr, *ebuf;

  if (!list || !*list) {
    notify(player, player, "What do you want to do with the list?");
    return;
  }

  strcpy(outbuf, "");

  curr = list;
  while (curr && *curr) {
    while (*curr == ' ')
      curr++;
    if (*curr) {
      objstring = parse_to(&curr, ' ', EV_STRIP);
      if (!flag) {
	/* @dolist, queue command */
	bind_and_queue(player, cause, command, objstring);
      } else {
      /* it's @map, add to the output list */
	ebuf = replace_string("##", objstring, command);
	ptr = exec(cause, ebuf, player, EV_STRIP | EV_FCHECK);
	if(ebuf) free(ebuf);
	if (!*outbuf)
	  strcpy(outbuf, ptr);
	else if (strlen(outbuf) + strlen(ptr) < BUFFER_LEN)
	  sprintf(outbuf, "%s %s", outbuf, ptr);
	if(ptr) free(ptr);
      }
    }
  }

  if (flag) {
    /* if we're doing a @map, copy the list to an attribute */
    atr_add(player, "MAPLIST", outbuf);
    notify(player, player, "Function mapped onto list.");
  }
}

/* for lack of better place the @switch code is here */
void do_switch(dbref player, char *exp, char *argv[], dbref cause, int first)
{
  int any = 0, a;
  char *buff;

  if (!argv[1]) return;

  /* now try a wild card match of buff with stuff in coms */
  for (a = 1; (a < (MAX_MUSH_ARG - 1)) && argv[a] && argv[a + 1]; a += 2) {
    /* eval expression */
    buff = exec(cause, argv[a], player, EV_STRIP | EV_FCHECK);

    /* check for a match */
    if (local_wild_match(buff, exp)) {
      if (first && any) {
	/* terminate loop if only want first match */
	free(buff);
	break;
      } else {
	any = 1;
	parse_que(player, argv[a + 1], cause);
	free(buff);
      }
    } else {
      free(buff);
    }
  }

  /* do default if nothing has been matched */
  if ((a < MAX_MUSH_ARG) && !any && argv[a])
    parse_que(player, argv[a], cause);
}


void did_it(player, thing, what, def, owhat, odef, awhat, loc)
    dbref player;
    dbref thing;
    char *what;
    char *def;
    char *owhat;
    char *odef;
    char *awhat;
    dbref loc;
{
  /* executes the @attr, @oattr, @aattr for a command - gives a message
   * to the enactor and others in the room with the enactor, and executes
   * an action.
   */

  char *d;
  char *tbuf1;
  char tbuf2[BUFFER_LEN];
  char mush_buff[BUFFER_LEN];

  if(player == NOTHING || thing == NOTHING) return;
  loc = (loc == NOTHING) ? DBFETCH(player)->location : loc;

  /* only give messages if the location is good */
  if (GoodObject(loc)) {

    /* message to player */
    if (what && *what) {
      d = atr_get(thing, what);
      if (d) {
	strcpy(tbuf2, d);
	tbuf1 = exec(player, tbuf2, thing, 0);
	notify(player, player, tbuf1);
	free(tbuf1);
      } else if (def && *def)
	notify(player, player, def);
    }
    /* message to neighbors */
    if (!Dark(player)) {
      if (owhat && *owhat) {
        d = atr_get(thing, owhat);
        if (d) {
	  strcpy(tbuf2, d);
	  tbuf1 = exec(player, tbuf2, thing, 0);
	  notify_except2(DBFETCH(loc)->contents, player, thing,
			 tprintf("%s %s", unparse_name(player), tbuf1));
	  free(tbuf1);
	} else {
	  if (odef && *odef) {
	    notify_except2(DBFETCH(loc)->contents, player, thing, 
			   tprintf("%s %s", (int) unparse_name(player), 
				   (int) odef));
	  }
	}
      }
    }
  }

  /* always do the action attribute if there is one (so things like
   * rooms can @trigger) */
  if (awhat && *awhat && (d = atr_get(thing, awhat))) {
    char *b, *ptr;
    strcpy(mush_buff, d);   /* Have to strcpy else exec changes the propdir */
    ptr = exec(player, mush_buff, thing, 0);
    strcpy(tbuf2, ptr);
    free(ptr);

    /* check if object has # of charges */
    b = atr_get_noparent(thing, "CHARGES");

    if (!b) {
      /* no charges set, just execute the action */
      parse_que(thing, tbuf2, player);
      return;
    } else {
      int num = atoi(b);
      if (num) {
	/* charges left, decrement and execute */
	(void) atr_add(thing, "CHARGES", tprintf("%d", num - 1));
	parse_que(thing, tbuf2, player);
	return;
      } else if (!(d = atr_get(thing, "RUNOUT")))
	/* no charges left and no runout; do nothing */
	return;
      /* no charges left, execute runout */
      strcpy(tbuf2, d);
      parse_que(thing, tbuf2, player);
    }
  }
}

void notify_except2(dbref first, dbref exc1, dbref exc2, char *msg)
{
  dbref loc = DBFETCH(first)->location;
  dbref e;
  char tbuf1[BUFFER_LEN];

  if(first < 0 || first >= db_top) return;

  if ((loc != exc1) && (loc != exc2))
    notify_noecho(loc, msg);

  DOLIST(first, first) {
    if (first != exc1 && first != exc2) {
      notify_noecho(first, msg);
    }
  }

  /* now do AUDIBLE stuff */
  if (Audible(loc)) {
    if (Typeof(loc) == TYPE_ROOM) {
      strcpy(tbuf1, msg);
      DOLIST(e, DBFETCH(loc)->exits) {
	if (Audible(e))
	  propagate_sound(e, tbuf1);
      }
    } else if ((loc != exc1) && (loc != exc2)) {
      strcpy(tbuf1, msg);
      propagate_sound(loc, tbuf1);
    }
  }
}

void propagate_sound(dbref thing, char *msg)
{
  /* pass a message on, for AUDIBLE, prepending a prefix, unless the
   * message matches a filter pattern.
   */
  char prefix[BUFFER_LEN];
  char tbuf1[BUFFER_LEN];
  char *a;
  dbref player;
  dbref loc = DBFETCH(thing)->location;

  if (!GoodObject(loc)) return;

  /* check to see that filter doesn't suppress message */
  if (filter_found(thing, msg, 0)) return;
  
  a = atr_get(thing, "PREFIX");

  /* copy prefix, making sure it doesn't spam buffer */
  if (!a || (strlen(a) > BUFFER_LEN)) {
    if (Typeof(thing) == TYPE_EXIT)
/*
      sprintf(prefix, "From %s,", DBRETCH(DBFETCH(thing)->exits)->name);
*/
      sprintf(prefix, "From %s,", unparse_name(thing));
    else
      sprintf(prefix, "From %s,", unparse_name(thing));
  } else 
    strcpy(prefix, a);

  /* do another length check, just in case */
  if (strlen(msg) + strlen(prefix) > BUFFER_LEN) {
    strcpy(tbuf1, msg);
    /* we know that msg is safe, and it's better to have the message
     * and lose the prefix, so just throw out the prefix.
     */
  } else
    strcpy(tbuf1, tprintf("%s %s", prefix, msg));

  if (Typeof(thing) == TYPE_EXIT) {
    /* exits pass the message on to things in the next room */
    DOLIST(player, DBFETCH(loc)->contents)
      notify(player, player, tbuf1);
  } else {
    DOLIST(player, DBFETCH(loc)->contents) {
      /* objects pass the message on to the things outside */
      /* don't want to tell yourself your own propagated message */
      if (player != thing)
	notify(player, player, tbuf1);
    }
  }
}

int filter_found(dbref thing, char *msg, int flag)
{
  /* check to see if the message matches the filter pattern on thing */
  /* flag 0 for @filter, 1 for @infilter */

  char *filter, *a, *p, *temp;
  int i, matched = 0;

  if (!flag)
    a = atr_get(thing, "FILTER");
  else
    a = atr_get(thing, "INFILTER");
  if (!a)
    return matched;

  filter = a;
  temp = filter;

  for (i = 0; (i < MAX_MUSH_ARG) && !matched; i++) {
    p = parse_to(&filter, ',', 0);
    if (p)
      matched = wild_match(p, msg);
  }
  free(temp);
  return matched;
}

int atr_comm_match(thing, player, type, end, str, just_match)
    dbref thing, player;
    char type, end;
    char *str;
   int just_match;		/* if 1, just give match */
{
  propdir *ptr;
  int match = 0;
  char tbuf1[BUFFER_LEN];
  char *s, *pname;
  dbref parent;
  int rec = 0;			/* prevent recursion */
  int ok = 1;			/* uselock check */

  /* if the NO_COMMAND flag is set, we can skip this object if we're
   * looking for $ (there's no reason for NO_COMMAND to block ^-patterns,
   * since setting an object !LISTENER already does that). Also,
   * if it's HALT, it can't execute commands anyway, so don't bother
   * checking. 
   */
  if (Halted(thing) || (Nocommand(thing) && (type == '$')))
    return 0;

  /* only $ checks should look at the parent and do a uselock check */
  if (type == '$') {
    parent = Parent(thing);
    ok = eval_boolexp(player, get_ue_locks(player, thing, USELOCK), thing);
  } else
    parent = NOTHING;
  
  /* the uselock on the child is the only thing which matters.
   * if it's locked, we don't need to bother checking for commands;
   * silently fail.
   */
  if (!ok)
    return 0;

  /* check object itself */
  for(ptr = DBFETCHPROP(thing); ptr; ptr = ptr->next) {
    if (ptr && check_perms(ptr->perms, access_rights(player, thing, NOTHING), 
        PT_READ)) {

      /* first record that the attribute has been read */
      if ((type == '$') && (parent != NOTHING))
	atr_hash_insert(ptr->name);

      /* we move the check for '$' as first char to here because we should
       * insert into the hash table no matter what's in the attribute.
       */
      if(ptr->data)
         pname = uncompress(ptr->data);
      else
         continue;

      if (*pname == type) {
	strcpy(tbuf1, pname);
	for(s = tbuf1 + 1; *s && (*s != end); s++);
	if(!*s)
	  continue;
	*s++ = '\0';
	if(wild_match(tbuf1 + 1, str)) {
	  match++;
	  if (!just_match)
	    parse_que(thing, s, player);
	}
      }
    }
  }

  /* now check parent */
  if (parent == NOTHING)
    return match;

  for (rec = 0; (parent != NOTHING) && (rec < MAX_PARENTS);
       rec++, parent = Parent(parent)) {
    for(ptr = DBFETCHPROP(parent); ptr; ptr = ptr->next) {
      if (ptr && check_perms(ptr->perms, 
          access_rights(player, parent, NOTHING), PT_READ)) {
	/* first check to see if attribute has been entered before. If
	 * not, enter it into the hash table and check it for a command.
	 */
	if (atr_hash_lookup(ptr->name) == NULL) {

	  if (Parent(parent) != NOTHING)
	    atr_hash_insert(ptr->name);

          pname = uncompress(ptr->data);
	  if (*pname == type) {
	    strcpy(tbuf1, pname);
	    for(s = tbuf1 + 1; *s && (*s != end); s++)
	      ;
	    if(!*s)
	      continue;
	    *s++ = '\0';
	    if(wild_match(tbuf1 + 1, str)) {
	      match++;
	      if (!just_match)
		parse_que(thing, s, player);
	    }
	  }

	}
      }
    }
  }
  if (rec > MAX_PARENTS)
    fprintf(stderr, 
      "** WARNING ** atr_comm_match recursion on parented object #%d.\n",
      thing);
  atr_hash_free();
  return match;
}


void do_use_enterlocks(dbref thing, char *lock, int lock_type, int add_rm)
{
    if(lock_type == ENTERLOCK)
     {
         if(add_rm) 
         {
            add_property(thing, "ENTERLOCK", lock, default_perms("_"), 
                        ACCESS_WI);
         }
         else
             remove_property(thing, "ENTERLOCK", ACCESS_WI);
     }
     else if(lock_type == USELOCK)
     {
         if(add_rm) 
         {
            add_property(thing, "USELOCK", lock, default_perms("_"), ACCESS_WI);
         }
         else
            remove_property(thing, "USELOCK", ACCESS_WI);
     }
     else
         fprintf(stderr, "WARNING BAD lock type %d\n", lock_type);
}

boolexp *get_ue_locks(dbref player, dbref thing, int type)
{
 char *str;
 boolexp *key;

  if(type == ENTERLOCK)
      str = get_property_data(thing, "ENTERLOCK", ACCESS_WI);
  else if(type == USELOCK)
      str = get_property_data(thing, "USELOCK", ACCESS_WI);
  else
     fprintf(stderr, "WARNING.  Bad lock type %d\n", type);
       if(!str)
          key = TRUE_BOOLEXP;
       else 
           key = parse_boolexp(player, str);
  return key;
}

void notify_check(dbref player, char *msg, int no_puppet)
{
  /* we only echo puppet messages if the puppet is in a different room
   * from its owner, or we're explicitly told that puppet echo is okay.
   */

  char *d;
  char tbuf1[BUFFER_LEN];
  char *bp;
 
/*
  if ((player < 0) || (player >= db_top))
    return;
  if (depth++ > 7) { 
    depth--;
    return;
  }
*/

  switch (Typeof(player)) {
  case TYPE_ROOM:
  case TYPE_EXIT:
/*  depth--; */
    return;
    break;			/* NOTREACHED */
  case TYPE_PLAYER:
    notify_nolisten(player, msg);
#ifndef PLAYER_LISTEN
/*  depth--; */
    return;
#endif				/* PLAYER_LISTEN */
    break;
  case TYPE_THING:
    if ((FLAGS(player) & QUELL)) return;
    if ((Puppet(player)) &&
	(!no_puppet || (Location(player) != Location(OWNER(player)) && 
         Location (player) != OWNER(player)))) {
      bp = tbuf1;
      safe_str(unparse_name(player), tbuf1, &bp);
      safe_chr('>', tbuf1, &bp);
      safe_chr(' ', tbuf1, &bp);
      safe_str(msg, tbuf1, &bp);
      *bp = '\0';
      notify_nolisten(OWNER(player), tbuf1);
    }
  }

  /* do @listen stuff */
  d = atr_get_noparent(player, "LISTEN");
  if (d) {
    sprintf(tbuf1, "%s",  d); 
    if(wild_match(tbuf1, msg)) {
      if (speaker != player)
	did_it(speaker,player, 0, NULL, 0, NULL, "AHEAR", NOTHING);
      else  
	did_it(speaker, player, 0, NULL, 0, NULL, "AMHEAR", NOTHING);

      did_it(speaker, player, 0, NULL, 0, NULL, "AAHEAR", NOTHING);
      /* also pass the message on
       * Note: not telling player protects against two forms
       * of recursion:
       * player doesn't tell itself (as container) or as contents
       * using teleport it is possible to create a recursive loop
       * but this will be terminated when the depth variable exceeds 30
       */
      if (!member(speaker, DBFETCH(player)->contents) &&
	  !filter_found(player, msg, 1)) {
	d = atr_get(player, "INPREFIX");
	if (d && (strlen(d) + strlen(msg) < BUFFER_LEN))
	  sprintf(tbuf1, "%s %s", d, msg);
	if (Typeof(player) == TYPE_THING && Puppet(player))
	  notify_except2(DBFETCH(player)->contents, player, OWNER(player),
			 (d) ? tbuf1 : msg);
	else
          if(DBFETCH(player)->contents != NOTHING)
             mush_notify_except(DBFETCH(player)->contents, player, 
                          (d) ? tbuf1 : msg);
      } 
    } 
  }

  /* if object is flagged LISTENER, check for ^ listen patterns
   * these are like AHEAR - object cannot trigger itself.
   * unlike normal @listen, don't pass the message on.
   */
  if ((speaker != player) && (Typeof(player) == TYPE_THING && Listen(player)))
    atr_comm_match(player, speaker, '^', ':', msg, 0);

/*  depth--; */
}

void mush_notify_except(dbref first, dbref exception, char *msg)
{
  dbref loc = DBFETCH(first)->location;
  dbref e;
  char tbuf1[BUFFER_LEN];

  if(first < 0 || first >= db_top)
    return;
  if (loc != exception)
    notify_noecho(loc, msg);
  DOLIST(first, first) {
    if (first != exception) {
      notify_noecho(first, msg);
    }
  }

  /* now do AUDIBLE stuff */
  if (Audible(loc)) {
    if (Typeof(loc) == TYPE_ROOM) {
      /* the strcpy is necessary to prevent choking in propagate_sound
       * when msg is an array of char and not a char * in the calling
       * function. Yes, this is ugly and stupid. It works. Kinda.
       */
      strcpy(tbuf1, msg);
      DOLIST(e, DBFETCH(loc)->exits) {
	if (Audible(e))
	  propagate_sound(e, tbuf1);
      }
    } else if (loc != exception) {
      strcpy(tbuf1, msg);
      propagate_sound(loc, tbuf1);
    }
  }
}

#endif /* MUSH */