mux2.0/
mux2.0/game/bin/
mux2.0/game/data/
mux2.0/src/tools/
// conf.cpp: set up configuration information and static data.
//
// $Id: conf.cpp,v 1.23 2000/09/26 06:27:41 sdennis Exp $
//

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

#include "mudconf.h"
#include "db.h"
#include "interface.h"
#include "command.h"
#include "htab.h"
#include "alloc.h"
#include "attrs.h"
#include "flags.h"
#include "powers.h"
#include "match.h"

/* Some systems are lame, and inet_addr() claims to return -1 on failure,
 * despite the fact that it returns an unsigned long. (It's not really a -1,
 * obviously.) Better-behaved systems use INADDR_NONE.
 */
#ifndef INADDR_NONE
#define INADDR_NONE -1
#endif

/*
 * ---------------------------------------------------------------------------
 * * CONFPARM: Data used to find fields in CONFDATA.
 */

typedef struct confparm
{
    char *pname;            // parm name 
    int (*interpreter)(int *vp, char *str, unsigned int extra, dbref player, char *cmd);  // routine to interp parameter 
    int flags;              // control flags 
    int *loc;               // where to store value 
    long extra;             // extra data for interpreter 
} CONF;

/*
 * ---------------------------------------------------------------------------
 * * External symbols.
 */

CONFDATA mudconf;
STATEDATA mudstate;

#ifndef STANDALONE
extern NAMETAB logdata_nametab[];
extern NAMETAB logoptions_nametab[];
extern NAMETAB access_nametab[];
extern NAMETAB attraccess_nametab[];
extern NAMETAB list_names[];
extern NAMETAB sigactions_nametab[];
extern CONF conftable[];

#endif

/*
 * ---------------------------------------------------------------------------
 * * cf_init: Initialize mudconf to default values.
 */

void NDECL(cf_init)
{
#ifndef STANDALONE
    int i;

    mudconf.indb = StringClone("netmux.db");
    mudconf.outdb = StringClone("");
    mudconf.crashdb = StringClone("");
    mudconf.game_dir = StringClone("");
    mudconf.game_pag = StringClone("");
    mudconf.mail_db   = StringClone("mail.db");
    mudconf.comsys_db = StringClone("comsys.db");
    mudconf.compress_db = 0;
    mudconf.compress = StringClone("gzip");
    mudconf.uncompress = StringClone("gzip -d");
    mudconf.status_file = StringClone("shutdown.status");
    mudconf.max_cache_size = 1*1024*1024;
    mudconf.port = 2860;
    mudconf.conc_port = 2861;
    mudconf.init_size = 1000;
    mudconf.guest_char = -1;
    mudconf.guest_nuker = 1;
    mudconf.number_guests = 30;
    strcpy(mudconf.guest_prefix, "Guest");
    mudconf.guest_file     = StringClone("text/guest.txt");
    mudconf.conn_file      = StringClone("text/connect.txt");
    mudconf.creg_file      = StringClone("text/register.txt");
    mudconf.regf_file      = StringClone("text/create_reg.txt");
    mudconf.motd_file      = StringClone("text/motd.txt");
    mudconf.wizmotd_file   = StringClone("text/wizmotd.txt");
    mudconf.quit_file      = StringClone("text/quit.txt");
    mudconf.down_file      = StringClone("text/down.txt");
    mudconf.full_file      = StringClone("text/full.txt");
    mudconf.site_file      = StringClone("text/badsite.txt");
    mudconf.crea_file      = StringClone("text/newuser.txt");
    mudconf.help_file      = StringClone("text/help.txt");
    mudconf.help_indx      = StringClone("text/help.indx");
    mudconf.news_file      = StringClone("text/news.txt");
    mudconf.news_indx      = StringClone("text/news.indx");
    mudconf.whelp_file     = StringClone("text/wizhelp.txt");
    mudconf.whelp_indx     = StringClone("text/wizhelp.indx");
    mudconf.plushelp_file  = StringClone("text/plushelp.txt");
    mudconf.plushelp_indx  = StringClone("text/plushelp.indx");
    mudconf.staffhelp_file = StringClone("text/staffhelp.txt");
    mudconf.staffhelp_indx = StringClone("text/staffhelp.indx");
    mudconf.wiznews_file   = StringClone("text/wiznews.txt");
    mudconf.wiznews_indx   = StringClone("text/wiznews.indx");
    mudconf.motd_msg[0] = '\0';
    mudconf.wizmotd_msg[0] = '\0';
    mudconf.downmotd_msg[0] = '\0';
    mudconf.fullmotd_msg[0] = '\0';
    mudconf.dump_msg[0] = '\0';
    mudconf.postdump_msg[0] = '\0';
    mudconf.fixed_home_msg[0] = '\0';
    mudconf.fixed_tel_msg[0] = '\0';
    strcpy(mudconf.public_channel, "Public");
    strcpy(mudconf.guests_channel, "Guests");
    strcpy(mudconf.pueblo_msg, "</xch_mudtext><img xch_mode=html>");
    mudconf.indent_desc = 0;
    mudconf.name_spaces = 1;
#if !defined(VMS) && !defined(WIN32)
    mudconf.fork_dump = 1;
    mudconf.fork_vfork = 0;
    mudstate.dumping = 0;
#endif // WIN32
    mudconf.have_comsys = 1;
    mudconf.have_mailer = 1;
    mudconf.have_zones = 1;
    mudconf.paranoid_alloc = 0;
    mudconf.sig_action = SA_DFLT;
    mudconf.max_players = -1;
    mudconf.dump_interval = 3600;
    mudconf.check_interval = 600;
    mudconf.events_daily_hour = 7;
    mudconf.dump_offset = 0;
    mudconf.check_offset = 300;
    mudconf.idle_timeout = 3600;
    mudconf.conn_timeout = 120;
    mudconf.idle_interval = 60;
    mudconf.retry_limit = 3;
    mudconf.output_limit = 16384;
    mudconf.paycheck = 0;
    mudconf.paystart = 0;
    mudconf.paylimit = 10000;
    mudconf.start_quota = 20;
    mudconf.site_chars = 25;
    mudconf.payfind = 0;
    mudconf.digcost = 10;
    mudconf.linkcost = 1;
    mudconf.opencost = 1;
    mudconf.createmin = 10;
    mudconf.createmax = 505;
    mudconf.killmin = 10;
    mudconf.killmax = 100;
    mudconf.killguarantee = 100;
    mudconf.robotcost = 1000;
    mudconf.pagecost = 10;
    mudconf.searchcost = 100;
    mudconf.waitcost = 10;
    mudconf.machinecost = 64;
    mudconf.exit_quota = 1;
    mudconf.player_quota = 1;
    mudconf.room_quota = 1;
    mudconf.thing_quota = 1;
    mudconf.mail_expiration = 14;
    mudconf.use_http = 0;
    mudconf.queuemax = 100;
    mudconf.queue_chunk = 10;
    mudconf.active_q_chunk = 10;
    mudconf.sacfactor = 5;
    mudconf.sacadjust = -1;
    mudconf.use_hostname = 1;
    mudconf.quotas = 0;
    mudconf.ex_flags = 1;
    mudconf.robot_speak = 1;
    mudconf.clone_copy_cost = 0;
    mudconf.pub_flags = 1;
    mudconf.quiet_look = 1;
    mudconf.exam_public = 1;
    mudconf.read_rem_desc = 0;
    mudconf.read_rem_name = 0;
    mudconf.sweep_dark = 0;
    mudconf.player_listen = 0;
    mudconf.quiet_whisper = 1;
    mudconf.dark_sleepers = 1;
    mudconf.see_own_dark = 1;
    mudconf.idle_wiz_dark = 0;
    mudconf.pemit_players = 0;
    mudconf.pemit_any = 0;
    mudconf.match_mine = 1;
    mudconf.match_mine_pl = 1;
    mudconf.switch_df_all = 1;
    mudconf.fascist_tport = 0;
    mudconf.terse_look = 1;
    mudconf.terse_contents = 1;
    mudconf.terse_exits = 1;
    mudconf.terse_movemsg = 1;
    mudconf.trace_topdown = 1;
    mudconf.trace_limit = 200;
    mudconf.safe_unowned = 0;
    mudconf.safer_passwords = 0;

    // -- ??? Running SC on a non-SC DB may cause problems.
    //
    mudconf.space_compress = 1;
    mudconf.allow_guest_from_registered_site = 1;
    mudconf.start_room = 0;
    mudconf.start_home = NOTHING;
    mudconf.default_home = NOTHING;
    mudconf.master_room = NOTHING;
    mudconf.player_flags.word1 = 0;
    mudconf.player_flags.word2 = 0;
    mudconf.player_flags.word3 = 0;
    mudconf.room_flags.word1 = 0;
    mudconf.room_flags.word2 = 0;
    mudconf.room_flags.word3 = 0;
    mudconf.exit_flags.word1 = 0;
    mudconf.exit_flags.word2 = 0;
    mudconf.exit_flags.word3 = 0;
    mudconf.thing_flags.word1 = 0;
    mudconf.thing_flags.word2 = 0;
    mudconf.thing_flags.word3 = 0;
    mudconf.robot_flags.word1 = ROBOT;
    mudconf.robot_flags.word2 = 0;
    mudconf.robot_flags.word3 = 0;
    mudconf.vattr_flags = AF_ODARK;
    strcpy(mudconf.mud_name, "MUX");
    strcpy(mudconf.one_coin, "penny");
    strcpy(mudconf.many_coins, "pennies");
    mudconf.timeslice = 1000;
    mudconf.cmd_quota_max = 100;
    mudconf.cmd_quota_incr = 1;
    mudconf.max_cmdsecs = 120;
    mudconf.control_flags = 0xffffffff; // Everything for now...
    mudconf.log_options = LOG_ALWAYS | LOG_BUGS | LOG_SECURITY |
        LOG_NET | LOG_LOGIN | LOG_DBSAVES | LOG_CONFIGMODS |
        LOG_SHOUTS | LOG_STARTUP | LOG_WIZARD |
        LOG_PROBLEMS | LOG_PCREATES | LOG_TIMEUSE;
    mudconf.log_info = LOGOPT_TIMESTAMP | LOGOPT_LOC;
    mudconf.markdata[0] = 0x01;
    mudconf.markdata[1] = 0x02;
    mudconf.markdata[2] = 0x04;
    mudconf.markdata[3] = 0x08;
    mudconf.markdata[4] = 0x10;
    mudconf.markdata[5] = 0x20;
    mudconf.markdata[6] = 0x40;
    mudconf.markdata[7] = 0x80;
    mudconf.func_nest_lim = 50;
    mudconf.func_invk_lim = 2500;
    mudconf.ntfy_nest_lim = 20;
    mudconf.lock_nest_lim = 20;
    mudconf.parent_nest_lim = 10;
    mudconf.zone_nest_lim = 20;
    mudconf.stack_limit = 50;
    mudconf.cache_names = 1;
    mudconf.toad_recipient = -1;
    mudstate.events_flag = 0;
    mudstate.initializing = 0;
    mudstate.panicking = 0;
    mudstate.logging = 0;
    mudstate.epoch = 0;
    mudstate.generation = 0;
    mudstate.curr_player = NOTHING;
    mudstate.curr_enactor = NOTHING;
    mudstate.shutdown_flag = 0;
    mudstate.attr_next = A_USER_START;
    mudstate.debug_cmd = (char *)"< init >";
    strcpy(mudstate.doing_hdr, "Doing");
    mudstate.access_list = NULL;
    mudstate.suspect_list = NULL;
    mudstate.badname_head = NULL;
    mudstate.mstat_ixrss[0] = 0;
    mudstate.mstat_ixrss[1] = 0;
    mudstate.mstat_idrss[0] = 0;
    mudstate.mstat_idrss[1] = 0;
    mudstate.mstat_isrss[0] = 0;
    mudstate.mstat_isrss[1] = 0;
    mudstate.mstat_secs[0] = 0;
    mudstate.mstat_secs[1] = 0;
    mudstate.mstat_curr = 0;
    mudstate.iter_alist.data = NULL;
    mudstate.iter_alist.len = 0;
    mudstate.iter_alist.next = NULL;
    mudstate.mod_alist = NULL;
    mudstate.mod_alist_len = 0;
    mudstate.mod_size = 0;
    mudstate.mod_al_id = NOTHING;
    mudstate.olist = NULL;
    mudstate.min_size = 0;
    mudstate.db_top = 0;
    mudstate.db_size = 0;
    mudstate.mail_db_top = 0;
    mudstate.mail_db_size = 0;
    mudstate.mail_freelist = 0;
    mudstate.freelist = NOTHING;
    mudstate.markbits = NULL;
    mudstate.func_nest_lev = 0;
    mudstate.func_invk_ctr = 0;
    mudstate.ntfy_nest_lev = 0;
    mudstate.lock_nest_lev = 0;
    mudstate.zone_nest_num = 0;
    mudstate.inpipe = 0;
    mudstate.pout = NULL;
    mudstate.poutnew = NULL;
    mudstate.poutbufc = NULL;
    mudstate.poutobj = -1;
    for (i = 0; i < MAX_GLOBAL_REGS; i++)
    {
        mudstate.global_regs[i] = NULL;
        mudstate.glob_reg_len[i] = 0;
    }
    mudstate.nObjEvalNest = 0;
#else
    mudconf.paylimit = 10000;
    mudconf.digcost = 10;
    mudconf.opencost = 1;
    mudconf.robotcost = 1000;
    mudconf.createmin = 5;
    mudconf.createmax = 505;
    mudconf.sacfactor = 5;
    mudconf.sacadjust = -1;
    mudconf.room_quota = 1;
    mudconf.exit_quota = 1;
    mudconf.thing_quota = 1;
    mudconf.player_quota = 1;
    mudconf.quotas = 0;
    mudconf.start_room = 0;
    mudconf.start_home = NOTHING;
    mudconf.default_home = NOTHING;
    mudconf.vattr_flags = AF_ODARK;
    mudconf.log_options = 0xffffffff;
    mudconf.log_info = 0;
    mudconf.markdata[0] = 0x01;
    mudconf.markdata[1] = 0x02;
    mudconf.markdata[2] = 0x04;
    mudconf.markdata[3] = 0x08;
    mudconf.markdata[4] = 0x10;
    mudconf.markdata[5] = 0x20;
    mudconf.markdata[6] = 0x40;
    mudconf.markdata[7] = 0x80;
    mudconf.ntfy_nest_lim = 20;

    mudstate.logging = 0;
    mudstate.attr_next = A_USER_START;
    mudstate.iter_alist.data = NULL;
    mudstate.iter_alist.len = 0;
    mudstate.iter_alist.next = NULL;
    mudstate.mod_alist = NULL;
    mudstate.mod_alist_len = 0;
    mudstate.mod_size = 0;
    mudstate.mod_al_id = NOTHING;
    mudstate.min_size = 0;
    mudstate.db_top = 0;
    mudstate.db_size = 0;
    mudstate.freelist = NOTHING;
    mudstate.markbits = NULL;
#endif // STANDALONE
}

#ifndef STANDALONE

/*
 * ---------------------------------------------------------------------------
 * * cf_log_notfound: Log a 'parameter not found' error.
 */

void cf_log_notfound(dbref player, char *cmd, const char *thingname, char *thing)
{
    char buff[LBUF_SIZE * 2];

    if (mudstate.initializing) 
    {
        STARTLOG(LOG_STARTUP, "CNF", "NFND");
        sprintf(buff, "%s: %s %s not found", cmd, thingname, thing);
        log_text(buff);
        ENDLOG;
    }
    else
    {
        sprintf(buff, "%s %s not found", thingname, thing);
        notify(player, buff);
    }
}

/*
 * ---------------------------------------------------------------------------
 * * cf_log_syntax: Log a syntax error.
 */
void DCL_CDECL cf_log_syntax(dbref player, char *cmd, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);

    char *buf = alloc_lbuf("cf_log_syntax");
    Tiny_vsnprintf(buf, LBUF_SIZE, fmt, ap);
    if (mudstate.initializing)
    {
        STARTLOG(LOG_STARTUP, "CNF", "SYNTX")
        log_text(cmd);
        log_text((char *)": ");
        log_text(buf);
        ENDLOG;
    }
    else
    {
        notify(player, buf);
    }
    free_lbuf(buf);
    va_end(ap);
}

/*
 * ---------------------------------------------------------------------------
 * * cf_status_from_succfail: Return command status from succ and fail info
 */

int cf_status_from_succfail(dbref player, char *cmd, int success, int failure)
{
    char *buff;

    /*
     * If any successes, return SUCCESS(0) if no failures or * * * * *
     * PARTIAL_SUCCESS(1) if any failures. 
     */

    if (success > 0)
        return ((failure == 0) ? 0 : 1);

    /*
     * No successes.  If no failures indicate nothing done. Always return 
     * 
     * *  * *  * *  * *  * * FAILURE(-1) 
     */

    if (failure == 0)
    {
        if (mudstate.initializing)
        {
            STARTLOG(LOG_STARTUP, "CNF", "NDATA")
            buff = alloc_lbuf("cf_status_from_succfail.LOG");
            sprintf(buff, "%s: Nothing to set", cmd);
            log_text(buff);
            free_lbuf(buff);
            ENDLOG
        }
        else
        {
            notify(player, "Nothing to set");
        }
    }
    return -1;
}

/*
 * ---------------------------------------------------------------------------
 * * cf_int: Set integer parameter.
 */

CF_HAND(cf_int)
{
    // Copy the numeric value to the parameter.
    //
    *vp = Tiny_atol(str);
    return 0;
}

/* ---------------------------------------------------------------------------
 * cf_bool: Set boolean parameter.
 */

NAMETAB bool_names[] =
{
    {(char *)"true",    1,  0,  1},
    {(char *)"false",   1,  0,  0},
    {(char *)"yes",     1,  0,  1},
    {(char *)"no",      1,  0,  0},
    {(char *)"1",       1,  0,  1},
    {(char *)"0",       1,  0,  0},
    {NULL,          0,  0,  0}
};

CF_HAND(cf_bool)
{
    *vp = (int) search_nametab(GOD, bool_names, str);
    if (*vp < 0)
        *vp = (long) 0;
    return 0;
}

/*
 * ---------------------------------------------------------------------------
 * * cf_option: Select one option from many choices.
 */

CF_HAND(cf_option)
{
    int i;

    i = search_nametab(GOD, (NAMETAB *) extra, str);
    if (i < 0)
    {
        cf_log_notfound(player, cmd, "Value", str);
        return -1;
    }
    *vp = i;
    return 0;
}

/*
 * ---------------------------------------------------------------------------
 * * cf_string: Set string parameter.
 */

CF_HAND(cf_string)
{
    char *pc = (char *)vp;

    // The following should never happen because extra is always a non-zero
    // constant in the config table.
    //
    if (extra <= 0) return 1;

    // Copy the string to the buffer if it is not too big.
    //
    int retval = 0;
    unsigned int nStr = strlen(str);
    if (nStr >= extra)
    {
        nStr = extra - 1;
        if (mudstate.initializing)
        {
            STARTLOG(LOG_STARTUP, "CNF", "NFND");
            char *buff = alloc_lbuf("cf_string.LOG");
            sprintf(buff, "%s: String truncated", cmd);
            log_text(buff);
            free_lbuf(buff);
            ENDLOG;
        }
        else
        {
            notify(player, "String truncated");
        }
        retval = 1;
    }
    memcpy(pc, str, nStr+1);
    pc[nStr] = '\0';

#ifdef WIN32
    if (pc == mudconf.mud_name)
    {
        // We are changing the name of the MUD. Let the logger know.
        //
        Log.ChangePrefix(mudconf.mud_name);
    }
#endif // WIN32

    return retval;
}

/*
 * ---------------------------------------------------------------------------
 * * cf_string_dyn: Set string parameter using dynamically allocated memory.
 */

CF_HAND(cf_string_dyn)
{
    char **ppc = (char **)vp;

    // Allocate memory for buffer and copy string to it. If extra is non-zero,
    // then there is a size limitation as well.
    //
    int retval = 0;
    unsigned int nStr = strlen(str);
    if (extra && nStr >= extra)
    {                   
        nStr = extra - 1;
        if (mudstate.initializing)
        {  
            STARTLOG(LOG_STARTUP, "CNF", "NFND");
            char *logbuff = alloc_lbuf("cf_string.LOG");
            sprintf(logbuff, "%s: String truncated", cmd);
            log_text(logbuff);
            free_lbuf(logbuff);
            ENDLOG;
        }
        else
        {  
            notify(player, "String truncated");
        }
        retval = 1;
    }             
    char *confbuff = StringCloneLen(str, nStr);

    // Free previous memory for buffer.
    //
    if (*ppc != NULL)
    {
        MEMFREE(*ppc);
    }
    *ppc = confbuff;

    return retval; 
}

/*
 * ---------------------------------------------------------------------------
 * * cf_alias: define a generic hash table alias.
 */

CF_HAND(cf_alias)
{
    TINY_STRTOK_STATE tts;
    Tiny_StrTokString(&tts, str);
    Tiny_StrTokControl(&tts, " \t=,");
    char *alias = Tiny_StrTokParse(&tts);
    char *orig = Tiny_StrTokParse(&tts);

    if (orig)
    {
        _strlwr(orig);
        int *cp = hashfindLEN(orig, strlen(orig), (CHashTable *) vp);
        if (cp == NULL)
        {
            _strupr(orig);
            cp = hashfindLEN(orig, strlen(orig), (CHashTable *) vp);
            if (cp == NULL)
            {
                cf_log_notfound(player, cmd, "Entry", orig);
                return -1;
            }
        }
        hashaddLEN(alias, strlen(alias), cp, (CHashTable *) vp);
        return 0;
    }
    return -1;
}

/*
 * ---------------------------------------------------------------------------
 * * cf_flagalias: define a flag alias.
 */

CF_HAND(cf_flagalias)
{
    TINY_STRTOK_STATE tts;
    Tiny_StrTokString(&tts, str);
    Tiny_StrTokControl(&tts, " \t=,");
    char *alias = Tiny_StrTokParse(&tts);
    char *orig = Tiny_StrTokParse(&tts);

    int success = 0;
    int  nName;
    BOOL bValid;
    int *cp;
    char *pName = MakeCanonicalFlagName(orig, &nName, &bValid);
    if (  bValid
       && (cp = hashfindLEN(pName, nName, &mudstate.flags_htab)))
    {
        char *pName = MakeCanonicalFlagName(alias, &nName, &bValid);
        if (bValid)
        {
            hashaddLEN(pName, nName, cp, &mudstate.flags_htab);
            success++;
        }
    }
    if (!success)
    {
        cf_log_notfound(player, cmd, "Flag", orig);
    }
    return ((success > 0) ? 0 : -1);
}

/*
 * ---------------------------------------------------------------------------
 * * cf_or_in_bits: OR in bits from namelist to a word.
 */

CF_HAND(cf_or_in_bits)
{
    int f, success, failure;

    // Walk through the tokens.
    //
    success = failure = 0;
    TINY_STRTOK_STATE tts;
    Tiny_StrTokString(&tts, str);
    Tiny_StrTokControl(&tts, " \t");
    char *sp = Tiny_StrTokParse(&tts);
    while (sp != NULL)
    {
        // Set the appropriate bit.
        //
        f = search_nametab(GOD, (NAMETAB *) extra, sp);
        if (f > 0)
        {
            *vp |= f;
            success++;
        }
        else
        {
            cf_log_notfound(player, cmd, "Entry", sp);
            failure++;
        }

        // Get the next token.
        //
        sp = Tiny_StrTokParse(&tts);
    }
    return cf_status_from_succfail(player, cmd, success, failure);
}

/*
 * ---------------------------------------------------------------------------
 * * cf_modify_bits: set or clear bits in a flag word from a namelist.
 */
CF_HAND(cf_modify_bits)
{
    int f, negate, success, failure;

    // Walk through the tokens.
    //
    success = failure = 0;
    TINY_STRTOK_STATE tts;
    Tiny_StrTokString(&tts, str);
    Tiny_StrTokControl(&tts, " \t");
    char *sp = Tiny_StrTokParse(&tts);
    while (sp != NULL)
    {
        // Check for negation.
        //
        negate = 0;
        if (*sp == '!')
        {
            negate = 1;
            sp++;
        }

        // Set or clear the appropriate bit.
        //
        f = search_nametab(GOD, (NAMETAB *) extra, sp);
        if (f > 0)
        {
            if (negate)
                *vp &= ~f;
            else
                *vp |= f;
            success++;
        }
        else
        {
            cf_log_notfound(player, cmd, "Entry", sp);
            failure++;
        }

        // Get the next token.
        //
        sp = Tiny_StrTokParse(&tts);
    }
    return cf_status_from_succfail(player, cmd, success, failure);
}

/*
 * ---------------------------------------------------------------------------
 * * cf_set_bits: Clear flag word and then set specified bits from namelist.
 */

CF_HAND(cf_set_bits)
{
    int f, success, failure;

    /*
     * Walk through the tokens 
     */

    success = failure = 0;
    *vp = 0;

    TINY_STRTOK_STATE tts;
    Tiny_StrTokString(&tts, str);
    Tiny_StrTokControl(&tts, " \t");
    char *sp = Tiny_StrTokParse(&tts);
    while (sp != NULL)
    {
        // Set the appropriate bit.
        //
        f = search_nametab(GOD, (NAMETAB *) extra, sp);
        if (f > 0)
        {
            *vp |= f;
            success++;
        }
        else
        {
            cf_log_notfound(player, cmd, "Entry", sp);
            failure++;
        }

        // Get the next token.
        //
        sp = Tiny_StrTokParse(&tts);
    }
    return cf_status_from_succfail(player, cmd, success, failure);
}

/*
 * ---------------------------------------------------------------------------
 * * cf_set_flags: Clear flag word and then set from a flags htab.
 */

CF_HAND(cf_set_flags)
{
    FLAGENT *fp;
    FLAGSET *fset;

    int success, failure;

    // Walk through the tokens.
    //
    success = failure = 0;
    TINY_STRTOK_STATE tts;
    Tiny_StrTokString(&tts, str);
    Tiny_StrTokControl(&tts, " \t");
    char *sp = Tiny_StrTokParse(&tts);
    fset = (FLAGSET *) vp;

    while (sp != NULL)
    {
        // Set the appropriate bit.
        //
        fp = (FLAGENT *) hashfindLEN(sp, strlen(sp), &mudstate.flags_htab);
        if (fp != NULL)
        {
            if (success == 0)
            {
                (*fset).word1 = 0;
                (*fset).word2 = 0;
                (*fset).word3 = 0;
            }
            if (fp->flagflag & FLAG_WORD3)
                (*fset).word3 |= fp->flagvalue;
            else if (fp->flagflag & FLAG_WORD2)
                (*fset).word2 |= fp->flagvalue;
            else
                (*fset).word1 |= fp->flagvalue;
            success++;
        }
        else
        {
            cf_log_notfound(player, cmd, "Entry", sp);
            failure++;
        }

        /*
         * Get the next token 
         */

        sp = Tiny_StrTokParse(&tts);
    }
    if ((success == 0) && (failure == 0))
    {
        (*fset).word1 = 0;
        (*fset).word2 = 0;
        (*fset).word3 = 0;
        return 0;
    }
    if (success > 0)
    {
        return ((failure == 0) ? 0 : 1);
    }
    return -1;
}

/*
 * ---------------------------------------------------------------------------
 * * cf_badname: Disallow use of player name/alias.
 */

CF_HAND(cf_badname)
{
    if (extra)
        badname_remove(str);
    else
        badname_add(str);
    return 0;
}

/* ---------------------------------------------------------------------------
 * sane_inet_addr: inet_addr() does not necessarily do reasonable checking
 * for sane syntax. On certain operating systems, if passed less than four
 * octets, it will cause a segmentation violation. This is unfriendly.
 * We take steps here to deal with it.
 *
 * This approach specifically disallows the Berkeley-only IP formats:
 *
 *    a.b.c (e.g., Class B 128.net.host)
 *    a.b   (e.g., class A: net.host)
 *    a     (single 32-bit number)
 *
 * Avoiding a SIGSEGV on certain operating systems is better than supporting
 * niche formats that are only available on Berkeley Unix.
 */
static unsigned long sane_inet_addr(char *str)
{
    int i;
    
    char *p = str;
    for (i = 1; (p = (char *) strchr(p, '.')) != NULL; i++, p++)
    {
        // Nothing
    }
    if (i < 4)
    {
        return (unsigned long)INADDR_NONE;
    }
    else
    {
        return inet_addr(str);
    }
}

// Given a host-ordered mask, this function will determine whether it is a
// valid one. Valid masks consist of a N-bit sequence of '1' bits followed by
// a (32-N)-bit sequence of '0' bits, where N is 0 to 32.
//
BOOL isValidSubnetMask(unsigned long ulMask)
{
    unsigned long ulTest = 0xFFFFFFFFUL;
    for (int i = 0; i <= 32; i++)
    {
        if (ulMask == ulTest)
        {
            return TRUE;
        }
        ulTest <<= 1;
    }
    return FALSE;
}

/*
 * ---------------------------------------------------------------------------
 * * cf_site: Update site information
 */

CF_HAND(cf_site)
{
    struct in_addr addr_num, mask_num;
    unsigned long ulMask;
    
    char *addr_txt;
    char *mask_txt = strchr(str, '/');
    if (!mask_txt)
    {
        // Standard IP range and netmask notation.
        //
        TINY_STRTOK_STATE tts;
        Tiny_StrTokString(&tts, str);
        Tiny_StrTokControl(&tts, " \t=,");
        addr_txt = Tiny_StrTokParse(&tts);
        mask_txt = NULL;
        if (addr_txt)
        {
            mask_txt = Tiny_StrTokParse(&tts);
        }
        if (!addr_txt || !*addr_txt || !mask_txt || !*mask_txt)
        {
            cf_log_syntax(player, cmd, "Missing host address or mask.", (char *)"");
            return -1;
        }
        mask_num.s_addr = sane_inet_addr(mask_txt);
        if (  mask_num.s_addr == INADDR_NONE
           || !isValidSubnetMask(ulMask = ntohl(mask_num.s_addr)))
        {
            cf_log_syntax(player, cmd, "Malformed mask address: %s", mask_txt);
            return -1;
        }
    }
    else
    {
        // RFC 1517, 1518, 1519, 1520: CIDR IP prefix notation
        //
        addr_txt = str;
        *mask_txt++ = '\0';
        int mask_bits = Tiny_atol(mask_txt);
        if ((mask_bits > 32) || (mask_bits < 0))
        {
            cf_log_syntax(player, cmd, "Mask bits (%d) in CIDR IP prefix out of range.", mask_bits);
            return -1;
        }
        else
        {
            // << [0,31] works. << 32 is problematic on some systems.
            //
            ulMask = 0;
            if (mask_bits > 0)
            {
                ulMask = 0xFFFFFFFFUL << (32 - mask_bits);
            }
            mask_num.s_addr = htonl(ulMask);
        }
    }
    addr_num.s_addr = sane_inet_addr(addr_txt);
    if (addr_num.s_addr == INADDR_NONE)
    {
        cf_log_syntax(player, cmd, "Malformed host address: %s", addr_txt);
        return -1;
    }
    unsigned long ulAddr = ntohl(addr_num.s_addr);

    if (ulAddr & ~ulMask)
    {
        // The given subnet address contains 'one' bits which are outside
        // the given subnet mask. If we don't clear these bits, they will
        // interfere with the subnet tests in site_check. The subnet spec
        // would be defunct and useless.
        //
        cf_log_syntax(player, cmd, "Non-zero host address bits outside the subnet mask (fixed): %s %s", addr_txt, mask_txt);
        ulAddr &= ulMask;
        addr_num.s_addr = htonl(ulAddr);
    }
    
    SITE *head = (SITE *) * vp;

    // Parse the access entry and allocate space for it.
    //
    SITE *site = (SITE *)MEMALLOC(sizeof(SITE));
    ISOUTOFMEMORY(site);
    
    // Initialize the site entry.
    //
    site->address.s_addr = addr_num.s_addr;
    site->mask.s_addr = mask_num.s_addr;
    site->flag = extra;
    site->next = NULL;
    
    // Link in the entry. Link it at the start if not initializing, at the
    // end if initializing. This is so that entries in the config file are
    // processed as you would think they would be, while entries made while
    // running are processed first.
    //
    if (mudstate.initializing)
    {
        if (head == NULL)
        {
            *vp = (int) site;
        }
        else
        {
            SITE *last;
            for (last = head; last->next; last = last->next)
            {
                // Nothing
            }
            last->next = site;
        }
    }
    else
    {
        site->next = head;
        *vp = (int) site;
    }
    return 0;
}

/*
 * ---------------------------------------------------------------------------
 * * cf_cf_access: Set access on config directives
 */

CF_HAND(cf_cf_access)
{
    CONF *tp;
    char *ap;

    for (ap = str; *ap && !Tiny_IsSpace[(unsigned char)*ap]; ap++)
    {
        ; // Nothing
    }
    if (*ap)
    {
        *ap++ = '\0';
    }

    for (tp = conftable; tp->pname; tp++)
    {
        if (!strcmp(tp->pname, str))
        {
            // Cannot modify parameters set CA_STATIC.
            //
            if (tp->flags & CA_STATIC)
            {
                notify(player, NOPERM_MESSAGE);
                STARTLOG(LOG_CONFIGMODS, "CFG", "PERM");
                log_name(player);
                log_text((char *) " tried to change access to static param: ");
                log_text(tp->pname);
                ENDLOG;
                return -1;
            }
            return cf_modify_bits(&tp->flags, ap, extra, player, cmd);
        }
    }
    cf_log_notfound(player, cmd, "Config directive", str);
    return -1;
}

/*
 * ---------------------------------------------------------------------------
 * * cf_include: Read another config file.  Only valid during startup.
 */

CF_HAND(cf_include)
{
    FILE *fp;
    char *cp, *ap, *zp, *buf;

    extern int FDECL(cf_set, (char *, char *, dbref));


    if (!mudstate.initializing)
        return -1;

    fp = fopen(str, "rb");
    if (fp == NULL)
    {
        cf_log_notfound(player, cmd, "Config file", str);
        return -1;
    }
    DebugTotalFiles++;
    buf = alloc_lbuf("cf_include");
    fgets(buf, LBUF_SIZE, fp);
    while (!feof(fp))
    {
        cp = buf;
        if (*cp == '#')
        {
            fgets(buf, LBUF_SIZE, fp);
            continue;
        }

        // Not a comment line. Strip off the NL and any characters
        // following it.  Then, split the line into the command and argument
        // portions (separated by a space).  Also, trim off the trailing
        // comment, if any (delimited by #).
        //
        for (cp = buf; *cp && *cp != '\n'; cp++) ;

        // Strip '\n'
        //
        *cp = '\0';

        // Strip spaces.
        //
        for (cp = buf; Tiny_IsSpace[(unsigned char)*cp]; cp++) ;

        // Skip over command.
        //
        for (ap = cp; *ap && !Tiny_IsSpace[(unsigned char)*ap]; ap++) ;

        // Trim command.
        //
        if (*ap)
            *ap++ = '\0';

        // Skip Spaces.
        //
        for (; Tiny_IsSpace[(unsigned char)*ap]; ap++) ;

        // Find comment.
        //
        for (zp = ap; *zp && (*zp != '#'); zp++) ;

        // Zap comment.
        //
        if (*zp)
            *zp = '\0';

        // Zap trailing spaces.
        //
        for (zp = zp - 1; zp >= ap && Tiny_IsSpace[(unsigned char)*zp]; zp--)
            *zp = '\0';

        cf_set(cp, ap, player);
        fgets(buf, LBUF_SIZE, fp);
    }
    free_lbuf(buf);
    if (fclose(fp) == 0)
    {
        DebugTotalFiles--;
    }
    return 0;
}

extern CF_HAND(cf_access);
extern CF_HAND(cf_cmd_alias);
extern CF_HAND(cf_acmd_access);
extern CF_HAND(cf_attr_access);
extern CF_HAND(cf_func_access);
extern CF_HAND(cf_flag_access);

/* ---------------------------------------------------------------------------
 * conftable: Table for parsing the configuration file.
 */

CONF conftable[] =
{
    {(char *)"access",  cf_access,  CA_GOD,     NULL,   (long)access_nametab},
    {(char *)"alias",   cf_cmd_alias,   CA_GOD,     (int *)&mudstate.command_htab,  0},
    {(char *)"attr_access",     cf_attr_access, CA_GOD,     NULL,   (long)attraccess_nametab},
    {(char *)"attr_alias",  cf_alias,   CA_GOD,     (int *)&mudstate.attr_name_htab,0},
    {(char *)"attr_cmd_access", cf_acmd_access, CA_GOD,     NULL, (long)access_nametab},
    {(char *)"bad_name",    cf_badname, CA_GOD,     NULL,               0},
    {(char *)"badsite_file",            cf_string_dyn,  CA_STATIC,  (int *)&mudconf.site_file,   SIZEOF_PATHNAME},
    {(char *)"cache_names",             cf_bool,        CA_STATIC,  &mudconf.cache_names,       0},
    {(char *)"check_interval",    cf_int,     CA_GOD,     &mudconf.check_interval,    0},
    {(char *)"check_offset",    cf_int,     CA_GOD,     &mudconf.check_offset,      0},
    {(char *)"clone_copies_cost",    cf_bool,    CA_GOD,     &mudconf.clone_copy_cost,   0},
    {(char *)"comsys_database",         cf_string_dyn,  CA_STATIC,  (int *)&mudconf.comsys_db,   SIZEOF_PATHNAME},
    {(char *)"command_quota_increment",    cf_int,     CA_GOD,     &mudconf.cmd_quota_incr,    0},
    {(char *)"command_quota_max",    cf_int,     CA_GOD,     &mudconf.cmd_quota_max,     0},
    {(char *)"compress_program",        cf_string_dyn,  CA_STATIC,  (int *)&mudconf.compress, SIZEOF_PATHNAME},
    {(char *)"compression",    cf_bool,    CA_GOD,     &mudconf.compress_db,       0},
    {(char *)"concentrator_port",       cf_int,         CA_STATIC,  &mudconf.conc_port,     0},
    {(char *)"config_access",    cf_cf_access,   CA_GOD,     NULL,    (long)access_nametab},
    {(char *)"conn_timeout",    cf_int,     CA_GOD,     &mudconf.conn_timeout,      0},
    {(char *)"connect_file",            cf_string_dyn,  CA_STATIC,  (int *)&mudconf.conn_file,   SIZEOF_PATHNAME},
    {(char *)"connect_reg_file",        cf_string_dyn,  CA_STATIC,  (int *)&mudconf.creg_file,   SIZEOF_PATHNAME},
    {(char *)"crash_database",          cf_string_dyn,  CA_STATIC,  (int *)&mudconf.crashdb,  SIZEOF_PATHNAME},
    {(char *)"create_max_cost",    cf_int,     CA_GOD,     &mudconf.createmax,     0},
    {(char *)"create_min_cost",    cf_int,     CA_GOD,     &mudconf.createmin,     0},
    {(char *)"dark_sleepers",    cf_bool,    CA_GOD,     &mudconf.dark_sleepers,     0},
    {(char *)"default_home",    cf_int,     CA_GOD,     &mudconf.default_home,      0},
    {(char *)"dig_cost",    cf_int,     CA_GOD,     &mudconf.digcost,       0},
    {(char *)"down_file",               cf_string_dyn,  CA_STATIC,  (int *)&mudconf.down_file,   SIZEOF_PATHNAME},
    {(char *)"down_motd_message",    cf_string,  CA_GOD,     (int *)mudconf.downmotd_msg,    GBUF_SIZE},
    {(char *)"dump_interval",    cf_int,     CA_GOD,     &mudconf.dump_interval,     0},
    {(char *)"dump_message",    cf_string,  CA_GOD,     (int *)mudconf.dump_msg,    128},
    {(char *)"postdump_message",    cf_string,  CA_GOD,     (int *)mudconf.postdump_msg,    128},
    {(char *)"pueblo_message",    cf_string,      CA_GOD,         (int *) mudconf.pueblo_msg,     GBUF_SIZE},
    {(char *)"dump_offset",    cf_int,     CA_GOD,     &mudconf.dump_offset,       0},
    {(char *)"earn_limit",    cf_int,     CA_GOD,     &mudconf.paylimit,      0},
    {(char *)"examine_flags",    cf_bool,    CA_GOD,     &mudconf.ex_flags,      0},
    {(char *)"examine_public_attrs",    cf_bool,    CA_GOD,     &mudconf.exam_public,       0},
    {(char *)"exit_flags",    cf_set_flags,   CA_GOD,     (int *)&mudconf.exit_flags, 0},
    {(char *)"exit_quota",    cf_int,     CA_GOD,     &mudconf.exit_quota,        0},
    {(char *)"events_daily_hour",    cf_int,     CA_GOD,     &mudconf.events_daily_hour, 0},
    {(char *)"fascist_teleport",    cf_bool,    CA_GOD,     &mudconf.fascist_tport,     0},
    {(char *)"fixed_home_message",      cf_string,      CA_STATIC,  (int *)mudconf.fixed_home_msg,  128},
    {(char *)"fixed_tel_message",       cf_string,      CA_STATIC,  (int *)mudconf.fixed_tel_msg,   128},
    {(char *)"find_money_chance",    cf_int,     CA_GOD,     &mudconf.payfind,       0},
    {(char *)"flag_access",   cf_flag_access, CA_GOD,     NULL,               0},
    {(char *)"flag_alias",    cf_flagalias,   CA_GOD,     NULL,               0},
    {(char *)"forbid_site",    cf_site,    CA_GOD,     (int *)&mudstate.access_list,    H_FORBIDDEN},
#if !defined(VMS) && !defined(WIN32)
    {(char *)"fork_dump",    cf_bool,    CA_GOD,     &mudconf.fork_dump,     0},
    {(char *)"fork_vfork",    cf_bool,    CA_GOD,     &mudconf.fork_vfork,        0},
#endif // WIN32
    {(char *)"full_file",               cf_string_dyn,  CA_STATIC,  (int *)&mudconf.full_file,   SIZEOF_PATHNAME},
    {(char *)"full_motd_message",    cf_string,  CA_GOD,     (int *)mudconf.fullmotd_msg,    GBUF_SIZE},
    {(char *)"function_access",    cf_func_access, CA_GOD,     NULL,    (long)access_nametab},
    {(char *)"function_alias",    cf_alias,   CA_GOD,     (int *)&mudstate.func_htab, 0},
    {(char *)"function_invocation_limit",    cf_int,         CA_GOD,         &mudconf.func_invk_lim,     0},
    {(char *)"function_recursion_limit",    cf_int,         CA_GOD,         &mudconf.func_nest_lim,     0},
    {(char *)"game_dir_file",           cf_string_dyn,  CA_STATIC,  (int *)&mudconf.game_dir,   SIZEOF_PATHNAME},
    {(char *)"game_pag_file",           cf_string_dyn,  CA_STATIC,  (int *)&mudconf.game_pag,   SIZEOF_PATHNAME},
    {(char *)"good_name",    cf_badname, CA_GOD,     NULL,               1},
    {(char *)"guest_char_num",          cf_int,         CA_STATIC,  &mudconf.guest_char,        0},
    {(char *)"guest_nuker",        cf_int,         CA_GOD,         &mudconf.guest_nuker,           0},
    {(char *)"guest_prefix",            cf_string,      CA_STATIC,  (int *)mudconf.guest_prefix,    32},
    {(char *)"guest_file",              cf_string_dyn,  CA_STATIC,  (int *)&mudconf.guest_file,  SIZEOF_PATHNAME},
    {(char *)"number_guests",           cf_int,         CA_STATIC,  &mudconf.number_guests,         0},
    {(char *)"guests_channel",          cf_string,      CA_STATIC,  (int *)mudconf.guests_channel,  32},
    {(char *)"guest_site",              cf_site,        CA_GOD,     (int *)&mudstate.access_list,   H_GUEST},
    {(char *)"have_comsys",             cf_bool,        CA_STATIC,  &mudconf.have_comsys,       0},
    {(char *)"have_mailer",             cf_bool,        CA_STATIC,  &mudconf.have_mailer,       0},
    {(char *)"have_zones",              cf_bool,        CA_STATIC,  &mudconf.have_zones,        0},
    {(char *)"help_file",               cf_string_dyn,  CA_STATIC,  (int *)&mudconf.help_file,   SIZEOF_PATHNAME},
    {(char *)"help_index",              cf_string_dyn,  CA_STATIC,  (int *)&mudconf.help_indx,   SIZEOF_PATHNAME},
    {(char *)"hostnames",    cf_bool,    CA_GOD,     &mudconf.use_hostname,      0},
    {(char *)"use_http",                cf_bool,        CA_STATIC,  &mudconf.use_http,      0},
    {(char *)"idle_wiz_dark",    cf_bool,    CA_GOD,     &mudconf.idle_wiz_dark,     0},
    {(char *)"idle_interval",    cf_int,     CA_GOD,     &mudconf.idle_interval,     0},
    {(char *)"idle_timeout",    cf_int,     CA_GOD,     &mudconf.idle_timeout,      0},
    {(char *)"include",                 cf_include,     CA_STATIC,  NULL,               0},
    {(char *)"indent_desc",    cf_bool,    CA_GOD,     &mudconf.indent_desc,       0},
    {(char *)"initial_size",            cf_int,         CA_STATIC,  &mudconf.init_size,     0},
    {(char *)"input_database",          cf_string_dyn,  CA_STATIC,  (int *)&mudconf.indb,    SIZEOF_PATHNAME},
    {(char *)"kill_guarantee_cost",    cf_int,     CA_GOD,     &mudconf.killguarantee,     0},
    {(char *)"kill_max_cost",    cf_int,     CA_GOD,     &mudconf.killmax,       0},
    {(char *)"kill_min_cost",    cf_int,     CA_GOD,     &mudconf.killmin,       0},
    {(char *)"lag_maximum",             cf_int,         CA_GOD,     &mudconf.max_cmdsecs,       0},
    {(char *)"link_cost",    cf_int,     CA_GOD,     &mudconf.linkcost,      0},
    {(char *)"list_access",    cf_ntab_access, CA_GOD,     (int *)list_names,    (long)access_nametab},
    {(char *)"lock_recursion_limit",    cf_int,         CA_WIZARD,         &mudconf.lock_nest_lim,      0},
    {(char *)"log",    cf_modify_bits, CA_GOD,     &mudconf.log_options,    (long)logoptions_nametab},
    {(char *)"log_options",    cf_modify_bits, CA_GOD,     &mudconf.log_info,    (long)logdata_nametab},
    {(char *)"logout_cmd_access",    cf_ntab_access, CA_GOD,     (int *)logout_cmdtable,    (long)access_nametab},
    {(char *)"logout_cmd_alias",    cf_alias,   CA_GOD,     (int *)&mudstate.logout_cmd_htab,0},
    {(char *)"look_obey_terse",    cf_bool,    CA_GOD,     &mudconf.terse_look,        0},
    {(char *)"machine_command_cost",    cf_int,     CA_GOD,     &mudconf.machinecost,       0},
    {(char *)"mail_database",    cf_string_dyn,  CA_GOD,     (int *)&mudconf.mail_db,     SIZEOF_PATHNAME},
    {(char *)"mail_expiration",    cf_int,     CA_GOD,     &mudconf.mail_expiration,   0},
    {(char *)"master_room",    cf_int,     CA_GOD,     &mudconf.master_room,       0},
    {(char *)"match_own_commands",    cf_bool,    CA_GOD,     &mudconf.match_mine,        0},
    {(char *)"max_players",    cf_int,     CA_GOD,     &mudconf.max_players,       0},
    {(char *)"max_cache_size", cf_int,     CA_GOD,     (int *)&mudconf.max_cache_size,    0},
    {(char *)"money_name_plural",    cf_string,  CA_GOD,     (int *)mudconf.many_coins,  32},
    {(char *)"money_name_singular",    cf_string,  CA_GOD,     (int *)mudconf.one_coin,    32},
    {(char *)"motd_file",               cf_string_dyn,  CA_STATIC,  (int *)&mudconf.motd_file,   SIZEOF_PATHNAME},
    {(char *)"motd_message",    cf_string,  CA_GOD,     (int *)mudconf.motd_msg,    GBUF_SIZE},
    {(char *)"mud_name",    cf_string,  CA_GOD,     (int *)mudconf.mud_name,    32},
    {(char *)"news_file",               cf_string_dyn,  CA_STATIC,  (int *)&mudconf.news_file,   SIZEOF_PATHNAME},
    {(char *)"news_index",              cf_string_dyn,  CA_STATIC,  (int *)&mudconf.news_indx,   SIZEOF_PATHNAME},
    {(char *)"newuser_file",            cf_string_dyn,  CA_STATIC,  (int *)&mudconf.crea_file,   SIZEOF_PATHNAME},
    {(char *)"notify_recursion_limit",    cf_int,         CA_GOD,         &mudconf.ntfy_nest_lim,     0},
    {(char *)"open_cost",    cf_int,     CA_GOD,     &mudconf.opencost,      0},
    {(char *)"output_database",         cf_string_dyn,  CA_STATIC,  (int *)&mudconf.outdb,   SIZEOF_PATHNAME},
    {(char *)"output_limit",    cf_int,     CA_GOD,     &mudconf.output_limit,      0},
    {(char *)"page_cost",    cf_int,     CA_GOD,     &mudconf.pagecost,      0},
    {(char *)"paranoid_allocate",    cf_bool,    CA_GOD,     &mudconf.paranoid_alloc,    0},
    {(char *)"parent_recursion_limit",    cf_int,     CA_GOD,     &mudconf.parent_nest_lim,   0},
    {(char *)"paycheck",    cf_int,     CA_GOD,     &mudconf.paycheck,      0},
    {(char *)"pemit_far_players",    cf_bool,    CA_GOD,     &mudconf.pemit_players,     0},
    {(char *)"pemit_any_object",    cf_bool,    CA_GOD,     &mudconf.pemit_any,     0},
    {(char *)"permit_site",    cf_site,    CA_GOD,     (int *)&mudstate.access_list,   0},
    {(char *)"player_flags",    cf_set_flags,   CA_GOD,     (int *)&mudconf.player_flags,   0},
    {(char *)"player_listen",    cf_bool,    CA_GOD,     &mudconf.player_listen,     0},
    {(char *)"player_match_own_commands",    cf_bool,    CA_GOD,     &mudconf.match_mine_pl,     0},
    {(char *)"player_name_spaces",    cf_bool,    CA_GOD,     &mudconf.name_spaces,       0},
    {(char *)"player_queue_limit",    cf_int,     CA_GOD,     &mudconf.queuemax,      0},
    {(char *)"player_quota",    cf_int,     CA_GOD,     &mudconf.player_quota,      0},
    {(char *)"player_starting_home",    cf_int,     CA_GOD,     &mudconf.start_home,        0},
    {(char *)"player_starting_room",    cf_int,     CA_GOD,     &mudconf.start_room,        0},
    {(char *)"plushelp_file",           cf_string_dyn,  CA_STATIC,  (int *)&mudconf.plushelp_file,  SIZEOF_PATHNAME},
    {(char *)"plushelp_index",          cf_string_dyn,  CA_STATIC,  (int *)&mudconf.plushelp_indx,  SIZEOF_PATHNAME},
    {(char *)"staffhelp_file",          cf_string_dyn,  CA_STATIC,  (int *)&mudconf.staffhelp_file, SIZEOF_PATHNAME},
    {(char *)"staffhelp_index",         cf_string_dyn,  CA_STATIC,  (int *)&mudconf.staffhelp_indx, SIZEOF_PATHNAME},
    {(char *)"public_channel",          cf_string,      CA_STATIC,  (int *)mudconf.public_channel,  32},
    {(char *)"wiznews_file",            cf_string_dyn,  CA_STATIC,  (int *)&mudconf.wiznews_file,   SIZEOF_PATHNAME},
    {(char *)"wiznews_index",           cf_string_dyn,  CA_STATIC,  (int *)&mudconf.wiznews_indx,   SIZEOF_PATHNAME},
    {(char *)"port",                    cf_int,         CA_STATIC,  &mudconf.port,          0},
    {(char *)"public_flags",    cf_bool,    CA_GOD,     &mudconf.pub_flags,     0},
    {(char *)"queue_active_chunk",    cf_int,     CA_GOD,     &mudconf.active_q_chunk,    0},
    {(char *)"queue_idle_chunk",    cf_int,     CA_GOD,     &mudconf.queue_chunk,       0},
    {(char *)"quiet_look",    cf_bool,    CA_GOD,     &mudconf.quiet_look,        0},
    {(char *)"quiet_whisper",    cf_bool,    CA_GOD,     &mudconf.quiet_whisper,     0},
    {(char *)"quit_file",               cf_string_dyn,  CA_STATIC,  (int *)&mudconf.quit_file,   SIZEOF_PATHNAME},
    {(char *)"quotas",    cf_bool,    CA_GOD,     &mudconf.quotas,        0},
    {(char *)"read_remote_desc",    cf_bool,    CA_GOD,     &mudconf.read_rem_desc,     0},
    {(char *)"read_remote_name",    cf_bool,    CA_GOD,     &mudconf.read_rem_name,     0},
    {(char *)"register_create_file",    cf_string_dyn,  CA_STATIC,  (int *)&mudconf.regf_file,   SIZEOF_PATHNAME},
    {(char *)"register_site",    cf_site,    CA_GOD,     (int *)&mudstate.access_list,    H_REGISTRATION},
    {(char *)"allow_guest_from_registered_site", cf_bool,    CA_GOD,     &mudconf.allow_guest_from_registered_site, 1},
    {(char *)"retry_limit",    cf_int,     CA_GOD,     &mudconf.retry_limit,       0},
    {(char *)"robot_cost",    cf_int,     CA_GOD,     &mudconf.robotcost,     0},
    {(char *)"robot_flags",    cf_set_flags,   CA_GOD,     (int *)&mudconf.robot_flags,    0},
    {(char *)"robot_speech",    cf_bool,    CA_GOD,     &mudconf.robot_speak,       0},
    {(char *)"room_flags",    cf_set_flags,   CA_GOD,     (int *)&mudconf.room_flags, 0},
    {(char *)"room_quota",    cf_int,     CA_GOD,     &mudconf.room_quota,        0},
    {(char *)"sacrifice_adjust",    cf_int,     CA_GOD,     &mudconf.sacadjust,     0},
    {(char *)"sacrifice_factor",    cf_int,     CA_GOD,     &mudconf.sacfactor,     0},
    {(char *)"safer_passwords",     cf_bool,    CA_GOD,     &mudconf.safer_passwords,   0},
    {(char *)"search_cost",    cf_int,     CA_GOD,     &mudconf.searchcost,        0},
    {(char *)"see_owned_dark",    cf_bool,    CA_GOD,     &mudconf.see_own_dark,      0},
    {(char *)"signal_action",           cf_option,      CA_STATIC,  &mudconf.sig_action,    (long)sigactions_nametab},
    {(char *)"site_chars",    cf_int,     CA_GOD,     (int *)&mudconf.site_chars,        0},
    {(char *)"space_compress",    cf_bool,    CA_GOD,     &mudconf.space_compress,    0},
    {(char *)"stack_limit",    cf_int,     CA_GOD,     &mudconf.stack_limit,       0},
    {(char *)"starting_money",    cf_int,     CA_GOD,     &mudconf.paystart,      0},
    {(char *)"starting_quota",    cf_int,     CA_GOD,     &mudconf.start_quota,       0},
    {(char *)"status_file",             cf_string_dyn,  CA_STATIC,  (int *)&mudconf.status_file, SIZEOF_PATHNAME},
    {(char *)"suspect_site",    cf_site,    CA_GOD,     (int *)&mudstate.suspect_list,    H_SUSPECT},
    {(char *)"sweep_dark",    cf_bool,    CA_GOD,     &mudconf.sweep_dark,        0},
    {(char *)"switch_default_all",    cf_bool,    CA_GOD,     &mudconf.switch_df_all,     0},
    {(char *)"terse_shows_contents",    cf_bool,    CA_GOD,     &mudconf.terse_contents,    0},
    {(char *)"terse_shows_exits",    cf_bool,    CA_GOD,     &mudconf.terse_exits,       0},
    {(char *)"terse_shows_move_messages",    cf_bool,    CA_GOD,     &mudconf.terse_movemsg,     0},
    {(char *)"thing_flags",    cf_set_flags,   CA_GOD,     (int *)&mudconf.thing_flags,    0},
    {(char *)"thing_quota",    cf_int,     CA_GOD,     &mudconf.thing_quota,       0},
    {(char *)"timeslice",    cf_int,     CA_GOD,     &mudconf.timeslice,     0},
    {(char *)"toad_recipient", cf_int,   CA_GOD,     &mudconf.toad_recipient, 0},
    {(char *)"trace_output_limit",    cf_int,     CA_GOD,     &mudconf.trace_limit,       0},
    {(char *)"trace_topdown",    cf_bool,    CA_GOD,     &mudconf.trace_topdown,     0},
    {(char *)"trust_site",    cf_site,    CA_GOD,     (int *)&mudstate.suspect_list,  0},
    {(char *)"uncompress_program",      cf_string_dyn,  CA_STATIC,  (int *)&mudconf.uncompress,  SIZEOF_PATHNAME},
    {(char *)"unowned_safe",    cf_bool,    CA_GOD,     &mudconf.safe_unowned,      0},
    {(char *)"user_attr_access",    cf_modify_bits, CA_GOD,     &mudconf.vattr_flags,    (long)attraccess_nametab},
    {(char *)"wait_cost",    cf_int,     CA_GOD,     &mudconf.waitcost,      0},
    {(char *)"wizard_help_file",        cf_string_dyn,  CA_STATIC,  (int *)&mudconf.whelp_file,  SIZEOF_PATHNAME},
    {(char *)"wizard_help_index",       cf_string_dyn,  CA_STATIC,  (int *)&mudconf.whelp_indx,  SIZEOF_PATHNAME},
    {(char *)"wizard_motd_file",        cf_string_dyn,  CA_STATIC,  (int *)&mudconf.wizmotd_file,    SIZEOF_PATHNAME},
    {(char *)"wizard_motd_message",    cf_string,  CA_GOD,     (int *)mudconf.wizmotd_msg, GBUF_SIZE},
    {(char *)"zone_recursion_limit",    cf_int,     CA_GOD,     &mudconf.zone_nest_lim,     0},
    { NULL,    NULL,       0,      NULL,               0}
};

/*
 * ---------------------------------------------------------------------------
 * * cf_set: Set config parameter.
 */

int cf_set(char *cp, char *ap, dbref player)
{
    CONF *tp;
    int i;
    char *buff = 0;

    // Search the config parameter table for the command. If we find
    // it, call the handler to parse the argument.
    //
    for (tp = conftable; tp->pname; tp++)
    {
        if (!strcmp(tp->pname, cp))
        {
            if (  !mudstate.initializing
               && !check_access(player, tp->flags))
            {
                notify(player, NOPERM_MESSAGE);
                return -1;
            }
            if (!mudstate.initializing)
            {
                buff = alloc_lbuf("cf_set");
                strcpy(buff, ap);
            }
            i = tp->interpreter(tp->loc, ap, tp->extra, player, cp);
            if (!mudstate.initializing)
            {
                STARTLOG(LOG_CONFIGMODS, "CFG", "UPDAT");
                log_name(player);
                log_text((char *)" entered config directive: ");
                log_text(cp);
                log_text((char *)" with args '");
                log_text(buff);
                log_text((char *)"'.  Status: ");
                switch (i)
                {
                case 0:
                    log_text((char *)"Success.");
                    break;

                case 1:
                    log_text((char *)"Partial success.");
                    break;

                case -1:
                    log_text((char *)"Failure.");
                    break;

                default:
                    log_text((char *)"Strange.");
                }
                ENDLOG;
                free_lbuf(buff);
            }
            return i;
        }
    }

    // Config directive not found.  Complain about it.
    //
    cf_log_notfound(player, (char *)"Set", "Config directive", cp);
    return -1;
}

/*
 * ---------------------------------------------------------------------------
 * * do_admin: Command handler to set config params at runtime 
 */

void do_admin(dbref player, dbref cause, int extra, char *kw, char *value)
{
    int i;

    i = cf_set(kw, value, player);
    if ((i >= 0) && !Quiet(player))
        notify(player, "Set.");
    return;
}

/*
 * ---------------------------------------------------------------------------
 * * cf_read: Read in config parameters from named file
 */

struct
{
    char **pFilename;
    char *pSuffix;
} DefaultSuffixes[]
=
{
    { &mudconf.outdb,    ".out" },
    { &mudconf.crashdb,  ".CRASH" },
    { &mudconf.game_dir, ".dir" },
    { &mudconf.game_pag, ".pag" },
    { 0, 0 }
};

int cf_read(void)
{
    int retval;

    mudstate.initializing = 1;
    retval = cf_include(NULL, mudconf.config_file, 0, 0, (char *)"init");
    mudstate.initializing = 0;

    // Fill in missing DB file names.
    //
    unsigned int nInDB = strlen(mudconf.indb);
    for (int i = 0; DefaultSuffixes[i].pFilename; i++)
    {
        char **p = DefaultSuffixes[i].pFilename;
        if (**p == '\0')
        {
            // The filename is an empty string so we should construct
            // a default filename.
            //
            char *pSuffix = DefaultSuffixes[i].pSuffix;
            int nSuffix = strlen(pSuffix);
            char *buff = (char *)MEMALLOC(nInDB + nSuffix + 1);
            ISOUTOFMEMORY(buff);
            memcpy(buff, mudconf.indb, nInDB);
            memcpy(buff + nInDB, pSuffix, nSuffix+1);
            MEMFREE(*p);
            *p = buff;
        }
    }
    return retval;
}

/*
 * ---------------------------------------------------------------------------
 * * list_cf_access: List access to config directives.
 */

void list_cf_access(dbref player)
{
    CONF *tp;
    char *buff;

    buff = alloc_mbuf("list_cf_access");
    for (tp = conftable; tp->pname; tp++) {
        if (God(player) || check_access(player, tp->flags)) {
            sprintf(buff, "%s:", tp->pname);
            listset_nametab(player, access_nametab, tp->flags,
                    buff, 1);
        }
    }
    free_mbuf(buff);
}

#endif /*
        * * STANDALONE  
        */