tinymush-3.1p2/game/backups/
tinymush-3.1p2/game/bin/
tinymush-3.1p2/game/data/
tinymush-3.1p2/game/modules/
tinymush-3.1p2/game/modules/old/
tinymush-3.1p2/src/modules/comsys/
tinymush-3.1p2/src/modules/hello/
tinymush-3.1p2/src/modules/mail/
tinymush-3.1p2/src/tools/
/* cque.c - commands and functions for manipulating the command queue */
/* $Id: cque.c,v 1.39 2003/06/03 16:18:38 rmg Exp $ */

#include "copyright.h"
#include "autoconf.h"
#include "config.h"

#include <signal.h>

#include "alloc.h"	/* required by mudconf */
#include "flags.h"	/* required by mudconf */
#include "htab.h"	/* required by mudconf */
#include "mudconf.h"	/* required by code */

#include "db.h"		/* required by externs */
#include "externs.h"	/* required by interface */
#include "interface.h"	/* required by code */

#include "match.h"	/* required by code */
#include "attrs.h"	/* required by code */
#include "powers.h"	/* required by code */
#include "command.h"	/* required by code */

extern int FDECL(a_Queue, (dbref, int));
extern void FDECL(s_Queue, (dbref, int));
extern int FDECL(QueueMax, (dbref));

/* ---------------------------------------------------------------------------
 * add_to: Adjust an object's queue or semaphore count.
 */

static int add_to(doer, player, am, attrnum)
dbref doer, player;
int am, attrnum;
{
	int num, aflags, alen;
	dbref aowner;
	char buff[20];
	char *atr_gotten;

	num = atoi(atr_gotten = atr_get(player, attrnum, &aowner,
					&aflags, &alen));
	free_lbuf(atr_gotten);
	num += am;
	if (num)
		ltos(buff, num);
	else
		*buff = '\0';
	atr_add(player, attrnum, buff, Owner(doer), aflags);
	return (num);
}

/* ---------------------------------------------------------------------------
 * give_que: Thread a queue block onto the high or low priority queue
 */

static void give_que(tmp)
BQUE *tmp;
{
	tmp->next = NULL;
	tmp->waittime = 0;

	/* Thread the command into the correct queue */

	if (Typeof(tmp->cause) == TYPE_PLAYER) {
		if (mudstate.qlast != NULL) {
			mudstate.qlast->next = tmp;
			mudstate.qlast = tmp;
		} else
			mudstate.qlast = mudstate.qfirst = tmp;
	} else {
		if (mudstate.qllast) {
			mudstate.qllast->next = tmp;
			mudstate.qllast = tmp;
		} else
			mudstate.qllast = mudstate.qlfirst = tmp;
	}
}

/* ---------------------------------------------------------------------------
 * que_want: Do we want this queue entry?
 */

static int que_want(entry, ptarg, otarg)
BQUE *entry;
dbref ptarg, otarg;
{
	if (!Good_obj(entry->player))
		return 0;
	if ((ptarg != NOTHING) && (ptarg != Owner(entry->player)))
		return 0;
	if ((otarg != NOTHING) && (otarg != entry->player))
		return 0;
	return 1;
}

/* ---------------------------------------------------------------------------
 * halt_que: Remove all queued commands from a certain player
 */

int halt_que(player, object)
dbref player, object;
{
	BQUE *trail, *point, *next;
	int numhalted, halt_all, i;
	int *dbrefs_array;

	numhalted = 0;
	halt_all = ((player == NOTHING) && (object == NOTHING)) ? 1 : 0; 
	if (halt_all)
	    dbrefs_array = (int *) XCALLOC(mudstate.db_top, sizeof(int),
					   "halt_que.dbrefs");

	/* Player queue */

	for (point = mudstate.qfirst; point; point = point->next)
		if (que_want(point, player, object)) {
			numhalted++;
			if (halt_all && Good_obj(point->player))
			    dbrefs_array[Owner(point->player)] += 1;
			point->player = NOTHING;
		} 

	/* Object queue */

	for (point = mudstate.qlfirst; point; point = point->next)
		if (que_want(point, player, object)) {
			numhalted++;
			if (halt_all && Good_obj(point->player))
			    dbrefs_array[Owner(point->player)] += 1;
			point->player = NOTHING;
		} 

	/* Wait queue */

	for (point = mudstate.qwait, trail = NULL; point; point = next)
		if (que_want(point, player, object)) {
			numhalted++;
			if (halt_all && Good_obj(point->player))
			    dbrefs_array[Owner(point->player)] += 1;
			if (trail)
				trail->next = next = point->next;
			else
				mudstate.qwait = next = point->next;
			Free_QData(point);
			free_qentry(point);
		} else
			next = (trail = point)->next;

	/* Semaphore queue */

	for (point = mudstate.qsemfirst, trail = NULL; point; point = next)
		if (que_want(point, player, object)) {
			numhalted++;
			if (halt_all && Good_obj(point->player))
			    dbrefs_array[Owner(point->player)] += 1;
			if (trail)
				trail->next = next = point->next;
			else
				mudstate.qsemfirst = next = point->next;
			if (point == mudstate.qsemlast)
				mudstate.qsemlast = trail;
			add_to(player, point->sem, -1, point->attr);
			Free_QData(point);
			free_qentry(point);
		} else
			next = (trail = point)->next;

	if (halt_all) {
	    for (i = 0; i < mudstate.db_top; i++) {
		if (dbrefs_array[i]) {
		    giveto(i, (mudconf.waitcost * dbrefs_array[i]));
		    s_Queue(i, 0);
		}
	    }
	    XFREE(dbrefs_array, "halt_que.dbrefs");
	    return numhalted;
	}

	if (player == NOTHING)
		player = Owner(object);
	giveto(player, (mudconf.waitcost * numhalted));
	if (object == NOTHING)
		s_Queue(player, 0);
	else
		a_Queue(player, -numhalted);
	return numhalted;
}

/* ---------------------------------------------------------------------------
 * do_halt: Command interface to halt_que.
 */

void do_halt(player, cause, key, target)
dbref player, cause;
int key;
char *target;
{
	dbref player_targ, obj_targ;
	int numhalted;

	if ((key & HALT_ALL) && !(Can_Halt(player))) {
		notify(player, NOPERM_MESSAGE);
		return;
	}
	/* Figure out what to halt */

	if (!target || !*target) {
		obj_targ = NOTHING;
		if (key & HALT_ALL) {
			player_targ = NOTHING;
		} else {
			player_targ = Owner(player);
			if (Typeof(player) != TYPE_PLAYER)
				obj_targ = player;
		}
	} else {
		if (Can_Halt(player))
			obj_targ = match_thing(player, target);
		else
			obj_targ = match_controlled(player, target);

		if (!Good_obj(obj_targ))
			return;
		if (key & HALT_ALL) {
			notify(player, "Can't specify a target and /all");
			return;
		}
		if (Typeof(obj_targ) == TYPE_PLAYER) {
			player_targ = obj_targ;
			obj_targ = NOTHING;
		} else {
			player_targ = NOTHING;
		}
	}

	numhalted = halt_que(player_targ, obj_targ);
	if (Quiet(player))
		return;
	if (numhalted == 1)
		notify(Owner(player), "1 queue entries removed.");
	else
		notify(Owner(player),
		       tprintf("%d queue entries removed.", numhalted));
}

/* ---------------------------------------------------------------------------
 * nfy_que: Notify commands from the queue and perform or discard them.
 */

int nfy_que(player, sem, attr, key, count)
dbref player, sem;
int attr, key, count;
{
	BQUE *point, *trail, *next;
	int num, aflags, alen;
	dbref aowner;
	char *str;

	if (attr) {
		str = atr_get(sem, attr, &aowner, &aflags, &alen);
		num = atoi(str);
		free_lbuf(str);
	} else {
		num = 1;
	}
	
	if (num > 0) {
		num = 0;
		for (point = mudstate.qsemfirst, trail = NULL; point; point = next) {
			if ((point->sem == sem) && 
			    ((point->attr == attr) || !attr)) {
				num++;
				if (trail)
					trail->next = next = point->next;
				else
					mudstate.qsemfirst = next = point->next;
				if (point == mudstate.qsemlast)
					mudstate.qsemlast = trail;

				/* Either run or discard the command */

				if (key != NFY_DRAIN) {
					give_que(point);
				} else {
					giveto(point->player,
					       mudconf.waitcost);
					a_Queue(Owner(point->player), -1);
					Free_QData(point);
					free_qentry(point);
				}
			} else {
				next = (trail = point)->next;
			}

			/* If we've notified enough, exit */

			if ((key == NFY_NFY) && (num >= count))
				next = NULL;
		}
	} else {
		num = 0;
	}

	/* Update the sem waiters count */

	if (key == NFY_NFY)
		add_to(player, sem, -count, (attr ? attr : A_SEMAPHORE));
	else
		atr_clr(sem, (attr ? attr: A_SEMAPHORE));

	return num;
}

/* ---------------------------------------------------------------------------
 * do_notify: Command interface to nfy_que
 */

void do_notify(player, cause, key, what, count)
dbref player, cause;
int key;
char *what, *count;
{
	dbref thing, aowner;
	int loccount, attr, aflags;
	ATTR *ap;
	char *obj;
	
	obj = parse_to(&what, '/', 0);
	init_match(player, obj, NOTYPE);
	match_everything(0);

	if ((thing = noisy_match_result()) < 0) {
		notify(player, "No match.");
	} else if (!controls(player, thing) && !Link_ok(thing)) {
		notify(player, NOPERM_MESSAGE);
	} else {
		if (!what || !*what) {
			ap = NULL;
		} else {
			ap = atr_str(what);
		}
		
		if (!ap) {
			attr = A_SEMAPHORE;
		} else {
			/* Do they have permission to set this attribute? */
			atr_pget_info(thing, ap->number, &aowner, &aflags);
			if (Set_attr(player, thing, ap, aflags)) {
				attr = ap->number;
			} else {
				notify_quiet(player, NOPERM_MESSAGE);
				return;
			}
		}

		if (count && *count)
			loccount = atoi(count);
		else
			loccount = 1;
		if (loccount > 0) {
			nfy_que(player, thing, attr, key, loccount);
			if (!(Quiet(player) || Quiet(thing))) {
				if (key == NFY_DRAIN)
					notify_quiet(player, "Drained.");
				else
					notify_quiet(player, "Notified.");
			}
		}
	}
}

/* ---------------------------------------------------------------------------
 * setup_que: Set up a queue entry.
 */

static BQUE *setup_que(player, cause, command, args, nargs, gargs)
dbref player, cause;
char *command, *args[];
int nargs;
GDATA *gargs;
{
	int a, tlen;
	BQUE *tmp;
	char *tptr;

	/* Can we run commands at all? */

	if (Halted(player))
		return NULL;

	/* make sure player can afford to do it */

	a = mudconf.waitcost;
	if (a && mudconf.machinecost && (Randomize(mudconf.machinecost) == 0))
		a++;
	if (!payfor(player, a)) {
		notify(Owner(player), "Not enough money to queue command.");
		return NULL;
	}
	/* Wizards and their objs may queue up to db_top+1 cmds. Players are
	 * limited to QUEUE_QUOTA. -mnp 
	 */

	a = QueueMax(Owner(player));
	if (a_Queue(Owner(player), 1) > a) {
		notify(Owner(player),
		    "Run away objects: too many commands queued.  Halted.");
		halt_que(Owner(player), NOTHING);

		/* halt also means no command execution allowed */
		s_Halted(player);
		return NULL;
	}
	/* We passed all the tests */

	/* Calculate the length of the save string */

	tlen = 0;
	if (command)
		tlen = strlen(command) + 1;
	if (nargs > NUM_ENV_VARS)
		nargs = NUM_ENV_VARS;
	for (a = 0; a < nargs; a++) {
		if (args[a])
			tlen += (strlen(args[a]) + 1);
	}
	if (gargs) {
		for (a = 0; a < gargs->q_alloc; a++) {
		    if (gargs->q_regs[a])
			tlen += gargs->q_lens[a] + 1;
		}
		for (a = 0; a < gargs->xr_alloc; a++) {
		    if (gargs->x_names[a] && gargs->x_regs[a]) {
			tlen += strlen(gargs->x_names[a]) + 
			        gargs->x_lens[a] + 2;
		    }
		}
	}

	/* Create the queue entry and load the save string */

	tmp = alloc_qentry("setup_que.qblock");
	if (!(tptr = tmp->text = (char *) XMALLOC(tlen, "setup_que"))) {
		free_qentry(tmp);
		return (BQUE *) NULL;
	}

	tmp->comm = NULL;
	for (a = 0; a < NUM_ENV_VARS; a++) {
		tmp->env[a] = NULL;
	}
	Alloc_RegData("setup_que", gargs, tmp->gdata);

	if (command) {
		strcpy(tptr, command);
		tmp->comm = tptr;
		tptr += (strlen(command) + 1);
	}
	for (a = 0; a < nargs; a++) {
		if (args[a]) {
			strcpy(tptr, args[a]);
			tmp->env[a] = tptr;
			tptr += (strlen(args[a]) + 1);
		}
	}
	if (gargs && gargs->q_alloc) {
	    for (a = 0; a < gargs->q_alloc; a++) {
		if (gargs->q_regs[a]) {
		    tmp->gdata->q_lens[a] = gargs->q_lens[a];
		    memcpy(tptr, gargs->q_regs[a], gargs->q_lens[a] + 1);
		    tmp->gdata->q_regs[a] = tptr;
		    tptr += gargs->q_lens[a] + 1;
		}
	    }
	}
	if (gargs && gargs->xr_alloc) {
	    for (a = 0; a < gargs->xr_alloc; a++) {
		if (gargs->x_names[a] && gargs->x_regs[a]) {
		    strcpy(tptr, gargs->x_names[a]);
		    tmp->gdata->x_names[a] = tptr;
		    tptr += strlen(gargs->x_names[a]) + 1;
		    tmp->gdata->x_lens[a] = gargs->x_lens[a];
		    memcpy(tptr, gargs->x_regs[a], gargs->x_lens[a] + 1);
		    tmp->gdata->x_regs[a] = tptr;
		    tptr += gargs->x_lens[a] + 1;
		}
	    }
	}

	/* Load the rest of the queue block */

	tmp->player = player;
	tmp->waittime = 0;
	tmp->next = NULL;
	tmp->sem = NOTHING;
	tmp->attr = 0;
	tmp->cause = cause;
	tmp->nargs = nargs;
	return tmp;
}

/* ---------------------------------------------------------------------------
 * wait_que: Add commands to the wait or semaphore queues.
 */

void wait_que(player, cause, wait, sem, attr, command, args, nargs, gargs)
dbref player, cause, sem;
int wait, nargs, attr;
char *command, *args[];
GDATA *gargs;
{
	BQUE *tmp, *point, *trail;

	if (mudconf.control_flags & CF_INTERP)
		tmp = setup_que(player, cause, command, args, nargs, gargs);
	else
		tmp = NULL;
	if (tmp == NULL) {
		return;
	}
	if (wait != 0)
		tmp->waittime = time(NULL) + wait;
	tmp->sem = sem;
	tmp->attr = attr;
	if (sem == NOTHING) {

		/* No semaphore, put on wait queue if wait value specified.
		 * Otherwise put on the normal queue. 
		 */

		if (wait <= 0) {
			give_que(tmp);
		} else {
			for (point = mudstate.qwait, trail = NULL;
			     point && point->waittime <= tmp->waittime;
			     point = point->next) {
				trail = point;
			}
			tmp->next = point;
			if (trail != NULL)
				trail->next = tmp;
			else
				mudstate.qwait = tmp;
		}
	} else {
		tmp->next = NULL;
		if (mudstate.qsemlast != NULL)
			mudstate.qsemlast->next = tmp;
		else
			mudstate.qsemfirst = tmp;
		mudstate.qsemlast = tmp;
	}
}

/* ---------------------------------------------------------------------------
 * do_wait: Command interface to wait_que
 */

void do_wait(player, cause, key, event, cmd, cargs, ncargs)
dbref player, cause;
int key, ncargs;
char *event, *cmd, *cargs[];
{
	dbref thing, aowner;
	int howlong, num, attr, aflags;
	char *what;
	ATTR *ap;
	

	/* If arg1 is all numeric, do simple (non-sem) timed wait. */

	if (is_number(event)) {
		howlong = atoi(event);
		wait_que(player, cause, howlong, NOTHING, 0, cmd,
			 cargs, ncargs, mudstate.rdata);
		return;
	}
	/* Semaphore wait with optional timeout */

	what = parse_to(&event, '/', 0);
	init_match(player, what, NOTYPE);
	match_everything(0);

	thing = noisy_match_result();
	if (!Good_obj(thing)) {
		notify(player, "No match.");
	} else if (!controls(player, thing) && !Link_ok(thing)) {
		notify(player, NOPERM_MESSAGE);
	} else {

		/* Get timeout, default 0 */

		if (event && *event && is_number(event)) {
			attr = A_SEMAPHORE;
			howlong = atoi(event);
		} else {
			attr = A_SEMAPHORE;
			howlong = 0;
		}

		if (event && *event && !is_number(event)) {
			ap = atr_str(event);
			if (!ap) {
				attr = mkattr(event);
				if (attr <= 0) {
					notify_quiet(player, "Invalid attribute.");
					return;
				}
				ap = atr_num(attr);
			}
			atr_pget_info(thing, ap->number, &aowner, &aflags);
			if (attr && Set_attr(player, thing, ap, aflags)) {
				attr = ap->number;
				howlong = 0;
			} else {
				notify_quiet(player, NOPERM_MESSAGE);
				return;
			}
		}
		
		num = add_to(player, thing, 1, attr);
		if (num <= 0) {

			/* thing over-notified, run the command immediately */
			thing = NOTHING;
			howlong = 0;
		}
		wait_que(player, cause, howlong, thing, attr, cmd,
			 cargs, ncargs, mudstate.rdata);
	}
}

/* ---------------------------------------------------------------------------
 * que_next: Return the time in seconds until the next command should be
 * run from the queue.
 */

int NDECL(que_next)
{
	int min, this;
	BQUE *point;

	/* If there are commands in the player queue, we want to run them
	 * immediately. 
	 */

	if (test_top())
		return 0;

	/* If there are commands in the object queue, we want to run them
	 * after a one-second pause. 
	 */

	if (mudstate.qlfirst != NULL)
		return 1;

	/* Walk the wait and semaphore queues, looking for the smallest
	 * wait value.  Return the smallest value - 1, because
	 * the command gets moved to the player queue when it has 
	 * 1 second to go.  
	 */

	min = 1000;
	for (point = mudstate.qwait; point; point = point->next) {
		this = point->waittime - mudstate.now;
		if (this <= 2)
			return 1;
		if (this < min)
			min = this;
	}

	for (point = mudstate.qsemfirst; point; point = point->next) {
		if (point->waittime == 0)	/* Skip if no timeout */
			continue;
		this = point->waittime - mudstate.now;
		if (this <= 2)
			return 1;
		if (this < min)
			min = this;
	}
	return min - 1;
}

/* ---------------------------------------------------------------------------
 * do_second: Check the wait and semaphore queues for commands to remove.
 */

void NDECL(do_second)
{
	BQUE *trail, *point, *next;
	char *cmdsave;

	/* move contents of low priority queue onto end of normal one this
	 * helps to keep objects from getting out of control since
	 * its affects on other objects happen only after one
	 * second  this should allow @halt to be type before
	 * getting blown away  by scrolling text 
	 */

	if ((mudconf.control_flags & CF_DEQUEUE) == 0)
		return;

	cmdsave = mudstate.debug_cmd;
	mudstate.debug_cmd = (char *)"< do_second >";

	if (mudstate.qlfirst) {
		if (mudstate.qlast)
			mudstate.qlast->next = mudstate.qlfirst;
		else
			mudstate.qfirst = mudstate.qlfirst;
		mudstate.qlast = mudstate.qllast;
		mudstate.qllast = mudstate.qlfirst = NULL;
	}
	/* Note: the point->waittime test would be 0 except the command is
	 * being put in the low priority queue to be done in one
	 * second anyway
	 */

	/* Do the wait queue */

	for (point = mudstate.qwait; point && point->waittime <= mudstate.now;
	     point = point->next) {
		mudstate.qwait = point->next;
		give_que(point);
	}

	/* Check the semaphore queue for expired timed-waits */

	for (point = mudstate.qsemfirst, trail = NULL; point; point = next) {
		if (point->waittime == 0) {
			next = (trail = point)->next;
			continue;	/* Skip if not timed-wait */
		}
		if (point->waittime <= mudstate.now) {
			if (trail != NULL)
				trail->next = next = point->next;
			else
				mudstate.qsemfirst = next = point->next;
			if (point == mudstate.qsemlast)
				mudstate.qsemlast = trail;
			add_to(point->player, point->sem, -1,
			       (point->attr ? point->attr : A_SEMAPHORE));
			point->sem = NOTHING;
			give_que(point);
		} else
			next = (trail = point)->next;
	}	
	mudstate.debug_cmd = cmdsave;
	return;
}

/* ---------------------------------------------------------------------------
 * do_top: Execute the command at the top of the queue
 */

int do_top(ncmds)
int ncmds;
{
    BQUE *tmp;
    dbref player;
    int count;
    char *cmdsave;

    if ((mudconf.control_flags & CF_DEQUEUE) == 0)
	return 0;

    cmdsave = mudstate.debug_cmd;
    mudstate.debug_cmd = (char *)"< do_top >";

    for (count = 0; count < ncmds; count++) {
	if (!test_top()) {
	    mudstate.debug_cmd = cmdsave;
	    Free_RegData(mudstate.rdata);
	    mudstate.rdata = NULL;
	    return count;
	}
	player = mudstate.qfirst->player;
	if ((player >= 0) && !Going(player)) {
	    giveto(player, mudconf.waitcost);
	    mudstate.curr_enactor = mudstate.qfirst->cause;
	    mudstate.curr_player = player;
	    a_Queue(Owner(player), -1);
	    mudstate.qfirst->player = NOTHING;
	    if (!Halted(player)) {

		/* Load scratch args */

		if (mudstate.qfirst->gdata) {
		    Free_RegData(mudstate.rdata);
		    Alloc_RegData("do_top", mudstate.qfirst->gdata,
				  mudstate.rdata);
		    Copy_RegData("do_top", mudstate.qfirst->gdata,
				 mudstate.rdata);
		} else {
		    Free_RegData(mudstate.rdata);
		    mudstate.rdata = NULL;
		}

		mudstate.cmd_invk_ctr = 0;

		process_cmdline(player, mudstate.qfirst->cause,
				mudstate.qfirst->comm,
				mudstate.qfirst->env,
				mudstate.qfirst->nargs, mudstate.qfirst);

	    }
	}

	if (mudstate.qfirst) {
	    tmp = mudstate.qfirst;
	    mudstate.qfirst = mudstate.qfirst->next;
	    Free_QData(tmp);
	    free_qentry(tmp);
	}
	if (!mudstate.qfirst)	/* gotta check this, as the value's changed */
	    mudstate.qlast = NULL;
    }

    Free_RegData(mudstate.rdata);
    mudstate.rdata = NULL;

    mudstate.debug_cmd = cmdsave;
    return count;
}

/* ---------------------------------------------------------------------------
 * do_ps: tell player what commands they have pending in the queue
 */

static void show_que(player, key, queue, qtot, qent, qdel,
		     player_targ, obj_targ, header)
dbref player, player_targ, obj_targ;
int key, *qtot, *qent, *qdel;
BQUE *queue;
const char *header;
{
	BQUE *tmp;
	char *bp, *bufp;
	int i;
	ATTR *ap;

	*qtot = 0;
	*qent = 0;
	*qdel = 0;
	for (tmp = queue; tmp; tmp = tmp->next) {
		(*qtot)++;
		if (que_want(tmp, player_targ, obj_targ)) {
			(*qent)++;
			if (key == PS_SUMM)
				continue;
			if (*qent == 1)
				notify(player,
				       tprintf("----- %s Queue -----",
					       header));
			bufp = unparse_object(player, tmp->player, 0);
			if ((tmp->waittime > 0) && (Good_obj(tmp->sem))) {
			    /* A minor shortcut. We can never timeout-wait
			     * on a non-Semaphore attribute.
			     */
			    notify(player,
				   tprintf("[#%d/%d] %s:%s",
					   tmp->sem,
					   tmp->waittime - mudstate.now,
					   bufp, tmp->comm));
			} else if (tmp->waittime > 0) {
				notify(player,
				       tprintf("[%d] %s:%s",
					       tmp->waittime - mudstate.now,
					       bufp, tmp->comm));
			} else if (Good_obj(tmp->sem)) {
			    if (tmp->attr == A_SEMAPHORE) {
				notify(player,
				       tprintf("[#%d] %s:%s", tmp->sem,
					       bufp, tmp->comm));
			    } else {
				ap = atr_num(tmp->attr);
				if (ap && ap->name) {
				    notify(player,
					   tprintf("[#%d/%s] %s:%s", tmp->sem,
						   ap->name,
						   bufp, tmp->comm));
				} else {
				    notify(player,
					   tprintf("[#%d] %s:%s", tmp->sem,
						   bufp, tmp->comm));
				}
			    }
			} else {
				notify(player,
				       tprintf("%s:%s", bufp, tmp->comm));
			}
			bp = bufp;
			if (key == PS_LONG) {
				for (i = 0; i < (tmp->nargs); i++) {
					if (tmp->env[i] != NULL) {
						safe_str((char *)"; Arg",
							 bufp, &bp);
						safe_chr(i + '0', bufp, &bp);
						safe_str((char *)"='",
							 bufp, &bp);
						safe_str(tmp->env[i],
							 bufp, &bp);
						safe_chr('\'', bufp, &bp);
					}
				}
				*bp = '\0';
				bp = unparse_object(player, tmp->cause, 0);
				notify(player,
				       tprintf("   Enactor: %s%s",
					       bp, bufp));
				free_lbuf(bp);
			}
			free_lbuf(bufp);
		} else if (tmp->player == NOTHING) {
			(*qdel)++;
		}
	}
	return;
}

void do_ps(player, cause, key, target)
dbref player, cause;
int key;
char *target;
{
	char *bufp;
	dbref player_targ, obj_targ;
	int pqent, pqtot, pqdel, oqent, oqtot, oqdel, wqent, wqtot, sqent,
	 sqtot, i;

	/* Figure out what to list the queue for */

	if ((key & PS_ALL) && !(See_Queue(player))) {
		notify(player, NOPERM_MESSAGE);
		return;
	}
	if (!target || !*target) {
		obj_targ = NOTHING;
		if (key & PS_ALL) {
			player_targ = NOTHING;
		} else {
			player_targ = Owner(player);
			if (Typeof(player) != TYPE_PLAYER)
				obj_targ = player;
		}
	} else {
		player_targ = Owner(player);
		if (See_Queue(player))
		    obj_targ = match_thing(player, target);
		else
		    obj_targ = match_controlled(player, target);
		if (!Good_obj(obj_targ))
			return;
		if (key & PS_ALL) {
			notify(player, "Can't specify a target and /all");
			return;
		}
		if (Typeof(obj_targ) == TYPE_PLAYER) {
			player_targ = obj_targ;
			obj_targ = NOTHING;
		}
	}
	key = key & ~PS_ALL;

	switch (key) {
	case PS_BRIEF:
	case PS_SUMM:
	case PS_LONG:
		break;
	default:
		notify(player, "Illegal combination of switches.");
		return;
	}

	/* Go do it */

	show_que(player, key, mudstate.qfirst, &pqtot, &pqent, &pqdel,
		 player_targ, obj_targ, "Player");
	show_que(player, key, mudstate.qlfirst, &oqtot, &oqent, &oqdel,
		 player_targ, obj_targ, "Object");
	show_que(player, key, mudstate.qwait, &wqtot, &wqent, &i,
		 player_targ, obj_targ, "Wait");
	show_que(player, key, mudstate.qsemfirst, &sqtot, &sqent, &i,
		 player_targ, obj_targ, "Semaphore");

	/* Display stats */

	bufp = alloc_mbuf("do_ps");
	if (See_Queue(player))
		sprintf(bufp, "Totals: Player...%d/%d[%ddel]  Object...%d/%d[%ddel]  Wait...%d/%d  Semaphore...%d/%d",
			pqent, pqtot, pqdel, oqent, oqtot, oqdel,
			wqent, wqtot, sqent, sqtot);
	else
		sprintf(bufp, "Totals: Player...%d/%d  Object...%d/%d  Wait...%d/%d  Semaphore...%d/%d",
		    pqent, pqtot, oqent, oqtot, wqent, wqtot, sqent, sqtot);
	notify(player, bufp);
	free_mbuf(bufp);
}

/* ---------------------------------------------------------------------------
 * do_queue: Queue management
 */

void do_queue(player, cause, key, arg)
dbref player, cause;
int key;
char *arg;
{
	BQUE *point;
	int i, ncmds, was_disabled;

	was_disabled = 0;
	if (key == QUEUE_KICK) {
		i = atoi(arg);
		if ((mudconf.control_flags & CF_DEQUEUE) == 0) {
			was_disabled = 1;
			mudconf.control_flags |= CF_DEQUEUE;
			notify(player, "Warning: automatic dequeueing is disabled.");
		}
		ncmds = do_top(i);
		if (was_disabled)
			mudconf.control_flags &= ~CF_DEQUEUE;
		if (!Quiet(player))
			notify(player,
			       tprintf("%d commands processed.", ncmds));
	} else if (key == QUEUE_WARP) {
		i = atoi(arg);
		if ((mudconf.control_flags & CF_DEQUEUE) == 0) {
			was_disabled = 1;
			mudconf.control_flags |= CF_DEQUEUE;
			notify(player, "Warning: automatic dequeueing is disabled.");
		}
		/* Handle the wait queue */

		for (point = mudstate.qwait; point; point = point->next) {
			point->waittime = -i;
		}

		/* Handle the semaphore queue */

		for (point = mudstate.qsemfirst; point; point = point->next) {
			if (point->waittime > 0) {
				point->waittime -= i;
				if (point->waittime <= 0)
					point->waittime = -1;
			}
		}

		do_second();
		if (was_disabled)
			mudconf.control_flags &= ~CF_DEQUEUE;
		if (Quiet(player))
			return;
		if (i > 0)
			notify(player,
			    tprintf("WaitQ timer advanced %d seconds.", i));
		else if (i < 0)
			notify(player,
			    tprintf("WaitQ timer set back %d seconds.", i));
		else
			notify(player,
			       "Object queue appended to player queue.");

	}
}