/
2.0.5/doc/
2.0.5/gnu/
2.0.5/sha/
/* queue.c */

#include "config.h"

/*
 *		       This file is part of TeenyMUD II.
 *		 Copyright(C) 1993, 1994, 1995 by Jason Downs.
 *                           All rights reserved.
 * 
 * TeenyMUD II is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * TeenyMUD II is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file 'COPYING'); if not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 */

#include <stdio.h>
#include <sys/types.h>
#include <time.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif			/* HAVE_STDLIB_H */
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif			/* HAVE_STRING_H */
#include <ctype.h>

#include "conf.h"
#include "teeny.h"
#include "commands.h"
#include "externs.h"

/* The TeenyMUD command queue system, inspired by MUSH. */

/* command queue structure. */
struct cque {
  int player;		/* object running command. */
  int cause;		/* object causing the command. */
  int flags;		/* special operators. */

  time_t when;		/* when to run command. */
  int obj;		/* object to use as a semaphore. */

  char *cmd;		/* what to run. */

  int eargc;		/* exec argument count. */
  char **eargv;		/* exec arguments. */

  struct cque *next;
  struct cque *prev;
};

/* iterative queue structure. */
struct ique {
  int player;		/* object running command. */
  int cause;		/* object causing the command. */
  
  char *var;		/* variable to update. */
  char *val;		/* value of variable. */
  char *cmd;		/* what to run. */

  int eargc;		/* exec argument count. */
  char **eargv;		/* exec arguments. */

  struct ique *next;
  struct ique *prev;
};

/* the queue list */
static struct cque *gqueue = (struct cque *)NULL;
static struct ique *iqueue = (struct ique *)NULL;

/* add something to the iterative queue. */
void queue_iadd(player, cause, var, val, command, eargc, eargv)
    int player, cause;
    char *var, *val, *command;
    int eargc;
    char *eargv[];
{
  struct ique *new;
  int owner, check;

  if(mudconf.enable_money) {
    if((get_int_elt(player, OWNER, &owner) == -1)
       || (get_int_elt(owner, QUEUE, &check) == -1)) {
      logfile(LOG_ERROR, "queue_iadd: bad database reference.\n");
      return;
    }
    if(check >= mudconf.queue_commands) {
      if(!can_afford(owner, cause, mudconf.queue_cost, 1))
	return;
      if(set_int_elt(owner, QUEUE, 0) == -1) {
	logfile(LOG_ERROR, "queue_iadd: couldn't reset queue count on #%d\n",
	        owner);
	return;
      }
    } else {
      if(set_int_elt(owner, QUEUE, ++check) == -1) {
	logfile(LOG_ERROR, "queue_iadd: couldn't reset queue count on #%d\n",
	        owner);
	return;
      }
    }
  }

  /* make the entry. */
  new = (struct ique *)ty_malloc(sizeof(struct ique), "queue_iadd");
  new->player = player;
  new->cause = cause;

  new->var = (char *)ty_strdup(var, "queue_iadd");
  new->val = (char *)ty_strdup(val, "queue_iadd");
  new->cmd = (char *)ty_strdup(command, "queue_iadd");

  new->eargc = eargc;
  new->eargv = eargv;

  new->next = (struct ique *)NULL;

  /* add it to the queue. */
  if(iqueue == (struct ique *)NULL) {
    new->prev = new;
    iqueue = new;
  } else {
    /* add it to the tail. */
    new->prev = iqueue->prev;
    (iqueue->prev)->next = new;
    iqueue->prev = new;
  }
}

/* add something to the command queue. */
void queue_add(player, cause, delay, sema, command, eargc, eargv)
    int player, cause, delay, sema;
    char *command;
    int eargc;
    char *eargv[];
{
  struct cque *new;
  int owner, check;

  if((command == (char *)NULL) || (command[0] == '\0'))
    return;

  if(mudconf.enable_money) {
    if((get_int_elt(player, OWNER, &owner) == -1)
       || (get_int_elt(owner, QUEUE, &check) == -1)) {
      logfile(LOG_ERROR, "queue_add: bad database reference.\n");
      return;
    }
    if(check >= mudconf.queue_commands) {
      if(!can_afford(owner, cause, mudconf.queue_cost, 1))
        return;
      if(set_int_elt(owner, QUEUE, 0) == -1) {
        logfile(LOG_ERROR, "queue_add: couldn't reset queue count on #%d\n",
		owner);
        return;
      }
    } else {
      if(set_int_elt(owner, QUEUE, ++check) == -1) {
        logfile(LOG_ERROR, "queue_add: couldn't reset queue count on #%d\n",
		owner);
	return;
      }
    }
  }

  /* make the entry. */
  new = (struct cque *)ty_malloc(sizeof(struct cque), "queue_add");
  new->player = player;
  new->cause = cause;
  new->flags = 0;

  new->when = (delay >= 0) ? mudstat.now + delay : -1;
  new->obj = sema;

  new->cmd = (char *)ty_strdup(command, "queue_add");

  new->eargc = eargc;
  new->eargv = eargv;

  new->next = (struct cque *)NULL;

  /* add it to the queue */
  if(gqueue == (struct cque *)NULL) {
    new->prev = new;
    gqueue = new;
  } else {
    /* add it to the tail. */
    new->prev = gqueue->prev;
    (gqueue->prev)->next = new;
    gqueue->prev = new;
  }
}

/* do count things off the top of the queue(s). */
void queue_run(count)
    int count;
{
  register struct cque *curr, *next;
  register struct ique *icurr, *inext;
  int sema, owner;

  /* optimization. */
  if((gqueue == (struct cque *)NULL) && (iqueue == (struct ique *)NULL)) {
    return;
  }

  /* command queue. */
  curr = gqueue;
  while(count && (curr != (struct cque *)NULL)) {
    if((curr->when >= 0) && (curr->when <= mudstat.now)) {	/* run it */
      /* if it's went HALT or no longer exists, just drain it. */
      if(!isHALT(curr->player) && exists_object(curr->player))
        handle_cmds(curr->player, curr->cause, curr->cmd, curr->eargc,
		    curr->eargv);

      /* update semaphore count */
      if(curr->obj != -1) {
        if((get_int_elt(curr->obj, SEMAPHORES, &sema) == -1)
	   || (set_int_elt(curr->obj, SEMAPHORES, --sema) == -1)) {
	  logfile(LOG_ERROR,
		  "queue_run: failed to update semaphore count on #%d\n",
	          curr->obj);
	}
      }

      /* remove it. */
      if(curr == gqueue) {
        if(curr->next == (struct cque *)NULL) {	/* only thing here. */
	  gqueue = (struct cque *)NULL;
	  next = (struct cque *)NULL;
	} else {
	  (curr->next)->prev = curr->prev;
	  gqueue = curr->next;
	  next = curr->next;
	}
      } else {
        if(curr->next == (struct cque *)NULL) {	/* last thing here. */
	  (curr->prev)->next = (struct cque *)NULL;
	  gqueue->prev = curr->prev;
	  next = (struct cque *)NULL;
	} else {
          (curr->next)->prev = curr->prev;
	  (curr->prev)->next = curr->next;
	  next = curr->next;
	}
      }
      ty_free((VOID *)curr->cmd);
      free_argv(curr->eargc, curr->eargv);
      ty_free((VOID *)curr->eargv);
      ty_free((VOID *)curr);

      count--;
      curr = next;
    } else
      curr = curr->next;
  }

  /* iterative queue. */
  icurr = iqueue;
  while(count && (icurr != (struct ique *)NULL)) {
    /* if it's went HALT, or no longer exists, just drain it. */
    if(!isHALT(icurr->player) && exists_object(icurr->player)) {
      /* set the variable. */
      if(icurr->var != (char *)NULL) {
	if(get_int_elt(icurr->player, OWNER, &owner) == -1) {
	  logfile(LOG_ERROR, "queue_run: couldn't get owner of #%d.\n",
	          icurr->player);
	  break;
	}
	if(icurr->val != (char *)NULL) {
	  var_set(owner, icurr->var, icurr->val);
	} else {
	  var_delete(owner, icurr->var);
	}
      }

      /* do the command. */
      if(icurr->cmd != (char *)NULL) {
	handle_cmds(icurr->player, icurr->cause, icurr->cmd, icurr->eargc,
		    icurr->eargv);
      }
    }

    if(icurr == iqueue) {			/* only thing here. */
      if(icurr->next == (struct ique *)NULL) {
	iqueue = (struct ique *)NULL;
	inext = (struct ique *)NULL;
      } else {
	(icurr->next)->prev = icurr->prev;
	iqueue = icurr->next;
	inext = icurr->next;
      }
    } else {
      if(icurr->next == (struct ique *)NULL) {	/* last thing here. */
	(icurr->prev)->next = (struct ique *)NULL;
	iqueue->prev = icurr->prev;
	inext = (struct ique *)NULL;
      } else {
	(icurr->next)->prev = icurr->prev;
	(icurr->prev)->next = icurr->next;
	inext = icurr->next;
      }
    }
    ty_free((VOID *)icurr->var);
    ty_free((VOID *)icurr->val);
    ty_free((VOID *)icurr->cmd);
    free_argv(icurr->eargc, icurr->eargv);
    ty_free((VOID *)icurr->eargv);
    ty_free((VOID *)icurr);

    count--;
    icurr = inext;
  }
}

/* run 'semaphores' off the queue. */
VOID do_notify(player, cause, switches, argone, argtwo)
    int player, cause, switches;
    char *argone, *argtwo;
{
  int obj, count, ocount, ran, sema;
  register struct cque *curr, *next;
  char *ptr;

  if((argone == (char *)NULL) || (argone[0] == '\0')) {
    if(!(switches & CMD_QUIET))
      notify_player(player, cause, player, "Notify what?", NOT_QUIET);
    return;
  }
  if((argtwo == (char *)NULL) || (argtwo[0] == '\0')) {
    count = 1;
    ocount = 1;
  } else {
    count = (int)strtol(argtwo, &ptr, 10);
    if(ptr == argtwo) {
      if(!(switches & CMD_QUIET))
        notify_player(player, cause, player, "I don't understand that count.",
		      NOT_QUIET);
      return;
    }
    if(count == 0) {
      if(!(switches & CMD_QUIET))
        notify_player(player, cause, player, "Nothing to do.", NOT_QUIET);
      return;
    }
    ocount = count;	/* save a copy */
  }

  obj = resolve_object(player, cause, argone,
  		       (!(switches & CMD_QUIET) ? RSLV_EXITS|RSLV_NOISY
		       				: RSLV_EXITS));
  if(obj == -1)
    return;

  curr = gqueue;
  ran = 0;
  while(((switches & NOTIFY_ALL) || count) && (curr != (struct cque *)NULL)) {
    if(curr->obj == obj) {		/* run */
      /* if it's went HALT, or no longer exists, just drain it. */
      if(!(switches & NOTIFY_DRAIN) && !isHALT(curr->player)
	 && exists_object(curr->player)) {
        handle_cmds(curr->player, curr->cause, curr->cmd, curr->eargc,
                    curr->eargv);
	ran++;
      }

      /* remove it. */
      if(curr == gqueue) {
        if(curr->next == (struct cque *)NULL) { /* only thing here. */
          gqueue = (struct cque *)NULL;
          next = (struct cque *)NULL;
        } else {
          curr->next->prev = curr->prev;
          gqueue = curr->next;
          next = curr->next;
        }
      } else {
        if(curr->next == (struct cque *)NULL) { /* last thing here. */
          curr->prev->next = (struct cque *)NULL;
          gqueue->prev = curr->prev;
          next = (struct cque *)NULL;
        } else {
          curr->next->prev = curr->prev;
          curr->prev->next = curr->next;
          next = curr->next;
        }
      }
      ty_free((VOID *)curr->cmd);
      free_argv(curr->eargc, curr->eargv);
      ty_free((VOID *)curr->eargv);
      ty_free((VOID *)curr);

      count--;
      curr = next;
    } else
      curr = curr->next;
  }
  if((ran == 0) && !(switches & NOTIFY_DRAIN) && !(switches & NOTIFY_ALL)) {
    /* fix up the semaphore count, this was a 'future' notify */
    ran = ocount;
  }

  /* update semaphore count */
  if(get_int_elt(obj, SEMAPHORES, &sema) == -1) {
    notify_bad(player);
    return;
  }
  sema = (switches & NOTIFY_DRAIN) ? 0 : sema - ran;
  if(set_int_elt(obj, SEMAPHORES, sema) == -1) {
    notify_bad(player);
    return;
  }

  if(!(switches & CMD_QUIET))
    notify_player(player, cause, player, "Notified.", NOT_QUIET);
}

/* queue related command handlers. */
VOID do_halt(player, cause, switches, arg)
    int player, cause, switches;
    char *arg;
{
  int victim;
  char buff[MEDBUFFSIZ], *name;
  register struct cque *curr, *next;
  register struct ique *icurr, *inext;

  if(switches & HALT_ALL) {	/* expunge the queue(s). */
    if(!(switches & CMD_QUIET)) {
      int wflags[FLAGS_LEN];

      bzero((VOID *)wflags, sizeof(wflags));
      if(get_str_elt(player, NAME, &name) == -1)
        name = "???";
      snprintf(buff, sizeof(buff), "System halted by %s.", name);
      tcp_wall(wflags, buff, -1);
    }

    /* poof */
    for(curr = gqueue; curr != (struct cque *)NULL; curr = next) {
      next = curr->next;
      ty_free((VOID *)curr->cmd);
      free_argv(curr->eargc, curr->eargv);
      ty_free((VOID *)curr->eargv);
      ty_free((VOID *)curr);
    }
    gqueue = (struct cque *)NULL;

    for(icurr = iqueue; icurr != (struct ique *)NULL; icurr = inext) {
      inext = icurr->next;
      ty_free((VOID *)icurr->var);
      ty_free((VOID *)icurr->val);
      ty_free((VOID *)icurr->cmd);
      free_argv(icurr->eargc, icurr->eargv);
      ty_free((VOID *)icurr->eargv);
      ty_free((VOID *)icurr);
    }
    iqueue = (struct ique *)NULL;
  } else {
    /* we just do a flag set, yawn. */
    victim = resolve_object(player, cause, arg,
    			    (!(switches & CMD_QUIET) ? RSLV_NOISY|RSLV_EXITS
			    			     : RSLV_EXITS));
    if(victim == -1)
      return;

    if(!controls(player, cause, victim)) {
      if(!(switches & CMD_QUIET))
        notify_player(player, cause, player, "Permission denied.", NOT_QUIET);
      return;
    }

    if(setHALT(victim) == -1) {
      notify_bad(player);
      return;
    }
  }

  if(!(switches & CMD_QUIET))
    notify_player(player, cause, player, "Halted.", NOT_QUIET);
}

VOID do_semaphore(player, cause, switches, argone, argtwo)
    int player, cause, switches;
    char *argone, *argtwo;
{
  int delay, obj, sema;
  char *ptr, *nptr;

  if((argone == (char *)NULL) || (argone[0] == '\0')
     || (argtwo == (char *)NULL) || (argtwo[0] == '\0')) {
    if(!(switches & CMD_QUIET))
      notify_player(player, cause, player,
		    "You must specify an object and a command.", NOT_QUIET);
    return;
  }

  for(ptr = argone; *ptr && (*ptr != '/'); ptr++);
  if(*ptr == '/') {
    *ptr++ = '\0';

    delay = (int)strtol(ptr, &nptr, 10);
    if(nptr == ptr) {
      if(!(switches & CMD_QUIET))
        notify_player(player, cause, player, 
		      "You must specify delay by number.", NOT_QUIET);
      return;
    }
  } else
    delay = -1;

  obj = resolve_object(player, cause, argone,
  		       (!(switches & CMD_QUIET) ? RSLV_NOISY|RSLV_EXITS
		       				: RSLV_EXITS));
  if(obj == -1)
    return;
  if(!controls(player, cause, obj) && !isLINK_OK(obj)) {
    if(!(switches & CMD_QUIET))
      notify_player(player, cause, player, "Permission denied.", NOT_QUIET);
    return;
  }

  if(get_int_elt(obj, SEMAPHORES, &sema) == -1) {
    notify_bad(player);
    return;
  }
  if(sema < 0) {
    /* fake out the queue */
    queue_add(player, cause, 1, -1, argtwo, 0, (char **)NULL);
  } else {
    queue_add(player, cause, delay, obj, argtwo, 0, (char **)NULL);
  }
  sema++;
  if(set_int_elt(obj, SEMAPHORES, sema) == -1) {
    notify_bad(player);
    return;
  }

  if(!(switches & CMD_QUIET))
    notify_player(player, cause, player, "Queued.", NOT_QUIET);
}

VOID do_wait(player, cause, switches, argone, argtwo)
    int player, cause, switches;
    char *argone, *argtwo;
{
  int obj, owner;
  time_t delay;
  char *ptr;

  if((argone == (char *)NULL) || (argone[0] == '\0')
     || (argtwo == (char *)NULL) || (argtwo[0] == '\0')) {
    if(!(switches & CMD_QUIET))
      notify_player(player, cause, player, 
		    "You must specify a delay and a command.", NOT_QUIET);
    return;
  }

  delay = (int)strtol(argone, &ptr, 10);
  if(!isdigit(argone[0])) {
    if(!(switches & CMD_QUIET))
      notify_player(player, cause, player,
		    "You must specify delay by number.", NOT_QUIET);
    return;
  }
  parse_time(argone, &delay);

  if(*ptr == '/') {	/* optional object. */
    ptr++;
    obj = resolve_object(player, cause, ptr,
    			 (!(switches & CMD_QUIET) ? RSLV_NOISY|RSLV_EXITS
			 			  : RSLV_EXITS));
    if(obj == -1)
      return;
    if(!controls(player, cause, obj)) {
      if(!(switches & CMD_QUIET))
        notify_player(player, cause, player, "Permission denied.", NOT_QUIET);
      return;
    }
    if(!isGOD(player) && isGOD(obj)) {
      if(!(switches & CMD_QUIET))
        notify_player(player, cause, player, "Permission denied.", NOT_QUIET);
      return;
    }
    if(get_int_elt(player, OWNER, &owner) == -1) {
      notify_bad(player);
      return;
    }
    if((obj == owner) && !isINHERIT(player) && !isINHERIT(owner)) {
      if(!(switches & CMD_QUIET))
        notify_player(player, cause, player, "Permission denied.", NOT_QUIET);
      return;
    }

    /* Swab the cause, just like @force. */
    queue_add(obj, obj, (int)delay, -1, argtwo, 0, (char **)NULL);
  } else
    queue_add(player, cause, (int)delay, -1, argtwo, 0, (char **)NULL);

  if(!(switches & CMD_QUIET))
    notify_player(player, cause, player, "Queued.", NOT_QUIET);
}

VOID do_ps(player, cause, switches, arg)
    int player, cause, switches;
    char *arg;
{
  int searchfor, powner, cowner;
  register struct cque *curr;
  char buff[MEDBUFFSIZ];

  if(switches & PS_ALL) {
    searchfor = -1;
  } else {
    if((arg == (char *)NULL) || (arg[0] == '\0')) {
      searchfor = player;
    } else {
      searchfor = resolve_object(player, cause, arg,
      				 (!(switches & CMD_QUIET)
				  ? RSLV_NOISY|RSLV_EXITS : RSLV_EXITS));
      if(searchfor == -1)
        return;
    }
  }

  if((searchfor != -1) && !controls(player, cause, searchfor)) {
    if(!(switches & CMD_QUIET))
      notify_player(player, cause, player, "Permission denied.", NOT_QUIET);
    return;
  }
  if((switches & PS_OWNED)
     && (get_int_elt(searchfor, OWNER, &searchfor) == -1)) {
    notify_bad(player);
    return;
  }

  /* dump the appropiate queue. */
  notify_player(player, cause, player, "Command Queue:", 0);

  if((searchfor != -1) && !(switches & PS_OWNED)) {
    for(curr = gqueue; curr != (struct cque *)NULL; curr = curr->next) {
      if(((switches & PS_CAUSE) && (curr->cause == searchfor))
	 || (!(switches & PS_CAUSE) && (curr->player == searchfor))) {
	snprintf(buff, sizeof(buff), "%s[->#%d]: ",
	         display_name(player, cause, curr->player), curr->cause);
	strncat(buff, curr->cmd, sizeof(buff) - strlen(buff) - 1);
	buff[MEDBUFFSIZ-1] = '\0';
	notify_player(player, cause, player, buff, 0);
      }
    }
  } else {
    for(curr = gqueue; curr != (struct cque *)NULL; curr = curr->next) {
      if((get_int_elt(curr->cause, OWNER, &cowner) == -1)
	 || (get_int_elt(curr->player, OWNER, &powner) == -1))
	continue;

      if((searchfor == -1)
         || ((switches & PS_CAUSE) && (cowner == searchfor))
	 || (!(switches & PS_CAUSE) && (powner == searchfor))) {
	snprintf(buff, sizeof(buff), "%s[->#%d]: ",
		 display_name(player, cause, curr->player), curr->cause);
	strncat(buff, curr->cmd, sizeof(buff) - strlen(buff) - 1);
	buff[MEDBUFFSIZ - 1] = '\0';
	notify_player(player, cause, player, buff, 0);
      }
    }
  }

  notify_player(player, cause, player, "***End of list***", 0);
}

VOID do_force(player, cause, switches, argone, argtwo)
    int player, cause, switches;
    char *argone, *argtwo;
{
  int obj, owner;

  if ((argone == (char *)NULL) || (argone[0] == '\0')
      || (argtwo == (char *)NULL) || (argtwo[0] == '\0')) {
      notify_player(player, cause, player, "Force who to do what?", NOT_QUIET);
    return;
  }
  obj = resolve_object(player, cause, argone,
  		       (!(switches & CMD_QUIET) ? RSLV_NOISY : 0));
  if(obj == -1)
    return;
  if (!controls(player, cause, obj)) {
    if(!(switches & CMD_QUIET))
      notify_player(player, cause, player, "Permission denied.", NOT_QUIET);
    return;
  }
  if (isGOD(obj) && !isGOD(player)) {
    if(!(switches & CMD_QUIET))
      notify_player(player, cause, player,
		    "You can't force God to do anything!", NOT_QUIET);
    return;
  }

  /* You can't force your owner unless you're set INHERIT. */
  if(get_int_elt(player, OWNER, &owner) == -1) {
    notify_bad(player);
    return;
  }
  if((owner == obj) && !isINHERIT(player) && !isINHERIT(owner)) {
    if(!(switches & CMD_QUIET))
      notify_player(player, cause, player, "Permission denied.", NOT_QUIET);
    return;
  }

  if(isROBOT(obj)) {	/* deliver the TinyMUD message only to robots. */
    char *name, buf[MEDBUFFSIZ];

    if(get_str_elt(player, NAME, &name) == -1)
      name = "???";
    snprintf(buf, sizeof(buf), "You sense that %s is forcing you to %s",
	     name, argtwo);
    notify_player(obj, cause, player, buf, NOT_QUIET);
  }

  /* As per MUSH, this command resets the cause. */
  handle_cmds(obj, obj, argtwo, 0, (char **)NULL);

  if(!(switches & CMD_QUIET) && !isQUIET(obj))
    notify_player(player, cause, player, "Ok.", NOT_QUIET);
}

VOID do_trigger(player, cause, switches, argc, argv)
    int player, cause, switches, argc;
    char *argv[];
{
  int obj, aflags, asource, eargc;
  char *atr, **eargv;

  if(argc < 2) {
    if(!(switches & CMD_QUIET))
      notify_player(player, cause, player, "Trigger what?", NOT_QUIET);
    return;
  }

  obj = resolve_object(player, cause, argv[0],
  		       (!(switches & CMD_QUIET) ? RSLV_NOISY|RSLV_EXITS
		       				: RSLV_EXITS));
  if(obj == -1)
    return;
  if(!controls(player, cause, obj)) {
    if(!(switches & CMD_QUIET))
      notify_player(player, cause, player, "Permission denied.", NOT_QUIET);
    return;
  }

  if(attr_get_parent(obj, argv[1], &atr, &aflags, &asource) == -1) {
    notify_bad(player);
    return;
  }
  if(atr == (char *)NULL) {
    if(!(switches & CMD_QUIET))
      notify_player(player, cause, player, "No such attribute.", NOT_QUIET);
    return;
  }

  /* queue it up. */
  argc -= 2;
  argv += 2;
  if(argc > 0) {
    eargv = (char **)ty_malloc(sizeof(char *) * argc, "do_trigger");
    for(eargc = 0; eargc < argc; eargc++) {
      eargv[eargc] = ty_strdup(argv[eargc], "do_trigger");
    }
  } else {
    eargc = 0;
    eargv = (char **)NULL;
  }

  queue_add(obj, player, 1, -1, atr, eargc, eargv);

  if(!(switches & CMD_QUIET))
    notify_player(player, cause, player, "Triggered.", NOT_QUIET);
}

/* iterate over a list. */
VOID do_foreach(player, cause, switches, argone, argtwo)
    int player, cause, switches;
    char *argone, *argtwo;
{
  char *ptr, *var, *lptr, *val;

  if((argone == (char *)NULL) || (argone[0] == '\0')
     || (argtwo == (char *)NULL) || (argtwo[0] == '\0')) {
    if(!(switches & CMD_QUIET))
      notify_player(player, cause, player,
		    "You can't foreach without a list.", NOT_QUIET);
    return;
  }

  argone = parse_slash(argone, &var);
  if(var == (char *)NULL) {
    if(!(switches & CMD_QUIET))
      notify_player(player, cause, player,
    		    "You must specify a variable name.", NOT_QUIET);
    return;
  }
  if(argone[0] == '\0') {
    if(!(switches & CMD_QUIET))
      notify_player(player, cause, player,
		    "You must specify a list.", NOT_QUIET);
    return;
  }

  /* the command parser doesn't parse this! */
  if(!(switches & ARG_FEXEC))
    lptr = exec(player, cause, player, argone, 0, 0, (char **)NULL);
  else
    lptr = argone;

  ptr = (char *)NULL;

  while((val = slist_next(lptr, &ptr)) != (char *)NULL)
    queue_iadd(player, cause, var, val, argtwo, 0, (char **)NULL);

  if(!(switches & ARG_FEXEC))
    ty_free((VOID *)lptr);
}

VOID do_case(player, cause, switches, argc, argv)
    int player, cause, switches, argc;
    char *argv[];
{
  register int idx = 1;
  register int done = 0;
  register int top = argc - 1;
  char *lptr;

  if(argc < 3) {
    if(!(switches & CMD_QUIET))
      notify_player(player, cause, player,
      		    "How can you case on that?", NOT_QUIET);
    return;
  }

  /* the command parser doesn't parse this! */
  if(!(switches & ARG_FEXEC))
    lptr = exec(player, cause, player, argv[0], 0, 0, (char **)NULL);
  else
    lptr = argv[0];

  while(1) {
    if((idx == top) && !done) {	/* default */
      queue_add(player, cause, 1, -1, argv[idx], 0, (char **)NULL);
      break;
    } else if(idx >= top)		/* simply done */
      break;

    if(quick_wild(argv[idx], lptr)) {
      /* queue this up */
      if(idx < top) {
        queue_add(player, cause, 1, -1, argv[idx+1], 0, (char **)NULL);
	done++;
      }
      if(switches & CASE_FIRST) /* all done */
	break;
    }

    idx += 2;
  }

  if(!(switches & ARG_FEXEC))
    ty_free((VOID *)lptr);
}