fbmuck-6.01/contrib/jresolver/
fbmuck-6.01/contrib/jresolver/org/
fbmuck-6.01/contrib/jresolver/org/fuzzball/
fbmuck-6.01/docs/devel/
fbmuck-6.01/game/
fbmuck-6.01/game/logs/
fbmuck-6.01/game/muf/
fbmuck-6.01/scripts/
fbmuck-6.01/src_docs/
/*
  Timequeue event code by Foxen
*/

#include "copyright.h"
#include "config.h"
#include "params.h"
#include "match.h"

#include "db.h"
#include "tune.h"
#include "mpi.h"
#include "props.h"
#include "interface.h"
#include "externs.h"
#include "mufevent.h"
#include "interp.h"

#include <stdio.h>
#include <sys/types.h>
#include <time.h>


#define TQ_MUF_TYP 0
#define TQ_MPI_TYP 1

#define TQ_MUF_QUEUE    0x0
#define TQ_MUF_DELAY    0x1
#define TQ_MUF_LISTEN   0x2
#define TQ_MUF_READ     0x3
#define TQ_MUF_TREAD    0x4
#define TQ_MUF_TIMER    0x5

#define TQ_MPI_QUEUE    0x0
#define TQ_MPI_DELAY    0x1

#define TQ_MPI_SUBMASK  0x7
#define TQ_MPI_LISTEN   0x8
#define TQ_MPI_OMESG   0x10
#define TQ_MPI_BLESSED 0x20


/*
 * Events types and data:
 *  What, typ, sub, when, user, where, trig, prog, frame, str1, cmdstr, str3
 *  qmpi   1    0   1     user  loc    trig  --    --     mpi   cmd     arg
 *  dmpi   1    1   when  user  loc    trig  --    --     mpi   cmd     arg
 *  lmpi   1    8   1     spkr  loc    lstnr --    --     mpi   cmd     heard
 *  oqmpi  1   16   1     user  loc    trig  --    --     mpi   cmd     arg
 *  odmpi  1   17   when  user  loc    trig  --    --     mpi   cmd     arg
 *  olmpi  1   24   1     spkr  loc    lstnr --    --     mpi   cmd     heard
 *  qmuf   0    0   0     user  loc    trig  prog  --     stk_s cmd@    --
 *  lmuf   0    1   0     spkr  loc    lstnr prog  --     heard cmd@    --
 *  dmuf   0    2   when  user  loc    trig  prog  frame  mode  --      --
 *  rmuf   0    3   -1    user  loc    trig  prog  frame  mode  --      --
 *  trmuf  0    4   when  user  loc    trig  prog  frame  mode  --      --
 *  tevmuf 0    5   when  user  loc    trig  prog  frame  mode  event   --
 */


typedef struct timenode {
	struct timenode *next;
	int typ;
	int subtyp;
	time_t when;
	int descr;
	dbref called_prog;
	char *called_data;
	char *command;
	char *str3;
	dbref uid;
	dbref loc;
	dbref trig;
	struct frame *fr;
	struct inst *where;
	int eventnum;
} *timequeue;

static timequeue tqhead = NULL;

void prog_clean(struct frame *fr);

static int
valid_objref(dbref obj)
{
	return (!((obj >= db_top)
			  || (obj >= 0 && Typeof(obj) == TYPE_GARBAGE)
			  || (obj < 0)));
}


extern int top_pid;
int process_count = 0;

static timequeue free_timenode_list = NULL;
static int free_timenode_count = 0;

static timequeue
alloc_timenode(int typ, int subtyp, time_t mytime, int descr, dbref player,
			   dbref loc, dbref trig, dbref program, struct frame *fr,
			   const char *strdata, const char *strcmd, const char *str3, timequeue nextone)
{
	timequeue ptr;

	if (free_timenode_list) {
		ptr = free_timenode_list;
		free_timenode_list = ptr->next;
		free_timenode_count--;
	} else {
		ptr = (timequeue) malloc(sizeof(struct timenode));
	}
	ptr->typ = typ;
	ptr->subtyp = subtyp;
	ptr->when = mytime;
	ptr->uid = player;
	ptr->loc = loc;
	ptr->trig = trig;
	ptr->descr = descr;
	ptr->fr = fr;
	ptr->called_prog = program;
	ptr->called_data = (char *) string_dup((char *) strdata);
	ptr->command = alloc_string(strcmd);
	ptr->str3 = alloc_string(str3);
	ptr->eventnum = (fr) ? fr->pid : top_pid++;
	ptr->next = nextone;
	return (ptr);
}

static void
free_timenode(timequeue ptr)
{
	if (ptr->command)
		free(ptr->command);
	if (ptr->called_data)
		free(ptr->called_data);
	if (ptr->str3)
		free(ptr->str3);
	if (ptr->fr) {
		if (ptr->typ != TQ_MUF_TYP || ptr->subtyp != TQ_MUF_TIMER) {
			if (ptr->fr->multitask != BACKGROUND)
				PLAYER_SET_BLOCK(ptr->uid, 0);
			prog_clean(ptr->fr);
		}
		if (ptr->typ == TQ_MUF_TYP && (ptr->subtyp == TQ_MUF_READ ||
									   ptr->subtyp == TQ_MUF_TREAD)) {
			FLAGS(ptr->uid) &= ~INTERACTIVE;
			FLAGS(ptr->uid) &= ~READMODE;
			notify_nolisten(ptr->uid, "Data input aborted.  The command you were using was killed.", 1);
		}
	}
	if (free_timenode_count < tp_free_frames_pool) {
		ptr->next = free_timenode_list;
		free_timenode_list = ptr;
		free_timenode_count++;
	} else {
		free(ptr);
	}
}

void
purge_timenode_free_pool()
{
	timequeue ptr = free_timenode_list;
	timequeue nxt = NULL;

	while (ptr) {
		nxt = ptr->next;
		free((void*)ptr);
		ptr = nxt;
	}
	free_timenode_count = 0;
	free_timenode_list = NULL;
}

int
control_process(dbref player, int pid)
{
	timequeue tmp, ptr = tqhead;

	tmp = ptr;
	while ((ptr) && (pid != ptr->eventnum)) {
		tmp = ptr;
		ptr = ptr->next;
	}

	if (!ptr) {
		return muf_event_controls(player, pid);
	}

	if (!controls(player, ptr->called_prog) && !controls(player, ptr->trig)) {
		return 0;
	}
	return 1;
}


int
add_event(int event_typ, int subtyp, int dtime, int descr, dbref player, dbref loc,
		  dbref trig, dbref program, struct frame *fr,
		  const char *strdata, const char *strcmd, const char *str3)
{
	timequeue ptr = tqhead;
	timequeue lastevent = NULL;
	time_t rtime = time((time_t *) NULL) + (time_t) dtime;
	int mypids = 0;

	for (ptr = tqhead, mypids = 0; ptr; ptr = ptr->next) {
		if (ptr->uid == player)
			mypids++;
		lastevent = ptr;
	}

	if (event_typ == TQ_MUF_TYP && subtyp == TQ_MUF_READ) {
		process_count++;
		if (lastevent) {
			lastevent->next = alloc_timenode(event_typ, subtyp, rtime, descr,
											 player, loc, trig, program, fr,
											 strdata, strcmd, str3, NULL);
			return (lastevent->next->eventnum);
		} else {
			tqhead = alloc_timenode(event_typ, subtyp, rtime, descr,
									player, loc, trig, program, fr,
									strdata, strcmd, str3, NULL);
			return (tqhead->eventnum);
		}
	}
	if (!(event_typ == TQ_MUF_TYP && subtyp == TQ_MUF_TREAD)) {
		if (process_count > tp_max_process_limit ||
			(mypids > tp_max_plyr_processes && !Wizard(OWNER(player)))) {
			if (fr) {
				if (fr->multitask != BACKGROUND)
					PLAYER_SET_BLOCK(player, 0);
				prog_clean(fr);
			}
			notify_nolisten(player, "Event killed.  Timequeue table full.", 1);
			return 0;
		}
	}
	process_count++;

	if (!tqhead) {
		tqhead = alloc_timenode(event_typ, subtyp, rtime, descr, player, loc, trig,
								program, fr, strdata, strcmd, str3, NULL);
		return (tqhead->eventnum);
	}
	if (rtime < tqhead->when || (tqhead->typ == TQ_MUF_TYP && tqhead->subtyp == TQ_MUF_READ)
			) {
		tqhead = alloc_timenode(event_typ, subtyp, rtime, descr, player, loc, trig,
								program, fr, strdata, strcmd, str3, tqhead);
		return (tqhead->eventnum);
	}

	ptr = tqhead;
	while ((ptr->next) && (rtime >= ptr->next->when) &&
		   !(ptr->next->typ == TQ_MUF_TYP && ptr->next->subtyp == TQ_MUF_READ)
			) {
		ptr = ptr->next;
	}

	ptr->next = alloc_timenode(event_typ, subtyp, rtime, descr, player, loc, trig,
							   program, fr, strdata, strcmd, str3, ptr->next);
	return (ptr->next->eventnum);
}


int
add_mpi_event(int delay, int descr, dbref player, dbref loc, dbref trig,
			  const char *mpi, const char *cmdstr, const char *argstr,
			  int listen_p, int omesg_p, int blessed_p)
{
	int subtyp = TQ_MPI_QUEUE;

	if (delay >= 1) {
		subtyp = TQ_MPI_DELAY;
	}
	if (blessed_p) {
		subtyp |= TQ_MPI_BLESSED;
	}
	if (listen_p) {
		subtyp |= TQ_MPI_LISTEN;
	}
	if (omesg_p) {
		subtyp |= TQ_MPI_OMESG;
	}
	return add_event(TQ_MPI_TYP, subtyp, delay, descr, player, loc, trig,
					 NOTHING, NULL, mpi, cmdstr, argstr);
}


int
add_muf_queue_event(int descr, dbref player, dbref loc, dbref trig, dbref prog,
					const char *argstr, const char *cmdstr, int listen_p)
{
	return add_event(TQ_MUF_TYP, (listen_p ? TQ_MUF_LISTEN : TQ_MUF_QUEUE), 0,
					 descr, player, loc, trig, prog, NULL, argstr, cmdstr, NULL);
}


int
add_muf_delayq_event(int delay, int descr, dbref player, dbref loc, dbref trig,
					 dbref prog, const char *argstr, const char *cmdstr, int listen_p)
{
	return add_event(TQ_MUF_TYP, (listen_p ? TQ_MUF_LISTEN : TQ_MUF_QUEUE),
					 delay, descr, player, loc, trig, prog, NULL, argstr, cmdstr, NULL);
}


int
add_muf_read_event(int descr, dbref player, dbref prog, struct frame *fr)
{
	FLAGS(player) |= (INTERACTIVE | READMODE);
	return add_event(TQ_MUF_TYP, TQ_MUF_READ, -1, descr, player, -1, fr->trig,
					 prog, fr, "READ", NULL, NULL);
}

int
add_muf_tread_event(int descr, dbref player, dbref prog, struct frame *fr, int delay)
{
	FLAGS(player) |= (INTERACTIVE | READMODE);
	return add_event(TQ_MUF_TYP, TQ_MUF_TREAD, delay, descr, player, -1, fr->trig,
					 prog, fr, "READ", NULL, NULL);
}

int
add_muf_timer_event(int descr, dbref player, dbref prog, struct frame *fr, int delay, char *id)
{
	char buf[40];
	snprintf(buf, sizeof(buf), "TIMER.%.32s", id);
	fr->timercount++;
	return add_event(TQ_MUF_TYP, TQ_MUF_TIMER, delay, descr, player, -1, fr->trig,
					 prog, fr, buf, NULL, NULL);
}

int
add_muf_delay_event(int delay, int descr, dbref player, dbref loc, dbref trig, dbref prog,
					struct frame *fr, const char *mode)
{
	return add_event(TQ_MUF_TYP, TQ_MUF_DELAY, delay, descr, player, loc, trig,
					 prog, fr, mode, NULL, NULL);
}



int
read_event_notify(int descr, dbref player, const char* cmd)
{
	timequeue ptr;

	if (muf_event_read_notify(descr, player, cmd)) {
		return 1;
	}

	ptr = tqhead;
	while (ptr) {
		if (ptr->uid == player) {
			if (ptr->fr && ptr->fr->multitask != BACKGROUND) {
				if (*cmd || ptr->fr->wantsblanks) {
					struct inst temp;

					temp.type = PROG_INTEGER;
					temp.data.number = descr;
					muf_event_add(ptr->fr, "READ", &temp, 1);
					return 1;
				}
			}
		}
		ptr = ptr->next;
	}
	return 0;
}


void
handle_read_event(int descr, dbref player, const char *command)
{
	struct frame *fr;
	timequeue ptr, lastevent;
	int flag, typ, nothing_flag;
	int oldflags;
	dbref prog;

	nothing_flag = 0;
	if (command == NULL) {
		nothing_flag = 1;
	}
	oldflags = FLAGS(player);
	FLAGS(player) &= ~(INTERACTIVE | READMODE);

	ptr = tqhead;
	lastevent = NULL;
	while (ptr) {
		if (ptr->typ == TQ_MUF_TYP && (ptr->subtyp == TQ_MUF_READ ||
									   ptr->subtyp == TQ_MUF_TREAD) && ptr->uid == player) {
			break;
		}
		lastevent = ptr;
		ptr = ptr->next;
	}

	/*
	 * When execution gets to here, either ptr will point to the
	 * READ event for the player, or else ptr will be NULL.
	 */

	if (ptr) {
		/* remember our program, and our execution frame. */
		fr = ptr->fr;
		if (!fr->brkpt.debugging || fr->brkpt.isread) {
			if (!fr->wantsblanks && command && !*command) {
				FLAGS(player) = oldflags;
				return;
			}
		}
		typ = ptr->subtyp;
		prog = ptr->called_prog;
		if (command) {
			/* remove the READ timequeue node from the timequeue */
			process_count--;
			if (lastevent) {
				lastevent->next = ptr->next;
			} else {
				tqhead = ptr->next;
			}
		}
		/* remember next timequeue node, to check for more READs later */
		lastevent = ptr;
		ptr = ptr->next;

		/* Make SURE not to let the program frame get freed.  We need it. */
		lastevent->fr = NULL;
		if (command) {
			/*
			 * Free up the READ timequeue node
			 * we just removed from the queue.
			 */
			free_timenode(lastevent);
		}

		if (fr->brkpt.debugging && !fr->brkpt.isread) {

			/* We're in the MUF debugger!  Call it with the input line. */
			if (command) {
				if (muf_debugger(descr, player, prog, command, fr)) {
					/* MUF Debugger exited.  Free up the program frame & exit */
					prog_clean(fr);
					return;
				}
			} else {
				if (muf_debugger(descr, player, prog, "", fr)) {
					/* MUF Debugger exited.  Free up the program frame & exit */
					prog_clean(fr);
					return;
				}
			}
		} else {
			/* This is a MUF READ event. */
			if (command && !string_compare(command, BREAK_COMMAND)) {

				/* Whoops!  The user typed @Q.  Free the frame and exit. */
				prog_clean(fr);
				return;
			}

			if ((fr->argument.top >= STACK_SIZE) ||
				(nothing_flag && fr->argument.top >= STACK_SIZE - 1)) {

				/*
				 * Uh oh! That MUF program's stack is full!
				 * Print an error, free the frame, and exit.
				 */
				notify_nolisten(player, "Program stack overflow.", 1);
				prog_clean(fr);
				return;
			}

			/*
			 * Everything looks okay.  Lets stuff the input line
			 * on the program's argument stack as a string item.
			 */
			fr->argument.st[fr->argument.top].type = PROG_STRING;
			fr->argument.st[fr->argument.top++].data.string =
					alloc_prog_string(command ? command : "");
			if (typ == TQ_MUF_TREAD) {
				if (nothing_flag) {
					fr->argument.st[fr->argument.top].type = PROG_INTEGER;
					fr->argument.st[fr->argument.top++].data.number = 0;
				} else {
					fr->argument.st[fr->argument.top].type = PROG_INTEGER;
					fr->argument.st[fr->argument.top++].data.number = 1;
				}
			}
		}

		/*
		 * When using the MUF Debugger, the debugger will set the
		 * INTERACTIVE bit on the user, if it does NOT want the MUF
		 * program to resume executing.
		 */
		flag = (FLAGS(player) & INTERACTIVE);

		if (!flag && fr) {
			interp_loop(player, prog, fr, 0);
			/* WORK: if more input is pending, send the READ mufevent again. */
			/* WORK: if no input is pending, clear READ mufevent from all of this player's programs. */
		}

		/*
		 * Check for any other READ events for this player.
		 * If there are any, set the READ related flags.
		 */
		while (ptr) {
			if (ptr->typ == TQ_MUF_TYP && (ptr->subtyp == TQ_MUF_READ ||
										   ptr->subtyp == TQ_MUF_TREAD)) {
				if (ptr->uid == player) {
					FLAGS(player) |= (INTERACTIVE | READMODE);
				}
			}
			ptr = ptr->next;
		}
	}
}


void
next_timequeue_event(void)
{
	struct frame *tmpfr;
	dbref tmpcp;
	int tmpbl, tmpfg;
	timequeue lastevent, event;
	int maxruns = 0;
	int forced_pid = 0;
	time_t rtime;

	time(&rtime);

	lastevent = tqhead;
	while ((lastevent) && (rtime >= lastevent->when) && (maxruns < 10)) {
		lastevent = lastevent->next;
		maxruns++;
	}

	while (tqhead && (tqhead != lastevent) && (maxruns--)) {
		if (tqhead->typ == TQ_MUF_TYP && tqhead->subtyp == TQ_MUF_READ) {
			break;
		}
		event = tqhead;
		tqhead = tqhead->next;
		process_count--;
		forced_pid = event->eventnum;
		event->eventnum = 0;
		if (event->typ == TQ_MPI_TYP) {
			char cbuf[BUFFER_LEN];
			int ival;

			strcpy(match_args, event->str3 ? event->str3 : "");
			strcpy(match_cmdname, event->command ? event->command : "");
			ival = (event->subtyp & TQ_MPI_OMESG) ? MPI_ISPUBLIC : MPI_ISPRIVATE;
			if (event->subtyp & TQ_MPI_BLESSED) {
				ival |= MPI_ISBLESSED;
			}
			if (event->subtyp & TQ_MPI_LISTEN) {
				ival |= MPI_ISLISTENER;
				do_parse_mesg(event->descr, event->uid, event->trig, event->called_data,
							  "(MPIlisten)", cbuf, ival);
			} else if ((event->subtyp & TQ_MPI_SUBMASK) == TQ_MPI_DELAY) {
				do_parse_mesg(event->descr, event->uid, event->trig, event->called_data,
							  "(MPIdelay)", cbuf, ival);
			} else {
				do_parse_mesg(event->descr, event->uid, event->trig, event->called_data,
							  "(MPIqueue)", cbuf, ival);
			}
			if (*cbuf) {
				if (!(event->subtyp & TQ_MPI_OMESG)) {
					notify_filtered(event->uid, event->uid, cbuf, 1);
				} else {
					char bbuf[BUFFER_LEN];
					dbref plyr;

					snprintf(bbuf, sizeof(bbuf), ">> %.4000s %.*s",
							NAME(event->uid),
							(int)(4000 - strlen(NAME(event->uid))),
							pronoun_substitute(event->descr, event->uid, cbuf));
					plyr = DBFETCH(event->loc)->contents;
					for (; plyr != NOTHING; plyr = DBFETCH(plyr)->next) {
						if (Typeof(plyr) == TYPE_PLAYER && plyr != event->uid)
							notify_filtered(event->uid, plyr, bbuf, 0);
					}
				}
			}
		} else if (event->typ == TQ_MUF_TYP) {
			if (Typeof(event->called_prog) == TYPE_PROGRAM) {
				if (event->subtyp == TQ_MUF_DELAY) {
					tmpcp = PLAYER_CURR_PROG(event->uid);
					tmpbl = PLAYER_BLOCK(event->uid);
					tmpfg = (event->fr->multitask != BACKGROUND);
					interp_loop(event->uid, event->called_prog, event->fr, 0);
					if (!tmpfg) {
						PLAYER_SET_BLOCK(event->uid, tmpbl);
					}
				} else if (event->subtyp == TQ_MUF_TIMER) {
					struct inst temp;

					temp.type = PROG_INTEGER;
					temp.data.number = event->when;
					event->fr->timercount--;
					muf_event_add(event->fr, event->called_data, &temp, 0);
				} else if (event->subtyp == TQ_MUF_TREAD) {
					handle_read_event(event->descr, event->uid, NULL);
				} else {
					strcpy(match_args, event->called_data ? event->called_data : "");
					strcpy(match_cmdname, event->command ? event->command : "");
					tmpfr = interp(event->descr, event->uid, event->loc, event->called_prog,
								   event->trig, BACKGROUND, STD_HARDUID, forced_pid);
					if (tmpfr) {
						interp_loop(event->uid, event->called_prog, tmpfr, 0);
					}
				}
			}
		}
		event->fr = NULL;
		free_timenode(event);
	}
}


int
in_timequeue(int pid)
{
	timequeue ptr = tqhead;

	if (!pid)
		return 0;
	if (muf_event_pid_frame(pid))
		return 1;
	if (!tqhead)
		return 0;
	while ((ptr) && (ptr->eventnum != pid))
		ptr = ptr->next;
	if (ptr)
		return 1;
	return 0;
}


struct frame*
timequeue_pid_frame(int pid)
{
	struct frame *out = NULL;
	timequeue ptr = tqhead;

	if (!pid)
		return NULL;
	out = muf_event_pid_frame(pid);
	if (out != NULL)
		return out;

	if (!tqhead)
		return NULL;
	while ((ptr) && (ptr->eventnum != pid))
		ptr = ptr->next;
	if (ptr)
		return ptr->fr;
	return NULL;
}


long
next_event_time(void)
{
	time_t rtime = time((time_t *) NULL);

	if (tqhead) {
		if (tqhead->when == -1) {
			return (-1L);
		} else if (rtime >= tqhead->when) {
			return (0L);
		} else {
			return ((long) (tqhead->when - rtime));
		}
	}
	return (-1L);
}

/* Checks the MUF timequeue for address references on the stack or */
/* dbref references on the callstack */
static int
has_refs(dbref program, timequeue ptr)
{
	int loop;

	if (ptr->typ != TQ_MUF_TYP || !(ptr->fr) ||
		Typeof(program) != TYPE_PROGRAM || !(PROGRAM_INSTANCES(program)))
		return 0;

	for (loop = 1; loop < ptr->fr->caller.top; loop++) {
		if (ptr->fr->caller.st[loop] == program)
			return 1;
	}

	for (loop = 0; loop < ptr->fr->argument.top; loop++) {
		if (ptr->fr->argument.st[loop].type == PROG_ADD &&
			ptr->fr->argument.st[loop].data.addr->progref == program)
			return 1;
	}

	return 0;
}


extern char *time_format_2(long dt);

void
list_events(dbref player)
{
	char buf[BUFFER_LEN];
	char pidstr[128];
	char duestr[128];
	char runstr[128];
	char inststr[128];
	char cpustr[128];
	char progstr[128];
	char prognamestr[128];
	int count = 0;
	timequeue ptr = tqhead;
	time_t rtime = time((time_t *) NULL);
	time_t etime;
	double pcnt;
	const char* strfmt = "%10s %4s %4s %6s %4s %7s %-10.10s %-12s %.512s";

	(void)snprintf(buf, sizeof(buf), strfmt, "PID", "Next", "Run", "KInst", "%CPU", "Prog#", "ProgName", "Player", "");
	notify_nolisten(player, buf, 1);

	while (ptr) {
		snprintf(pidstr, sizeof(pidstr), "%d", ptr->eventnum);
		strcpy(duestr, ((ptr->when - rtime) > 0) ?
				time_format_2((long) (ptr->when - rtime)) : "Due");
		strcpy(runstr, ptr->fr ?
				time_format_2((long) (rtime - ptr->fr->started)): "0s");
		snprintf(inststr, sizeof(inststr), "%d", ptr->fr? (ptr->fr->instcnt / 1000) : 0);

		if (ptr->fr) {
			etime = rtime - ptr->fr->started;
			if (etime > 0) {
				pcnt = ptr->fr->totaltime.tv_sec;
				pcnt += ptr->fr->totaltime.tv_usec / 1000000;
				pcnt = pcnt * 100 / etime;
				if (pcnt > 99.9) {
					pcnt = 99.9;
				}
			} else {
				pcnt = 0.0;
			}
		} else {
			pcnt = 0.0;
		}
		snprintf(cpustr, sizeof(cpustr), "%4.1f", pcnt);
		if (ptr->fr) {
			snprintf(progstr, sizeof(progstr), "#%d", ptr->fr->caller.st[1]);
			snprintf(prognamestr, sizeof(prognamestr), "%s", NAME(ptr->fr->caller.st[1]));
		} else if (ptr->typ == TQ_MPI_TYP) {
			snprintf(progstr, sizeof(progstr), "#%d", ptr->trig);
			snprintf(prognamestr, sizeof(prognamestr), "%s", "");
		} else {
			snprintf(progstr, sizeof(progstr), "#%d", ptr->called_prog);
			snprintf(prognamestr, sizeof(prognamestr), "%s", NAME(ptr->called_prog));
		}

		if (ptr->typ == TQ_MUF_TYP && ptr->subtyp == TQ_MUF_READ) {
			strcpy(duestr, "--");
		} else if (ptr->typ == TQ_MUF_TYP && ptr->subtyp == TQ_MUF_TIMER) {
			snprintf(pidstr, sizeof(pidstr), "(%d)", ptr->eventnum);
		} else if (ptr->typ == TQ_MPI_TYP) {
			strcpy(runstr, "--");
			strcpy(inststr, "MPI");
			strcpy(cpustr, "--");
		}
		(void) snprintf(buf, sizeof(buf), strfmt, pidstr, duestr, runstr, inststr,
					                cpustr, progstr, prognamestr, NAME(ptr->uid), 
									ptr->called_data? ptr->called_data : "");
		if (Wizard(OWNER(player)) || ptr->uid == player) {
			notify_nolisten(player, buf, 1);
		} else if (ptr->called_prog != NOTHING && OWNER(ptr->called_prog) == OWNER(player)) {
			notify_nolisten(player, buf, 1);
		}
		ptr = ptr->next;
		count++;
	}
	count += muf_event_list(player, strfmt);
	snprintf(buf, sizeof(buf), "%d events.", count);
	notify_nolisten(player, buf, 1);
}

stk_array *
get_pids(dbref ref)
{
	struct inst temp1, temp2;
	stk_array  *nw;
	int count = 0;

	timequeue ptr = tqhead;
	nw = new_array_packed(0);
	while (ptr) {
		if (((ptr->typ != TQ_MPI_TYP) ? (ptr->called_prog == ref) : (ptr->trig == ref)) ||
			(ptr->uid == ref) || (ref < 0) ) {
			temp2.type = PROG_INTEGER;
			temp2.data.number = ptr->eventnum;
			temp1.type = PROG_INTEGER;
			temp1.data.number = count++;
			array_setitem(&nw, &temp1, &temp2);
			CLEAR(&temp1);
			CLEAR(&temp2);
		}
		ptr = ptr->next;
	}
	nw = get_mufevent_pids(nw, ref);
	return nw;
}

stk_array *
get_pidinfo(int pid)
{
	struct inst temp1, temp2;
	stk_array  *nw;
	time_t      rtime = time(NULL);
	time_t      etime = 0;
	double      pcnt  = 0.0;

	timequeue ptr = tqhead;
	nw = new_array_dictionary();
	while (ptr) {
		if (ptr->eventnum == pid) {
			if (ptr->typ != TQ_MUF_TYP || ptr->subtyp != TQ_MUF_TIMER) {
				break;
			}
		}
		ptr = ptr->next;
	}
	if (ptr && (ptr->eventnum == pid) &&
			(ptr->typ != TQ_MUF_TYP || ptr->subtyp != TQ_MUF_TIMER)) {
		if (ptr->fr) {
			etime = rtime - ptr->fr->started;
			if (etime > 0) {
				pcnt = ptr->fr->totaltime.tv_sec;
				pcnt += ptr->fr->totaltime.tv_usec / 1000000;
				pcnt = pcnt * 100 / etime;
				if (pcnt > 100.0) {
					pcnt = 100.0;
				}
			} else {
				pcnt = 0.0;
			}
		}
		temp1.type = PROG_STRING;
		temp1.data.string = alloc_prog_string("PID");
		temp2.type = PROG_INTEGER;
		temp2.data.number = ptr->eventnum;
		array_setitem(&nw, &temp1, &temp2);
		CLEAR(&temp1);
		CLEAR(&temp2);
		temp1.type = PROG_STRING;
		temp1.data.string = alloc_prog_string("CALLED_PROG");
		temp2.type = PROG_OBJECT;
		temp2.data.objref = ptr->called_prog;
		array_setitem(&nw, &temp1, &temp2);
		CLEAR(&temp1);
		CLEAR(&temp2);
		temp1.type = PROG_STRING;
		temp1.data.string = alloc_prog_string("TRIG");
		temp2.type = PROG_OBJECT;
		temp2.data.objref = ptr->trig;
		array_setitem(&nw, &temp1, &temp2);
		CLEAR(&temp1);
		CLEAR(&temp2);
		temp1.type = PROG_STRING;
		temp1.data.string = alloc_prog_string("PLAYER");
		temp2.type = PROG_OBJECT;
		temp2.data.objref = ptr->uid;
		array_setitem(&nw, &temp1, &temp2);
		CLEAR(&temp1);
		CLEAR(&temp2);
		temp1.type = PROG_STRING;
		temp1.data.string = alloc_prog_string("CALLED_DATA");
		temp2.type = PROG_STRING;
		temp2.data.string = alloc_prog_string(ptr->called_data);
		array_setitem(&nw, &temp1, &temp2);
		CLEAR(&temp1);
		CLEAR(&temp2);
		temp1.type = PROG_STRING;
		temp1.data.string = alloc_prog_string("INSTCNT");
		temp2.type = PROG_INTEGER;
		temp2.data.number = ptr->fr->instcnt;
		array_setitem(&nw, &temp1, &temp2);
		CLEAR(&temp1);
		CLEAR(&temp2);
		temp1.type = PROG_STRING;
		temp1.data.string = alloc_prog_string("DESCR");
		temp2.type = PROG_INTEGER;
		temp2.data.number = ptr->descr;
		array_setitem(&nw, &temp1, &temp2);
		CLEAR(&temp1);
		CLEAR(&temp2);
		temp1.type = PROG_STRING;
		temp1.data.string = alloc_prog_string("CPU");
		temp2.type = PROG_FLOAT;
		temp2.data.fnumber = pcnt;
		array_setitem(&nw, &temp1, &temp2);
		CLEAR(&temp1);
		CLEAR(&temp2);
		temp1.type = PROG_STRING;
		temp1.data.string = alloc_prog_string("NEXTRUN");
		temp2.type = PROG_INTEGER;
		temp2.data.number = (int) ptr->when;
		array_setitem(&nw, &temp1, &temp2);
		CLEAR(&temp1);
		CLEAR(&temp2);
		temp1.type = PROG_STRING;
		temp1.data.string = alloc_prog_string("STARTED");
		temp2.type = PROG_INTEGER;
		temp2.data.number = (int) ptr->fr->started;
		array_setitem(&nw, &temp1, &temp2);
		CLEAR(&temp1);
		CLEAR(&temp2);
		temp1.type = PROG_STRING;
		temp1.data.string = alloc_prog_string("TYPE");
		temp2.type = PROG_STRING;
		temp2.data.string = 	(ptr->typ == TQ_MUF_TYP) ? alloc_prog_string("MUF") :
						(ptr->typ == TQ_MPI_TYP) ? alloc_prog_string("MPI") : alloc_prog_string("UNK") ;
		array_setitem(&nw, &temp1, &temp2);
		CLEAR(&temp1);
		CLEAR(&temp2);
		temp1.type = PROG_STRING;
		temp1.data.string = alloc_prog_string("SUBTYPE");
		temp2.type = PROG_STRING;
		if (ptr->typ == TQ_MUF_TYP) {
			temp2.data.string = (ptr->subtyp == TQ_MUF_READ) ? alloc_prog_string("READ") :
							(ptr->subtyp == TQ_MUF_TREAD) ? alloc_prog_string("TREAD") :
							(ptr->subtyp == TQ_MUF_QUEUE) ? alloc_prog_string("QUEUE") :
							(ptr->subtyp == TQ_MUF_LISTEN) ? alloc_prog_string("LISTEN") :
							(ptr->subtyp == TQ_MUF_TIMER) ? alloc_prog_string("TIMER") :
							(ptr->subtyp == TQ_MUF_DELAY) ? alloc_prog_string("DELAY") :
							alloc_prog_string("");
		} else if (ptr->typ == TQ_MPI_TYP) {
			int subtyp = (ptr->subtyp & TQ_MPI_SUBMASK);
			temp2.data.string = (subtyp == TQ_MPI_QUEUE) ? alloc_prog_string("QUEUE") :
							(subtyp == TQ_MPI_DELAY) ? alloc_prog_string("DELAY") :
							alloc_prog_string("");
		} else {
			temp2.data.string = alloc_prog_string("");
		}
		array_setitem(&nw, &temp1, &temp2);
		CLEAR(&temp1);
		CLEAR(&temp2);
	} else {
		nw = get_mufevent_pidinfo(nw, pid);
	}
	return nw;
}

/*
 * killmode values:
 *     0: kill all matching processes, MUF or MPI
 *     1: kill all matching MUF processes
 *     2: kill all matching foreground MUF processes
 */
int
dequeue_prog(dbref program, int killmode)
{
	int count = 0;
	timequeue tmp, ptr;

	while (tqhead) {
		if (tqhead->called_prog != program && !has_refs(program, tqhead) && tqhead->uid != program) {
			break;
		}
		if (killmode == 2) {
			if (tqhead->fr && tqhead->fr->multitask == BACKGROUND) {
				break;
			}
		} else if (killmode == 1) {
			if (!tqhead->fr) {
				break;
			}
		}
		ptr = tqhead;
		tqhead = tqhead->next;
		free_timenode(ptr);
		process_count--;
		count++;
	}

	if (tqhead) {
		for (tmp = tqhead, ptr = tqhead->next; ptr; tmp = ptr, ptr = ptr->next) {
			if (ptr->called_prog != program && !has_refs(program, ptr) && ptr->uid != program) {
				continue;
			}
			if (killmode == 2) {
				if (ptr->fr && ptr->fr->multitask == BACKGROUND) {
					continue;
				}
			} else if (killmode == 1) {
				if (!ptr->fr) {
					continue;
				}
			}
			tmp->next = ptr->next;
			free_timenode(ptr);
			process_count--;
			count++;
			ptr = tmp;
		}
	}
	count += muf_event_dequeue(program, killmode);
	for (ptr = tqhead; ptr; ptr = ptr->next) {
		if (ptr->typ == TQ_MUF_TYP && (ptr->subtyp == TQ_MUF_READ ||
									   ptr->subtyp == TQ_MUF_TREAD)) {
			FLAGS(ptr->uid) |= (INTERACTIVE | READMODE);
		}
	}
	return (count);
}


int
dequeue_process(int pid)
{
	timequeue tmp, ptr;
	int deqflag = 0;

	if (!pid)
		return 0;

	if (muf_event_dequeue_pid(pid)) {
		process_count--;
		deqflag = 1;
	}

	tmp = ptr = tqhead;
	while (ptr) {
		if (pid == ptr->eventnum) {
			if (tmp == ptr) {
				tqhead = tmp = tmp->next;
				free_timenode(ptr);
				ptr = tmp;
			} else {
				tmp->next = ptr->next;
				free_timenode(ptr);
				ptr = tmp->next;
			}
			process_count--;
			deqflag = 1;
		} else {
			tmp = ptr;
			ptr = ptr->next;
		}
	}

	if (!deqflag) {
		return 0;
	}

	for (ptr = tqhead; ptr; ptr = ptr->next) {
		if (ptr->typ == TQ_MUF_TYP && (ptr->subtyp == TQ_MUF_READ ||
									ptr->subtyp == TQ_MUF_TREAD)) {
			FLAGS(ptr->uid) |= (INTERACTIVE | READMODE);
		}
	}
	return 1;
}


int
dequeue_timers(int pid, char* id)
{
	char buf[40];
	timequeue tmp, ptr;
	int deqflag = 0;

	if (!pid)
		return 0;

	if (id)
		snprintf(buf, sizeof(buf), "TIMER.%.30s", id);

	tmp = ptr = tqhead;
	while (ptr) {
		if (pid == ptr->eventnum &&
			ptr->typ == TQ_MUF_TYP && ptr->subtyp == TQ_MUF_TIMER &&
			(!id || !strcmp(ptr->called_data, buf)))
		{
			if (tmp == ptr) {
				tqhead = tmp = tmp->next;
				ptr->fr->timercount--;
				ptr->fr = NULL;
				free_timenode(ptr);
				ptr = tmp;
			} else {
				tmp->next = ptr->next;
				ptr->fr->timercount--;
				ptr->fr = NULL;
				free_timenode(ptr);
				ptr = tmp->next;
			}
			process_count--;
			deqflag = 1;
		} else {
			tmp = ptr;
			ptr = ptr->next;
		}
	}

	return deqflag;
}


void
do_dequeue(int descr, dbref player, const char *arg1)
{
	char buf[BUFFER_LEN];
	int count;
	dbref match;
	struct match_data md;
	timequeue tmp, ptr = tqhead;


	if (*arg1 == '\0') {
		notify_nolisten(player, "What event do you want to dequeue?", 1);
	} else {
		if (!string_compare(arg1, "all")) {
			if (!Wizard(OWNER(player))) {
				notify_nolisten(player, "Permission denied", 1);
				return;
			}
			while (ptr) {
				tmp = ptr;
				tqhead = ptr = ptr->next;
				free_timenode(tmp);
				process_count--;
			}
			tqhead = NULL;
			muf_event_dequeue(NOTHING, 0);
			notify_nolisten(player, "Time queue cleared.", 1);
		} else {
			if (!number(arg1)) {
				init_match(descr, player, arg1, NOTYPE, &md);
				match_absolute(&md);
				match_everything(&md);

				match = noisy_match_result(&md);
				if (match == NOTHING) {
					notify_nolisten(player, "I don't know what you want to dequeue!", 1);
					return;
				}
				if (!valid_objref(match)) {
					notify_nolisten(player, "I don't recognize that object.", 1);
					return;
				}
				if ((!Wizard(OWNER(player))) && (OWNER(match) != OWNER(player))) {
					notify_nolisten(player, "Permission denied.", 1);
					return;
				}
				count = dequeue_prog(match, 0);
				if (!count) {
					notify_nolisten(player, "That program wasn't in the time queue.", 1);
					return;
				}
				if (count > 1) {
					snprintf(buf, sizeof(buf), "%d processes dequeued.", count);
				} else {
					snprintf(buf, sizeof(buf), "Process dequeued.");
				}
				notify_nolisten(player, buf, 1);
			} else {
				if ((count = atoi(arg1))) {
					if (!(control_process(player, count))) {
						notify_nolisten(player, "Permission denied.", 1);
						return;
					}
					if (!(dequeue_process(count))) {
						notify_nolisten(player, "No such process!", 1);
						return;
					}
					process_count--;
					notify_nolisten(player, "Process dequeued.", 1);
				} else {
					notify_nolisten(player, "What process do you want to dequeue?", 1);
				}
			}
		}
	}
	return;
}


int
scan_instances(dbref program)
{
	timequeue tq = tqhead;
	int i = 0, loop;

	while (tq) {
		if (tq->typ == TQ_MUF_TYP && tq->fr) {
			if (tq->called_prog == program) {
				i++;
			}
			for (loop = 1; loop < tq->fr->caller.top; loop++) {
				if (tq->fr->caller.st[loop] == program)
					i++;
			}
			for (loop = 0; loop < tq->fr->argument.top; loop++) {
				if (tq->fr->argument.st[loop].type == PROG_ADD &&
					tq->fr->argument.st[loop].data.addr->progref == program)
					i++;
			}
		}
		tq = tq->next;
	}
	return i;
}


static int propq_level = 0;
void
propqueue(int descr, dbref player, dbref where, dbref trigger, dbref what, dbref xclude,
		  const char *propname, const char *toparg, int mlev, int mt)
{
	const char *tmpchar;
	const char *pname;
	dbref the_prog;
	char buf[BUFFER_LEN];
	char exbuf[BUFFER_LEN];

	the_prog = NOTHING;
	tmpchar = NULL;

	/* queue up program referred to by the given property */
	if (((the_prog = get_property_dbref(what, propname)) != NOTHING) ||
		(tmpchar = get_property_class(what, propname))) {
#ifdef COMPRESS
		if (tmpchar)
			tmpchar = uncompress(tmpchar);
#endif
		if ((tmpchar && *tmpchar) || the_prog != NOTHING) {
			if (tmpchar) {
				if (*tmpchar == '&') {
					the_prog = AMBIGUOUS;
				} else if (*tmpchar == NUMBER_TOKEN && number(tmpchar + 1)) {
					the_prog = (dbref) atoi(++tmpchar);
				} else if (*tmpchar == REGISTERED_TOKEN) {
					the_prog = find_registered_obj(what, tmpchar);
				} else if (number(tmpchar)) {
					the_prog = (dbref) atoi(tmpchar);
				} else {
					the_prog = NOTHING;
				}
			} else {
				if (the_prog == AMBIGUOUS)
					the_prog = NOTHING;
			}
			if (the_prog != AMBIGUOUS) {
				if (the_prog < 0 || the_prog >= db_top) {
					the_prog = NOTHING;
				} else if (Typeof(the_prog) != TYPE_PROGRAM) {
					the_prog = NOTHING;
				} else if ((OWNER(the_prog) != OWNER(player)) && !(FLAGS(the_prog) & LINK_OK)) {
					the_prog = NOTHING;
				} else if (MLevel(the_prog) < mlev) {
					the_prog = NOTHING;
				} else if (MLevel(OWNER(the_prog)) < mlev) {
					the_prog = NOTHING;
				} else if (the_prog == xclude) {
					the_prog = NOTHING;
				}
			}
			if (propq_level < 8) {
				propq_level++;
				if (the_prog == AMBIGUOUS) {
					char cbuf[BUFFER_LEN];
					int ival;

					strcpy(match_args, "");
					strcpy(match_cmdname, toparg);
					ival = (mt == 0) ? MPI_ISPUBLIC : MPI_ISPRIVATE;
					if (Prop_Blessed(what, propname))
						ival |= MPI_ISBLESSED;
					do_parse_mesg(descr, player, what, tmpchar + 1, "(MPIqueue)", cbuf, ival);
					if (*cbuf) {
						if (mt) {
							notify_filtered(player, player, cbuf, 1);
						} else {
							char bbuf[BUFFER_LEN];
							dbref plyr;

							snprintf(bbuf, sizeof(bbuf), ">> %.4000s",
									pronoun_substitute(descr, player, cbuf));
							plyr = DBFETCH(where)->contents;
							while (plyr != NOTHING) {
								if (Typeof(plyr) == TYPE_PLAYER && plyr != player)
									notify_filtered(player, plyr, bbuf, 0);
								plyr = DBFETCH(plyr)->next;
							}
						}
					}
				} else if (the_prog != NOTHING) {
					struct frame *tmpfr;

					strcpy(match_args, toparg ? toparg : "");
					strcpy(match_cmdname, "Queued event.");
					tmpfr = interp(descr, player, where, the_prog, trigger,
								   BACKGROUND, STD_HARDUID, 0);
					if (tmpfr) {
						interp_loop(player, the_prog, tmpfr, 0);
					}
				}
				propq_level--;
			} else {
				notify_nolisten(player, "Propqueue stopped to prevent infinite loop.", 1);
			}
		}
	}
	strcpyn(buf, sizeof(buf), propname);
	if (is_propdir(what, buf)) {
		strcatn(buf, sizeof(buf), "/");
		while ((pname = next_prop_name(what, exbuf, sizeof(exbuf), buf))) {
			strcpy(buf, pname);
			propqueue(descr, player, where, trigger, what, xclude, buf, toparg, mlev, mt);
		}
	}
}


void
envpropqueue(int descr, dbref player, dbref where, dbref trigger, dbref what, dbref xclude,
			 const char *propname, const char *toparg, int mlev, int mt)
{
	while (what != NOTHING) {
		propqueue(descr, player, where, trigger, what, xclude, propname, toparg, mlev, mt);
		what = getparent(what);
	}
}


void
listenqueue(int descr, dbref player, dbref where, dbref trigger, dbref what, dbref xclude,
			const char *propname, const char *toparg, int mlev, int mt, int mpi_p)
{
	const char *tmpchar;
	const char *pname, *sep, *ptr;
	dbref the_prog = NOTHING;
	char buf[BUFFER_LEN];
	char exbuf[BUFFER_LEN];
	char *ptr2;

	if (!(FLAGS(what) & LISTENER) && !(FLAGS(OWNER(what)) & ZOMBIE))
		return;

	the_prog = NOTHING;
	tmpchar = NULL;

	/* queue up program referred to by the given property */
	if (((the_prog = get_property_dbref(what, propname)) != NOTHING) ||
		(tmpchar = get_property_class(what, propname))) {

		if (tmpchar) {
#ifdef COMPRESS
			tmpchar = uncompress(tmpchar);
#endif
			sep = tmpchar;
			while (*sep) {
				if (*sep == '\\') {
					sep++;
				} else if (*sep == '=') {
					break;
				}
				if (*sep)
					sep++;
			}
			if (*sep == '=') {
				for (ptr = tmpchar, ptr2 = buf; ptr < sep; *ptr2++ = *ptr++) ;
				*ptr2 = '\0';
				strcpy(exbuf, toparg);
				if (!equalstr(buf, exbuf)) {
					tmpchar = NULL;
				} else {
					tmpchar = ++sep;
				}
			}
		}

		if ((tmpchar && *tmpchar) || the_prog != NOTHING) {
			if (tmpchar) {
				if (*tmpchar == '&') {
					the_prog = AMBIGUOUS;
				} else if (*tmpchar == NUMBER_TOKEN && number(tmpchar + 1)) {
					the_prog = (dbref) atoi(++tmpchar);
				} else if (*tmpchar == REGISTERED_TOKEN) {
					the_prog = find_registered_obj(what, tmpchar);
				} else if (number(tmpchar)) {
					the_prog = (dbref) atoi(tmpchar);
				} else {
					the_prog = NOTHING;
				}
			} else {
				if (the_prog == AMBIGUOUS)
					the_prog = NOTHING;
			}
			if (the_prog != AMBIGUOUS) {
				if (the_prog < 0 || the_prog >= db_top) {
					the_prog = NOTHING;
				} else if (Typeof(the_prog) != TYPE_PROGRAM) {
					the_prog = NOTHING;
				} else if (OWNER(the_prog) != OWNER(player) && !(FLAGS(the_prog) & LINK_OK)) {
					the_prog = NOTHING;
				} else if (MLevel(the_prog) < mlev) {
					the_prog = NOTHING;
				} else if (MLevel(OWNER(the_prog)) < mlev) {
					the_prog = NOTHING;
				} else if (the_prog == xclude) {
					the_prog = NOTHING;
				}
			}
			if (the_prog == AMBIGUOUS) {
				if (mpi_p) {
					add_mpi_event(1, descr, player, where, trigger, tmpchar + 1,
								  (mt ? "Listen" : "Olisten"), toparg, 1, (mt == 0),
								  Prop_Blessed(what, propname));
				}
			} else if (the_prog != NOTHING) {
				add_muf_queue_event(descr, player, where, trigger, the_prog, toparg,
									"(_Listen)", 1);
			}
		}
	}
	strcpyn(buf, sizeof(buf), propname);
	if (is_propdir(what, buf)) {
		strcatn(buf, sizeof(buf), "/");
		while ((pname = next_prop_name(what, exbuf, sizeof(exbuf), buf))) {
			*buf = '\0';
			strcatn(buf, sizeof(buf), pname);
			listenqueue(descr, player, where, trigger, what, xclude, buf,
						toparg, mlev, mt, mpi_p);
		}
	}
}