/* conf.c:	set up configuration information and static data */

#include "autoconf.h"
#include "copyright.h"
#ifndef	lint
static char RCSid[] = "$Id: conf.c,v 1.10 1995/03/20 23:59:54 ambar Exp $";
USE(RCSid);
#endif

#include "externs.h"
#include "interface.h"
#include "command.h"
#include "htab.h"
#include "alloc.h"
#include "attrs.h"
#include "flags.h"
#include "udb_defs.h"

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

typedef struct confparm CONF;
  struct confparm {
      char *pname;		/* parm name */
      int (*interpreter) ();	/* routine to interp parameter */
      int flags;		/* control flags */
      int *loc;			/* where to store value */
      void *extra;		/* extra data for interpreter */
  };

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

    strcpy(mudconf.indb, "tinymush.db");
    strcpy(mudconf.outdb, "");
    strcpy(mudconf.crashdb, "");
    strcpy(mudconf.gdbm, "");
    strcpy(mudconf.status_file, "shutdown.status");
    mudconf.port = 6250;
    mudconf.init_size = 1000;
    mudconf.use_global_aconn = 1;
    mudconf.num_guests = -1;
    for (i = 0; i < MAX_GUESTS; i++)
	mudconf.guest_chars[i] = -1;
    strcpy(mudconf.guest_file, "guest.txt");
    strcpy(mudconf.conn_file, "connect.txt");
    strcpy(mudconf.creg_file, "register.txt");
    strcpy(mudconf.regf_file, "create_reg.txt");
    strcpy(mudconf.motd_file, "motd.txt");
    strcpy(mudconf.wizmotd_file, "wizmotd.txt");
    strcpy(mudconf.quit_file, "quit.txt");
    strcpy(mudconf.down_file, "down.txt");
    strcpy(mudconf.full_file, "full.txt");
    strcpy(mudconf.site_file, "badsite.txt");
    strcpy(mudconf.crea_file, "newuser.txt");
#ifdef PUEBLO_SUPPORT
    strcpy(mudconf.htmlconn_file, "htmlconn.txt");
#endif /* PUEBLO_SUPPORT */
    strcpy(mudconf.help_file, "help.txt");
    strcpy(mudconf.help_indx, "help.indx");
    strcpy(mudconf.news_file, "news.txt");
    strcpy(mudconf.news_indx, "news.indx");
    strcpy(mudconf.whelp_file, "wizhelp.txt");
    strcpy(mudconf.whelp_indx, "wizhelp.indx");
    strcpy(mudconf.motd_msg, "");
    strcpy(mudconf.wizmotd_msg, "");
    strcpy(mudconf.downmotd_msg, "");
    strcpy(mudconf.fullmotd_msg, "");
    strcpy(mudconf.dump_msg, "");
    strcpy(mudconf.postdump_msg, "");
#ifdef PUEBLO_SUPPORT
    strcpy(mudconf.pueblo_msg, "</xch_mudtext><img xch_mode=html><tt>");
#endif /* PUEBLO_SUPPORT */
    mudconf.name_spaces = 1;
    mudconf.fork_dump = 1;
    mudconf.fork_vfork = 0;
    mudconf.sig_action = SA_DFLT;
    mudconf.paranoid_alloc = 0;
    mudconf.max_players = -1;
    mudconf.garbage_chunk = 3;
    mudconf.dump_interval = 3600;
    mudconf.check_interval = 600;
    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 = mudconf.start_room_quota = mudconf.start_exit_quota
	= mudconf.start_thing_quota = mudconf.start_player_quota = 20;
    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.building_limit = 50000;
    mudconf.exit_quota = 1;
    mudconf.player_quota = 1;
    mudconf.room_quota = 1;
    mudconf.thing_quota = 1;
    mudconf.queuemax = 100;
    mudconf.queue_chunk = 10;
    mudconf.active_q_chunk = 10;
    mudconf.sacfactor = 5;
    mudconf.sacadjust = -1;
    mudconf.use_hostname = 1;
    mudconf.recycle = 1;
    mudconf.quotas = 0;
    mudconf.typed_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 = 0;
    mudconf.match_mine_pl = 0;
    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.parent_control = 0;
    mudconf.wiz_obey_linklock = 0;
    mudconf.local_masters = 1;
    mudconf.req_cmds_flag = 1;
    mudconf.parent_zones = 1;

    /* -- ??? Running SC on a non-SC DB may cause problems */
    mudconf.space_compress = 1;
    mudconf.start_room = 0;
    mudconf.start_home = -1;
    mudconf.default_home = -1;
    mudconf.master_room = -1;
    mudconf.player_flags.word1 = 0;
    mudconf.player_flags.word2 = 0;
    mudconf.room_flags.word1 = 0;
    mudconf.room_flags.word2 = 0;
    mudconf.exit_flags.word1 = 0;
    mudconf.exit_flags.word2 = 0;
    mudconf.thing_flags.word1 = 0;
    mudconf.thing_flags.word2 = 0;
    mudconf.robot_flags.word1 = ROBOT;
    mudconf.robot_flags.word2 = 0;
    mudconf.vattr_flags = AF_ODARK;
    mudconf.abort_on_bug = 0;
    mudconf.rwho_transmit = 0;
    strcpy(mudconf.mud_name, "TinyMUSH");
    strcpy(mudconf.rwho_host, "139.78.1.15");	/* riemann.math.okstate.edu */
    strcpy(mudconf.rwho_pass, "get_your_own");
    mudconf.rwho_info_port = 6888;
    mudconf.rwho_data_port = 6889;
    mudconf.rwho_interval = 241;
    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.control_flags &= ~CF_GODMONITOR; /* Except for monitoring... */
    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;
    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.cache_depth = CACHE_DEPTH;
    mudconf.cache_width = CACHE_WIDTH;
    mudconf.cache_names = 1;
    mudconf.cache_steal_dirty = 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.curr_cmd = (char *) "< none >";
    mudstate.shutdown_flag = 0;
    mudstate.rwho_on = 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.qfirst = NULL;
    mudstate.qlast = NULL;
    mudstate.qlfirst = NULL;
    mudstate.qllast = NULL;
    mudstate.qwait = NULL;
    mudstate.qsemfirst = NULL;
    mudstate.qsemlast = NULL;
    mudstate.badname_head = NULL;
    mudstate.iter_alist.data = NULL;
    mudstate.iter_alist.len = 0;
    mudstate.iter_alist.next = NULL;
    mudstate.mod_alist = NULL;
    mudstate.mod_size = 0;
    mudstate.mod_al_id = NOTHING;
    mudstate.olist_head = NULL;
    mudstate.olist_tail = NULL;
    mudstate.olist_cblock = NULL;
    mudstate.olist_count = 0;
    mudstate.olist_citm = 0;
    mudstate.min_size = 0;
    mudstate.db_top = 0;
    mudstate.db_size = 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;
    for (i = 0; i < MAX_GLOBAL_REGS; i++)
	mudstate.global_regs[i] = NULL;
#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.start_room = 0;
    mudconf.start_home = -1;
    mudconf.default_home = -1;
    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;
    mudconf.cache_steal_dirty = 1;

    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_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(player, cmd, thingname, thing)
    dbref player;
    char *cmd, *thing;
    const char *thingname;
{
    char *buff;

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

/* ---------------------------------------------------------------------------
 * cf_log_syntax: Log a syntax error.
 */

void 
cf_log_syntax(player, cmd, template, arg)
    dbref player;
    char *cmd, *arg;
    const char *template;
{
    char *buff;

    if (mudstate.initializing) {
	STARTLOG(LOG_STARTUP, "CNF", "SYNTX")
	    buff = alloc_lbuf("cf_log_syntax.LOG");
	sprintf(buff, template, arg);
	log_text(cmd);
	log_text((char *) ": ");
	log_text(buff);
	free_lbuf(buff);
	ENDLOG
    } else {
	buff = alloc_lbuf("cf_log_syntax");
	sprintf(buff, template, arg);
	notify(player, buff);
	free_lbuf(buff);
    }
}

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

int 
cf_status_from_succfail(player, cmd, success, failure)
    dbref player;
    char *cmd;
    int success, 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 */

    sscanf(str, "%d", vp);
    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 = search_nametab(GOD, bool_names, str);
    if (*vp == -1)
	*vp = 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 == -1) {
	cf_log_notfound(player, cmd, "Value", str);
	return -1;
    }
    *vp = i;
    return 0;
}

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

CF_HAND(cf_string)
{
    int retval;
    char *buff;

    /* Copy the string to the buffer if it is not too big */

    retval = 0;
    if (strlen(str) >= (long) extra) {
	str[(long) extra - 1] = '\0';
	if (mudstate.initializing) {
	    STARTLOG(LOG_STARTUP, "CNF", "NFND")
		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;
    }
    strcpy((char *) vp, str);
    return retval;
}

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

CF_HAND(cf_alias)
{
    char *alias, *orig;
    int *cp;

    alias = strtok(str, " \t=,");
    orig = strtok(NULL, " \t=,");
    cp = hashfind(orig, (HASHTAB *) vp);
    if (cp == NULL) {
	cf_log_notfound(player, cmd, "Entry", orig);
	return -1;
    }
    hashadd(alias, cp, (HASHTAB *) vp);
    return 0;
}

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

CF_HAND(cf_flagalias)
{
    char *alias, *orig;
    int *cp, success;

    success = 0;
    alias = strtok(str, " \t=,");
    orig = strtok(NULL, " \t=,");

    cp = hashfind(orig, &mudstate.flags_htab);
    if (cp != NULL) {
	hashadd(alias, 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)
{
    char *sp;
    int f, success, failure;

    /* Walk through the tokens */

    success = failure = 0;
    sp = strtok(str, " \t");
    while (sp != NULL) {

	/* Set the appropriate bit */

	f = search_nametab(GOD, (NAMETAB *) extra, sp);
	if (f != -1) {
	    *vp |= f;
	    success++;
	} else {
	    cf_log_notfound(player, cmd, "Entry", sp);
	    failure++;
	}

	/* Get the next token */

	sp = strtok(NULL, " \t");
    }
    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)
{
    char *sp;
    int f, negate, success, failure;

    /* Walk through the tokens */

    success = failure = 0;
    sp = strtok(str, " \t");
    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 != -1) {
	    if (negate)
		*vp &= ~f;
	    else
		*vp |= f;
	    success++;
	} else {
	    cf_log_notfound(player, cmd, "Entry", sp);
	    failure++;
	}

	/* Get the next token */

	sp = strtok(NULL, " \t");
    }
    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)
{
    char *sp;
    int f, success, failure;

    /* Walk through the tokens */

    success = failure = 0;
    *vp = 0;
    sp = strtok(str, " \t");
    while (sp != NULL) {

	/* Set the appropriate bit */

	f = search_nametab(GOD, (NAMETAB *) extra, sp);
	if (f != -1) {
	    *vp |= f;
	    success++;
	} else {
	    cf_log_notfound(player, cmd, "Entry", sp);
	    failure++;
	}

	/* Get the next token */

	sp = strtok(NULL, " \t");
    }
    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)
{
    char *sp;
    FLAGENT *fp;
    FLAGSET *fset;

    int success, failure;

    /* Walk through the tokens */

    success = failure = 0;
    sp = strtok(str, " \t");
    fset = (FLAGSET *) vp;

    while (sp != NULL) {

	/* Set the appropriate bit */

	fp = (FLAGENT *) hashfind(sp, &mudstate.flags_htab);
	if (fp != NULL) {
	    if (success == 0) {
		(*fset).word1 = 0;
		(*fset).word2 = 0;
	    }
	    if (fp->flagflag & FLAG_EXT)
		(*fset).word2 |= fp->flagvalue;
	    else
		(*fset).word1 |= fp->flagvalue;
	    success++;
	} else {
	    cf_log_notfound(player, cmd, "Entry", sp);
	    failure++;
	}

	/* Get the next token */

	sp = strtok(NULL, " \t");
    }
    if ((success == 0) && (failure == 0)) {
	(*fset).word1 = 0;
	(*fset).word2 = 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 ((long) extra)
	badname_remove(str);
    else
	badname_add(str);
    return 0;
}

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

CF_HANDP(cf_site)
{
    SITE *site, *last, *head;
    char *addr_txt, *mask_txt;
    struct in_addr addr_num, mask_num;

    addr_txt = strtok(str, " \t=,");
    mask_txt = NULL;
    if (addr_txt)
	mask_txt = strtok(NULL, " \t=,");
    if (!addr_txt || !*addr_txt || !mask_txt || !*mask_txt) {
	cf_log_syntax(player, cmd, "Missing host address or mask.",
		      (char *) "");
	return -1;
    }
    addr_num.s_addr = inet_addr(addr_txt);
    mask_num.s_addr = inet_addr(mask_txt);

    if (addr_num.s_addr == -1) {
	cf_log_syntax(player, cmd, "Bad host address: %s", addr_txt);
	return -1;
    }
    head = (SITE *) * vp;
    /* Parse the access entry and allocate space for it */

    if (!(site = (SITE *) XMALLOC(sizeof(SITE), "cf_site")))
	return -1;

    /* Initialize the site entry */

    site->address.s_addr = addr_num.s_addr;
    site->mask.s_addr = mask_num.s_addr;
    site->flag = (long) 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 {
	    for (last = head; last->next; last = last->next);
	    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 && !isspace(*ap); ap++);
    if (*ap)
	*ap++ = '\0';

    for (tp = conftable; tp->pname; tp++) {
	if (!strcmp(tp->pname, str)) {
	    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, "r");
    if (fp == NULL) {
	cf_log_notfound(player, cmd, "Config file", str);
	return -1;
    }
    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++);
	*cp = '\0';		/* strip \n */
	for (cp = buf; *cp && isspace(*cp); cp++);	/* strip spaces */
	for (ap = cp; *ap && !isspace(*ap); ap++);	/* skip over command */
	if (*ap)
	    *ap++ = '\0';	/* trim command */
	for (; *ap && isspace(*ap); ap++);	/* skip spaces */
	for (zp = ap; *zp && (*zp != '#'); zp++);	/* find comment */
	if (*zp)
	    *zp = '\0';		/* zap comment */
	for (zp = zp - 1; zp >= ap && isspace(*zp); zp--)
	    *zp = '\0';		/* zap trailing spcs */

	cf_set(cp, ap, player);
	fgets(buf, LBUF_SIZE, fp);
    }
    free_lbuf(buf);
    fclose(fp);
    return 0;
}

/* ---------------------------------------------------------------------------
 * cf_dbref_list: Set a list of dbref values.  extra is a pointer
 * to an "int" which will contain the number of dbrefs found.
 */

CF_HAND(cf_dbref_list)
{
    char *tmp = strtok(str, " \t");
    int *num = (int *) extra;

    *num = 0;
    while (tmp) {
	vp[*num] = atoi(tmp);
	(*num)++;
	tmp = strtok(NULL, " \t");
    }
    return 0;
}

extern CF_HDCL(cf_access);
extern CF_HDCL(cf_cmd_alias);
extern CF_HDCL(cf_acmd_access);
extern CF_HDCL(cf_attr_access);
extern CF_HDCL(cf_ntab_access);
extern CF_HDCL(cf_func_access);

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

CONF conftable[] =
{
    {"abort_on_bug", cf_bool, CA_DISABLED,
     &mudconf.abort_on_bug, 0},
    {"access", cf_access, CA_GOD,
     NULL, access_nametab},
    {"alias", cf_cmd_alias, CA_GOD,
     (int *) &mudstate.command_htab, 0},
    {"attr_access", cf_attr_access, CA_GOD,
     NULL, attraccess_nametab},
    {"attr_alias", cf_alias, CA_GOD,
     (int *) &mudstate.attr_name_htab, 0},
    {"attr_cmd_access", cf_acmd_access, CA_GOD,
     NULL, access_nametab},
    {"bad_name", cf_badname, CA_GOD,
     NULL, 0},
    {"badsite_file", cf_string, CA_DISABLED,
     (int *) mudconf.site_file, (void *) 32},
    {"building_limit", cf_int, CA_GOD,
     &mudconf.building_limit, 0},
    {"cache_depth", cf_int, CA_DISABLED,
     &mudconf.cache_depth, 0},
    {"cache_names", cf_bool, CA_DISABLED,
     &mudconf.cache_names, 0},
    {"cache_steal_dirty", cf_bool, CA_GOD,
     &mudconf.cache_steal_dirty, 0},
    {"cache_width", cf_int, CA_DISABLED,
     &mudconf.cache_width, 0},
    {"check_interval", cf_int, CA_GOD,
     &mudconf.check_interval, 0},
    {"check_offset", cf_int, CA_GOD,
     &mudconf.check_offset, 0},
    {"clone_copies_cost", cf_bool, CA_GOD,
     &mudconf.clone_copy_cost, 0},
    {"command_quota_increment", cf_int, CA_GOD,
     &mudconf.cmd_quota_incr, 0},
    {"command_quota_max", cf_int, CA_GOD,
     &mudconf.cmd_quota_max, 0},
    {"config_access", cf_cf_access, CA_GOD,
     NULL, access_nametab},
    {"conn_timeout", cf_int, CA_GOD,
     &mudconf.conn_timeout, 0},
    {"connect_file", cf_string, CA_DISABLED,
     (int *) mudconf.conn_file, (void *) 32},
    {"connect_reg_file", cf_string, CA_DISABLED,
     (int *) mudconf.creg_file, (void *) 32},
    {"crash_database", cf_string, CA_DISABLED,
     (int *) mudconf.crashdb, (void *) 128},
    {"create_max_cost", cf_int, CA_GOD,
     &mudconf.createmax, 0},
    {"create_min_cost", cf_int, CA_GOD,
     &mudconf.createmin, 0},
    {"dark_sleepers", cf_bool, CA_GOD,
     &mudconf.dark_sleepers, 0},
    {"default_home", cf_int, CA_GOD,
     &mudconf.default_home, 0},
    {"dig_cost", cf_int, CA_GOD,
     &mudconf.digcost, 0},
    {"down_file", cf_string, CA_DISABLED,
     (int *) mudconf.down_file, (void *) 32},
    {"down_motd_message", cf_string, CA_GOD,
     (int *) mudconf.downmotd_msg, (void *) 1024},
    {"dump_interval", cf_int, CA_GOD,
     &mudconf.dump_interval, 0},
    {"dump_message", cf_string, CA_GOD,
     (int *) mudconf.dump_msg, (void *) 128},
    {"postdump_message", cf_string, CA_GOD,
     (int *) mudconf.postdump_msg, (void *) 128},
#ifdef PUEBLO_SUPPORT
    {"html_connect_file", cf_string, CA_DISABLED,
     (int *) mudconf.htmlconn_file, (void *) 32},
    {"pueblo_message", cf_string, CA_GOD,
     (int *) mudconf.pueblo_msg, (void *) 1024},
#endif /* PUEBLO_SUPPORT */
    {"dump_offset", cf_int, CA_GOD,
     &mudconf.dump_offset, 0},
    {"earn_limit", cf_int, CA_GOD,
     &mudconf.paylimit, 0},
    {"examine_flags", cf_bool, CA_GOD,
     &mudconf.ex_flags, 0},
    {"examine_public_attrs", cf_bool, CA_GOD,
     &mudconf.exam_public, 0},
    {"exit_flags", cf_set_flags, CA_GOD,
     (int *) &mudconf.exit_flags, 0},
    {"exit_quota", cf_int, CA_GOD,
     &mudconf.exit_quota, 0},
    {"fascist_teleport", cf_bool, CA_GOD,
     &mudconf.fascist_tport, 0},
    {"find_money_chance", cf_int, CA_GOD,
     &mudconf.payfind, 0},
    {"flag_alias", cf_flagalias, CA_GOD,
     NULL, 0},
    {"forbid_site", cf_site, CA_GOD,
     (int *) &mudstate.access_list, (void *) H_FORBIDDEN},
    {"fork_dump", cf_bool, CA_GOD,
     &mudconf.fork_dump, 0},
    {"fork_vfork", cf_bool, CA_GOD,
     &mudconf.fork_vfork, 0},
    {"full_file", cf_string, CA_DISABLED,
     (int *) mudconf.full_file, (void *) 32},
    {"full_motd_message", cf_string, CA_GOD,
     (int *) mudconf.fullmotd_msg, (void *) 1024},
    {"function_access", cf_func_access, CA_GOD,
     NULL, access_nametab},
    {"function_alias", cf_alias, CA_GOD,
     (int *) &mudstate.func_htab, 0},
    {"function_invocation_limit", cf_int, CA_GOD,
     &mudconf.func_invk_lim, 0},
    {"function_recursion_limit", cf_int, CA_GOD,
     &mudconf.func_nest_lim, 0},
    {"garbage_chunk", cf_int, CA_GOD,
     &mudconf.garbage_chunk, 0},
    {"gdbm_database", cf_string, CA_DISABLED,
     (int *) mudconf.gdbm, (void *) 128},
    {"good_name", cf_badname, CA_GOD,
     NULL, (void *) 1},
    {"guests", cf_dbref_list, CA_GOD,
     mudconf.guest_chars, &mudconf.num_guests},
    {"guest_file", cf_string, CA_DISABLED,
     (int *) mudconf.guest_file, (void *) 32},
    {"guest_site", cf_site, CA_GOD,
     (int *) &mudstate.access_list, (void *) H_GUEST},
    {"help_file", cf_string, CA_DISABLED,
     (int *) mudconf.help_file, (void *) 32},
    {"help_index", cf_string, CA_DISABLED,
     (int *) mudconf.help_indx, (void *) 32},
    {"hostnames", cf_bool, CA_GOD,
     &mudconf.use_hostname, 0},
    {"idle_wiz_dark", cf_bool, CA_GOD,
     &mudconf.idle_wiz_dark, 0},
    {"idle_interval", cf_int, CA_GOD,
     &mudconf.idle_interval, 0},
    {"idle_timeout", cf_int, CA_GOD,
     &mudconf.idle_timeout, 0},
    {"include", cf_include, CA_DISABLED,
     NULL, 0},
    {"initial_size", cf_int, CA_DISABLED,
     &mudconf.init_size, 0},
    {"input_database", cf_string, CA_DISABLED,
     (int *) mudconf.indb, (void *) 128},
    {"kill_guarantee_cost", cf_int, CA_GOD,
     &mudconf.killguarantee, 0},
    {"kill_max_cost", cf_int, CA_GOD,
     &mudconf.killmax, 0},
    {"kill_min_cost", cf_int, CA_GOD,
     &mudconf.killmin, 0},
    {"lag_maximum", cf_int, CA_GOD,
     &mudconf.max_cmdsecs, 0},
    {"link_cost", cf_int, CA_GOD,
     &mudconf.linkcost, 0},
    {"list_access", cf_ntab_access, CA_GOD,
     (int *) list_names, access_nametab},
    {"local_master_rooms", cf_bool, CA_GOD,
     &mudconf.local_masters, 0},
    {"lock_recursion_limit", cf_int, CA_GOD,
     &mudconf.lock_nest_lim, 0},
    {"log", cf_modify_bits, CA_GOD,
     &mudconf.log_options, logoptions_nametab},
    {"log_options", cf_modify_bits, CA_GOD,
     &mudconf.log_info, logdata_nametab},
    {"logout_cmd_access", cf_ntab_access, CA_GOD,
     (int *) logout_cmdtable, access_nametab},
    {"logout_cmd_alias", cf_alias, CA_GOD,
     (int *) &mudstate.logout_cmd_htab, 0},
    {"look_obey_terse", cf_bool, CA_GOD,
     &mudconf.terse_look, 0},
    {"machine_command_cost", cf_int, CA_GOD,
     &mudconf.machinecost, 0},
    {"master_room", cf_int, CA_GOD,
     &mudconf.master_room, 0},
    {"match_own_commands", cf_bool, CA_GOD,
     &mudconf.match_mine, 0},
    {"max_players", cf_int, CA_GOD,
     &mudconf.max_players, 0},
    {"money_name_plural", cf_string, CA_GOD,
     (int *) mudconf.many_coins, (void *) 32},
    {"money_name_singular", cf_string, CA_GOD,
     (int *) mudconf.one_coin, (void *) 32},
    {"motd_file", cf_string, CA_DISABLED,
     (int *) mudconf.motd_file, (void *) 32},
    {"motd_message", cf_string, CA_GOD,
     (int *) mudconf.motd_msg, (void *) 1024},
    {"mud_name", cf_string, CA_GOD,
     (int *) mudconf.mud_name, (void *) 32},
    {"news_file", cf_string, CA_DISABLED,
     (int *) mudconf.news_file, (void *) 32},
    {"news_index", cf_string, CA_DISABLED,
     (int *) mudconf.news_indx, (void *) 32},
    {"newuser_file", cf_string, CA_DISABLED,
     (int *) mudconf.crea_file, (void *) 32},
    {"notify_recursion_limit", cf_int, CA_GOD,
     &mudconf.ntfy_nest_lim, 0},
    {"open_cost", cf_int, CA_GOD,
     &mudconf.opencost, 0},
    {"output_database", cf_string, CA_DISABLED,
     (int *) mudconf.outdb, (void *) 128},
    {"output_limit", cf_int, CA_GOD,
     &mudconf.output_limit, 0},
    {"page_cost", cf_int, CA_GOD,
     &mudconf.pagecost, 0},
    {"paranoid_allocate", cf_bool, CA_GOD,
     &mudconf.paranoid_alloc, 0},
    {"parentable_control_lock", cf_bool, CA_DISABLED,
     &mudconf.parent_control, 0},
    {"parent_zones", cf_bool, CA_GOD,
     &mudconf.parent_zones, 0},
    {"paycheck", cf_int, CA_GOD,
     &mudconf.paycheck, 0},
    {"pemit_far_players", cf_bool, CA_GOD,
     &mudconf.pemit_players, 0},
    {"pemit_any_object", cf_bool, CA_GOD,
     &mudconf.pemit_any, 0},
    {"permit_site", cf_site, CA_GOD,
     (int *) &mudstate.access_list, 0},
    {"player_flags", cf_set_flags, CA_GOD,
     (int *) &mudconf.player_flags, 0},
    {"player_listen", cf_bool, CA_GOD,
     &mudconf.player_listen, 0},
    {"player_match_own_commands", cf_bool, CA_GOD,
     &mudconf.match_mine_pl, 0},
    {"player_name_spaces", cf_bool, CA_GOD,
     &mudconf.name_spaces, 0},
    {"player_queue_limit", cf_int, CA_GOD,
     &mudconf.queuemax, 0},
    {"player_quota", cf_int, CA_GOD,
     &mudconf.player_quota, 0},
    {"player_starting_home", cf_int, CA_GOD,
     &mudconf.start_home, 0},
    {"player_starting_room", cf_int, CA_GOD,
     &mudconf.start_room, 0},
    {"port", cf_int, CA_DISABLED,
     &mudconf.port, 0},
    {"power_access", cf_ntab_access, CA_GOD,
     (int *) powers_nametab, access_nametab},
    {"public_flags", cf_bool, CA_GOD,
     &mudconf.pub_flags, 0},
    {"queue_active_chunk", cf_int, CA_GOD,
     &mudconf.active_q_chunk, 0},
    {"queue_idle_chunk", cf_int, CA_GOD,
     &mudconf.queue_chunk, 0},
    {"quiet_look", cf_bool, CA_GOD,
     &mudconf.quiet_look, 0},
    {"quiet_whisper", cf_bool, CA_GOD,
     &mudconf.quiet_whisper, 0},
    {"quit_file", cf_string, CA_DISABLED,
     (int *) mudconf.quit_file, (void *) 32},
    {"quotas", cf_bool, CA_GOD,
     &mudconf.quotas, 0},
    {"read_remote_desc", cf_bool, CA_GOD,
     &mudconf.read_rem_desc, 0},
    {"read_remote_name", cf_bool, CA_GOD,
     &mudconf.read_rem_name, 0},
    {"recycling", cf_bool, CA_GOD,
     &mudconf.recycle, 0},
    {"register_create_file", cf_string, CA_DISABLED,
     (int *) mudconf.regf_file, (void *) 32},
    {"register_site", cf_site, CA_GOD,
     (int *) &mudstate.access_list, (void *) H_REGISTRATION},
    {"require_cmds_flag", cf_bool, CA_GOD,
     &mudconf.req_cmds_flag, 0},
    {"retry_limit", cf_int, CA_GOD,
     &mudconf.retry_limit, 0},
    {"robot_cost", cf_int, CA_GOD,
     &mudconf.robotcost, 0},
    {"robot_flags", cf_set_flags, CA_GOD,
     (int *) &mudconf.robot_flags, 0},
    {"robot_speech", cf_bool, CA_GOD,
     &mudconf.robot_speak, 0},
    {"room_flags", cf_set_flags, CA_GOD,
     (int *) &mudconf.room_flags, 0},
    {"room_quota", cf_int, CA_GOD,
     &mudconf.room_quota, 0},
    {"rwho_data_port", cf_int, CA_GOD,
     &mudconf.rwho_data_port, 0},
    {"rwho_dump_interval", cf_int, CA_GOD,
     &mudconf.rwho_interval, 0},
    {"rwho_host", cf_string, CA_GOD,
     (int *) mudconf.rwho_host, (void *) 64},
    {"rwho_info_port", cf_int, CA_GOD,
     &mudconf.rwho_info_port, 0},
    {"rwho_password", cf_string, CA_GOD,
     (int *) mudconf.rwho_pass, (void *) 32},
    {"rwho_transmit", cf_bool, CA_GOD,
     &mudconf.rwho_transmit, 0},
    {"sacrifice_adjust", cf_int, CA_GOD,
     &mudconf.sacadjust, 0},
    {"sacrifice_factor", cf_int, CA_GOD,
     &mudconf.sacfactor, 0},
    {"search_cost", cf_int, CA_GOD,
     &mudconf.searchcost, 0},
    {"see_owned_dark", cf_bool, CA_GOD,
     &mudconf.see_own_dark, 0},
    {"signal_action", cf_option, CA_DISABLED,
     &mudconf.sig_action, sigactions_nametab},
    {"space_compress", cf_bool, CA_GOD,
     &mudconf.space_compress, 0},
    {"starting_money", cf_int, CA_GOD,
     &mudconf.paystart, 0},
    {"starting_quota", cf_int, CA_GOD,
     &mudconf.start_quota, 0},
    {"starting_exit_quota", cf_int, CA_GOD,
     &mudconf.start_exit_quota, 0},
    {"starting_player_quota", cf_int, CA_GOD,
     &mudconf.start_player_quota, 0},
    {"starting_room_quota", cf_int, CA_GOD,
     &mudconf.start_room_quota, 0},
    {"starting_thing_quota", cf_int, CA_GOD,
     &mudconf.start_thing_quota, 0},
    {"status_file", cf_string, CA_DISABLED,
     (int *) mudconf.status_file, (void *) 128},
    {"suspect_site", cf_site, CA_GOD,
     (int *) &mudstate.suspect_list, (void *) H_SUSPECT},
    {"sweep_dark", cf_bool, CA_GOD,
     &mudconf.sweep_dark, 0},
    {"switch_default_all", cf_bool, CA_GOD,
     &mudconf.switch_df_all, 0},
    {"terse_shows_contents", cf_bool, CA_GOD,
     &mudconf.terse_contents, 0},
    {"terse_shows_exits", cf_bool, CA_GOD,
     &mudconf.terse_exits, 0},
    {"terse_shows_move_messages", cf_bool, CA_GOD,
     &mudconf.terse_movemsg, 0},
    {"thing_flags", cf_set_flags, CA_GOD,
     (int *) &mudconf.thing_flags, 0},
    {"thing_quota", cf_int, CA_GOD,
     &mudconf.thing_quota, 0},
    {"timeslice", cf_int, CA_GOD,
     &mudconf.timeslice, 0},
    {"trace_output_limit", cf_int, CA_GOD,
     &mudconf.trace_limit, 0},
    {"trace_topdown", cf_bool, CA_GOD,
     &mudconf.trace_topdown, 0},
    {"trust_site", cf_site, CA_GOD,
     (int *) &mudstate.suspect_list, 0},
    {"typed_quotas", cf_bool, CA_GOD,
     &mudconf.typed_quotas, 0},
    {"unowned_safe", cf_bool, CA_GOD,
     &mudconf.safe_unowned, 0},
    {"user_attr_access", cf_modify_bits, CA_GOD,
     &mudconf.vattr_flags, attraccess_nametab},
    {"use_global_aconn", cf_bool, CA_GOD,
     &mudconf.use_global_aconn, 0},
    {"wait_cost", cf_int, CA_GOD,
     &mudconf.waitcost, 0},
    {"wizard_obeys_linklock", cf_bool, CA_GOD,
     &mudconf.wiz_obey_linklock, 0},
    {"wizard_help_file", cf_string, CA_DISABLED,
     (int *) mudconf.whelp_file, (void *) 32},
    {"wizard_help_index", cf_string, CA_DISABLED,
     (int *) mudconf.whelp_indx, (void *) 32},
    {"wizard_motd_file", cf_string, CA_DISABLED,
     (int *) mudconf.wizmotd_file, (void *) 32},
    {"wizard_motd_message", cf_string, CA_GOD,
     (int *) mudconf.wizmotd_msg, (void *) 1024},
    {NULL, NULL, 0,
     NULL, 0}};

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

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

    /* 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,
		       "Permission denied.");
		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(player, cause, extra, kw, value)
    dbref player, cause;
    int extra;
    char *kw, *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
 */

int 
cf_read(fn)
    char *fn;
{
    int retval;

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

    /* Fill in missing DB file names */

    if (!*mudconf.outdb) {
	strcpy(mudconf.outdb, mudconf.indb);
	strcat(mudconf.outdb, ".out");
    }
    if (!*mudconf.crashdb) {
	strcpy(mudconf.crashdb, mudconf.indb);
	strcat(mudconf.crashdb, ".CRASH");
    }
    if (!*mudconf.gdbm) {
	strcpy(mudconf.gdbm, mudconf.indb);
	strcat(mudconf.gdbm, ".gdbm");
    }
    return retval;
}

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

void 
list_cf_access(player)
    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 */
