/**
* \file conf.c
*
* \brief PennMUSH runtime configuration.
*
* configuration adjustment. Some of the ideas and bits and pieces of the
* code here are based on TinyMUSH 2.0.
*
*/
#include "copyrite.h"
#include "config.h"
#include <stdio.h>
#ifdef I_SYS_TIME
#include <sys/time.h>
#else
#include <time.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "conf.h"
#include "externs.h"
#include "pueblo.h"
#include "mushdb.h"
#include "parse.h"
#include "command.h"
#include "flags.h"
#include "log.h"
#include "dbdefs.h"
#include "game.h"
#include "attrib.h"
#include "help.h"
#include "function.h"
#include "confmagic.h"
time_t mudtime; /**< game time, in seconds */
static void show_compile_options(dbref player);
static char *config_list_helper(dbref player, PENNCONF * cp, int lc);
static char *config_list_helper2(dbref player, PENNCONF * cp, int lc);
OPTTAB options; /**< The table of configuration options */
HASHTAB local_options; /**< Hash table for local config options */
int config_set(const char *opt, char *val, int source, int restrictions);
void conf_default_set(void);
/** Table of all runtime configuration options. */
PENNCONF conftable[] = {
{"input_database", cf_str, options.input_db, sizeof options.input_db, 0,
"files"}
,
{"output_database", cf_str, options.output_db, sizeof options.output_db, 0,
"files"}
,
{"crash_database", cf_str, options.crash_db, sizeof options.crash_db, 0,
"files"}
,
{"mail_database", cf_str, options.mail_db, sizeof options.mail_db, 0, "files"}
,
{"chat_database", cf_str, options.chatdb, sizeof options.chatdb, 0, "files"}
,
{"compress_suffix", cf_str, options.compresssuff, sizeof options.compresssuff,
0,
"files"}
,
{"compress_program", cf_str, options.compressprog,
sizeof options.compressprog, 0,
"files"}
,
{"uncompress_program", cf_str, options.uncompressprog,
sizeof options.uncompressprog, 0,
"files"}
,
{"access_file", cf_str, options.access_file, sizeof options.access_file, 0,
"files"}
,
{"names_file", cf_str, options.names_file, sizeof options.access_file, 0,
"files"}
,
{"connect_file", cf_str, options.connect_file[0],
sizeof options.connect_file[0], 0, "messages"}
,
{"motd_file", cf_str, options.motd_file[0], sizeof options.motd_file[0], 0,
"messages"}
,
{"wizmotd_file", cf_str, options.wizmotd_file[0],
sizeof options.wizmotd_file[0], 0, "messages"}
,
{"newuser_file", cf_str, options.newuser_file[0],
sizeof options.newuser_file[0], 0, "messages"}
,
{"register_create_file", cf_str, options.register_file[0],
sizeof options.register_file[0],
0, "messages"}
,
{"quit_file", cf_str, options.quit_file[0], sizeof options.quit_file[0], 0,
"messages"}
,
{"down_file", cf_str, options.down_file[0], sizeof options.down_file[0], 0,
"messages"}
,
{"full_file", cf_str, options.full_file[0], sizeof options.full_file[0], 0,
"messages"}
,
{"guest_file", cf_str, options.guest_file[0], sizeof options.guest_file[0], 0,
"messages"}
,
{"connect_html_file", cf_str, options.connect_file[1],
sizeof options.connect_file[1], 0,
"messages"}
,
{"motd_html_file", cf_str, options.motd_file[1],
sizeof options.connect_file[1], 0,
"messages"}
,
{"wizmotd_html_file", cf_str, options.wizmotd_file[1],
sizeof options.wizmotd_file[1], 0,
"messages"}
,
{"newuser_html_file", cf_str, options.newuser_file[1],
sizeof options.newuser_file[1], 0,
"messages"}
,
{"register_create_html_file", cf_str, options.register_file[1],
sizeof options.register_file[1], 0, "messages"}
,
{"quit_html_file", cf_str, options.quit_file[1], sizeof options.quit_file[1],
0,
"messages"}
,
{"down_html_file", cf_str, options.down_file[1], sizeof options.down_file[1],
0,
"messages"}
,
{"full_html_file", cf_str, options.full_file[1], sizeof options.full_file[1],
0,
"messages"}
,
{"guest_html_file", cf_str, options.guest_file[1],
sizeof options.guest_file[1], 0,
"messages"}
,
{"player_start", cf_dbref, &options.player_start, 100000, 0, "db"}
,
{"master_room", cf_dbref, &options.master_room, 100000, 0, "db"}
,
{"base_room", cf_dbref, &options.base_room, 100000, 0, "db"}
,
{"default_home", cf_dbref, &options.default_home, 100000, 0, "db"}
,
{"exits_connect_rooms", cf_bool, &options.exits_connect_rooms, 2, 0, "db"}
,
{"zone_control_zmp_only", cf_bool, &options.zone_control, 2, 0, "db"}
,
{"ancestor_room", cf_int, &options.ancestor_room, 100000, 0, "db"}
,
{"ancestor_exit", cf_int, &options.ancestor_exit, 100000, 0, "db"}
,
{"ancestor_thing", cf_int, &options.ancestor_thing, 100000, 0, "db"}
,
{"ancestor_player", cf_int, &options.ancestor_player, 100000, 0, "db"}
,
{"mud_name", cf_str, options.mud_name, 128, 0, "net"}
,
{"ip_addr", cf_str, options.ip_addr, 64, 0, "net"}
,
{"ssl_ip_addr", cf_str, options.ssl_ip_addr, 64, 0, "net"}
,
{"port", cf_int, &options.port, 32000, 0, "net"}
,
{"ssl_port", cf_int, &options.ssl_port, 32000, 0, "net"}
,
{"use_dns", cf_bool, &options.use_dns, 2, 0, "net"}
,
{"use_ident", cf_bool, &options.use_ident, 2, 0, "net"}
,
{"ident_timeout", cf_time, &options.ident_timeout, 60, 0, "net"}
,
{"logins", cf_bool, &options.login_allow, 2, 0, "net"}
,
{"player_creation", cf_bool, &options.create_allow, 2, 0, "net"}
,
{"guests", cf_bool, &options.guest_allow, 2, 0, "net"}
,
{"pueblo", cf_bool, &options.support_pueblo, 2, 0, "net"}
,
#ifdef HAS_MYSQL
{"sql_platform", cf_str, options.sql_platform, sizeof options.sql_platform, 0,
"net"}
,
{"sql_host", cf_str, options.sql_host, sizeof options.sql_host, 0, "net"}
,
{"sql_username", cf_str, options.sql_username, sizeof options.sql_username, 0,
NULL}
,
{"sql_password", cf_str, options.sql_password, sizeof options.sql_password, 0,
NULL}
,
{"sql_database", cf_str, options.sql_database, sizeof options.sql_database, 0,
NULL}
,
#endif
{"forking_dump", cf_bool, &options.forking_dump, 2, 0, "dump"}
,
{"dump_message", cf_str, options.dump_message, sizeof options.dump_message, 0,
"dump"}
,
{"dump_complete", cf_str, options.dump_complete, sizeof options.dump_complete,
0, "dump"}
,
{"dump_warning_1min", cf_str, options.dump_warning_1min,
sizeof options.dump_warning_1min,
0, "dump"}
,
{"dump_warning_5min", cf_str, options.dump_warning_5min,
sizeof options.dump_warning_5min, 0,
"dump"}
,
{"dump_interval", cf_time, &options.dump_interval, 100000, 0, "dump"}
,
{"warn_interval", cf_time, &options.warn_interval, 32000, 0, "dump"}
,
{"purge_interval", cf_time, &options.purge_interval, 10000, 0, "dump"}
,
{"dbck_interval", cf_time, &options.dbck_interval, 10000, 0, "dump"}
,
{"money_singular", cf_str, options.money_singular,
sizeof options.money_singular, 0,
"cosmetic"}
,
{"money_plural", cf_str, options.money_plural, sizeof options.money_plural, 0,
"cosmetic"}
,
{"player_name_spaces", cf_bool, &options.player_name_spaces, 2, 0,
"cosmetic"}
,
{"ansi_names", cf_bool, &options.ansi_names, 2, 0, "cosmetic"}
,
{"only_ascii_in_names", cf_bool, &options.ascii_names, 2, 0, "cosmetic"}
,
{"float_precision", cf_int, &options.float_precision, 10000, 0, "cosmetic"}
,
{"comma_exit_list", cf_bool, &options.comma_exit_list, 2, 0, "cosmetic"}
,
{"count_all", cf_bool, &options.count_all, 2, 0, "cosmetic"}
,
{"blind_page", cf_bool, &options.blind_page, 2, 0, "cosmetic"}
,
{"page_aliases", cf_bool, &options.page_aliases, 2, 0, "cosmetic"}
,
{"flags_on_examine", cf_bool, &options.flags_on_examine, 2, 0, "cosmetic"}
,
{"ex_public_attribs", cf_bool, &options.ex_public_attribs, 2, 0,
"cosmetic"}
,
{"wizwall_prefix", cf_str, options.wizwall_prefix,
sizeof options.wizwall_prefix, 0,
"cosmetic"}
,
{"rwall_prefix", cf_str, options.rwall_prefix, sizeof options.rwall_prefix, 0,
"cosmetic"}
,
{"wall_prefix", cf_str, options.wall_prefix, sizeof options.wall_prefix, 0,
"cosmetic"}
,
{"announce_connects", cf_bool, &options.announce_connects, 2, 0, "cosmetic"}
,
{"chat_strip_quote", cf_bool, &options.chat_strip_quote, 2, 0, "cosmetic"}
,
{"newline_one_char", cf_bool, &options.newline_one_char, 2, 0, "cosmetic"}
,
{"max_dbref", cf_dbref, &options.max_dbref, -1, 0, "limits"}
,
{"max_attrs_per_obj", cf_int, &options.max_attrcount, 8192, 0, "limits"}
,
{"max_logins", cf_int, &options.max_logins, 128, 0, "limits"}
,
{"max_guests", cf_int, &options.max_guests, 128, 0, "limits"}
,
{"idle_timeout", cf_time, &options.idle_timeout, 100000, 0, "limits"}
,
{"unconnected_idle_timeout", cf_time, &options.unconnected_idle_timeout,
100000, 0, "limits"}
,
{"whisper_loudness", cf_int, &options.whisper_loudness, 100, 0, "limits"}
,
{"starting_quota", cf_int, &options.starting_quota, 10000, 0, "limits"}
,
{"starting_money", cf_int, &options.starting_money, 10000, 0, "limits"}
,
{"paycheck", cf_int, &options.paycheck, 1000, 0, "limits"}
,
{"guest_paycheck", cf_int, &options.guest_paycheck, 1000, 0, "limits"}
,
{"max_pennies", cf_int, &options.max_pennies, 100000, 0, "limits"}
,
{"max_guest_pennies", cf_int, &options.max_guest_pennies, 100000, 0,
"limits"}
,
{"max_parents", cf_int, &options.max_parents, 10000, 0, "limits"}
,
{"mail_limit", cf_int, &options.mail_limit, 5000, 0, "limits"}
,
{"max_depth", cf_int, &options.max_depth, 10000, 0, "limits"}
,
{"player_queue_limit", cf_int, &options.player_queue_limit, 100000, 0,
"limits"}
,
{"queue_loss", cf_int, &options.queue_loss, 10000, 0, "limits"}
,
{"queue_chunk", cf_int, &options.queue_chunk, 100000, 0, "limits"}
,
{"active_queue_chunk", cf_int, &options.active_q_chunk, 100000, 0,
"limits"}
,
{"function_recursion_limit", cf_int, &options.func_nest_lim, 100000, 0,
"limits"}
,
{"function_invocation_limit", cf_int, &options.func_invk_lim, 100000, 0,
"limits"}
,
{"call_limit", cf_int, &options.call_lim, 1000000, 0,
"limits"}
,
{"player_name_len", cf_int, &options.player_name_len, BUFFER_LEN, 0,
"limits"}
,
{"queue_entry_cpu_time", cf_int, &options.queue_entry_cpu_time, 100000, 0,
"limits"}
,
{"max_global_fns", cf_int, &options.max_global_fns, 2000, 0, 0}
,
{"use_quota", cf_bool, &options.use_quota, 2, 0, "limits"}
,
{"max_channels", cf_int, &options.max_channels, 1000, 0, "chat"}
,
{"max_player_chans", cf_int, &options.max_player_chans, 100, 0, "chat"}
,
{"chan_cost", cf_int, &options.chan_cost, 10000, 0, "chat"}
,
{"log_commands", cf_bool, &options.log_commands, 2, 0, "log"}
,
{"log_forces", cf_bool, &options.log_forces, 2, 0, "log"}
,
{"error_log", cf_str, options.error_log, sizeof options.error_log, 0,
"log"}
,
{"command_log", cf_str, options.command_log, sizeof options.command_log, 0,
"log"}
,
{"wizard_log", cf_str, options.wizard_log, sizeof options.wizard_log, 0,
"log"}
,
{"checkpt_log", cf_str, options.checkpt_log, sizeof options.checkpt_log, 0,
"log"}
,
{"trace_log", cf_str, options.trace_log, sizeof options.trace_log, 0,
"log"}
,
{"connect_log", cf_str, options.connect_log, sizeof options.connect_log, 0,
"log"}
,
{"player_flags", cf_flag, options.player_flags, sizeof options.player_flags,
0, "flags"}
,
{"room_flags", cf_flag, options.room_flags, sizeof options.room_flags, 0,
"flags"}
,
{"exit_flags", cf_flag, options.exit_flags, sizeof options.exit_flags, 0,
"flags"}
,
{"thing_flags", cf_flag, options.thing_flags, sizeof options.thing_flags, 0,
"flags"}
,
{"safer_ufun", cf_bool, &options.safer_ufun, 2, 0, "funcs"}
,
{"function_side_effects", cf_bool, &options.function_side_effects, 2, 0,
"funcs"}
,
{"noisy_whisper", cf_bool, &options.noisy_whisper, 2, 0, "cmds"}
,
{"possessive_get", cf_bool, &options.possessive_get, 2, 0, "cmds"}
,
{"possessive_get_d", cf_bool, &options.possessive_get_d, 2, 0, "cmds"}
,
{"link_to_object", cf_bool, &options.link_to_object, 2, 0, "cmds"}
,
{"owner_queues", cf_bool, &options.owner_queues, 2, 0, "cmds"}
,
{"full_invis", cf_bool, &options.full_invis, 2, 0, "cmds"}
,
{"wiz_noaenter", cf_bool, &options.wiz_noaenter, 2, 0, "cmds"}
,
{"really_safe", cf_bool, &options.really_safe, 2, 0, "cmds"}
,
{"destroy_possessions", cf_bool, &options.destroy_possessions, 2, 0,
"cmds"}
,
{"null_eq_zero", cf_bool, &options.null_eq_zero, 2, 0, "tiny"}
,
{"tiny_booleans", cf_bool, &options.tiny_booleans, 2, 0, "tiny"}
,
{"tiny_trim_fun", cf_bool, &options.tiny_trim_fun, 2, 0, "tiny"}
,
{"tiny_math", cf_bool, &options.tiny_math, 2, 0, "tiny"}
,
{"silent_pemit", cf_bool, &options.silent_pemit, 2, 0, "tiny"}
,
{"adestroy", cf_bool, &options.adestroy, 2, 0, "attribs"}
,
{"amail", cf_bool, &options.amail, 2, 0, "attribs"}
,
{"player_listen", cf_bool, &options.player_listen, 2, 0, "attribs"}
,
{"player_ahear", cf_bool, &options.player_ahear, 2, 0, "attribs"}
,
{"startups", cf_bool, &options.startups, 2, 0, "attribs"}
,
{"read_remote_desc", cf_bool, &options.read_remote_desc, 2, 0, "attribs"}
,
{"room_connects", cf_bool, &options.room_connects, 2, 0, "attribs"}
,
{"reverse_shs", cf_bool, &options.reverse_shs, 2, 0, "attribs"}
,
{"empty_attrs", cf_bool, &options.empty_attrs, 2, 0, "attribs"}
,
{"object_cost", cf_int, &options.object_cost, 10000, 0, "costs"}
,
{"exit_cost", cf_int, &options.exit_cost, 10000, 0, "costs"}
,
{"link_cost", cf_int, &options.link_cost, 10000, 0, "costs"}
,
{"room_cost", cf_int, &options.room_cost, 10000, 0, "costs"}
,
{"queue_cost", cf_int, &options.queue_cost, 10000, 0, "costs"}
,
{"quota_cost", cf_int, &options.quota_cost, 10000, 0, "costs"}
,
{"find_cost", cf_int, &options.find_cost, 10000, 0, "costs"}
,
{"page_cost", cf_int, &options.page_cost, 10000, 0, "costs"}
,
{"kill_default_cost", cf_int, &options.kill_default_cost, 10000, 0,
"costs"}
,
{"kill_min_cost", cf_int, &options.kill_min_cost, 10000, 0, "costs"}
,
{"kill_bonus", cf_int, &options.kill_bonus, 100, 0, "costs"}
,
{"log_wipe_passwd", cf_str, options.log_wipe_passwd,
sizeof options.log_wipe_passwd, 0,
NULL}
,
{"chunk_swap_file", cf_str, options.chunk_swap_file,
sizeof options.chunk_swap_file, 0, "files"}
,
{"chunk_cache_memory", cf_int, &options.chunk_cache_memory,
1000000000, 65510 * 2, "files"}
,
{"chunk_migrate", cf_int, &options.chunk_migrate_amount, 100000, 0,
"limits"}
,
#ifdef HAS_OPENSSL
{"ssl_private_key_file", cf_str, options.ssl_private_key_file,
sizeof options.ssl_private_key_file, 0, "files"}
,
{"ssl_ca_file", cf_str, options.ssl_ca_file,
sizeof options.ssl_ca_file, 0, "files"}
,
{"ssl_require_client_cert", cf_bool, &options.ssl_require_client_cert,
2, 0, "net"}
,
#endif
{"mem_check", cf_bool, &options.mem_check, 2, 0, "log"}
,
{NULL, NULL, NULL, 0, 0, NULL}
};
/** A runtime configuration group.
* This struction represents the name and information about a group
* of runtime configuration directives. Groups are used to organize
* the display of configuration options.
*/
typedef struct confgroupparm {
const char *name; /**< name of group */
const char *desc; /**< description of group */
int viewperms; /**< who can view this group */
} PENNCONFGROUP;
/** The table of all configuration groups. */
PENNCONFGROUP confgroups[] = {
{"attribs", "Options affecting attributes", 0},
{"chat", "Chat system options", 0},
{"cmds", "Options affecting command behavior", 0},
{"compile", "Compile-time options", 0},
{"cosmetic", "Cosmetic options", 0},
{"costs", "Costs", 0},
{"db", "Database options", 0},
{"dump", "Options affecting dumps and other periodic processes", 0},
{"files", "Files used by the MUSH", CGP_GOD},
{"flags", "Default flags for new objects", 0},
{"funcs", "Options affecting function behavior", 0},
{"limits", "Limits and other constants", 0},
{"log", "Logging options", 0},
{"messages", "Message files sent by the MUSH", CGP_GOD},
{"net", "Networking and connection-related options", 0},
{"tiny", "TinyMUSH compatibility options", 0},
{NULL, NULL, 0}
};
/** Returns a pointer to a newly allocated PENNCONF object.
* \return pointer to newly allocated PENNCONF object.
*/
PENNCONF *
new_config(void)
{
return ((PENNCONF *) mush_malloc(sizeof(PENNCONF), "config"));
}
/** Add a new local runtime configuration parameter to local_options.
* This function will not override an existing local configuration
* option.
* \param name name of the configuration option.
* \param handler cf_* function to handle the option.
* \param loc address to store the value of the option.
* \param max maximum value allowed for the option.
* \param group name of the option group the option should display with.
* \return pointer to configuration structure or NULL for failure.
*/
PENNCONF *
add_config(const char *name, config_func handler, void *loc, int max,
const char *group)
{
PENNCONF *cnf;
if ((cnf = get_config(name)))
return cnf;
if ((cnf = new_config()) == NULL)
return NULL;
cnf->name = mush_strdup(strupper(name), "config name");
cnf->handler = handler;
cnf->loc = loc;
cnf->max = max;
cnf->overridden = 0;
cnf->group = group;
hashadd(name, (void *) cnf, &local_options);
return cnf;
}
/** Return a local runtime configuration parameter by name.
* This function returns a point to a configuration structure (PENNCONF *)
* if one exists in the local runtime options that matches the given
* name. Only local_options is searched.
* \param name name of the configuration option.
* \return pointer to configuration structure or NULL for failure.
*/
PENNCONF *
get_config(const char *name)
{
return ((PENNCONF *) hashfind(name, &local_options));
}
/** Parse a boolean configuration option.
* \param opt name of the configuration option.
* \param val value of the option.
* \param loc address to store the value.
* \param maxval (unused).
* \param source 0 if read from config file; 1 if from command.
* \retval 0 failure (unable to parse val).
* \retval 1 success.
*/
int
cf_bool(const char *opt, const char *val, void *loc,
int maxval __attribute__ ((__unused__)), int source)
{
/* enter boolean parameter */
if (!strcasecmp(val, "yes") || !strcasecmp(val, "true") ||
!strcasecmp(val, "1"))
*((int *) loc) = 1;
else if (!strcasecmp(val, "no") || !strcasecmp(val, "false") ||
!strcasecmp(val, "0"))
*((int *) loc) = 0;
else {
if (source == 0) {
do_rawlog(LT_ERR, T("CONFIG: option %s value %s invalid.\n"), opt, val);
}
return 0;
}
return 1;
}
/** Parse a string configuration option.
* \param opt name of the configuration option.
* \param val value of the option.
* \param loc address to store the value.
* \param maxval maximum length of value string.
* \param source 0 if read from config file; 1 if from command.
* \retval 0 failure (unable to parse val).
* \retval 1 success.
*/
int
cf_str(const char *opt, const char *val, void *loc, int maxval, int source)
{
/* enter string parameter */
size_t len = strlen(val);
/* truncate if necessary */
if (len >= (size_t) maxval) {
if (source == 0) {
do_rawlog(LT_ERR, T("CONFIG: option %s value truncated\n"), opt);
}
len = maxval - 1;
}
memcpy(loc, val, len);
*((char *) loc + len) = '\0';
return 1;
}
/** Parse a dbref configuration option.
* \param opt name of the configuration option.
* \param val value of the option.
* \param loc address to store the value.
* \param maxval maximum dbref.
* \param source 0 if read from config file; 1 if from command.
* \retval 0 failure (unable to parse val).
* \retval 1 success.
*/
int
cf_dbref(const char *opt, const char *val, void *loc, int maxval, int source)
{
/* enter dbref or integer parameter */
int n;
size_t offset = 0;
if (val && val[0] == '#')
offset = 1;
n = parse_integer(val + offset);
/* enforce limits */
if ((maxval >= 0) && (n > maxval)) {
n = maxval;
if (source == 0) {
do_rawlog(LT_ERR, T("CONFIG: option %s value limited to #%d\n"), opt,
maxval);
}
}
if (source && (!GoodObject(n) || IsGarbage(n))) {
do_rawlog(LT_ERR,
T("CONFIG: attempt to set option %s to a bad dbref (#%d)"),
opt, n);
return 0;
}
*((dbref *) loc) = n;
return 1;
}
/** Parse an integer configuration option.
* \param opt name of the configuration option.
* \param val value of the option.
* \param loc address to store the value.
* \param maxval maximum value.
* \param source 0 if read from config file; 1 if from command.
* \retval 0 failure (unable to parse val).
* \retval 1 success.
*/
int
cf_int(const char *opt, const char *val, void *loc, int maxval, int source)
{
/* enter integer parameter */
int n;
int offset = 0;
if (val && val[0] == '#')
offset = 1;
n = parse_integer(val + offset);
/* enforce limits */
if ((maxval >= 0) && (n > maxval)) {
n = maxval;
if (source == 0) {
do_rawlog(LT_ERR, T("CONFIG: option %s value limited to %d\n"), opt,
maxval);
}
}
*((int *) loc) = n;
return 1;
}
/** Parse an time configuration option with a default unit of seconds
* \param opt name of the configuration option.
* \param val value of the option.
* \param loc address to store the value.
* \param maxval maximum value.
* \param source 0 if read from config file; 1 if from command.
* \retval 0 failure (unable to parse val).
* \retval 1 success.
*/
int
cf_time(const char *opt, const char *val, void *loc, int maxval, int source)
{
/* enter time parameter */
char *end = NULL;
int in_minutes = 0;
int n, secs = 0;
if (strstr(opt, "idle"))
in_minutes = 1;
while (val && *val) {
n = strtol(val, &end, 10);
switch (*end) {
case '\0':
if (in_minutes)
secs += n * 60;
else
secs += n;
goto done; /* Sigh. What I wouldn't give for named loops in C */
case 's':
case 'S':
secs += n;
break;
case 'm':
case 'M':
secs += n * 60;
break;
case 'h':
case 'H':
secs += n * 3600;
break;
default:
if (source == 0)
do_rawlog(LT_ERR, T("CONFIG: Unknown time interval in option %s"), opt);
return 0;
}
val = end + 1;
}
done:
/* enforce limits */
if ((maxval >= 0) && (secs > maxval)) {
secs = maxval;
if (source == 0) {
do_rawlog(LT_ERR, T("CONFIG: option %s value limited to %d\n"), opt,
maxval);
}
}
*((int *) loc) = secs;
return 1;
}
/** Parse a flag configuration option.
* This is just like parsing a string option, but collects multiple
* string options with the same name into a single value.
* \param opt name of the configuration option.
* \param val value of the option.
* \param loc address to store the value.
* \param maxval maximum length of value.
* \param source 0 if read from config file; 1 if from command.
* \retval 0 failure (unable to parse val).
* \retval 1 success.
*/
int
cf_flag(const char *opt, const char *val, void *loc, int maxval, int source)
{
size_t len = strlen(val);
size_t total = strlen((char *) loc);
/* truncate if necessary */
if (len + total + 1 >= (size_t) maxval) {
len = maxval - total - 1;
if (len <= 0) {
if (source == 0)
do_rawlog(LT_ERR, T("CONFIG: option %s value overflow\n"), opt);
return 0;
}
if (source == 0)
do_rawlog(LT_ERR, T("CONFIG: option %s value truncated\n"), opt);
}
sprintf((char *) loc, "%s %s", (char *) loc, val);
return 1;
}
/** Set a configuration option.
* This function sets a runtime configuration option. During the load
* of the configuration file, it gets run twice - once to set the
* main set of options and once again to set restrictions and aliases
* that require having the flag table available.
* \param opt name of the option.
* \param val value to set the option to.
* \param source 0 if being set from mush.cnf, 1 from softcode.
* \param restrictions 1 if we're setting restriction options, 0 for others.
* \retval 1 success.
* \retval 0 failure.
*/
int
config_set(const char *opt, char *val, int source, int restrictions)
{
PENNCONF *cp;
char *p;
if (!val)
return 0; /* NULL val is no good, but "" is ok */
/* Was this "restrict_command <command> <restriction>"? If so, do it */
if (!strcasecmp(opt, "restrict_command")) {
if (!restrictions)
return 0;
for (p = val; *p && !isspace((unsigned char) *p); p++) ;
if (*p) {
*p++ = '\0';
if (!restrict_command(val, p)) {
if (source == 0) {
do_rawlog(LT_ERR,
T("CONFIG: Invalid command or restriction for %s.\n"), val);
}
return 0;
}
} else {
if (source == 0) {
do_rawlog(LT_ERR,
T
("CONFIG: restrict_command %s requires a restriction value.\n"),
val);
}
return 0;
}
return 1;
} else if (!strcasecmp(opt, "restrict_function")) {
if (!restrictions)
return 0;
for (p = val; *p && !isspace((unsigned char) *p); p++) ;
if (*p) {
*p++ = '\0';
if (!restrict_function(val, p)) {
if (source == 0) {
do_rawlog(LT_ERR,
T("CONFIG: Invalid function or restriction for %s.\n"),
val);
}
return 0;
}
} else {
if (source == 0) {
do_rawlog(LT_ERR,
T
("CONFIG: restrict_function %s requires a restriction value.\n"),
val);
}
return 0;
}
return 1;
} else if (!strcasecmp(opt, "reserve_alias")) {
if (!restrictions)
return 0;
reserve_alias(val);
return 1;
} else if (!strcasecmp(opt, "command_alias")) {
if (!restrictions)
return 0;
for (p = val; *p && !isspace((unsigned char) *p); p++) ;
if (*p) {
*p++ = '\0';
if (!alias_command(val, p)) {
if (source == 0) {
do_rawlog(LT_ERR, T("CONFIG: Couldn't alias %s to %s.\n"), p, val);
}
return 0;
}
} else {
if (source == 0) {
do_rawlog(LT_ERR,
T("CONFIG: command_alias %s requires an alias.\n"), val);
}
return 0;
}
return 1;
} else if (!strcasecmp(opt, "attribute_alias")) {
if (!restrictions)
return 0;
for (p = val; *p && !isspace((unsigned char) *p); p++) ;
if (*p) {
*p++ = '\0';
if (!alias_attribute(val, p)) {
if (source == 0) {
do_rawlog(LT_ERR, T("CONFIG: Couldn't alias %s to %s.\n"), p, val);
}
return 0;
}
} else {
if (source == 0) {
do_rawlog(LT_ERR,
T("CONFIG: attribute_alias %s requires an alias.\n"), val);
}
return 0;
}
return 1;
} else if (!strcasecmp(opt, "function_alias")) {
if (!restrictions)
return 0;
for (p = val; *p && !isspace((unsigned char) *p); p++) ;
if (*p) {
*p++ = '\0';
if (!alias_function(val, p)) {
if (source == 0) {
do_rawlog(LT_ERR, T("CONFIG: Couldn't alias %s to %s.\n"), p, val);
}
return 0;
}
} else {
if (source == 0) {
do_rawlog(LT_ERR,
T("CONFIG: function_alias %s requires an alias.\n"), val);
}
return 0;
}
return 1;
} else if (!strcasecmp(opt, "help_command")
|| !strcasecmp(opt, "ahelp_command")) {
char *comm, *file;
int admin = !strcasecmp(opt, "ahelp_command");
if (!restrictions)
return 0;
/* Add a new help-like command */
if (source == 1)
return 0;
if (!val || !*val) {
do_rawlog(LT_ERR,
T
("CONFIG: help_command requires a command name and file name.\n"));
return 0;
}
comm = val;
for (file = val; *file && !isspace((unsigned char) *file); file++) ;
if (*file) {
*file++ = '\0';
add_help_file(comm, file, admin);
return 1;
} else {
do_rawlog(LT_ERR,
T
("CONFIG: help_command requires a command name and file name.\n"));
return 0;
}
} else if (restrictions) {
return 0;
}
/* search conf table for the option; if found, add it, if not found,
* complain about it. Forbid use of @config to set options without
* groups (log_wipe_passwd), or the file and message groups (@config/set
* output_data=../../.bashrc? Ouch.) */
for (cp = conftable; cp->name; cp++) {
int i = 0;
if ((!source || (cp->group && strcmp(cp->group, "files") != 0
&& strcmp(cp->group, "messages") != 0))
&& !strcasecmp(cp->name, opt)) {
i = cp->handler(opt, val, cp->loc, cp->max, source);
if (i)
cp->overridden = 1;
return i;
}
}
for (cp = (PENNCONF *) hash_firstentry(&local_options); cp;
cp = (PENNCONF *) hash_nextentry(&local_options)) {
int i = 0;
if ((!source || (cp->group && strcmp(cp->group, "files") != 0
&& strcmp(cp->group, "messages") != 0))
&& !strcasecmp(cp->name, opt)) {
i = cp->handler(opt, val, cp->loc, cp->max, source);
if (i)
cp->overridden = 1;
return i;
}
}
if (source == 0) {
do_rawlog(LT_ERR, T("CONFIG: directive '%s' in cnf file ignored.\n"), opt);
}
return 0;
}
/** Set the default configuration options.
*/
void
conf_default_set(void)
{
strcpy(options.mud_name, "TinyMUSH");
options.port = 4201;
options.ssl_port = 0;
strcpy(options.input_db, "data/indb");
strcpy(options.output_db, "data/outdb");
strcpy(options.crash_db, "data/PANIC.db");
strcpy(options.chatdb, "data/chatdb");
options.chan_cost = 1000;
options.max_player_chans = 3;
options.max_channels = 200;
strcpy(options.mail_db, "data/maildb");
options.player_start = 0;
options.master_room = 2;
options.base_room = 0;
options.default_home = 0;
options.ancestor_room = -1;
options.ancestor_exit = -1;
options.ancestor_thing = -1;
options.ancestor_player = -1;
options.idle_timeout = 0;
options.unconnected_idle_timeout = 300;
options.dump_interval = 3601;
strcpy(options.dump_message,
T("GAME: Dumping database. Game may freeze for a minute"));
strcpy(options.dump_complete, T("GAME: Dump complete. Time in."));
options.ident_timeout = 5;
options.max_logins = 128;
options.max_guests = 0;
options.whisper_loudness = 100;
options.blind_page = 1;
options.page_aliases = 0;
options.paycheck = 50;
options.guest_paycheck = 0;
options.starting_money = 100;
options.starting_quota = 20;
options.player_queue_limit = 100;
options.queue_chunk = 3;
options.active_q_chunk = 0;
options.func_nest_lim = 50;
options.func_invk_lim = 2500;
options.call_lim = 0;
options.use_quota = 1;
options.function_side_effects = 1;
options.empty_attrs = 1;
strcpy(options.money_singular, "Penny");
strcpy(options.money_plural, "Pennies");
strcpy(options.log_wipe_passwd, "zap!");
#ifdef WIN32
strcpy(options.compressprog, "");
strcpy(options.uncompressprog, "");
strcpy(options.compresssuff, "");
#else
strcpy(options.compressprog, "compress");
strcpy(options.uncompressprog, "uncompress");
strcpy(options.compresssuff, ".Z");
#endif /* WIN32 */
strcpy(options.connect_file[0], "txt/connect.txt");
strcpy(options.motd_file[0], "txt/motd.txt");
strcpy(options.wizmotd_file[0], "txt/wizmotd.txt");
strcpy(options.newuser_file[0], "txt/newuser.txt");
strcpy(options.register_file[0], "txt/register.txt");
strcpy(options.quit_file[0], "txt/quit.txt");
strcpy(options.down_file[0], "txt/down.txt");
strcpy(options.full_file[0], "txt/full.txt");
strcpy(options.guest_file[0], "txt/guest.txt");
strcpy(options.error_log, "");
strcpy(options.connect_log, "");
strcpy(options.command_log, "");
strcpy(options.trace_log, "");
strcpy(options.wizard_log, "");
strcpy(options.checkpt_log, "");
options.log_commands = 0;
options.log_forces = 1;
options.support_pueblo = 0;
options.login_allow = 1;
options.guest_allow = 1;
options.create_allow = 1;
strcpy(options.player_flags, "");
strcpy(options.room_flags, "");
strcpy(options.exit_flags, "");
strcpy(options.thing_flags, "");
options.warn_interval = 3600;
options.use_dns = 1;
options.safer_ufun = 1;
strcpy(options.dump_warning_1min,
T("GAME: Database will be dumped in 1 minute."));
strcpy(options.dump_warning_5min,
T("GAME: Database will be dumped in 5 minutes."));
options.noisy_whisper = 0;
options.possessive_get = 1;
options.possessive_get_d = 1;
options.really_safe = 1;
options.destroy_possessions = 1;
options.null_eq_zero = 0;
options.tiny_booleans = 0;
options.tiny_math = 0;
options.tiny_trim_fun = 0;
options.adestroy = 0;
options.amail = 0;
options.mail_limit = 5000;
options.player_listen = 1;
options.player_ahear = 1;
options.startups = 1;
options.room_connects = 0;
options.reverse_shs = 1;
options.ansi_names = 1;
options.comma_exit_list = 1;
options.count_all = 0;
options.exits_connect_rooms = 0;
options.zone_control = 1;
options.link_to_object = 1;
options.owner_queues = 0;
options.wiz_noaenter = 0;
options.use_ident = 1;
strcpy(options.ip_addr, "");
strcpy(options.ssl_ip_addr, "");
options.player_name_spaces = 0;
options.forking_dump = 1;
options.restrict_building = 0;
options.free_objects = 1;
options.flags_on_examine = 1;
options.ex_public_attribs = 1;
options.full_invis = 0;
options.silent_pemit = 0;
options.max_dbref = 0;
options.chat_strip_quote = 1;
strcpy(options.wizwall_prefix, "Broadcast:");
strcpy(options.rwall_prefix, "Admin:");
strcpy(options.wall_prefix, "Announcement:");
strcpy(options.access_file, "access.cnf");
strcpy(options.names_file, "names.cnf");
options.object_cost = 10;
options.exit_cost = 1;
options.link_cost = 1;
options.room_cost = 10;
options.queue_cost = 10;
options.quota_cost = 1;
options.find_cost = 100;
options.page_cost = 0;
options.kill_default_cost = 100;
options.kill_min_cost = 10;
options.kill_bonus = 50;
options.queue_loss = 63;
options.max_pennies = 100000;
options.max_guest_pennies = 100000;
options.max_depth = 10;
options.max_parents = 10;
options.max_global_fns = 50;
options.purge_interval = 601;
options.dbck_interval = 599;
options.max_attrcount = 2048;
options.float_precision = 6;
options.newline_one_char = 1;
options.player_name_len = 16;
options.queue_entry_cpu_time = 1500;
options.ascii_names = 1;
options.call_lim = 10000;
strcpy(options.chunk_swap_file, "data/chunkswap");
options.chunk_cache_memory = 1000000;
options.chunk_migrate_amount = 50;
options.read_remote_desc = 0;
#ifdef HAS_OPENSSL
strcpy(options.ssl_private_key_file, "");
strcpy(options.ssl_ca_file, "");
options.ssl_require_client_cert = 0;
#endif
options.mem_check = 0;
#ifdef HAS_MYSQL
strcpy(options.sql_platform, "disabled");
strcpy(options.sql_database, "");
strcpy(options.sql_username, "");
strcpy(options.sql_password, "");
strcpy(options.sql_host, "127.0.0.1");
#endif
}
/* Limit how many files we can nest */
static int conf_recursion = 0;
/** Read a configuration file.
* This function is called to read a configuration file. We may recurse,
* as there's an 'include' directive. It's called twice, once before
* the flag table load (when we want all options except restriction/alias
* options) and once after (when we only want restriction/alias options.)
* \param conf name of configuration file to read.
* \param restrictions 1 if reading restriction options, 0 otherwise.
* \retval 1 success.
* \retval 0 failure.
*/
int
config_file_startup(const char *conf, int restrictions)
{
/* read a configuration file. Return 0 on failure, 1 on success */
/* If 'restrictions' is 0, ignore restrict*. If it's 1, only
* look at restrict*
*/
FILE *fp = NULL;
PENNCONF *cp;
char tbuf1[BUFFER_LEN];
char *p, *q, *s;
static char cfile[BUFFER_LEN]; /* Remember the last one */
if (conf_recursion == 0) {
if (conf && *conf)
strcpy(cfile, conf);
fp = fopen(cfile, FOPEN_READ);
if (fp == NULL) {
do_rawlog(LT_ERR, T("ERROR: Cannot open configuration file %s."), cfile);
return 0;
}
do_rawlog(LT_ERR, "Reading %s", cfile);
} else {
if (conf && *conf)
fp = fopen(conf, FOPEN_READ);
if (fp == NULL) {
do_rawlog(LT_ERR, T("ERROR: Cannot open configuration file %s."),
(conf && *conf) ? conf : "Unknown");
return 0;
}
do_rawlog(LT_ERR, "Reading %s", conf);
}
fgets(tbuf1, BUFFER_LEN, fp);
while (!feof(fp)) {
p = tbuf1;
if (*p == '#') {
/* comment line */
fgets(tbuf1, BUFFER_LEN, fp);
continue;
}
/* this is a real line. Strip the end-of-line and characters following it.
* Split the line into command and argument portions. If it exists,
* also strip off the trailing comment. We try to make this work
* whether the eol is \n (unix, yay), \r\n (dos/win, ew), or \r (mac, hmm)
* This basically rules out embedded newlines as currently written
*/
for (p = tbuf1; *p && (*p != '\n') && (*p != '\r'); p++) ;
*p = '\0'; /* strip the end of line char(s) */
for (p = tbuf1; *p && isspace((unsigned char) *p); p++) /* strip spaces */
;
for (q = p; *q && !isspace((unsigned char) *q); q++) /* move over command */
;
if (*q)
*q++ = '\0'; /* split off command */
for (; *q && isspace((unsigned char) *q); q++) /* skip spaces */
;
/* If the first character of the value is a #, and that is
followed by a number, treat it as a dbref instead of a
comment. */
if (*q == '#' && isdigit((unsigned char) *(q + 1))) {
for (s = q + 1; *s && (*s != '#'); s++) /* look for a real comment */
;
} else {
for (s = q; *s && (*s != '#'); s++) /* look for comment */
;
}
if (*s) /* if found nuke it */
*s = '\0';
for (s = s - 1; (s >= q) && isspace((unsigned char) *s); s--) /* smash trailing stuff */
*s = '\0';
if (strlen(p) != 0) { /* skip blank lines */
/* Handle include filename directives separetly */
if (strcasecmp(p, "include") == 0) {
conf_recursion++;
if (conf_recursion > 10) {
do_rawlog(LT_ERR, T("CONFIG: include depth too deep in file %s"),
conf);
} else {
config_file_startup(q, restrictions);
}
conf_recursion--;
} else
config_set(p, q, 0, restrictions);
}
fgets(tbuf1, BUFFER_LEN, fp);
}
/* Warn about any config options that aren't overridden by the
* config file.
*/
if (conf_recursion == 0) {
for (cp = conftable; cp->name; cp++) {
if (!cp->overridden) {
do_rawlog(LT_ERR,
T
("CONFIG: directive '%s' missing from cnf file, using default value."),
cp->name);
}
}
for (cp = (PENNCONF *) hash_firstentry(&local_options); cp;
cp = (PENNCONF *) hash_nextentry(&local_options)) {
if (!cp->overridden) {
do_rawlog(LT_ERR,
T
("CONFIG: local directive '%s' missing from cnf file. using default value."),
cp->name);
}
}
/* these directives aren't player-settable but need to be initialized */
mudtime = time(NULL);
options.dump_counter = mudtime + options.dump_interval;
options.purge_counter = mudtime + options.purge_interval;
options.dbck_counter = mudtime + options.dbck_interval;
options.warn_counter = mudtime + options.warn_interval;
#ifdef WIN32
/* if we're on Win32, complain about compression */
if ((options.compressprog && *options.compressprog)) {
do_rawlog(LT_ERR,
T
("CONFIG: compression program is specified but not used in Win32, ignoring"),
options.compressprog);
}
if (((options.compresssuff && *options.compresssuff))) {
do_rawlog(LT_ERR,
T
("CONFIG: compression suffix is specified but not used in Win32, ignoring"),
options.compresssuff);
}
/* Also remove the compression options */
*options.uncompressprog = 0;
*options.compressprog = 0;
*options.compresssuff = 0;
#endif
}
fclose(fp);
return 1;
}
/** List configuration directives or groups.
* \param player the enactor.
* \param type the directive or group name.
* \param lc if 1, list in lowercase.
*/
void
do_config_list(dbref player, const char *type, int lc)
{
PENNCONFGROUP *cgp;
PENNCONF *cp;
if (SUPPORT_PUEBLO)
notify_noenter(player, tprintf("%cSAMP%c", TAG_START, TAG_END));
if (type && *type) {
/* Look up the type in the group table */
int found = 0;
for (cgp = confgroups; cgp->name; cgp++) {
if (string_prefix(T(cgp->name), type)
&& Can_View_Config_Group(player, cgp)) {
found = 1;
break;
}
}
if (!found) {
/* It wasn't a group. Is is one or more specific options? */
for (cp = conftable; cp->name; cp++) {
if (cp->group && string_prefix(cp->name, type)) {
notify(player, config_list_helper(player, cp, lc));
found = 1;
}
}
if (!found) {
/* Ok, maybe a local option? */
for (cp = (PENNCONF *) hash_firstentry(&local_options); cp;
cp = (PENNCONF *) hash_nextentry(&local_options)) {
if (cp->group && !strcasecmp(cp->name, type)) {
notify(player, config_list_helper(player, cp, lc));
found = 1;
}
}
}
if (!found) {
/* Wasn't found at all. Ok. */
notify(player, T("I only know the following types of options:"));
for (cgp = confgroups; cgp->name; cgp++) {
if (Can_View_Config_Group(player, cgp))
notify_format(player, " %-15s %s", T(cgp->name), cgp->desc);
}
}
} else {
/* Show all entries of that type */
notify(player, cgp->desc);
if (string_prefix("compile", type))
show_compile_options(player);
else {
for (cp = conftable; cp->name; cp++) {
if (cp->group && !strcmp(cp->group, cgp->name)) {
notify(player, config_list_helper(player, cp, lc));
}
}
for (cp = (PENNCONF *) hash_firstentry(&local_options); cp;
cp = (PENNCONF *) hash_nextentry(&local_options)) {
if (cp->group && !strcasecmp(cp->group, cgp->name)) {
notify(player, config_list_helper(player, cp, lc));
}
}
}
}
} else {
/* If we're here, we ran @config without a type. */
notify(player,
T("Use: @config/list <type of options> where type is one of:"));
for (cgp = confgroups; cgp->name; cgp++) {
if (Can_View_Config_Group(player, cgp))
notify_format(player, " %-15s %s", T(cgp->name), cgp->desc);
}
}
if (SUPPORT_PUEBLO)
notify_noenter(player, tprintf("%c/SAMP%c", TAG_START, TAG_END));
}
/** Lowercase a string if we've been asked to */
#define MAYBE_LC(x) (lc ? strlower(x) : x)
static char *
config_list_helper(dbref player
__attribute__ ((__unused__)), PENNCONF * cp, int lc)
{
static char result[BUFFER_LEN];
char *bp = result;
if ((cp->handler == cf_str) || (cp->handler == cf_flag))
safe_format(result, &bp, " %-40s %s", MAYBE_LC(cp->name), (char *) cp->loc);
else if (cp->handler == cf_int)
safe_format(result, &bp, " %-40s %d", MAYBE_LC(cp->name),
*((int *) cp->loc));
else if (cp->handler == cf_time) {
div_t n;
int secs = *(int *) cp->loc;
safe_format(result, &bp, " %-40s ", MAYBE_LC(cp->name));
if (secs >= 3600) {
n = div(secs, 3600);
secs = n.rem;
safe_format(result, &bp, "%dh", n.quot);
}
if (secs >= 60) {
n = div(secs, 60);
secs = n.rem;
safe_format(result, &bp, "%dm", n.quot);
}
if (secs)
safe_format(result, &bp, "%ds", secs);
} else if (cp->handler == cf_bool)
safe_format(result, &bp, " %-40s %s", MAYBE_LC(cp->name),
(*((int *) cp->loc) ? "Yes" : "No"));
else if (cp->handler == cf_dbref)
safe_format(result, &bp, " %-40s #%d", MAYBE_LC(cp->name),
*((dbref *) cp->loc));
*bp = '\0';
return result;
}
/* This one doesn't return the names */
static char *
config_list_helper2(dbref player
__attribute__ ((__unused__)), PENNCONF * cp, int lc
__attribute__ ((__unused__)))
{
static char result[BUFFER_LEN];
char *bp = result;
if ((cp->handler == cf_str) || (cp->handler == cf_flag))
safe_format(result, &bp, "%s", (char *) cp->loc);
else if (cp->handler == cf_int)
safe_format(result, &bp, "%d", *((int *) cp->loc));
else if (cp->handler == cf_time) {
div_t n;
int secs = *(int *) cp->loc;
if (secs >= 3600) {
n = div(secs, 3600);
secs = n.rem;
safe_format(result, &bp, "%dh", n.quot);
}
if (secs >= 60) {
n = div(secs, 60);
secs = n.rem;
safe_format(result, &bp, "%dm", n.quot);
}
if (secs)
safe_format(result, &bp, "%ds", secs);
} else if (cp->handler == cf_bool)
safe_format(result, &bp, "%s", (*((int *) cp->loc) ? "Yes" : "No"));
else if (cp->handler == cf_dbref)
safe_format(result, &bp, "#%d", *((dbref *) cp->loc));
*bp = '\0';
return result;
}
#undef MAYBE_LC
/* config(option): returns value of option
* config(): returns list of all option names
*/
FUNCTION(fun_config)
{
PENNCONF *cp;
if (args[0] && *args[0]) {
for (cp = conftable; cp->name; cp++) {
if (cp->group && !strcasecmp(cp->name, args[0])) {
safe_str(config_list_helper2(executor, cp, 0), buff, bp);
return;
}
}
for (cp = (PENNCONF *) hash_firstentry(&local_options); cp;
cp = (PENNCONF *) hash_nextentry(&local_options)) {
if (cp->group && !strcasecmp(cp->name, args[0])) {
safe_str(config_list_helper2(executor, cp, 0), buff, bp);
return;
}
}
safe_str(T("#-1 NO SUCH CONFIG OPTION"), buff, bp);
return;
} else {
for (cp = conftable; cp->name; cp++) {
safe_str(cp->name, buff, bp);
safe_chr(' ', buff, bp);
}
for (cp = (PENNCONF *) hash_firstentry(&local_options); cp;
cp = (PENNCONF *) hash_nextentry(&local_options)) {
safe_str(cp->name, buff, bp);
safe_chr(' ', buff, bp);
}
}
}
/** Enable or disable a configuration option.
* \param player the enactor.
* \param param the option to enable/disable.
* \param state 1 to enable, 0 to disable.
*/
void
do_enable(dbref player, const char *param, int state)
{
PENNCONF *cp;
for (cp = conftable; cp->name; cp++) {
if (cp->group && !strcasecmp(cp->name, param)) {
if (cp->handler == cf_bool) {
cf_bool(param, (state ? "yes" : "no"), cp->loc, cp->max, 1);
if (state == 0)
notify(player, T("Disabled."));
else
notify(player, T("Enabled."));
do_log(LT_WIZ, player, NOTHING, "%s %s",
cp->name, (state) ? "ENABLED" : "DISABLED");
} else
notify(player, T("That isn't a on/off option."));
return;
}
}
notify(player, T("No such option."));
}
static void
show_compile_options(dbref player)
{
#if (COMPRESSION_TYPE == 0)
notify(player, T(" Attributes are not compressed in memory."));
#endif
#if (COMPRESSION_TYPE == 1) || (COMPRESSION_TYPE == 2)
notify(player, T(" Attributes are Huffman compressed in memory."));
#endif
#if (COMPRESSION_TYPE == 3)
notify(player, T(" Attributes are word compressed in memory."));
#endif
#if (COMPRESSION_TYPE == 4)
notify(player, T(" Attributes are 8-bit word compressed in memory."));
#endif
#ifdef HAS_OPENSSL
notify(player, T(" The MUSH was compiled with SSL support."));
#endif
#ifdef INFO_SLAVE
notify(player, T(" DNS and ident lookups are handled by a slave process."));
#else
notify(player, T(" DNS and ident lookups are handled by the MUSH process."));
#endif
#ifdef NT_TCP
notify(player, T(" Windows NT native TCP/IP networking functions in use."));
#else
notify(player, T(" BSD sockets networking in use."));
#endif
#ifdef HAS_GETDATE
notify(player, T(" Extended convtime() is supported."));
#else
notify(player, T(" convtime() is stricter."));
#endif
#if defined(HAS_ITIMER) || defined(WIN32)
notify(player, T(" CPU usage limiting is supported."));
#else
notify(player, T(" CPU usage limiting is NOT supported."));
#endif
}