btmux-0.6-rc4/doc/
btmux-0.6-rc4/event/
btmux-0.6-rc4/game/
btmux-0.6-rc4/game/maps/
btmux-0.6-rc4/game/mechs/
btmux-0.6-rc4/game/text/help/
btmux-0.6-rc4/game/text/help/cat_faction/
btmux-0.6-rc4/game/text/help/cat_inform/
btmux-0.6-rc4/game/text/help/cat_misc/
btmux-0.6-rc4/game/text/help/cat_mux/
btmux-0.6-rc4/game/text/help/cat_mux/cat_commands/
btmux-0.6-rc4/game/text/help/cat_mux/cat_functions/
btmux-0.6-rc4/game/text/help/cat_templates/
btmux-0.6-rc4/game/text/wizhelp/
btmux-0.6-rc4/include/
btmux-0.6-rc4/misc/
btmux-0.6-rc4/python/
btmux-0.6-rc4/src/hcode/btech/
btmux-0.6-rc4/tree/
/*
 * cque.c -- commands and functions for manipulating the command queue 
 */

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

#include <signal.h>

#include "mudconf.h"
#include "config.h"
#include "db.h"
#include "htab.h"
#include "interface.h"
#include "match.h"
#include "externs.h"
#include "attrs.h"
#include "flags.h"
#include "powers.h"
#include "command.h"
#include "alloc.h"
#include "functions.h"
#include "cque.h"
#include "mmdb.h"

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

static rbtree obq = NULL;

static int objqe_compare(dbref left, dbref right, void *arg)
{
	return (right - left);
}

int cque_init()
{
	obq = rb_init((void *) objqe_compare, NULL);
	return 1;
};

static OBJQE *cque_find(dbref player)
{
	OBJQE *tmp = NULL;

	if(obq == NULL) {
		cque_init();
	}

	tmp = rb_find(obq, (void *) player);

	if(!tmp && Good_obj(player)) {
		tmp = malloc(sizeof(OBJQE));
		tmp->obj = player;
		tmp->cque = NULL;
		tmp->ctail = NULL;
		tmp->next = NULL;
		tmp->queued = 0;
		tmp->wait_que = NULL;
		tmp->pending_que = NULL;
		rb_insert(obq, (void *) player, tmp);
	}

	return tmp;
}

static BQUE *cque_peek(dbref player)
{
	OBJQE *tmp;
	tmp = cque_find(player);
	return tmp->cque;
}

static BQUE *cque_deque(dbref player)
{
	OBJQE *tmp;
	BQUE *cmd;

	tmp = cque_find(player);
	if(!tmp)
		return NULL;

	dassert(tmp);

	if(!tmp->cque)
		return NULL;

	cmd = tmp->cque;
	if(!cmd->next) {
		tmp->cque = tmp->ctail = NULL;
	} else {
		tmp->cque = cmd->next;
	}
	return cmd;
}

static void cque_enqueue(dbref player, BQUE * cmd)
{
    BQUE *point, *trail;
	OBJQE *current, *blocker;
	struct timeval tv;
    OBJQE *tmp;

    cmd->next = NULL;
    
    tv.tv_sec = cmd->waittime - mudstate.now;
    tv.tv_usec = 0;

    if(cmd->sem == NOTHING) {
        /*
         * No semaphore, put on wait queue if wait value specified.
         * Otherwise put on the normal queue. 
         */

        if(cmd->waittime <= mudstate.now) {
            cmd->waittime = 0;
            tmp = cque_find(player);

            dassert(tmp);

            if(!tmp->ctail) {
                tmp->cque = tmp->ctail = cmd;
                cmd->next = NULL;
            } else {
                tmp->ctail->next = cmd;
                tmp->ctail = cmd;
                tmp->ctail->next = NULL;
            }

            if(!tmp->queued) {
                if(!mudstate.qhead) {
                    mudstate.qhead = mudstate.qtail = tmp;
                    tmp->next = NULL;
                } else {
                    mudstate.qtail->next = tmp;
                    mudstate.qtail = tmp;
                    mudstate.qtail->next = NULL;
                }
                tmp->queued = 1;
            }
        } else {
            evtimer_add(&cmd->ev, &tv);
            for(point = mudstate.qwait, trail = NULL;
                    point && point->waittime <= cmd->waittime;
                    point = point->next) {
                trail = point;
            }
            cmd->next = point;
            if(trail != NULL)
                trail->next = cmd;
            else
                mudstate.qwait = cmd;
        }
    } else {
        cmd->next = NULL;
        if(mudstate.qsemlast != NULL)
            mudstate.qsemlast->next = cmd;
        else
            mudstate.qsemfirst = cmd;
        mudstate.qsemlast = cmd;
    }
}

static void wakeup_wait_que(int fd, short event, void *arg)
{
	BQUE *pending = (BQUE *) arg;
	BQUE *point, trail;

	if(mudstate.qwait == pending) {
		mudstate.qwait = pending->next;
	} else {
		for(point = mudstate.qwait; point; point = point->next) {
			if(point->next == pending) {
				point->next = point->next->next;
				break;
			}
		}
	}
    
    pending->waittime = 0;
	cque_enqueue(pending->player, pending);
}

static int dump_bqe(struct mmdb_t *mmdb, BQUE *bqe) {
    if(bqe == NULL) {
        mmdb_write_uint32(mmdb, 0);
        return 1;
    }
    mmdb_write_uint32(mmdb, 1);
    mmdb_write_uint32(mmdb, bqe->player);
    mmdb_write_uint32(mmdb, bqe->cause);
    mmdb_write_uint32(mmdb, bqe->sem);
    mmdb_write_uint32(mmdb, bqe->waittime-mudstate.now);
    mmdb_write_uint32(mmdb, bqe->attr);
    mmdb_write_string(mmdb, bqe->text);
    mmdb_write_string(mmdb, bqe->comm);
    mmdb_write_uint32(mmdb, NUM_ENV_VARS);
    for(int i = 0; i < NUM_ENV_VARS; i++) {
        mmdb_write_string(mmdb, bqe->env[i]);
    }
    mmdb_write_uint32(mmdb, NUM_ENV_VARS);
    for(int i = 0; i < NUM_ENV_VARS; i++) {
        mmdb_write_string(mmdb, bqe->scr[i]);
    }
    mmdb_write_uint32(mmdb, bqe->nargs);
    dump_bqe(mmdb, bqe->next);
    return 1;
}

static int dump_objqe(void *key, void *data, int depth, void *arg) {
    struct mmdb_t *mmdb = (struct mmdb_t *)arg;
    OBJQE *coq = (OBJQE *)data;
    mmdb_write_uint32(mmdb, coq->obj);
    dump_bqe(mmdb, coq->cque);
    return 1;
}
    

void cque_dump_restart(struct mmdb_t *mmdb) {
    if(obq == NULL) {
        cque_init();
    }
    mmdb_write_uint32(mmdb, rb_size(obq));
    if(rb_size(obq) > 0) {
        rb_walk(obq, WALK_INORDER, dump_objqe, mmdb);
    }
    dump_bqe(mmdb, mudstate.qwait);
    dump_bqe(mmdb, mudstate.qsemfirst);
}

static void load_bqe(struct mmdb_t *mmdb) {
    int exists, count;
    struct timeval tv;
    BQUE *tmp;
    exists = mmdb_read_uint32(mmdb);
    if(!exists)
        return;
    tmp = malloc(sizeof(BQUE));
    memset(tmp, 0, sizeof(BQUE));
    
    evtimer_set(&tmp->ev, wakeup_wait_que, tmp);

    tmp->player = mmdb_read_uint32(mmdb);
    tmp->cause = mmdb_read_uint32(mmdb);
    tmp->sem = mmdb_read_uint32(mmdb);
    tmp->waittime = mudstate.now + mmdb_read_uint32(mmdb);
    tmp->attr = mmdb_read_uint32(mmdb);
    tmp->text = mmdb_read_string(mmdb);
    tmp->comm = mmdb_read_string(mmdb);
    count = mmdb_read_uint32(mmdb);
    if(count != NUM_ENV_VARS)
        printk("brain damage, count(%d) != NUM_ENV_VARS(%d)", count, NUM_ENV_VARS);
    for(int i = 0; i < count; i++) {
        if(i < NUM_ENV_VARS)
            tmp->env[i] = mmdb_read_string(mmdb);
        else 
            free(mmdb_read_string(mmdb));
    }
    count = mmdb_read_uint32(mmdb);
    if(count != NUM_ENV_VARS)
        printk("brain damage, count(%d) != NUM_ENV_VARS(%d)", count, NUM_ENV_VARS);
    for(int i = 0; i < count; i++) {
        if(i < NUM_ENV_VARS)
            tmp->scr[i] = mmdb_read_string(mmdb);
        else 
            free(mmdb_read_string(mmdb));
    }
    tmp->nargs = mmdb_read_uint32(mmdb);
    cque_enqueue(tmp->player, tmp);
    load_bqe(mmdb);
}

static void load_objqe(struct mmdb_t *mmdb) {
    int object;
    OBJQE *coq;
    object = mmdb_read_uint32(mmdb);
    coq = cque_find(object);
    load_bqe(mmdb);
}
    
    
void cque_load_restart(struct mmdb_t *mmdb) {
    int count;
    count = mmdb_read_uint32(mmdb);
    for(int i = 0; i < count; i++) {
        load_objqe(mmdb);
    }
    load_bqe(mmdb); // wait q
    load_bqe(mmdb); // sem q
}

#if 0
void cque_dump_restart(FILE *f) {
    OBJQE *coq;
    BQUE *bqe;
    if(!mudstate.qhead) {
        fprintf(f, "%d\n", 0);
        return;
    }
    for(coq = mudstate.qhead; coq != NULL; coq = coq->next) {
        fprintf("%d\n", coq->obj);
        for(bqe = coq->cque; bqe != NULL; bqe = bqe->next) {
            fprintf(f, "1\n");
            fprintf(f, "%d\n%d\n%d\n", bqe->player, bqe->cause, bqe->sem);
            fprintf(f, "%d\n%d\n", bqe->waittime - mudstate.now, bqe->attr);
            fprintf(f, "");
	}
    }
#endif
    
/*
 * ---------------------------------------------------------------------------
 * * add_to: Adjust an object's queue or semaphore count.
 */

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

	num = atoi(atr_gotten = atr_get(player, attrnum, &aowner, &aflags));
	free_lbuf(atr_gotten);
	num += am;
	if(num)
		sprintf(buff, "%d", num);
	else
		*buff = '\0';
	atr_add_raw(player, attrnum, buff);
	return (num);
}

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

static int que_want(BQUE * entry, dbref ptarg, dbref otarg)
{
	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(dbref player, dbref object)
{
	BQUE *trail, *point, *next;
	OBJQE *pque;

	int numhalted;

	numhalted = 0;

	/* Player's que */
	// XXX: nuke queu

	pque = cque_find(player);
	if(pque && pque->cque) {
		while ((point = cque_deque(player)) != NULL) {
			free(point->text);
			point->text = NULL;
			free_qentry(point);
			point = NULL;
			numhalted++;
		}
	}
	pque = cque_find(object);
	if(pque && pque->cque) {
		while ((point = cque_deque(object)) != NULL) {
			free(point->text);
			point->text = NULL;
			free_qentry(point);
			point = NULL;
			numhalted++;
		}
	}

	/*
	 * Wait queue 
	 */

	for(point = mudstate.qwait, trail = NULL; point; point = next)
		if(que_want(point, player, object)) {
			numhalted++;
			if(trail)
				trail->next = next = point->next;
			else
				mudstate.qwait = next = point->next;
			if(evtimer_pending(&point->ev, NULL))
				evtimer_del(&point->ev);
			free(point->text);
			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(trail)
				trail->next = next = point->next;
			else
				mudstate.qsemfirst = next = point->next;
			if(point == mudstate.qsemlast)
				mudstate.qsemlast = trail;
			add_to(point->sem, -1, point->attr);
			free(point->text);
			free_qentry(point);
		} else
			next = (trail = point)->next;

	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(dbref player, dbref cause, int key, char *target)
{
	dbref player_targ, obj_targ;
	int numhalted;

	if((key & HALT_ALL) && !(Can_Halt(player))) {
		notify(player, "Permission denied.");
		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(obj_targ == NOTHING)
			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_printf(Owner(player), "%d queue entries removed.", numhalted);
}

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

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

	if(attr) {
		str = atr_get(sem, attr, &aowner, &aflags);
		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) {
                    point->sem = NOTHING;
                    point->waittime = 0;
					cque_enqueue(point->player, point);
				} else {
					giveto(point->player, mudconf.waitcost);
					a_Queue(Owner(point->player), -1);
					free(point->text);
					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(sem, -count, attr);
	else
		atr_clr(sem, attr);

	return num;
}

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

void do_notify(dbref player, dbref cause, int key, char *what, char *count)
{
	dbref thing, aowner;
	int loccount, attr = -1, 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, "Permission denied.");
	} 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, "Permission denied.");
				return;
			}
		}

		if(count && *count)
			loccount = atoi(count);
		else
			loccount = 1;
		if(loccount > 0) {
			nfy_que(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(dbref player, dbref cause, char *command, char *args[],
					   int nargs, char *sargs[])
{
	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(mudconf.machinecost && ((random() % 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(sargs) {
		for(a = 0; a < NUM_ENV_VARS; a++) {
			if(sargs[a])
				tlen += (strlen(sargs[a]) + 1);
		}
	}
	/*
	 * Create the qeue entry and load the save string 
	 */

	tmp = malloc(sizeof(BQUE));
    memset(tmp, 0, sizeof(BQUE));
    tmp->comm = NULL;
	for(a = 0; a < NUM_ENV_VARS; a++) {
		tmp->env[a] = NULL;
	}
	for(a = 0; a < MAX_GLOBAL_REGS; a++) {
		tmp->scr[a] = NULL;
	}

	tptr = tmp->text = (char *) malloc(tlen);
	if(command) {
		StringCopy(tptr, command);
		tmp->comm = tptr;
		tptr += (strlen(command) + 1);
	}
	for(a = 0; a < nargs; a++) {
		if(args[a]) {
			StringCopy(tptr, args[a]);
			tmp->env[a] = tptr;
			tptr += (strlen(args[a]) + 1);
		}
	}
	if(sargs) {
		for(a = 0; a < MAX_GLOBAL_REGS; a++) {
			if(sargs[a]) {
				StringCopy(tptr, sargs[a]);
				tmp->scr[a] = tptr;
				tptr += (strlen(sargs[a]) + 1);
			}
		}
	}
	/*
	 * Load the rest of the queue block 
	 */

	evtimer_set(&tmp->ev, wakeup_wait_que, tmp);

	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(dbref player, dbref cause, int wait, dbref sem, int attr,
			  char *command, char *args[], int nargs, char *sargs[])
{
	BQUE *cmd;
	if(mudconf.control_flags & CF_INTERP)
		cmd = setup_que(player, cause, command, args, nargs, sargs);
	else
		cmd = NULL;

	if(cmd == NULL) {
		return;
	}

    if(wait > 0) {
        cmd->waittime = mudstate.now + wait;
    } else {
        cmd->waittime = 0;
    }

    cmd->sem = sem;
    cmd->attr = attr;

    cque_enqueue(player, cmd);
}


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

void do_wait(dbref player, dbref cause, int key, char *event, char *cmd,
			 char *cargs[], int ncargs)
{
	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.global_regs);
		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, "Permission denied.");
	} 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, "Permission denied.");
				return;
			}
		}

		num = add_to(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.global_regs);
	}
}

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

void do_second(void)
{
	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 >";

	/*
	 * 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 anyways 
	 */

	/*
	 * 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->sem, -1, point->attr);
			point->sem = NOTHING;
            point->waittime = 0;
			printk("promoting, %d/%s", point->player, point->comm);
			cque_enqueue(point->player, 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(int ncmds)
{
	BQUE *tmp, *walk;
	OBJQE *current_object;
	dbref object, player, last_player;
	int count, i;
	char *command, *cp, *cmdsave;

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

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

	if(!mudstate.qhead)
		return 0;

	current_object = mudstate.qhead;
	count = 0;

	while (count < ncmds && mudstate.qhead) {
		if(!mudstate.qhead)
			break;

		object = mudstate.qhead->obj;
		tmp = cque_deque(object);

		if(!mudstate.qhead->cque) {
			mudstate.qhead->queued = 0;
			mudstate.qhead = mudstate.qhead->next;
			if(mudstate.qhead == NULL)
				mudstate.qtail = NULL;
		} else {
			mudstate.qtail->next = mudstate.qhead;
			mudstate.qtail = mudstate.qtail->next;
			mudstate.qhead = mudstate.qhead->next;
			mudstate.qtail->next = NULL;
		}
		if(!tmp)
			continue;

		dassert(tmp);
		count++;
		if((object >= 0) && !Going(object)) {
			giveto(object, mudconf.waitcost);
			mudstate.curr_enactor = tmp->cause;
			mudstate.curr_player = object;
			a_Queue(Owner(object), -1);
			if(!Halted(object)) {
				for(i = 0; i < MAX_GLOBAL_REGS; i++) {
					if(tmp->scr[i]) {
						StringCopy(mudstate.global_regs[i], tmp->scr[i]);
					} else {
						*mudstate.global_regs[i] = '\0';
					}
				}

				command = tmp->comm;

				if(command) {
					if(isPlayer(object) && Connected(object))
						choke_player(object);
					while (command) {
						cp = parse_to(&command, ';', 0);
						if(cp && *cp) {
							while (command && (*command == '|')) {
								command++;
								mudstate.inpipe = 1;
								mudstate.poutnew =
									alloc_lbuf("process_command.pipe");
								mudstate.poutbufc = mudstate.poutnew;
								mudstate.poutobj = object;
								process_command(object, tmp->cause, 0, cp,
												tmp->env, tmp->nargs);
								if(mudstate.pout) {
									free_lbuf(mudstate.pout);
									mudstate.pout = NULL;
								}

								*mudstate.poutbufc = '\0';
								mudstate.pout = mudstate.poutnew;
								cp = parse_to(&command, ';', 0);
							}
							mudstate.inpipe = 0;
							process_command(object, tmp->cause, 0,
											cp, tmp->env, tmp->nargs);
							if(mudstate.pout) {
								free_lbuf(mudstate.pout);
								mudstate.pout = NULL;
							}
						}
					}
					if(isPlayer(object) && Connected(object))
						release_player(object);
				}
			}
		}
		free(tmp->text);
		free_qentry(tmp);
	}

	for(i = 0; i < MAX_GLOBAL_REGS; i++)
		*mudstate.global_regs[i] = '\0';
	mudstate.debug_cmd = cmdsave;
	return count;
}

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

static void show_que(dbref player, int key, BQUE * queue, int *qent,
					 const char *header)
{
	BQUE *tmp;
	char *bp, *bufp;
	int i;

	for(tmp = queue; tmp; tmp = tmp->next) {
		(*qent)++;
		if(key == PS_SUMM)
			continue;
		if(*qent == 1)
			notify_printf(player, "----- %s Queue -----", header);

		bufp = unparse_object(player, tmp->player, 0);
		if((tmp->waittime > 0) && (Good_obj(tmp->sem)))
			notify_printf(player, "[#%d/%d]%s:%s", tmp->sem,
						  tmp->waittime-mudstate.now, bufp, tmp->comm);
		else if(tmp->waittime > 0)
			notify_printf(player, "[%d]%s:%s", tmp->waittime-mudstate.now,
						  bufp, tmp->comm);
		else if(Good_obj(tmp->sem))
			notify_printf(player, "[#%d]%s:%s", tmp->sem, bufp, tmp->comm);
		else
			notify_printf(player, "%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_printf(player, "   Enactor: %s%s", bp, bufp);
			free_lbuf(bp);
		}
		free_lbuf(bufp);
	}
	return;
}

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

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

	if((key & PS_ALL) && !(See_Queue(player))) {
		notify(player, "Permission denied.");
		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);
		obj_targ = match_controlled(player, target);
		if(obj_targ == NOTHING)
			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 
	 */
	pqtot = 0;
	if(player_targ == NOTHING) {
		objq = mudstate.qhead;
		while (objq && (objq = objq->next) != NULL) {
			pqent = 0;
			show_que(player, key, objq->cque, &pqent, "PLAYAH");
			pqtot += pqent;
		}
	} else {
		pqent = 0;
		objq = cque_find(player_targ);
		if(objq) {
			show_que(player, key, objq->cque, &pqent, "PLAYAH");
		}
	}

	wqent = 0;
	sqent = 0;
	wqtot = 0;
	sqtot = 0;
	show_que(player, key, mudstate.qwait, &wqent, "Wait");
	show_que(player, key, mudstate.qsemfirst, &sqent, "Semaphore");

	/*
	 * Display stats 
	 */

	if(See_Queue(player))
		notify_printf(player,
					  "Totals: Player...%d/%d  Wait...%d/%d  Semaphore...%d/%d",
					  pqent, pqtot, wqent, wqtot, sqent, sqtot);
	else
		notify_printf(player,
					  "Totals: Player...%d/%d  Wait...%d/%d  Semaphore...%d/%d",
					  pqent, pqtot, wqent, wqtot, sqent, sqtot);
}

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

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

	dprintk("WTF?");
	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_printf(player, "%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 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_printf(player, "WaitQ timer advanced %d seconds.", i);
		else if(i < 0)
			notify_printf(player, "WaitQ timer set back %d seconds.", i);
		else
			notify(player, "Object queue appended to player queue.");

	}
}