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/
/*
 * game.c 
 */

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

#include <sys/stat.h>
#include <signal.h>
#include <event.h>
#include <regex.h>

#include "mudconf.h"
#include "config.h"
#include "file_c.h"
#include "db.h"
#include "interface.h"
#include "match.h"
#include "externs.h"
#include "flags.h"
#include "powers.h"
#include "attrs.h"
#include "alloc.h"
#include "vattr.h"
#include "commac.h"
#ifdef SQL_SUPPORT
#include "sqlchild.h"
#endif

#ifndef NEXT
#endif

#ifdef HAVE_IEEEFP_H
#include <ieeefp.h>
#endif
#ifdef HAVE_SYS_UCONTEXT_H
#include <sys/ucontext.h>
#endif

#define NSUBEXP 10

extern void init_attrtab(void);
extern void init_cmdtab(void);
extern void init_mactab(void);
extern void init_chantab(void);
extern void cf_init(void);
extern void pcache_init(void);
extern int cf_read(char *fn);
extern void init_functab(void);
extern void close_sockets(int emergency, char *message);
extern void init_version(void);
extern void init_logout_cmdtab(void);
extern void init_timer(void);
extern void raw_notify(dbref, const char *);
extern void do_second(void);
extern void do_dbck(dbref, dbref, int);

#ifdef ARBITRARY_LOGFILES
void logcache_init();
void logcache_destruct();
#endif

#ifdef HUDINFO_SUPPORT
extern void init_hudinfo(void);
#endif

void fork_and_dump(int);
void dump_database(void);
void do_dump_optimize(dbref, dbref, int);
void pcache_sync(void);
void dump_database_internal(int);
static void init_rlimit(void);

int reserved;

extern int corrupt;

/*
 * used to allocate storage for temporary stuff, cleared before command
 * execution
 */

void do_dump(dbref player, dbref cause, int key)
{
	notify(player, "Dumping...");

	/*
	 * DUMP_OPTIMIZE takes advantage of a feature of GDBM to compress  
	 * unused space in the database, and will not be very useful
	 * except sparingly, perhaps done every month or so. 
	 */

	if(key & DUMP_OPTIMIZE)
		do_dump_optimize(player, cause, key);
	else
		fork_and_dump(key);
}

void do_dump_optimize(dbref player, dbref cause, int key)
{
	raw_notify(player, "Database is memory based.");
}

/**
 * print out stuff into error file 
 */
void report(void)
{
	STARTLOG(LOG_BUGS, "BUG", "INFO") {
		log_text((char *) "Command: '");
		log_text(mudstate.debug_cmd);
		log_text((char *) "'");
		ENDLOG;
	} if(Good_obj(mudstate.curr_player)) {
		STARTLOG(LOG_BUGS, "BUG", "INFO") {
			log_text((char *) "Player: ");
			log_name_and_loc(mudstate.curr_player);
			if((mudstate.curr_enactor != mudstate.curr_player) &&
			   Good_obj(mudstate.curr_enactor)) {
				log_text((char *) " Enactor: ");
				log_name_and_loc(mudstate.curr_enactor);
			}
			ENDLOG;
	}}
}

/*
 * Load a regular expression match and insert it into
 * registers.
 */
int regexp_match(char *pattern, char *str, char *args[], int nargs)
{
	regex_t re;
	int got_match;
	regmatch_t pmatch[NSUBEXP];
	int i, len;

	/*
	 * Load the regexp pattern. This allocates memory which must be
	 * later freed. A free() of the regexp does free all structures
	 * under it.
	 */

	if(regcomp(&re, pattern, REG_EXTENDED) != 0) {
		/*
		 * This is a matching error. We have an error message in
		 * regexp_errbuf that we can ignore, since we're doing
		 * command-matching.
		 */
		return 0;
	}

	/* 
	 * Now we try to match the pattern. The relevant fields will
	 * automatically be filled in by this.
	 */
	got_match = (regexec(&re, str, NSUBEXP, pmatch, 0) == 0);
	if(!got_match) {
		regfree(&re);
		return 0;
	}

	/*
	 * Now we fill in our args vector. Note that in regexp matching,
	 * 0 is the entire string matched, and the parenthesized strings
	 * go from 1 to 9. We DO PRESERVE THIS PARADIGM, for consistency
	 * with other languages.
	 */

	for(i = 0; i < nargs; i++) {
		args[i] = NULL;
	}

	/* Convenient: nargs and NSUBEXP are the same.
	 * We are also guaranteed that our buffer is going to be LBUF_SIZE
	 * so we can copy without fear.
	 */

	for(i = 0; (i < NSUBEXP) && (pmatch[i].rm_so != -1) && (pmatch[i].rm_eo != -1); i++) {
		len = pmatch[i].rm_eo - pmatch[i].rm_so;
		args[i] = alloc_lbuf("regexp_match");
        memset(args[i], 0, LBUF_SIZE);
		strncpy(args[i], str + pmatch[i].rm_so, len);
		args[i][len] = '\0';	/* strncpy() does not null-terminate */
	}

	regfree(&re);
	return 1;
}

/**
 * Check attribute list for wild card matches and queue them.
 */
static int atr_match1(dbref thing, dbref parent, dbref player, char type,
					  char *str, int check_exclude, int hash_insert)
{
	dbref aowner;
	int match, attr, aflags, i;
	char buff[LBUF_SIZE], *s, *as;
	char *args[10];
	ATTR *ap;

    memset(args, 0, sizeof(args));
    
	/*
	 * See if we can do it.  Silently fail if we can't. 
	 */

	if(!could_doit(player, parent, A_LUSE))
		return -1;

	match = 0;
	for(attr = atr_head(parent, &as); attr; attr = atr_next(&as)) {
		ap = atr_num(attr);

		/*
		 * Never check NOPROG attributes. 
		 */

		if(!ap || (ap->flags & AF_NOPROG))
			continue;

		/*
		 * If we aren't the bottom level check if we saw this attr *
		 * * * * before.  Also exclude it if the attribute type is *
		 * * PRIVATE. 
		 */

		if(check_exclude && ((ap->flags & AF_PRIVATE) ||
							 nhashfind(ap->number, &mudstate.parent_htab))) {
			continue;
		}
		atr_get_str(buff, parent, attr, &aowner, &aflags);

		/*
		 * Skip if private and on a parent 
		 */

		if(check_exclude && (aflags & AF_PRIVATE)) {
			continue;
		}
		/*
		 * If we aren't the top level remember this attr so we * * *
		 * exclude * it from now on. 
		 */

		if(hash_insert)
			nhashadd(ap->number, (int *) &attr, &mudstate.parent_htab);

		/*
		 * Check for the leadin character after excluding the attrib
		 * * * * * This lets non-command attribs on the child block * 
		 * *  * commands * on the parent. 
		 */

		if((buff[0] != type) || (aflags & AF_NOPROG))
			continue;

		/*
		 * decode it: search for first un escaped : 
		 */

		for(s = buff + 1; *s && (*s != ':'); s++);
		if(!*s)
			continue;
		*s++ = 0;
		if(((aflags & AF_REGEXP) && regexp_match(buff + 1, str, args, 10))
		   || wild(buff + 1, str, args, 10)) {
			match = 1;
			wait_que(thing, player, 0, NOTHING, 0, s, args, 10,
					 mudstate.global_regs);
			for(i = 0; i < 10; i++) {
				if(args[i])
					free_lbuf(args[i]);
			}
		}
	}
	return (match);
}

int atr_match(dbref thing, dbref player, char type, char *str,
			  int check_parents)
{
	int match, lev, result, exclude, insert;
	dbref parent;

	/*
	 * If thing is halted, don't check anything 
	 */

	if(Halted(thing))
		return 0;

	/*
	 * If not checking parents, just check the thing 
	 */

	match = 0;
	if(!check_parents)
		return atr_match1(thing, thing, player, type, str, 0, 0);

	/*
	 * Check parents, ignoring halted objects 
	 */

	exclude = 0;
	insert = 1;
	nhashflush(&mudstate.parent_htab, 0);
	ITER_PARENTS(thing, parent, lev) {
		if(!Good_obj(Parent(parent)))
			insert = 0;
		result =
			atr_match1(thing, parent, player, type, str, exclude, insert);
		if(result > 0) {
			match = 1;
		} else if(result < 0) {
			return match;
		}
		exclude = 1;
	}

	return match;
}

/**
 * Notifies the object #target of the message msg, and
 * optionally notify the contents, neighbors, and location also.
 */
int check_filter(dbref object, dbref player, int filter, const char *msg)
{
	int aflags;
	dbref aowner;
	char *buf, *nbuf, *cp, *dp, *str;

	buf = atr_pget(object, filter, &aowner, &aflags);
	if(!*buf) {
		free_lbuf(buf);
		return (1);
	}
	nbuf = dp = alloc_lbuf("check_filter");
	str = buf;
	exec(nbuf, &dp, 0, object, player, EV_FIGNORE | EV_EVAL | EV_TOP, &str,
		 (char **) NULL, 0);
	*dp = '\0';
	dp = nbuf;
	free_lbuf(buf);
	do {
		cp = parse_to(&dp, ',', EV_STRIP);
		if(quick_wild(cp, (char *) msg)) {
			free_lbuf(nbuf);
			return (0);
		}
	} while (dp != NULL);
	free_lbuf(nbuf);
	return (1);
}

static char *add_prefix(dbref object, dbref player, int prefix,
						const char *msg, const char *dflt)
{
	int aflags;
	dbref aowner;
	char *buf, *nbuf, *cp, *bp, *str;

	buf = atr_pget(object, prefix, &aowner, &aflags);
	if(!*buf) {
		cp = buf;
		safe_str((char *) dflt, buf, &cp);
	} else {
		nbuf = bp = alloc_lbuf("add_prefix");
		str = buf;
		exec(nbuf, &bp, 0, object, player, EV_FIGNORE | EV_EVAL | EV_TOP,
			 &str, (char **) NULL, 0);
		*bp = '\0';
		free_lbuf(buf);
		buf = nbuf;
		cp = &buf[strlen(buf)];
	}
	if(cp != buf)
		safe_str((char *) " ", buf, &cp);
	safe_str((char *) msg, buf, &cp);
	*cp = '\0';
	return (buf);
}

static char *dflt_from_msg(dbref sender, dbref sendloc)
{
	char *tp, *tbuff;

	tp = tbuff = alloc_lbuf("notify_checked.fwdlist");
	safe_str((char *) "From ", tbuff, &tp);
	if(Good_obj(sendloc))
		safe_str(Name(sendloc), tbuff, &tp);
	else
		safe_str(Name(sender), tbuff, &tp);
	safe_chr(',', tbuff, &tp);
	*tp = '\0';
	return tbuff;
}

char *colorize(dbref player, char *from);

void notify_checked(dbref target, dbref sender, const char *msg, int key)
{
	char *msg_ns, *mp, *tbuff, *tp, *buff, *colbuf = NULL;
	char *args[10];
	dbref aowner, targetloc, recip, obj;
	int i, nargs, aflags, has_neighbors, pass_listen;
	int check_listens, pass_uselock, is_audible;
	FWDLIST *fp;

	/*
	 * If speaker is invalid or message is empty, just exit 
	 */

	if(!Good_obj(target) || !msg || !*msg)
		return;

	/*
	 * Enforce a recursion limit 
	 */

	mudstate.ntfy_nest_lev++;
	if(mudstate.ntfy_nest_lev >= mudconf.ntfy_nest_lim) {
		mudstate.ntfy_nest_lev--;
		return;
	}
	/*
	 * If we want NOSPOOF output, generate it.  It is only needed if 
	 * we are sending the message to the target object 
	 */

	if(key & MSG_ME) {
		mp = msg_ns = alloc_lbuf("notify_checked");
		if(Nospoof(target) && (target != sender) &&
		   (target != mudstate.curr_enactor) &&
		   (target != mudstate.curr_player && Good_obj(sender))) {

			/*
			 * I'd really like to use tprintf here but I can't 
			 * because the caller may have.
			 * notify(target, tprintf(...)) is quite common 
			 * in the code. 
			 */

			tbuff = alloc_sbuf("notify_checked.nospoof");
			safe_chr('[', msg_ns, &mp);
			safe_str(Name(sender), msg_ns, &mp);
			sprintf(tbuff, "(#%d)", sender);
			safe_str(tbuff, msg_ns, &mp);

			if(sender != Owner(sender)) {
				safe_chr('{', msg_ns, &mp);
				safe_str(Name(Owner(sender)), msg_ns, &mp);
				safe_chr('}', msg_ns, &mp);
			}
			if(sender != mudstate.curr_enactor) {
				sprintf(tbuff, "<-(#%d)", mudstate.curr_enactor);
				safe_str(tbuff, msg_ns, &mp);
			}
			safe_str((char *) "] ", msg_ns, &mp);
			free_sbuf(tbuff);
		}
		safe_str((char *) msg, msg_ns, &mp);
		*mp = '\0';
	} else {
		msg_ns = NULL;
	}

	/*
	 * msg contains the raw message, msg_ns contains the NOSPOOFed msg 
	 */

	check_listens = Halted(target) ? 0 : 1;
	switch (Typeof(target)) {
	case TYPE_PLAYER:
		if(key & MSG_ME) {
			if(key & MSG_COLORIZE)
				colbuf = colorize(target, msg_ns);
			raw_notify(target, colbuf ? colbuf : msg_ns);


		}

		if(colbuf)
			free_lbuf(colbuf);
		if(!mudconf.player_listen)
			check_listens = 0;
	case TYPE_THING:
	case TYPE_ROOM:

		/* If we're in a pipe, objects can receive raw_notify
		 * if they're not a player and connected (if we didn't
		 * do this, they'd be notified twice! */

		if(mudstate.inpipe && (!isPlayer(target) || (isPlayer(target) &&
													 !Connected(target)))) {
			raw_notify(target, msg_ns);
		}

		/*
		 * Forward puppet message if it is for me 
		 */

		has_neighbors = Has_location(target);
		targetloc = where_is(target);
		is_audible = Audible(target);

		if((key & MSG_ME) && Puppet(target) && (target != Owner(target))
		   && ((key & MSG_PUP_ALWAYS) ||
			   ((targetloc != Location(Owner(target))) &&
				(targetloc != Owner(target))))) {
			tp = tbuff = alloc_lbuf("notify_checked.puppet");
			safe_str(Name(target), tbuff, &tp);
			safe_str((char *) "> ", tbuff, &tp);
			if(key & MSG_COLORIZE)
				colbuf = colorize(Owner(target), msg_ns);
			safe_str(colbuf ? colbuf : msg_ns, tbuff, &tp);
			*tp = '\0';
			raw_notify(Owner(target), tbuff);
			if(colbuf)
				free_lbuf(colbuf);
			free_lbuf(tbuff);
		}
		/*
		 * Check for @Listen match if it will be useful 
		 */

		pass_listen = 0;
		nargs = 0;
		if(check_listens && (key & (MSG_ME | MSG_INV_L)) && H_Listen(target)) {
			tp = atr_get(target, A_LISTEN, &aowner, &aflags);
			if(*tp && wild(tp, (char *) msg, args, 10)) {
				for(nargs = 10;
					nargs && (!args[nargs - 1] || !(*args[nargs - 1]));
					nargs--);
				pass_listen = 1;
			}
			free_lbuf(tp);
		}
		/*
		 * If we matched the @listen or are monitoring, check the * * 
		 * USE lock 
		 */

		if(sender < 0)
			sender = GOD;
		pass_uselock = 0;
		if((key & MSG_ME) && check_listens && (pass_listen ||
											   Monitor(target)))
			pass_uselock = could_doit(sender, target, A_LUSE);

		/*
		 * Process AxHEAR if we pass LISTEN, USElock and it's for me 
		 */

		if((key & MSG_ME) && pass_listen && pass_uselock) {
			if(sender != target)
				did_it(sender, target, 0, NULL, 0, NULL, A_AHEAR, args,
					   nargs);
			else
				did_it(sender, target, 0, NULL, 0, NULL, A_AMHEAR, args,
					   nargs);
			did_it(sender, target, 0, NULL, 0, NULL, A_AAHEAR, args, nargs);
		}
		/*
		 * Get rid of match arguments. We don't need them anymore 
		 */

		if(pass_listen) {
			for(i = 0; i < 10; i++)
				if(args[i] != NULL)
					free_lbuf(args[i]);
		}
		/*
		 * Process ^-listens if for me, MONITOR, and we pass USElock 
		 */
		/*
		 * \todo Eventually come up with a cleaner method for making sure
		 * the sender isn't the same as the target.
		 */
		if((key & MSG_ME) && (sender != target || Staff(target))
		   && pass_uselock && Monitor(target)) {
			(void) atr_match(target, sender, AMATCH_LISTEN, (char *) msg, 0);
		}
		/*
		 * Deliver message to forwardlist members 
		 */

		if((key & MSG_FWDLIST) && Audible(target) &&
		   check_filter(target, sender, A_FILTER, msg)) {
			tbuff = dflt_from_msg(sender, target);
			buff = add_prefix(target, sender, A_PREFIX, msg, tbuff);
			free_lbuf(tbuff);

			fp = fwdlist_get(target);
			if(fp) {
				for(i = 0; i < fp->count; i++) {
					recip = fp->data[i];
					if(!Good_obj(recip) || (recip == target))
						continue;
					notify_checked(recip, sender, buff,
								   (MSG_ME | MSG_F_UP | MSG_F_CONTENTS |
									MSG_S_INSIDE));
				}
			}
			free_lbuf(buff);
		}
		/*
		 * Deliver message through audible exits 
		 */

		if(key & MSG_INV_EXITS) {
			DOLIST(obj, Exits(target)) {
				recip = Location(obj);
				if(Audible(obj) && ((recip != target) &&
									check_filter(obj, sender, A_FILTER,
												 msg))) {
					buff =
						add_prefix(obj, target, A_PREFIX, msg,
								   "From a distance,");
					notify_checked(recip, sender, buff,
								   MSG_ME | MSG_F_UP | MSG_F_CONTENTS |
								   MSG_S_INSIDE);
					free_lbuf(buff);
				}
			}
		}
		/*
		 * Deliver message through neighboring audible exits 
		 */

		if(has_neighbors && ((key & MSG_NBR_EXITS) ||
							 ((key & MSG_NBR_EXITS_A) && is_audible))) {

			/*
			 * If from inside, we have to add the prefix string * 
			 * 
			 * *  * * of * the container. 
			 */

			if(key & MSG_S_INSIDE) {
				tbuff = dflt_from_msg(sender, target);
				buff = add_prefix(target, sender, A_PREFIX, msg, tbuff);
				free_lbuf(tbuff);
			} else {
				buff = (char *) msg;
			}

			DOLIST(obj, Exits(Location(target))) {
				recip = Location(obj);
				if(Good_obj(recip) && Audible(obj) && (recip != targetloc)
				   && (recip != target) &&
				   check_filter(obj, sender, A_FILTER, msg)) {
					tbuff =
						add_prefix(obj, target, A_PREFIX, buff,
								   "From a distance,");
					notify_checked(recip, sender, tbuff,
								   MSG_ME | MSG_F_UP | MSG_F_CONTENTS |
								   MSG_S_INSIDE);
					free_lbuf(tbuff);
				}
			}
			if(key & MSG_S_INSIDE) {
				free_lbuf(buff);
			}
		}
		/*
		 * Deliver message to contents 
		 */

		if(((key & MSG_INV) || ((key & MSG_INV_L) && pass_listen)) &&
		   (check_filter(target, sender, A_INFILTER, msg))) {

			/*
			 * Don't prefix the message if we were given the * *
			 * * * MSG_NOPREFIX key. 
			 */

			if(key & MSG_S_OUTSIDE) {
				buff = add_prefix(target, sender, A_INPREFIX, msg, "");
			} else {
				buff = (char *) msg;
			}
			DOLIST(obj, Contents(target)) {
				if(Slave(obj) && (key & MSG_NO_SLAVE))
					continue;
				if(obj != target) {
					notify_checked(obj, sender, buff,
								   MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE );
				}
			}
			if(key & MSG_S_OUTSIDE)
				free_lbuf(buff);
		}
		/*
		 * Deliver message to neighbors 
		 */

		if(has_neighbors && ((key & MSG_NBR) || ((key & MSG_NBR_A) &&
												 is_audible &&
												 check_filter(target, sender,
															  A_FILTER,
															  msg)))) {
			if(key & MSG_S_INSIDE) {
				tbuff = dflt_from_msg(sender, target);
				buff = add_prefix(target, sender, A_PREFIX, msg, "");
				free_lbuf(tbuff);
			} else {
				buff = (char *) msg;
			}
			DOLIST(obj, Contents(targetloc)) {
				if((obj != target) && (obj != targetloc)) {
					notify_checked(obj, sender, buff,
								   MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE | (key
																		  &
																		  MSG_COLORIZE));
				}
			}
			if(key & MSG_S_INSIDE) {
				free_lbuf(buff);
			}
		}
		/*
		 * Deliver message to container 
		 */

		if(has_neighbors && ((key & MSG_LOC) || ((key & MSG_LOC_A) &&
												 is_audible &&
												 check_filter(target, sender,
															  A_FILTER,
															  msg)))) {
			if(key & MSG_S_INSIDE) {
				tbuff = dflt_from_msg(sender, target);
				buff = add_prefix(target, sender, A_PREFIX, msg, tbuff);
				free_lbuf(tbuff);
			} else {
				buff = (char *) msg;
			}
			notify_checked(targetloc, sender, buff,
						   MSG_ME | MSG_F_UP | MSG_S_INSIDE);
			if(key & MSG_S_INSIDE) {
				free_lbuf(buff);
			}
		}
	}
	if(msg_ns)
		free_lbuf(msg_ns);
	mudstate.ntfy_nest_lev--;
}

void notify_except(dbref loc, dbref player, dbref exception, const char *msg)
{
	dbref first;

	if(loc != exception)
		notify_checked(loc, player, msg,
					   (MSG_ME_ALL | MSG_F_UP | MSG_S_INSIDE |
						MSG_NBR_EXITS_A));
	DOLIST(first, Contents(loc)) {
		if(exception == NOSLAVE)
			if(Slave(first))
				continue;
		if(first != exception)
			notify_checked(first, player, msg,
						   (MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE |
							(exception == NOSLAVE ? MSG_NO_SLAVE : 0)));
	}
}

void notify_except2(dbref loc, dbref player, dbref exc1, dbref exc2,
					const char *msg)
{
	dbref first;

	if((loc != exc1) && (loc != exc2))
		notify_checked(loc, player, msg,
					   (MSG_ME_ALL | MSG_F_UP | MSG_S_INSIDE |
						MSG_NBR_EXITS_A));
	DOLIST(first, Contents(loc)) {
		if(first != exc1 && first != exc2) {
			notify_checked(first, player, msg,
						   (MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE));
		}
	}
}

void do_shutdown(dbref player, dbref cause, int key, char *message)
{
	FILE *fs;

	ResetSpecialObjects();
	if(player != NOTHING) {
		raw_broadcast(0, "Game: Shutdown by %s", Name(Owner(player)));
		STARTLOG(LOG_ALWAYS, "WIZ", "SHTDN") {
			log_text((char *) "Shutdown by ");
			log_name(player);
			ENDLOG;
		}
	} else {
		raw_broadcast(0, "Game: Fatal Error: %s", message);
		STARTLOG(LOG_ALWAYS, "WIZ", "SHTDN") {
			log_text((char *) "Fatal error: ");
			log_text(message);
			ENDLOG;
		}
	}
	STARTLOG(LOG_ALWAYS, "WIZ", "SHTDN") {
		log_text((char *) "Shutdown status: ");
		log_text(message);
		ENDLOG;
	}

	fs = fopen(mudconf.status_file, "w");
	fprintf(fs, "%s\n", message);
	fclose(fs);

	/*
	 * Do we perform a normal or an emergency shutdown?  Normal shutdown
	 * * * * * is handled by exiting the main loop in shovechars,
	 * emergency  * * * * shutdown is done here. 
	 */

	if(key & SHUTDN_PANIC) {

		/*
		 * Close down the network interface 
		 */

		emergency_shutdown();

		/*
		 * Close the attribute text db and dump the header db 
		 */

		pcache_sync();
		STARTLOG(LOG_ALWAYS, "DMP", "PANIC") {
			log_text((char *) "Panic dump: ");
			log_text(mudconf.crashdb);
			ENDLOG;
		} dump_database_internal(DUMP_CRASHED);

		STARTLOG(LOG_ALWAYS, "DMP", "DONE") {
			log_text((char *) "Panic dump complete: ");
			log_text(mudconf.crashdb);
			ENDLOG;
		}
	}
	/*
	 * Set up for normal shutdown 
	 */

	mudstate.shutdown_flag = 1;
	event_loopexit(NULL);
	return;
}

void dump_database_internal(int dump_type)
{
	char tmpfile[256], outfn[256], prevfile[256];
	FILE *f;

#ifdef USE_PYTHON
	runPythonHook("save");
#endif

	if(dump_type == DUMP_CRASHED) {
		unlink(mudconf.crashdb);
		f = fopen(mudconf.crashdb, "w");
		if(f != NULL) {
			db_write(f, F_MUX, UNLOAD_VERSION | UNLOAD_OUTFLAGS);
			fclose(f);
		} else {
			log_perror("DMP", "FAIL", "Opening crash file", mudconf.crashdb);
		}
		if(mudconf.have_mailer)
			if((f = fopen(mudconf.mail_db, "w"))) {
				dump_mail(f);
				fclose(f);
			}
		if(mudconf.have_comsys || mudconf.have_macros)
			save_comsys_and_macros(mudconf.commac_db);
		SaveSpecialObjects(DUMP_CRASHED);
		return;
	}

	if(dump_type == DUMP_RESTART) {
		f = fopen(mudconf.indb, "w");
		if(f != NULL) {
			/* Write a flatfile */
			db_write(f, F_MUX, UNLOAD_VERSION | UNLOAD_OUTFLAGS);
			fclose(f);
		} else {
			log_perror("DMP", "FAIL", "Opening restart file", mudconf.indb);
		}
		if(mudconf.have_mailer)
			if((f = fopen(mudconf.mail_db, "w"))) {
				dump_mail(f);
				fclose(f);
			}
		if(mudconf.have_comsys || mudconf.have_macros)
			save_comsys_and_macros(mudconf.commac_db);
		if(mudconf.have_specials)
			SaveSpecialObjects(DUMP_RESTART);
		return;
	}
	if(dump_type == DUMP_KILLED) {
		sprintf(tmpfile, "%s.KILLED", mudconf.indb);
		f = fopen(tmpfile, "w");
		if(f != NULL) {
			/* Write a flatfile */
			db_write(f, F_MUX, UNLOAD_VERSION | UNLOAD_OUTFLAGS);
			fclose(f);
		} else {
			log_perror("DMP", "FAIL", "Opening killed file", mudconf.indb);
		}
		if(mudconf.have_mailer)
			if((f = fopen(mudconf.mail_db, "w"))) {
				dump_mail(f);
				fclose(f);
			}
		if(mudconf.have_comsys || mudconf.have_macros)
			save_comsys_and_macros(mudconf.commac_db);
		if(mudconf.have_specials)
			SaveSpecialObjects(DUMP_KILLED);
		return;
	}

	sprintf(prevfile, "%s.prev", mudconf.outdb);
	sprintf(tmpfile, "%s.#%d#", mudconf.outdb, mudstate.epoch - 1);
	unlink(tmpfile);			/*
								 * nuke our predecessor 
								 */
	sprintf(tmpfile, "%s.#%d#", mudconf.outdb, mudstate.epoch);

	if(mudconf.compress_db) {
		sprintf(tmpfile, "%s.#%d#.gz", mudconf.outdb, mudstate.epoch - 1);
		unlink(tmpfile);
		sprintf(tmpfile, "%s.#%d#.gz", mudconf.outdb, mudstate.epoch);
		StringCopy(outfn, mudconf.outdb);
		strcat(outfn, ".gz");
		f = popen(tprintf("%s > %s", mudconf.compress, tmpfile), "w");
		if(f) {
			db_write(f, F_MUX, OUTPUT_VERSION | OUTPUT_FLAGS);
			pclose(f);
			rename(mudconf.outdb, prevfile);
			if(rename(tmpfile, outfn) < 0)
				log_perror("SAV", "FAIL",
						   "Renaming output file to DB file", tmpfile);
		} else {
			log_perror("SAV", "FAIL", "Opening", tmpfile);
		}
	} else {
		f = fopen(tmpfile, "w");
		if(f) {
			db_write(f, F_MUX, OUTPUT_VERSION | OUTPUT_FLAGS);
			fclose(f);
			rename(mudconf.outdb, prevfile);
			if(rename(tmpfile, mudconf.outdb) < 0)
				log_perror("SAV", "FAIL",
						   "Renaming output file to DB file", tmpfile);
		} else {
			log_perror("SAV", "FAIL", "Opening", tmpfile);
		}
	}

	if(mudconf.have_mailer)
		if((f = fopen(mudconf.mail_db, "w"))) {
			dump_mail(f);
			fclose(f);
		}
	if(mudconf.have_comsys || mudconf.have_macros)
		save_comsys_and_macros(mudconf.commac_db);
	if(mudconf.have_specials)
		SaveSpecialObjects(DUMP_NORMAL);
}

void dump_database(void)
{
	char *buff;

	mudstate.epoch++;
	mudstate.dumping = 1;
	buff = alloc_mbuf("dump_database");
	sprintf(buff, "%s.#%d#", mudconf.outdb, mudstate.epoch);
	STARTLOG(LOG_DBSAVES, "DMP", "DUMP") {
		log_text((char *) "Dumping: ");
		log_text(buff);
		ENDLOG;
	} pcache_sync();

	dump_database_internal(DUMP_NORMAL);
	STARTLOG(LOG_DBSAVES, "DMP", "DONE") {
		log_text((char *) "Dump complete: ");
		log_text(buff);
		ENDLOG;
	} free_mbuf(buff);

	mudstate.dumping = 0;
}

void fork_and_dump(int key)
{
	char *buff;

	if(*mudconf.dump_msg)
		raw_broadcast(0, "%s", mudconf.dump_msg);

	check_mail_expiration();
	mudstate.epoch++;
	mudstate.dumping = 1;
	buff = alloc_mbuf("fork_and_dump");
	sprintf(buff, "%s.#%d#", mudconf.outdb, mudstate.epoch);
   
    log_error(LOG_DBSAVES, "DMP", "CHKPT", "Saving database: %s", buff);
    
    pcache_sync();
    
	if(!key || (key & DUMP_STRUCT)) {
		if(!fork()) {
            dprintk("child database write process starting.");
			unbind_signals();
			dump_database_internal(DUMP_NORMAL);
            dprintk("child database write process finished.");
            exit(0);
		}
	}

	mudstate.dumping = 0;

	if(*mudconf.postdump_msg)
		raw_broadcast(0, "%s", mudconf.postdump_msg);
}

static int load_game(void)
{
	FILE *f;
	int compressed;
	char infile[256];
	struct stat statbuf;
	int db_format, db_version, db_flags;

	f = NULL;
	compressed = 0;
	if(mudconf.compress_db) {
		StringCopy(infile, mudconf.indb);
		strcat(infile, ".gz");
		if(stat(infile, &statbuf) == 0) {
			if((f = popen(tprintf(" %s < %s", mudconf.uncompress,
								  infile), "r")) != NULL)
				compressed = 1;
		}
	}
	if(compressed == 0) {
		StringCopy(infile, mudconf.indb);
		if((f = fopen(mudconf.indb, "r")) == NULL)
			return -1;
	}
	/*
	 * ok, read it in 
	 */

	STARTLOG(LOG_STARTUP, "INI", "LOAD") {
		log_text((char *) "Loading: ");
		log_text(infile);
		ENDLOG;
	};
	if(db_read(f, &db_format, &db_version, &db_flags) < 0) {
		STARTLOG(LOG_ALWAYS, "INI", "FATAL") {
			log_text((char *) "Error loading ");
			log_text(infile);
			ENDLOG;
		}
		if(compressed)
			pclose(f);
		else
			fclose(f);
		return -1;
	}
	if(compressed)
		pclose(f);
	else
		fclose(f);

	if(mudconf.have_comsys || mudconf.have_macros)
		load_comsys_and_macros(mudconf.commac_db);

	/* Load the mecha stuff.. */
	if(mudconf.have_specials)
		LoadSpecialObjects();

	if(mudconf.have_mailer)
		if((f = fopen(mudconf.mail_db, "r"))) {
			load_mail(f);
			fclose(f);
		}
	STARTLOG(LOG_STARTUP, "INI", "LOAD") {
		log_text((char *) "Load complete.");
		ENDLOG;
	}
	/*
	 * everything ok 
	 */
	return (0);
}

/**
 * match a list of things, using the no_command flag 
 */
int list_check(dbref thing, dbref player, char type, char *str,
			   int check_parent)
{
	int match, limit;

	match = 0;
	limit = mudstate.db_top;
	while (thing != NOTHING) {
		if((thing != player) && (!(No_Command(thing)))) {
			if(atr_match(thing, player, type, str, check_parent) > 0)
				match = 1;
		}
		thing = Next(thing);
		if(--limit < 0)
			return match;
	}
	return match;
}

int Hearer(dbref thing)
{
	char *as, *buff, *s;
	dbref aowner;
	int attr, aflags;
	ATTR *ap;

	if(mudstate.inpipe && (thing == mudstate.poutobj))
		return 1;

	if(Connected(thing) || Puppet(thing))
		return 1;

	if(Monitor(thing))
		buff = alloc_lbuf("Hearer");
	else
		buff = NULL;
	for(attr = atr_head(thing, &as); attr; attr = atr_next(&as)) {
		if(attr == A_LISTEN) {
			if(buff)
				free_lbuf(buff);
			return 1;
		}
		if(Monitor(thing)) {
			ap = atr_num(attr);
			if(!ap || (ap->flags & AF_NOPROG))
				continue;

			atr_get_str(buff, thing, attr, &aowner, &aflags);

			/*
			 * Make sure we can execute it 
			 */

			if((buff[0] != AMATCH_LISTEN) || (aflags & AF_NOPROG))
				continue;

			/*
			 * Make sure there's a : in it 
			 */

			for(s = buff + 1; *s && (*s != ':'); s++);
			if(s) {
				free_lbuf(buff);
				return 1;
			}
		}
	}
	if(buff)
		free_lbuf(buff);
	return 0;
}

void do_readcache(dbref player, dbref cause, int key)
{
	helpindex_load(player);
	fcache_load(player);
}

static void process_preload(void)
{
	dbref thing, parent, aowner;
	int aflags, lev, i;
	char *tstr;
	FWDLIST *fp;

	fp = (FWDLIST *) alloc_lbuf("process_preload.fwdlist");
	tstr = alloc_lbuf("process_preload.string");
	i = 0;
	DO_WHOLE_DB(thing) {

		/*
		 * Ignore GOING objects 
		 */

		if(Going(thing))
			continue;

		do_top(10);

		/*
		 * Look for a STARTUP attribute in parents 
		 */

		ITER_PARENTS(thing, parent, lev) {
			if(Flags(thing) & HAS_STARTUP) {
				did_it(Owner(thing), thing, 0, NULL, 0, NULL, A_STARTUP,
					   (char **) NULL, 0);
				/*
				 * Process queue entries as we add them 
				 */

				do_second();
				do_top(10);
				break;
			}
		}

		/*
		 * Look for a FORWARDLIST attribute 
		 */

		if(H_Fwdlist(thing)) {
			(void) atr_get_str(tstr, thing, A_FORWARDLIST, &aowner, &aflags);
			if(*tstr) {
				fwdlist_load(fp, GOD, tstr);
				if(fp->count > 0)
					fwdlist_set(thing, fp);
			}
		}
	}
	free_lbuf(fp);
	free_lbuf(tstr);
}

int main(int argc, char *argv[])
{
	int mindb;

	if((argc > 2) && (!strcmp(argv[1], "-s") && (argc > 3))) {
		fprintf(stderr, "Usage: %s [-s] [config-file]\n", argv[0]);
		exit(1);
	}

	event_init();

#if defined(HAVE_IEEEFP_H) && defined(HAVE_SYS_UCONTEXT_H)
	/*
	 * Inhibit IEEE fp exception on overflow 
	 */

	fpsetmask(fpgetmask() & ~FP_X_OFL);
#endif

	mindb = 0;					/* Are we creating a new db? */
	corrupt = 0;				/* Database isn't corrupted. */
    memset(&mudstate, 0, sizeof(mudstate));
	time(&mudstate.start_time);
	time(&mudstate.restart_time);
	mudstate.executable_path = strdup(argv[0]);
    mudstate.db_top = -1;
	tcache_init();
	pcache_init();
	cf_init();
	init_rlimit();
	init_cmdtab();
	init_mactab();
	init_chantab();
	init_logout_cmdtab();
	init_flagtab();
	init_powertab();
	init_functab();
	init_attrtab();
	init_version();

#ifdef HUDINFO_SUPPORT
	init_hudinfo();
#endif

	hashinit(&mudstate.player_htab, 250 * HASH_FACTOR);
	nhashinit(&mudstate.mail_htab, 50 * HASH_FACTOR);
	nhashinit(&mudstate.fwdlist_htab, 25 * HASH_FACTOR);
	nhashinit(&mudstate.parent_htab, 5 * HASH_FACTOR);
	mudstate.desctree = rb_init(desc_cmp, NULL);
	vattr_init();

	if(argc > 1 && !strcmp(argv[1], "-s")) {
		mindb = 1;
		if(argc == 3)
			cf_read(argv[2]);
		else
			cf_read((char *) CONF_FILE);
	} else if(argc == 2) {
		cf_read(argv[1]);
	} else {
		cf_read((char *) CONF_FILE);
	}

	fcache_init();
	helpindex_init();
	db_free();

	mudstate.record_players = 0;

	if(mindb)
		db_make_minimal();
	else if(load_game() < 0) {
		STARTLOG(LOG_ALWAYS, "INI", "LOAD") {
			log_text((char *) "Couldn't load: ");
			log_text(mudconf.indb);
			ENDLOG;
		} exit(2);
	}
#ifdef USE_PYTHON
	MUXPy_Init();
	runPythonHook("load");
#endif

	/* initialize random.. */
	srandom(getpid());
	/* set singnals.. */
	bind_signals();

	/*
	 * Do a consistency check and set up the freelist 
	 */

	do_dbck(NOTHING, NOTHING, 0);

	/*
	 * Reset all the hash stats 
	 */

	hashreset(&mudstate.command_htab);
	hashreset(&mudstate.macro_htab);
	hashreset(&mudstate.channel_htab);
	nhashreset(&mudstate.mail_htab);
	hashreset(&mudstate.logout_cmd_htab);
	hashreset(&mudstate.func_htab);
	hashreset(&mudstate.flags_htab);
	hashreset(&mudstate.attr_name_htab);
	hashreset(&mudstate.player_htab);
	nhashreset(&mudstate.fwdlist_htab);
	hashreset(&mudstate.news_htab);
	hashreset(&mudstate.help_htab);
	hashreset(&mudstate.wizhelp_htab);
	hashreset(&mudstate.plushelp_htab);
	hashreset(&mudstate.wiznews_htab);

	for(mindb = 0; mindb < MAX_GLOBAL_REGS; mindb++) {
		mudstate.global_regs[mindb] = alloc_lbuf("main.global_reg");
        memset(mudstate.global_regs[mindb], 0, LBUF_SIZE);
	}

	mudstate.now = time(NULL);
	process_preload();

	dnschild_init();

    if(!load_restart_db_xdr()) {
        load_restart_db();
    }

#ifdef SQL_SUPPORT
	sqlchild_init();
#endif

#ifdef ARBITRARY_LOGFILES
	logcache_init();
#endif

#ifdef MCHECK
	mtrace();
#endif

	/*
	 * go do it 
	 */

	mudstate.now = time(NULL);
	init_timer();
	shovechars(mudconf.port);

#ifdef MCHECK
	muntrace();
#endif

	close_sockets(0, (char *) "Going down - Bye");
	dump_database();

#ifdef ARBITRARY_LOGFILES
	logcache_destruct();
#endif
#ifdef SQL_SUPPORT
	sqlchild_destruct();
#endif

	exit(0);
}

static void init_rlimit(void)
{
#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
	struct rlimit *rlp;

	rlp = (struct rlimit *) alloc_lbuf("rlimit");

	if(getrlimit(RLIMIT_NOFILE, rlp)) {
		log_perror("RLM", "FAIL", NULL, "getrlimit()");
		free_lbuf(rlp);
		return;
	}
	rlp->rlim_cur = rlp->rlim_max;
	if(setrlimit(RLIMIT_NOFILE, rlp))
		log_perror("RLM", "FAIL", NULL, "setrlimit()");
	free_lbuf(rlp);

#endif /*
	    * HAVE_SETRLIMIT 
	    */
}