btmux/autom4te.cache/
btmux/doc/.svn/
btmux/event/.svn/
btmux/game/.svn/
btmux/game/bin/.svn/
btmux/game/data/.svn/
btmux/game/logs/.svn/
btmux/game/maps/
btmux/game/maps/.svn/
btmux/game/maps/.svn/prop-base/
btmux/game/maps/.svn/props/
btmux/game/maps/.svn/text-base/
btmux/game/maps/.svn/wcprops/
btmux/game/mechs/
btmux/game/mechs/.svn/
btmux/game/mechs/.svn/prop-base/
btmux/game/mechs/.svn/props/
btmux/game/mechs/.svn/text-base/
btmux/game/mechs/.svn/wcprops/
btmux/game/text/.svn/
btmux/include/.svn/
btmux/misc/
btmux/misc/.svn/
btmux/misc/.svn/prop-base/
btmux/misc/.svn/props/
btmux/misc/.svn/text-base/
btmux/misc/.svn/wcprops/
btmux/python/
btmux/python/.svn/
btmux/python/.svn/prop-base/
btmux/python/.svn/props/
btmux/python/.svn/text-base/
btmux/python/.svn/wcprops/
btmux/src/.svn/prop-base/
btmux/src/.svn/props/
btmux/src/.svn/text-base/
btmux/src/.svn/wcprops/
btmux/src/hcode/.svn/
btmux/src/hcode/btech/
btmux/src/hcode/btech/.svn/
btmux/src/hcode/btech/.svn/prop-base/
btmux/src/hcode/btech/.svn/props/
btmux/src/hcode/btech/.svn/text-base/
btmux/src/hcode/btech/.svn/wcprops/
btmux/src/hcode/include/.svn/
/*
 * game.c 
 */

/*
 * $Id: game.c,v 1.8 2005/08/08 10:30:11 murrayma Exp $ 
 */

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

#include <sys/stat.h>
#include <signal.h>
#include <event.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 "slave.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

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);
extern void boot_slave(void);

#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 pid_t slave_pid;
extern int slave_socket;

#ifdef MEMORY_BASED
extern int corrupt;
#endif

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

void do_dump(player, cause, key)
dbref player, 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(player, cause, key)
dbref player, cause;
int key;
{
#ifdef MEMORY_BASED
    raw_notify(player, "Database is memory based.");
#else
    raw_notify(player, "Optimizing database...");
    if (dddb_optimize() < 0)
	raw_notify(player, "Database optimization failed.");
    else
	raw_notify(player, "Database optmization completed.");
#endif				/*
				 * 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;
    }}
}

/* ----------------------------------------------------------------------
 * regexp_match: Load a regular expression match and insert it into
 * registers.
 */ int regexp_match(pattern, str, args, nargs)
char *pattern;
char *str;
char *args[];
int nargs;
{
    regexp *re;
    int got_match;
    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 ((re = regcomp(pattern)) == NULL) {
	/*
	 * 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);
    if (!got_match) {
	free(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) && (re->startp[i]) && (re->endp[i]); i++) {
	len = re->endp[i] - re->startp[i];
	args[i] = alloc_lbuf("regexp_match");
	strncpy(args[i], re->startp[i], len);
	args[i][len] = '\0';	/* strncpy() does not null-terminate */
    }

    free(re);
    return 1;
}


/*
 * ----------------------------------------------------------------------
 * * atr_match: Check attribute list for wild card matches and queue them.
 */

static int atr_match1(thing, parent, player, type, str, check_exclude,
    hash_insert)
dbref thing, parent, player;
char type, *str;
int check_exclude, hash_insert;
{
    dbref aowner;
    int match, attr, aflags, i;
    char buff[LBUF_SIZE], *s, *as;
    char *args[10];
    ATTR *ap;

    /*
     * See if we can do it.  Silently fail if we can't. 
     */

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

    match = 0;
    atr_push();
    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]);
	    }
	}
    }
    atr_pop();
    return (match);
}

int atr_match(thing, player, type, str, check_parents)
dbref thing, player;
char type, *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;
}

/*
 * ---------------------------------------------------------------------------
 * * notify_checked: notifies the object #target of the message msg, and
 * * optionally notify the contents, neighbors, and location also.
 */

int check_filter(object, player, filter, msg)
dbref object, 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(object, player, prefix, msg, dflt)
dbref object, player;
int prefix;
const char *msg, *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(sender, sendloc)
dbref sender, 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;
}

/* Do HTML escaping, converting < to &lt;, etc.  'dest' needs to be
 * allocated & freed by the caller.
 *
 * If you're using this to append to a string, you can pass in the
 * safe_{str|chr} (char **) so we can just do the append directly,
 * saving you an alloc_lbuf()...free_lbuf().  If you want us to append
 * from the start of 'dest', just pass in a 0 for 'destp'.
 *
 * Returns 0 if the copy succeeded, 1 if it failed.
 */
int html_escape(src, dest, destp)
const char *src;
char *dest;
char **destp;
{
    const char *msg_orig;
    char *temp;
    int ret = 0;

    if (destp == 0) {
	temp = dest;
	destp = &temp;
    }

    for (msg_orig = src; msg_orig && *msg_orig && !ret; msg_orig++) {
	switch (*msg_orig) {
	case '<':
	    ret = safe_str("&lt;", dest, destp);
	    break;
	case '>':
	    ret = safe_str("&gt;", dest, destp);
	    break;
	case '&':
	    ret = safe_str("&amp;", dest, destp);
	    break;
	case '\"':
	    ret = safe_str("&quot;", dest, destp);
	    break;
	default:
	    ret = safe_chr(*msg_orig, dest, destp);
	    break;
	}
    }
    **destp = 0;
    return ret;
}

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_HTML) {
                    raw_notify_html(target, msg_ns);
                } else {
                    if (Html(target)) {
                        char *msg_ns_escaped;

                        msg_ns_escaped = alloc_lbuf("notify_checked_escape");
                        html_escape(msg_ns, msg_ns_escaped, 0);
                        raw_notify(target, msg_ns_escaped);
                        free_lbuf(msg_ns_escaped);
                    } else {
                        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 | (key &
                                    MSG_HTML));
                    }
                }
                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(loc, player, exception, msg)
dbref loc, player, 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(loc, player, exc1, exc2, msg)
dbref loc, player, exc1, 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;
    return;
}

void dump_database_internal(dump_type)
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);
        }
    }

#ifndef STANDALONE
    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);
#endif
}

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(key)
int key;
{
    int child;
    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);
    STARTLOG(LOG_DBSAVES, "DMP", "CHKPT") {
	if (!key || (key & DUMP_TEXT)) {
	    log_text((char *) "SYNCing");
	    if (!key || (key & DUMP_STRUCT))
		log_text((char *) " and ");
	}
	if (!key || (key & DUMP_STRUCT)) {
	    log_text((char *) "Checkpointing: ");
	    log_text(buff);
	}
	ENDLOG;
    } free_mbuf(buff);

#ifndef MEMORY_BASED
    al_store();			/*
				 * Save cached modified attribute list 
				 */
#endif				/*
				 * MEMORY_BASED 
				 */
    if (!key || (key & DUMP_TEXT))
	pcache_sync();
    if (!key || (key & DUMP_STRUCT)) {
	if (mudconf.fork_dump) {
	    if (mudconf.fork_vfork) {
		child = vfork();
	    } else {
		child = fork();
	    }
	} else {
	    child = 0;
	}
	if (child == 0) {
	    dump_database_internal(DUMP_NORMAL);
	    if (mudconf.fork_dump)
		_exit(0);
	} else if (child < 0) {
	    log_perror("DMP", "FORK", NULL, "fork()");
	}
    }

    if (!mudconf.fork_dump)
	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(thing, player, type, str, check_parent)
dbref thing, player;
char type, *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(thing)
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;
    atr_push();
    for (attr = atr_head(thing, &as); attr; attr = atr_next(&as)) {
	if (attr == A_LISTEN) {
	    if (buff)
		free_lbuf(buff);
	    atr_pop();
	    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);
		atr_pop();
		return 1;
	    }
	}
    }
    if (buff)
	free_lbuf(buff);
    atr_pop();
    return 0;
}

void do_readcache(player, cause, key)
dbref player, 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);
#ifndef MEMORY_BASED
		cache_reset(0);
#endif				/*
				 * MEMORY_BASED 
				 */
		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);
#ifndef MEMORY_BASED
		cache_reset(0);
#endif				/*
				 * MEMORY_BASED 
				 */
	    }
	}
    }
    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? 
                         */
#ifdef MEMORY_BASED
    corrupt = 0;		/* Database isn't corrupted. */
#endif
    time(&mudstate.start_time);
    time(&mudstate.restart_time);
#if 0
    pool_init(POOL_LBUF, LBUF_SIZE);
    pool_init(POOL_MBUF, MBUF_SIZE);
    pool_init(POOL_SBUF, SBUF_SIZE);
    pool_init(POOL_BOOL, sizeof(struct boolexp));

    pool_init(POOL_DESC, sizeof(DESC));
    pool_init(POOL_QENTRY, sizeof(BQUE));
#endif
    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();

#ifndef MEMORY_BASED
    if (mindb)
        unlink(mudconf.gdbm);
    if (init_gdbm_db(mudconf.gdbm) < 0) {
        STARTLOG(LOG_ALWAYS, "INI", "LOAD") {
            log_text((char *) "Couldn't load text database: ");
            log_text(mudconf.gdbm);
            ENDLOG;
        } exit(2);
    }
#else
    db_free();
#endif				/*
                     * MEMORY_BASED 
                     */

    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.. */
    set_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");
    }

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

    load_restart_db();

    boot_slave();

#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();

    if (slave_socket != -1) {
        kill(slave_pid, SIGKILL);
    }
#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 
				 */
}