2077 lines
53 KiB
C
2077 lines
53 KiB
C
/* NetHack 3.7 cfgfiles.c $NHDT-Date: 1740532826 2025/02/25 17:20:26 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.417 $ */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/*-Copyright (c) Derek S. Ray, 2015. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#define NEED_VARARGS
|
|
|
|
#include "hack.h"
|
|
#include "dlb.h"
|
|
#include <errno.h>
|
|
|
|
#if (!defined(MAC) && !defined(O_WRONLY) && !defined(AZTEC_C)) \
|
|
|| defined(USE_FCNTL)
|
|
#include <fcntl.h>
|
|
#endif
|
|
|
|
#define BIGBUFSZ (5 * BUFSZ) /* big enough to format a 4*BUFSZ string (from
|
|
* config file parsing) with modest decoration;
|
|
* result will then be truncated to BUFSZ-1 */
|
|
|
|
#ifdef USER_SOUNDS
|
|
extern char *sounddir; /* defined in sounds.c */
|
|
#endif
|
|
|
|
staticfn void vconfig_error_add(const char *, va_list);
|
|
staticfn FILE *fopen_config_file(const char *, int);
|
|
staticfn int get_uchars(char *, uchar *, boolean, int, const char *);
|
|
#ifdef NOCWD_ASSUMPTIONS
|
|
staticfn void adjust_prefix(char *, int);
|
|
#endif
|
|
staticfn char *choose_random_part(char *, char);
|
|
staticfn boolean config_error_nextline(const char *);
|
|
staticfn void free_config_sections(void);
|
|
staticfn char *is_config_section(char *);
|
|
staticfn boolean handle_config_section(char *);
|
|
boolean parse_config_line(char *);
|
|
staticfn char *find_optparam(char *);
|
|
#ifndef SFCTOOL
|
|
staticfn boolean cnf_line_OPTIONS(char *);
|
|
staticfn boolean cnf_line_AUTOPICKUP_EXCEPTION(char *);
|
|
staticfn boolean cnf_line_BINDINGS(char *);
|
|
staticfn boolean cnf_line_AUTOCOMPLETE(char *);
|
|
staticfn boolean cnf_line_MSGTYPE(char *);
|
|
staticfn boolean cnf_line_HACKDIR(char *);
|
|
staticfn boolean cnf_line_LEVELDIR(char *);
|
|
staticfn boolean cnf_line_SAVEDIR(char *);
|
|
staticfn boolean cnf_line_BONESDIR(char *);
|
|
staticfn boolean cnf_line_DATADIR(char *);
|
|
staticfn boolean cnf_line_SCOREDIR(char *);
|
|
staticfn boolean cnf_line_LOCKDIR(char *);
|
|
staticfn boolean cnf_line_CONFIGDIR(char *);
|
|
staticfn boolean cnf_line_TROUBLEDIR(char *);
|
|
staticfn boolean cnf_line_NAME(char *);
|
|
staticfn boolean cnf_line_ROLE(char *);
|
|
staticfn boolean cnf_line_dogname(char *);
|
|
staticfn boolean cnf_line_catname(char *);
|
|
#endif /* SFCTOOL */
|
|
#ifdef SYSCF
|
|
staticfn boolean cnf_line_WIZARDS(char *);
|
|
staticfn boolean cnf_line_SHELLERS(char *);
|
|
staticfn boolean cnf_line_MSGHANDLER(char *);
|
|
staticfn boolean cnf_line_EXPLORERS(char *);
|
|
staticfn boolean cnf_line_DEBUGFILES(char *);
|
|
staticfn boolean cnf_line_DUMPLOGFILE(char *);
|
|
staticfn boolean cnf_line_GENERICUSERS(char *);
|
|
staticfn boolean cnf_line_BONES_POOLS(char *);
|
|
staticfn boolean cnf_line_SUPPORT(char *);
|
|
staticfn boolean cnf_line_RECOVER(char *);
|
|
staticfn boolean cnf_line_CHECK_SAVE_UID(char *);
|
|
staticfn boolean cnf_line_CHECK_PLNAME(char *);
|
|
staticfn boolean cnf_line_SEDUCE(char *);
|
|
staticfn boolean cnf_line_HIDEUSAGE(char *);
|
|
staticfn boolean cnf_line_MAXPLAYERS(char *);
|
|
staticfn boolean cnf_line_PERSMAX(char *);
|
|
staticfn boolean cnf_line_PERS_IS_UID(char *);
|
|
staticfn boolean cnf_line_ENTRYMAX(char *);
|
|
staticfn boolean cnf_line_POINTSMIN(char *);
|
|
staticfn boolean cnf_line_MAX_STATUENAME_RANK(char *);
|
|
staticfn boolean cnf_line_LIVELOG(char *);
|
|
staticfn boolean cnf_line_PANICTRACE_LIBC(char *);
|
|
staticfn boolean cnf_line_PANICTRACE_GDB(char *);
|
|
staticfn boolean cnf_line_GDBPATH(char *);
|
|
staticfn boolean cnf_line_GREPPATH(char *);
|
|
staticfn boolean cnf_line_CRASHREPORTURL(char *);
|
|
staticfn boolean cnf_line_ACCESSIBILITY(char *);
|
|
|
|
staticfn boolean cnf_line_PORTABLE_DEVICE_PATHS(char *);
|
|
#endif /* SYSCF */
|
|
#ifndef SFCTOOL
|
|
staticfn boolean cnf_line_BOULDER(char *);
|
|
staticfn boolean cnf_line_MENUCOLOR(char *);
|
|
staticfn boolean cnf_line_HILITE_STATUS(char *);
|
|
staticfn boolean cnf_line_WARNINGS(char *);
|
|
staticfn boolean cnf_line_ROGUESYMBOLS(char *);
|
|
staticfn boolean cnf_line_SYMBOLS(char *);
|
|
staticfn boolean cnf_line_WIZKIT(char *);
|
|
#ifdef USER_SOUNDS
|
|
staticfn boolean cnf_line_SOUNDDIR(char *);
|
|
staticfn boolean cnf_line_SOUND(char *);
|
|
#endif
|
|
staticfn boolean cnf_line_QT_TILEWIDTH(char *);
|
|
staticfn boolean cnf_line_QT_TILEHEIGHT(char *);
|
|
staticfn boolean cnf_line_QT_FONTSIZE(char *);
|
|
staticfn boolean cnf_line_QT_COMPACT(char *);
|
|
#endif /* SFCTOOL */
|
|
struct _cnf_parser_state; /* defined below (far below...) */
|
|
staticfn void cnf_parser_init(struct _cnf_parser_state *parser);
|
|
staticfn void cnf_parser_done(struct _cnf_parser_state *parser);
|
|
staticfn void parse_conf_buf(struct _cnf_parser_state *parser,
|
|
boolean (*proc)(char *arg));
|
|
/* next one is in extern.h; why here too? */
|
|
boolean parse_conf_str(const char *str, boolean (*proc)(char *arg));
|
|
static boolean ignore_errors_on_unmatched = FALSE,
|
|
ignore_statement_errors = FALSE;
|
|
|
|
#ifdef SFCTOOL
|
|
#ifdef wait_synch
|
|
#undef wait_synch
|
|
#endif
|
|
#define wait_synch()
|
|
#endif /* SFCTOOL */
|
|
|
|
/* ---------- BEGIN CONFIG FILE HANDLING ----------- */
|
|
|
|
/* used for messaging. Also used in options.c */
|
|
static const char *default_configfile =
|
|
#ifdef UNIX
|
|
".nethackrc";
|
|
#else
|
|
#if defined(MAC) || defined(__BEOS__)
|
|
"NetHack Defaults";
|
|
#else
|
|
#if defined(MSDOS) || defined(WIN32)
|
|
CONFIG_FILE;
|
|
#else
|
|
"NetHack.cnf";
|
|
#endif
|
|
#endif
|
|
#endif
|
|
static char configfile[BUFSZ];
|
|
|
|
char *
|
|
get_configfile(void)
|
|
{
|
|
return configfile;
|
|
}
|
|
|
|
const char *
|
|
get_default_configfile(void)
|
|
{
|
|
return default_configfile;
|
|
}
|
|
|
|
#ifdef MSDOS
|
|
/* conflict with speed-dial under windows
|
|
* for XXX.cnf file so support of NetHack.cnf
|
|
* is for backward compatibility only.
|
|
* Preferred name (and first tried) is now defaults.nh but
|
|
* the game will try the old name if there
|
|
* is no defaults.nh.
|
|
*/
|
|
const char *backward_compat_configfile = "nethack.cnf";
|
|
#endif
|
|
|
|
#ifndef SFCTOOL
|
|
|
|
/* #saveoptions - save config options into file */
|
|
int
|
|
do_write_config_file(void)
|
|
{
|
|
FILE *fp;
|
|
char tmp[BUFSZ];
|
|
|
|
if (!configfile[0]) {
|
|
pline("Strange, could not figure out config file name.");
|
|
return ECMD_OK;
|
|
}
|
|
if (flags.suppress_alert < FEATURE_NOTICE_VER(3,7,0)) {
|
|
pline("Warning: saveoptions is highly experimental!");
|
|
wait_synch();
|
|
pline("Some settings are not saved!");
|
|
wait_synch();
|
|
pline("All manual customization and comments are removed"
|
|
" from the file!");
|
|
wait_synch();
|
|
}
|
|
#define overwrite_prompt "Overwrite config file %.*s?"
|
|
Sprintf(tmp, overwrite_prompt,
|
|
(int) (BUFSZ - sizeof overwrite_prompt - 2), configfile);
|
|
#undef overwrite_prompt
|
|
if (!paranoid_query(TRUE, tmp))
|
|
return ECMD_OK;
|
|
|
|
fp = fopen(configfile, "w");
|
|
if (fp) {
|
|
size_t len, wrote;
|
|
strbuf_t buf;
|
|
|
|
strbuf_init(&buf);
|
|
all_options_strbuf(&buf);
|
|
len = strlen(buf.str);
|
|
wrote = fwrite(buf.str, 1, len, fp);
|
|
fclose(fp);
|
|
strbuf_empty(&buf);
|
|
if (wrote != len)
|
|
pline("An error occurred, wrote only partial data (%zu/%zu).",
|
|
wrote, len);
|
|
}
|
|
return ECMD_OK;
|
|
}
|
|
#endif /* SFCTOOL */
|
|
|
|
/* remember the name of the file we're accessing;
|
|
if may be used in option reject messages */
|
|
void
|
|
set_configfile_name(const char *fname)
|
|
{
|
|
(void) strncpy(configfile, fname, sizeof configfile - 1);
|
|
configfile[sizeof configfile - 1] = '\0';
|
|
}
|
|
|
|
staticfn FILE *
|
|
fopen_config_file(const char *filename, int src)
|
|
{
|
|
FILE *fp;
|
|
#if defined(UNIX) || defined(VMS)
|
|
char tmp_config[BUFSZ];
|
|
char *envp;
|
|
#endif
|
|
|
|
if (src == set_in_sysconf) {
|
|
/* SYSCF_FILE; if we can't open it, caller will bail */
|
|
if (filename && *filename) {
|
|
set_configfile_name(fqname(filename, SYSCONFPREFIX, 0));
|
|
fp = fopen(configfile, "r");
|
|
} else
|
|
fp = (FILE *) 0;
|
|
return fp;
|
|
}
|
|
/* If src != set_in_sysconf, "filename" is an environment variable, so it
|
|
* should hang around. If set, it is expected to be a full path name
|
|
* (if relevant)
|
|
*/
|
|
if (filename && *filename) {
|
|
set_configfile_name(filename);
|
|
#ifdef UNIX
|
|
if (!strncmp(configfile, "~/", 2) && (envp = nh_getenv("HOME")) != 0) {
|
|
/* support for command line '--nethackrc=~/path' (or for
|
|
NETHACKOPTIONS='@~/path'; we don't support ~user/path) */
|
|
Snprintf(tmp_config, sizeof tmp_config, "%s/%s",
|
|
envp, configfile + 2); /* insert $HOME/ and remove ~/ */
|
|
set_configfile_name(tmp_config);
|
|
}
|
|
if (access(configfile, 4) == -1) { /* 4 is R_OK on newer systems */
|
|
/* nasty sneaky attempt to read file through
|
|
* NetHack's setuid permissions -- this is the only
|
|
* place a file name may be wholly under the player's
|
|
* control (but SYSCF_FILE is not under the player's
|
|
* control so it's OK).
|
|
*/
|
|
raw_printf("Access to %s denied (%d).", configfile, errno);
|
|
wait_synch();
|
|
/* fall through to standard names */
|
|
} else
|
|
#endif
|
|
if ((fp = fopen(configfile, "r")) != (FILE *) 0) {
|
|
return fp;
|
|
#if defined(UNIX) || defined(VMS)
|
|
} else {
|
|
/* access() above probably caught most problems for UNIX */
|
|
raw_printf("Couldn't open requested config file %s (%d).",
|
|
configfile, errno);
|
|
wait_synch();
|
|
#endif
|
|
}
|
|
}
|
|
/* fall through to standard names */
|
|
|
|
#if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
|
|
set_configfile_name(fqname(default_configfile, CONFIGPREFIX, 0));
|
|
if ((fp = fopen(configfile, "r")) != (FILE *) 0) {
|
|
return fp;
|
|
} else if (strcmp(default_configfile, configfile)) {
|
|
set_configfile_name(default_configfile);
|
|
if ((fp = fopen(configfile, "r")) != (FILE *) 0)
|
|
return fp;
|
|
}
|
|
#ifdef MSDOS
|
|
set_configfile_name(fqname(backward_compat_configfile, CONFIGPREFIX, 0));
|
|
if ((fp = fopen(configfile, "r")) != (FILE *) 0) {
|
|
return fp;
|
|
} else if (strcmp(backward_compat_configfile, configfile)) {
|
|
set_configfile_name(backward_compat_configfile);
|
|
if ((fp = fopen(configfile, "r")) != (FILE *) 0)
|
|
return fp;
|
|
}
|
|
#endif
|
|
#else
|
|
/* constructed full path names don't need fqname() */
|
|
#ifdef VMS
|
|
/* no punctuation, so might be a logical name */
|
|
set_configfile_name("nethackini");
|
|
if ((fp = fopen(configfile, "r")) != (FILE *) 0)
|
|
return fp;
|
|
set_configfile_name("sys$login:nethack.ini");
|
|
if ((fp = fopen(configfile, "r")) != (FILE *) 0)
|
|
return fp;
|
|
|
|
envp = nh_getenv("HOME");
|
|
if (!envp || !*envp)
|
|
Strcpy(tmp_config, "NetHack.cnf");
|
|
else
|
|
Sprintf(tmp_config, "%s%s%s", envp,
|
|
!strchr(":]>/", envp[strlen(envp) - 1]) ? "/" : "",
|
|
"NetHack.cnf");
|
|
set_configfile_name(tmp_config);
|
|
if ((fp = fopen(configfile, "r")) != (FILE *) 0)
|
|
return fp;
|
|
#else /* should be only UNIX left */
|
|
envp = nh_getenv("HOME");
|
|
if (!envp)
|
|
Strcpy(tmp_config, ".nethackrc");
|
|
else
|
|
Sprintf(tmp_config, "%s/%s", envp, ".nethackrc");
|
|
|
|
set_configfile_name(tmp_config);
|
|
if ((fp = fopen(configfile, "r")) != (FILE *) 0)
|
|
return fp;
|
|
#if defined(__APPLE__) /* UNIX+__APPLE__ => OSX || MacOS */
|
|
/* try an alternative */
|
|
if (envp) {
|
|
/* keep 'tmp_config' intact here; if alternates fail, use it to
|
|
restore configfile[] to its preferred setting (".nethackrc") */
|
|
char alt_config[sizeof tmp_config];
|
|
|
|
/* OSX-style configuration settings */
|
|
Snprintf(alt_config, sizeof alt_config, "%s/%s", envp,
|
|
"Library/Preferences/NetHack Defaults");
|
|
set_configfile_name(alt_config);
|
|
if ((fp = fopen(configfile, "r")) != (FILE *) 0)
|
|
return fp;
|
|
/* may be easier for user to edit if filename has '.txt' suffix */
|
|
Snprintf(alt_config, sizeof alt_config, "%s/%s", envp,
|
|
"Library/Preferences/NetHack Defaults.txt");
|
|
set_configfile_name(alt_config);
|
|
if ((fp = fopen(configfile, "r")) != (FILE *) 0)
|
|
return fp;
|
|
/* couldn't open either of the alternate names; for use in
|
|
messages, put 'configfile' back to the normal value rather than
|
|
leaving it set to last alternate; retry open() to reset 'errno' */
|
|
set_configfile_name(tmp_config);
|
|
if ((fp = fopen(configfile, "r")) != (FILE *) 0)
|
|
return fp;
|
|
}
|
|
#endif /*__APPLE__*/
|
|
if (errno != ENOENT) {
|
|
const char *details;
|
|
|
|
/* e.g., problems when setuid NetHack can't search home
|
|
directory restricted to user */
|
|
#if defined(NHSTDC) && !defined(NOTSTDC)
|
|
if ((details = strerror(errno)) == 0)
|
|
#endif
|
|
details = "";
|
|
raw_printf("Couldn't open default config file %s %s(%d).",
|
|
configfile, details, errno);
|
|
wait_synch();
|
|
}
|
|
#endif /* !VMS => Unix */
|
|
#endif /* !(MICRO || MAC || __BEOS__ || WIN32) */
|
|
return (FILE *) 0;
|
|
}
|
|
|
|
/*
|
|
* Retrieve a list of integers from buf into a uchar array.
|
|
*
|
|
* NOTE: zeros are inserted unless modlist is TRUE, in which case the list
|
|
* location is unchanged. Callers must handle zeros if modlist is FALSE.
|
|
*/
|
|
staticfn int
|
|
get_uchars(char *bufp, /* current pointer */
|
|
uchar *list, /* return list */
|
|
boolean modlist, /* TRUE: list is being modified in place */
|
|
int size, /* return list size */
|
|
const char *name) /* name of option for error message */
|
|
{
|
|
unsigned int num = 0;
|
|
int count = 0;
|
|
boolean havenum = FALSE;
|
|
|
|
while (1) {
|
|
switch (*bufp) {
|
|
case ' ':
|
|
case '\0':
|
|
case '\t':
|
|
case '\n':
|
|
if (havenum) {
|
|
/* if modifying in place, don't insert zeros */
|
|
if (num || !modlist)
|
|
list[count] = num;
|
|
count++;
|
|
num = 0;
|
|
havenum = FALSE;
|
|
}
|
|
if (count == size || !*bufp)
|
|
return count;
|
|
bufp++;
|
|
break;
|
|
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
havenum = TRUE;
|
|
num = num * 10 + (*bufp - '0');
|
|
bufp++;
|
|
break;
|
|
|
|
case '\\':
|
|
goto gi_error;
|
|
break;
|
|
|
|
default:
|
|
gi_error:
|
|
raw_printf("Syntax error in %s", name);
|
|
wait_synch();
|
|
return count;
|
|
}
|
|
}
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
#ifdef NOCWD_ASSUMPTIONS
|
|
staticfn void
|
|
adjust_prefix(char *bufp, int prefixid)
|
|
{
|
|
char *ptr;
|
|
|
|
if (!bufp)
|
|
return;
|
|
#ifdef WIN32
|
|
if (fqn_prefix_locked[prefixid])
|
|
return;
|
|
#endif
|
|
/* Backward compatibility, ignore trailing ;n */
|
|
if ((ptr = strchr(bufp, ';')) != 0)
|
|
*ptr = '\0';
|
|
if (strlen(bufp) > 0) {
|
|
gf.fqn_prefix[prefixid] = (char *) alloc(strlen(bufp) + 2);
|
|
Strcpy(gf.fqn_prefix[prefixid], bufp);
|
|
append_slash(gf.fqn_prefix[prefixid]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Choose at random one of the sep separated parts from str. Mangles str. */
|
|
staticfn char *
|
|
choose_random_part(char *str, char sep)
|
|
{
|
|
int nsep = 1;
|
|
int csep;
|
|
int len = 0;
|
|
char *begin = str;
|
|
|
|
if (!str)
|
|
return (char *) 0;
|
|
|
|
while (*str) {
|
|
if (*str == sep)
|
|
nsep++;
|
|
str++;
|
|
}
|
|
#ifndef SFCTOOL
|
|
csep = rn2(nsep);
|
|
#else
|
|
nhUse(nsep);
|
|
csep = 1;
|
|
#endif
|
|
str = begin;
|
|
while ((csep > 0) && *str) {
|
|
str++;
|
|
if (*str == sep)
|
|
csep--;
|
|
}
|
|
if (*str) {
|
|
if (*str == sep)
|
|
str++;
|
|
begin = str;
|
|
while (*str && *str != sep) {
|
|
str++;
|
|
len++;
|
|
}
|
|
*str = '\0';
|
|
if (len)
|
|
return begin;
|
|
}
|
|
return (char *) 0;
|
|
}
|
|
|
|
staticfn void
|
|
free_config_sections(void)
|
|
{
|
|
if (gc.config_section_chosen) {
|
|
free(gc.config_section_chosen);
|
|
gc.config_section_chosen = NULL;
|
|
}
|
|
if (gc.config_section_current) {
|
|
free(gc.config_section_current);
|
|
gc.config_section_current = NULL;
|
|
}
|
|
}
|
|
|
|
/* check for " [ anything-except-bracket-or-empty ] # arbitrary-comment"
|
|
with spaces optional; returns pointer to "anything-except..." (with
|
|
trailing " ] #..." stripped) if ok, otherwise Null */
|
|
staticfn char *
|
|
is_config_section(
|
|
char *str) /* trailing spaces are stripped, ']' too iff result is good */
|
|
{
|
|
char *a, *c, *z;
|
|
|
|
/* remove any spaces at start and end; won't significantly interfere
|
|
with echoing the string in a config error message, if warranted */
|
|
a = trimspaces(str);
|
|
/* first character should be open square bracket; set pointer past it */
|
|
if (*a++ != '[')
|
|
return (char *) 0;
|
|
/* last character should be close bracket, ignoring any comment */
|
|
z = strchr(a, ']');
|
|
if (!z)
|
|
return (char *) 0;
|
|
/* comment, if present, can be preceded by spaces */
|
|
for (c = z + 1; *c == ' '; ++c)
|
|
continue;
|
|
if (*c && *c != '#')
|
|
return (char *) 0;
|
|
/* we now know that result is good; there won't be a config error
|
|
message so we can modify the input string */
|
|
*z = '\0';
|
|
/* 'a' points past '[' and the string ends where ']' was; remove any
|
|
spaces between '[' and choice-start and between choice-end and ']' */
|
|
return trimspaces(a);
|
|
}
|
|
|
|
staticfn boolean
|
|
handle_config_section(char *buf)
|
|
{
|
|
char *sect = is_config_section(buf);
|
|
|
|
if (sect) {
|
|
if (gc.config_section_current)
|
|
free(gc.config_section_current), gc.config_section_current = 0;
|
|
/* is_config_section() removed brackets from 'sect' */
|
|
if (!gc.config_section_chosen) {
|
|
config_error_add("Section \"[%s]\" without CHOOSE", sect);
|
|
return TRUE;
|
|
}
|
|
if (*sect) { /* got a section name */
|
|
gc.config_section_current = dupstr(sect);
|
|
debugpline1("set config section: '%s'",
|
|
gc.config_section_current);
|
|
} else { /* empty section name => end of sections */
|
|
free_config_sections();
|
|
debugpline0("unset config section");
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
if (gc.config_section_current) {
|
|
if (!gc.config_section_chosen)
|
|
return TRUE;
|
|
if (strcmp(gc.config_section_current, gc.config_section_chosen))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
#define match_varname(INP, NAM, LEN) match_optname(INP, NAM, LEN, TRUE)
|
|
|
|
/* find the '=' or ':' */
|
|
staticfn char *
|
|
find_optparam(char *buf)
|
|
{
|
|
char *bufp, *altp;
|
|
|
|
bufp = strchr(buf, '=');
|
|
altp = strchr(buf, ':');
|
|
if (!bufp || (altp && altp < bufp))
|
|
bufp = altp;
|
|
|
|
return bufp;
|
|
}
|
|
|
|
#ifndef SFCTOOL
|
|
|
|
staticfn boolean
|
|
cnf_line_OPTIONS(char *origbuf)
|
|
{
|
|
char *bufp = find_optparam(origbuf);
|
|
|
|
++bufp; /* skip '='; parseoptions() handles spaces */
|
|
return parseoptions(bufp, TRUE, TRUE);
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_AUTOPICKUP_EXCEPTION(char *bufp)
|
|
{
|
|
add_autopickup_exception(bufp);
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_BINDINGS(char *bufp)
|
|
{
|
|
return parsebindings(bufp);
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_AUTOCOMPLETE(char *bufp)
|
|
{
|
|
parseautocomplete(bufp, TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_MSGTYPE(char *bufp)
|
|
{
|
|
return msgtype_parse_add(bufp);
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_HACKDIR(char *bufp)
|
|
{
|
|
#ifdef NOCWD_ASSUMPTIONS
|
|
adjust_prefix(bufp, HACKPREFIX);
|
|
#else /*NOCWD_ASSUMPTIONS*/
|
|
#ifdef MICRO
|
|
(void) strncpy(gh.hackdir, bufp, PATHLEN - 1);
|
|
#else /* MICRO */
|
|
nhUse(bufp);
|
|
#endif /* MICRO */
|
|
#endif /*NOCWD_ASSUMPTIONS*/
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_LEVELDIR(char *bufp)
|
|
{
|
|
#ifdef NOCWD_ASSUMPTIONS
|
|
adjust_prefix(bufp, LEVELPREFIX);
|
|
#else /*NOCWD_ASSUMPTIONS*/
|
|
#ifdef MICRO
|
|
if (strlen(bufp) >= PATHLEN)
|
|
bufp[PATHLEN - 1] = '\0';
|
|
Strcpy(g.permbones, bufp);
|
|
if (!ramdisk_specified || !*levels)
|
|
Strcpy(levels, bufp);
|
|
gr.ramdisk = (strcmp(g.permbones, levels) != 0);
|
|
#else /* MICRO */
|
|
nhUse(bufp);
|
|
#endif /* MICRO */
|
|
#endif /*NOCWD_ASSUMPTIONS*/
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_SAVEDIR(char *bufp)
|
|
{
|
|
#ifdef NOCWD_ASSUMPTIONS
|
|
adjust_prefix(bufp, SAVEPREFIX);
|
|
#else /*NOCWD_ASSUMPTIONS*/
|
|
#ifdef MICRO
|
|
char *ptr;
|
|
|
|
if ((ptr = strchr(bufp, ';')) != 0) {
|
|
*ptr = '\0';
|
|
}
|
|
|
|
(void) strncpy(gs.SAVEP, bufp, SAVESIZE - 1);
|
|
append_slash(gs.SAVEP);
|
|
#else /* MICRO */
|
|
nhUse(bufp);
|
|
#endif /* MICRO */
|
|
#endif /*NOCWD_ASSUMPTIONS*/
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_BONESDIR(char *bufp)
|
|
{
|
|
#ifdef NOCWD_ASSUMPTIONS
|
|
adjust_prefix(bufp, BONESPREFIX);
|
|
#else
|
|
nhUse(bufp);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_DATADIR(char *bufp)
|
|
{
|
|
#ifdef NOCWD_ASSUMPTIONS
|
|
adjust_prefix(bufp, DATAPREFIX);
|
|
#else
|
|
nhUse(bufp);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_SCOREDIR(char *bufp)
|
|
{
|
|
#ifdef NOCWD_ASSUMPTIONS
|
|
adjust_prefix(bufp, SCOREPREFIX);
|
|
#else
|
|
nhUse(bufp);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_LOCKDIR(char *bufp)
|
|
{
|
|
#ifdef NOCWD_ASSUMPTIONS
|
|
adjust_prefix(bufp, LOCKPREFIX);
|
|
#else
|
|
nhUse(bufp);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_CONFIGDIR(char *bufp)
|
|
{
|
|
#ifdef NOCWD_ASSUMPTIONS
|
|
adjust_prefix(bufp, CONFIGPREFIX);
|
|
#else
|
|
nhUse(bufp);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_TROUBLEDIR(char *bufp)
|
|
{
|
|
#ifdef NOCWD_ASSUMPTIONS
|
|
adjust_prefix(bufp, TROUBLEPREFIX);
|
|
#else
|
|
nhUse(bufp);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_NAME(char *bufp)
|
|
{
|
|
(void) strncpy(svp.plname, bufp, PL_NSIZ - 1);
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_ROLE(char *bufp)
|
|
{
|
|
int len;
|
|
|
|
if ((len = str2role(bufp)) >= 0)
|
|
flags.initrole = len;
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_dogname(char *bufp)
|
|
{
|
|
(void) strncpy(gd.dogname, bufp, PL_PSIZ - 1);
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_catname(char *bufp)
|
|
{
|
|
(void) strncpy(gc.catname, bufp, PL_PSIZ - 1);
|
|
return TRUE;
|
|
}
|
|
#endif /* SFCTOOL */
|
|
|
|
#ifdef SYSCF
|
|
|
|
staticfn boolean
|
|
cnf_line_WIZARDS(char *bufp)
|
|
{
|
|
if (sysopt.wizards)
|
|
free((genericptr_t) sysopt.wizards);
|
|
sysopt.wizards = dupstr(bufp);
|
|
if (strlen(sysopt.wizards) && strcmp(sysopt.wizards, "*")) {
|
|
/* pre-format WIZARDS list now; it's displayed during a panic
|
|
and since that panic might be due to running out of memory,
|
|
we don't want to risk attempting to allocate any memory then */
|
|
if (sysopt.fmtd_wizard_list)
|
|
free((genericptr_t) sysopt.fmtd_wizard_list);
|
|
sysopt.fmtd_wizard_list = build_english_list(sysopt.wizards);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_SHELLERS(char *bufp)
|
|
{
|
|
if (sysopt.shellers)
|
|
free((genericptr_t) sysopt.shellers);
|
|
sysopt.shellers = dupstr(bufp);
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_MSGHANDLER(char *bufp)
|
|
{
|
|
if (sysopt.msghandler)
|
|
free((genericptr_t) sysopt.msghandler);
|
|
sysopt.msghandler = dupstr(bufp);
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_EXPLORERS(char *bufp)
|
|
{
|
|
if (sysopt.explorers)
|
|
free((genericptr_t) sysopt.explorers);
|
|
sysopt.explorers = dupstr(bufp);
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_DEBUGFILES(char *bufp)
|
|
{
|
|
/* might already have a vaule from getenv("DEBUGFILES");
|
|
if so, ignore this value from SYSCF */
|
|
if (!sysopt.env_dbgfl) {
|
|
if (sysopt.debugfiles)
|
|
free((genericptr_t) sysopt.debugfiles);
|
|
sysopt.debugfiles = dupstr(bufp);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_DUMPLOGFILE(char *bufp)
|
|
{
|
|
#ifdef DUMPLOG
|
|
if (sysopt.dumplogfile)
|
|
free((genericptr_t) sysopt.dumplogfile);
|
|
sysopt.dumplogfile = dupstr(bufp);
|
|
#else
|
|
nhUse(bufp);
|
|
#endif /*DUMPLOG*/
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_GENERICUSERS(char *bufp)
|
|
{
|
|
if (sysopt.genericusers)
|
|
free((genericptr_t) sysopt.genericusers);
|
|
sysopt.genericusers = dupstr(bufp);
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_BONES_POOLS(char *bufp)
|
|
{
|
|
/* max value of 10 guarantees (N % bones.pools) will be one digit
|
|
so we don't lose control of the length of bones file names */
|
|
int n = atoi(bufp);
|
|
|
|
sysopt.bones_pools = (n <= 0) ? 0 : min(n, 10);
|
|
/* note: right now bones_pools==0 is the same as bones_pools==1,
|
|
but we could change that and make bones_pools==0 become an
|
|
indicator to suppress bones usage altogether */
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_SUPPORT(char *bufp)
|
|
{
|
|
if (sysopt.support)
|
|
free((genericptr_t) sysopt.support);
|
|
sysopt.support = dupstr(bufp);
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_RECOVER(char *bufp)
|
|
{
|
|
if (sysopt.recover)
|
|
free((genericptr_t) sysopt.recover);
|
|
sysopt.recover = dupstr(bufp);
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_CHECK_SAVE_UID(char *bufp)
|
|
{
|
|
int n = atoi(bufp);
|
|
|
|
sysopt.check_save_uid = n;
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_CHECK_PLNAME(char *bufp)
|
|
{
|
|
int n = atoi(bufp);
|
|
|
|
sysopt.check_plname = n;
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_SEDUCE(char *bufp)
|
|
{
|
|
int n = !!atoi(bufp); /* XXX this could be tighter */
|
|
#ifdef SYSCF
|
|
int src = iflags.parse_config_file_src;
|
|
boolean in_sysconf = (src == set_in_sysconf);
|
|
#else
|
|
boolean in_sysconf = FALSE;
|
|
#endif
|
|
|
|
/* allow anyone to disable it but can only enable it in sysconf
|
|
or as a no-op for the user when sysconf hasn't disabled it */
|
|
if (!in_sysconf && !sysopt.seduce && n != 0) {
|
|
config_error_add("Illegal value in SEDUCE");
|
|
n = 0;
|
|
}
|
|
sysopt.seduce = n;
|
|
sysopt_seduce_set(sysopt.seduce);
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_HIDEUSAGE(char *bufp)
|
|
{
|
|
int n = !!atoi(bufp);
|
|
|
|
sysopt.hideusage = n;
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_MAXPLAYERS(char *bufp)
|
|
{
|
|
int n = atoi(bufp);
|
|
|
|
/* XXX to get more than 25, need to rewrite all lock code */
|
|
if (n < 0 || n > 25) {
|
|
config_error_add("Illegal value in MAXPLAYERS (maximum is 25)");
|
|
n = 5;
|
|
}
|
|
sysopt.maxplayers = n;
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_PERSMAX(char *bufp)
|
|
{
|
|
int n = atoi(bufp);
|
|
|
|
if (n < 1) {
|
|
config_error_add("Illegal value in PERSMAX (minimum is 1)");
|
|
n = 0;
|
|
}
|
|
sysopt.persmax = n;
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_PERS_IS_UID(char *bufp)
|
|
{
|
|
int n = atoi(bufp);
|
|
|
|
if (n != 0 && n != 1) {
|
|
config_error_add("Illegal value in PERS_IS_UID (must be 0 or 1)");
|
|
n = 0;
|
|
}
|
|
sysopt.pers_is_uid = n;
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_ENTRYMAX(char *bufp)
|
|
{
|
|
int n = atoi(bufp);
|
|
|
|
if (n < 10) {
|
|
config_error_add("Illegal value in ENTRYMAX (minimum is 10)");
|
|
n = 10;
|
|
}
|
|
sysopt.entrymax = n;
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_POINTSMIN(char *bufp)
|
|
{
|
|
int n = atoi(bufp);
|
|
|
|
if (n < 1) {
|
|
config_error_add("Illegal value in POINTSMIN (minimum is 1)");
|
|
n = 100;
|
|
}
|
|
sysopt.pointsmin = n;
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_MAX_STATUENAME_RANK(char *bufp)
|
|
{
|
|
int n = atoi(bufp);
|
|
|
|
if (n < 1) {
|
|
config_error_add("Illegal value in MAX_STATUENAME_RANK"
|
|
" (minimum is 1)");
|
|
n = 10;
|
|
}
|
|
sysopt.tt_oname_maxrank = n;
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_LIVELOG(char *bufp)
|
|
{
|
|
/* using 0 for base accepts "dddd" as decimal provided that first 'd'
|
|
isn't '0', "0xhhhh" as hexadecimal, and "0oooo" as octal; ignores
|
|
any trailing junk, including '8' or '9' for leading '0' octal */
|
|
long L = strtol(bufp, NULL, 0);
|
|
|
|
if (L < 0L || L > 0xffffL) {
|
|
config_error_add("Illegal value for LIVELOG"
|
|
" (must be between 0 and 0xFFFF).");
|
|
return 0;
|
|
}
|
|
sysopt.livelog = L;
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_PANICTRACE_LIBC(char *bufp)
|
|
{
|
|
int n = atoi(bufp);
|
|
|
|
#if defined(PANICTRACE) && defined(PANICTRACE_LIBC)
|
|
if (n < 0 || n > 2) {
|
|
config_error_add("Illegal value in PANICTRACE_LIBC (not 0,1,2)");
|
|
n = 0;
|
|
}
|
|
#endif
|
|
sysopt.panictrace_libc = n;
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_PANICTRACE_GDB(char *bufp)
|
|
{
|
|
int n = atoi(bufp);
|
|
|
|
#if defined(PANICTRACE)
|
|
if (n < 0 || n > 2) {
|
|
config_error_add("Illegal value in PANICTRACE_GDB (not 0,1,2)");
|
|
n = 0;
|
|
}
|
|
#endif
|
|
sysopt.panictrace_gdb = n;
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_GDBPATH(char *bufp)
|
|
{
|
|
#if defined(PANICTRACE) && !defined(VMS)
|
|
if (!file_exists(bufp)) {
|
|
config_error_add("File specified in GDBPATH does not exist");
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
if (sysopt.gdbpath)
|
|
free((genericptr_t) sysopt.gdbpath);
|
|
sysopt.gdbpath = dupstr(bufp);
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_GREPPATH(char *bufp)
|
|
{
|
|
#if defined(PANICTRACE) && !defined(VMS)
|
|
if (!file_exists(bufp)) {
|
|
config_error_add("File specified in GREPPATH does not exist");
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
if (sysopt.greppath)
|
|
free((genericptr_t) sysopt.greppath);
|
|
sysopt.greppath = dupstr(bufp);
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_CRASHREPORTURL(char *bufp)
|
|
{
|
|
if (sysopt.crashreporturl)
|
|
free((genericptr_t) sysopt.crashreporturl);
|
|
sysopt.crashreporturl = dupstr(bufp);
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_ACCESSIBILITY(char *bufp)
|
|
{
|
|
int n = atoi(bufp);
|
|
|
|
if (n < 0 || n > 1) {
|
|
config_error_add("Illegal value in ACCESSIBILITY (not 0,1)");
|
|
n = 0;
|
|
}
|
|
sysopt.accessibility = n;
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_PORTABLE_DEVICE_PATHS(char *bufp)
|
|
{
|
|
#ifdef WIN32
|
|
int n = atoi(bufp);
|
|
|
|
if (n < 0 || n > 1) {
|
|
config_error_add("Illegal value in PORTABLE_DEVICE_PATHS"
|
|
" (not 0 or 1)");
|
|
n = 0;
|
|
}
|
|
sysopt.portable_device_paths = n;
|
|
#else /* Windows-only directive encountered by non-Windows config */
|
|
nhUse(bufp);
|
|
config_error_add("PORTABLE_DEVICE_PATHS is not supported");
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
#endif /* SYSCF */
|
|
|
|
#ifndef SFCTOOL
|
|
|
|
staticfn boolean
|
|
cnf_line_BOULDER(char *bufp)
|
|
{
|
|
(void) get_uchars(bufp, &go.ov_primary_syms[SYM_BOULDER + SYM_OFF_X],
|
|
TRUE, 1, "BOULDER");
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_MENUCOLOR(char *bufp)
|
|
{
|
|
return add_menu_coloring(bufp);
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_HILITE_STATUS(char *bufp)
|
|
{
|
|
#ifdef STATUS_HILITES
|
|
return parse_status_hl1(bufp, TRUE);
|
|
#else
|
|
nhUse(bufp);
|
|
return TRUE;
|
|
#endif
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_WARNINGS(char *bufp)
|
|
{
|
|
uchar translate[MAXPCHARS];
|
|
|
|
(void) get_uchars(bufp, translate, FALSE, WARNCOUNT, "WARNINGS");
|
|
assign_warnings(translate);
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_ROGUESYMBOLS(char *bufp)
|
|
{
|
|
if (parsesymbols(bufp, ROGUESET)) {
|
|
switch_symbols(TRUE);
|
|
return TRUE;
|
|
}
|
|
config_error_add("Error in ROGUESYMBOLS definition '%s'", bufp);
|
|
return FALSE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_SYMBOLS(char *bufp)
|
|
{
|
|
if (parsesymbols(bufp, PRIMARYSET)) {
|
|
switch_symbols(TRUE);
|
|
return TRUE;
|
|
}
|
|
if (!config_unmatched_ignored())
|
|
config_error_add("Error in SYMBOLS definition '%s'", bufp);
|
|
return FALSE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_WIZKIT(char *bufp)
|
|
{
|
|
(void) strncpy(gw.wizkit, bufp, WIZKIT_MAX - 1);
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef USER_SOUNDS
|
|
staticfn boolean
|
|
cnf_line_SOUNDDIR(char *bufp)
|
|
{
|
|
if (sounddir)
|
|
free((genericptr_t) sounddir);
|
|
sounddir = dupstr(bufp);
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_SOUND(char *bufp)
|
|
{
|
|
add_sound_mapping(bufp);
|
|
return TRUE;
|
|
}
|
|
#endif /*USER_SOUNDS*/
|
|
|
|
staticfn boolean
|
|
cnf_line_QT_TILEWIDTH(char *bufp)
|
|
{
|
|
#ifdef QT_GRAPHICS
|
|
extern char *qt_tilewidth;
|
|
|
|
if (qt_tilewidth == NULL)
|
|
qt_tilewidth = dupstr(bufp);
|
|
#else
|
|
nhUse(bufp);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_QT_TILEHEIGHT(char *bufp)
|
|
{
|
|
#ifdef QT_GRAPHICS
|
|
extern char *qt_tileheight;
|
|
|
|
if (qt_tileheight == NULL)
|
|
qt_tileheight = dupstr(bufp);
|
|
#else
|
|
nhUse(bufp);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_QT_FONTSIZE(char *bufp)
|
|
{
|
|
#ifdef QT_GRAPHICS
|
|
extern char *qt_fontsize;
|
|
|
|
if (qt_fontsize == NULL)
|
|
qt_fontsize = dupstr(bufp);
|
|
#else
|
|
nhUse(bufp);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
cnf_line_QT_COMPACT(char *bufp)
|
|
{
|
|
#ifdef QT_GRAPHICS
|
|
extern int qt_compact_mode;
|
|
|
|
qt_compact_mode = atoi(bufp);
|
|
#else
|
|
nhUse(bufp);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
#endif /* SFCTOOL */
|
|
|
|
typedef boolean (*config_line_stmt_func)(char *);
|
|
|
|
/* normal */
|
|
#define CNFL_N(n, l) { #n, l, FALSE, FALSE, cnf_line_##n }
|
|
/* normal, alias */
|
|
#define CNFL_NA(n, l, f) { #n, l, FALSE, FALSE, cnf_line_##f }
|
|
/* sysconf only */
|
|
#define CNFL_S(n, l) { #n, l, TRUE, FALSE, cnf_line_##n }
|
|
|
|
static const struct match_config_line_stmt {
|
|
const char *name;
|
|
int len;
|
|
boolean syscnf_only;
|
|
boolean origbuf;
|
|
config_line_stmt_func fn;
|
|
} config_line_stmt[] = {
|
|
#ifndef SFCTOOL
|
|
/* OPTIONS handled separately */
|
|
{ "OPTIONS", 4, FALSE, TRUE, cnf_line_OPTIONS },
|
|
CNFL_N(AUTOPICKUP_EXCEPTION, 5),
|
|
CNFL_N(BINDINGS, 4),
|
|
CNFL_N(AUTOCOMPLETE, 5),
|
|
CNFL_N(MSGTYPE, 7),
|
|
CNFL_N(HACKDIR, 4),
|
|
CNFL_N(LEVELDIR, 4),
|
|
CNFL_NA(LEVELS, 4, LEVELDIR),
|
|
CNFL_N(SAVEDIR, 4),
|
|
CNFL_N(BONESDIR, 5),
|
|
CNFL_N(DATADIR, 4),
|
|
CNFL_N(SCOREDIR, 4),
|
|
CNFL_N(LOCKDIR, 4),
|
|
CNFL_N(CONFIGDIR, 4),
|
|
CNFL_N(TROUBLEDIR, 4),
|
|
CNFL_N(NAME, 4),
|
|
CNFL_N(ROLE, 4),
|
|
CNFL_NA(CHARACTER, 4, ROLE),
|
|
CNFL_N(dogname, 3),
|
|
CNFL_N(catname, 3),
|
|
#endif /* SFCTOOL */
|
|
#ifdef SYSCF
|
|
CNFL_S(WIZARDS, 7),
|
|
CNFL_S(SHELLERS, 8),
|
|
CNFL_S(MSGHANDLER, 9),
|
|
CNFL_S(EXPLORERS, 7),
|
|
CNFL_S(DEBUGFILES, 5),
|
|
CNFL_S(DUMPLOGFILE, 7),
|
|
CNFL_S(GENERICUSERS, 12),
|
|
CNFL_S(BONES_POOLS, 10),
|
|
CNFL_S(SUPPORT, 7),
|
|
CNFL_S(RECOVER, 7),
|
|
CNFL_S(CHECK_SAVE_UID, 14),
|
|
CNFL_S(CHECK_PLNAME, 12),
|
|
CNFL_S(SEDUCE, 6),
|
|
CNFL_S(HIDEUSAGE, 9),
|
|
CNFL_S(MAXPLAYERS, 10),
|
|
CNFL_S(PERSMAX, 7),
|
|
CNFL_S(PERS_IS_UID, 11),
|
|
CNFL_S(ENTRYMAX, 8),
|
|
CNFL_S(POINTSMIN, 9),
|
|
CNFL_S(MAX_STATUENAME_RANK, 10),
|
|
CNFL_S(LIVELOG, 7),
|
|
CNFL_S(PANICTRACE_LIBC, 15),
|
|
CNFL_S(PANICTRACE_GDB, 14),
|
|
CNFL_S(CRASHREPORTURL, 13),
|
|
CNFL_S(GDBPATH, 7),
|
|
CNFL_S(GREPPATH, 7),
|
|
CNFL_S(ACCESSIBILITY, 13),
|
|
CNFL_S(PORTABLE_DEVICE_PATHS, 8),
|
|
#endif /*SYSCF*/
|
|
#ifndef SFCTOOL
|
|
CNFL_N(BOULDER, 3),
|
|
CNFL_N(MENUCOLOR, 9),
|
|
CNFL_N(HILITE_STATUS, 6),
|
|
CNFL_N(WARNINGS, 5),
|
|
CNFL_N(ROGUESYMBOLS, 4),
|
|
CNFL_N(SYMBOLS, 4),
|
|
CNFL_N(WIZKIT, 6),
|
|
#ifdef USER_SOUNDS
|
|
CNFL_N(SOUNDDIR, 8),
|
|
CNFL_N(SOUND, 5),
|
|
#endif /*USER_SOUNDS*/
|
|
CNFL_N(QT_TILEWIDTH, 12),
|
|
CNFL_N(QT_TILEHEIGHT, 13),
|
|
CNFL_N(QT_FONTSIZE, 11),
|
|
CNFL_N(QT_COMPACT, 10)
|
|
#endif /* SFCTOOL */
|
|
};
|
|
|
|
#undef CNFL_N
|
|
#undef CNFL_NA
|
|
#undef CNFL_S
|
|
|
|
static boolean disregarded_config_lines[SIZE(config_line_stmt)];
|
|
|
|
boolean
|
|
parse_config_line(char *origbuf)
|
|
{
|
|
#if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS)
|
|
static boolean ramdisk_specified = FALSE;
|
|
#endif
|
|
#ifdef SYSCF
|
|
int src = iflags.parse_config_file_src;
|
|
boolean in_sysconf = (src == set_in_sysconf);
|
|
#endif
|
|
char *bufp, buf[4 * BUFSZ];
|
|
int i;
|
|
|
|
while (*origbuf == ' ' || *origbuf == '\t') /* skip leading whitespace */
|
|
++origbuf; /* (caller probably already did this) */
|
|
(void) strncpy(buf, origbuf, sizeof buf - 1);
|
|
buf[sizeof buf - 1] = '\0'; /* strncpy not guaranteed to NUL terminate */
|
|
/* convert any tab to space, condense consecutive spaces into one,
|
|
remove leading and trailing spaces (exception: if there is nothing
|
|
but spaces, one of them will be kept even though it leads/trails) */
|
|
mungspaces(buf);
|
|
|
|
/* find the '=' or ':' */
|
|
bufp = find_optparam(buf);
|
|
if (!bufp) {
|
|
if (!ignore_statement_errors)
|
|
config_error_add("Not a config statement, missing '='");
|
|
return FALSE;
|
|
}
|
|
/* skip past '=', then space between it and value, if any */
|
|
++bufp;
|
|
if (*bufp == ' ')
|
|
++bufp;
|
|
|
|
for (i = 0; i < SIZE(config_line_stmt); i++) {
|
|
#ifdef SYSCF
|
|
if (config_line_stmt[i].syscnf_only && !in_sysconf)
|
|
continue;
|
|
#endif
|
|
if (match_varname(buf, config_line_stmt[i].name,
|
|
config_line_stmt[i].len)) {
|
|
char *parm = config_line_stmt[i].origbuf ? origbuf : bufp;
|
|
|
|
if (!disregarded_config_lines[i])
|
|
return config_line_stmt[i].fn(parm);
|
|
}
|
|
}
|
|
|
|
if (!ignore_errors_on_unmatched)
|
|
config_error_add("Unknown config statement");
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef USER_SOUNDS
|
|
boolean
|
|
can_read_file(const char *filename)
|
|
{
|
|
return (boolean) (access(filename, 4) == 0);
|
|
}
|
|
#endif /* USER_SOUNDS */
|
|
|
|
struct _config_error_errmsg {
|
|
int line_num;
|
|
char *errormsg;
|
|
struct _config_error_errmsg *next;
|
|
};
|
|
|
|
struct _config_error_frame {
|
|
int line_num;
|
|
int num_errors;
|
|
boolean origline_shown;
|
|
boolean fromfile;
|
|
boolean secure;
|
|
char origline[4 * BUFSZ];
|
|
char source[BUFSZ];
|
|
struct _config_error_frame *next;
|
|
};
|
|
|
|
static struct _config_error_frame *config_error_data = 0;
|
|
static struct _config_error_errmsg *config_error_msg = 0;
|
|
|
|
void
|
|
config_error_init(boolean from_file, const char *sourcename, boolean secure)
|
|
{
|
|
struct _config_error_frame *tmp = (struct _config_error_frame *)
|
|
alloc(sizeof *tmp);
|
|
|
|
tmp->line_num = 0;
|
|
tmp->num_errors = 0;
|
|
tmp->origline_shown = FALSE;
|
|
tmp->fromfile = from_file;
|
|
tmp->secure = secure;
|
|
tmp->origline[0] = '\0';
|
|
if (sourcename && sourcename[0]) {
|
|
(void) strncpy(tmp->source, sourcename, sizeof (tmp->source) - 1);
|
|
tmp->source[sizeof (tmp->source) - 1] = '\0';
|
|
} else
|
|
tmp->source[0] = '\0';
|
|
|
|
tmp->next = config_error_data;
|
|
config_error_data = tmp;
|
|
program_state.config_error_ready = TRUE;
|
|
}
|
|
|
|
staticfn boolean
|
|
config_error_nextline(const char *line)
|
|
{
|
|
struct _config_error_frame *ced = config_error_data;
|
|
|
|
if (!ced)
|
|
return FALSE;
|
|
|
|
if (ced->num_errors && ced->secure)
|
|
return FALSE;
|
|
|
|
ced->line_num++;
|
|
ced->origline_shown = FALSE;
|
|
if (line && line[0]) {
|
|
(void) strncpy(ced->origline, line, sizeof (ced->origline) - 1);
|
|
ced->origline[sizeof (ced->origline) - 1] = '\0';
|
|
} else
|
|
ced->origline[0] = '\0';
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#ifndef SFCTOOL
|
|
int
|
|
l_get_config_errors(lua_State *L)
|
|
{
|
|
struct _config_error_errmsg *dat = config_error_msg;
|
|
struct _config_error_errmsg *tmp;
|
|
int idx = 1;
|
|
|
|
lua_newtable(L);
|
|
|
|
while (dat) {
|
|
lua_pushinteger(L, idx++);
|
|
lua_newtable(L);
|
|
nhl_add_table_entry_int(L, "line", dat->line_num);
|
|
nhl_add_table_entry_str(L, "error", dat->errormsg);
|
|
lua_settable(L, -3);
|
|
tmp = dat->next;
|
|
free(dat->errormsg);
|
|
dat->errormsg = (char *) 0;
|
|
free(dat);
|
|
dat = tmp;
|
|
}
|
|
config_error_msg = (struct _config_error_errmsg *) 0;
|
|
|
|
return 1;
|
|
}
|
|
#endif /* SFCTOOL */
|
|
|
|
/* varargs 'config_error_add()' moved to pline.c */
|
|
void
|
|
config_erradd(const char *buf)
|
|
{
|
|
char lineno[QBUFSZ];
|
|
const char *punct;
|
|
|
|
if (!buf || !*buf)
|
|
buf = "Unknown error";
|
|
|
|
/* if buf[] doesn't end in a period, exclamation point, or question mark,
|
|
we'll include a period (in the message, not appended to buf[]) */
|
|
punct = c_eos((char *) buf) - 1; /* eos(buf)-1 is valid */
|
|
punct = strchr(".!?", *punct) ? "" : ".";
|
|
|
|
if (!program_state.config_error_ready) {
|
|
/* either very early, where pline() will use raw_print(), or
|
|
player gave bad value when prompted by interactive 'O' command */
|
|
pline("%s%s%s", !iflags.window_inited ? "config_error_add: " : "",
|
|
buf, punct);
|
|
wait_synch();
|
|
return;
|
|
}
|
|
|
|
if (iflags.in_lua) {
|
|
struct _config_error_errmsg *dat
|
|
= (struct _config_error_errmsg *) alloc(sizeof *dat);
|
|
|
|
dat->next = config_error_msg;
|
|
dat->line_num = config_error_data->line_num;
|
|
dat->errormsg = dupstr(buf);
|
|
config_error_msg = dat;
|
|
return;
|
|
}
|
|
|
|
config_error_data->num_errors++;
|
|
if (!config_error_data->origline_shown && !config_error_data->secure) {
|
|
pline("\n%s", config_error_data->origline);
|
|
config_error_data->origline_shown = TRUE;
|
|
}
|
|
if (config_error_data->line_num > 0 && !config_error_data->secure) {
|
|
Sprintf(lineno, "Line %d: ", config_error_data->line_num);
|
|
} else
|
|
lineno[0] = '\0';
|
|
|
|
pline("%s %s%s%s", config_error_data->secure ? "Error:" : " *",
|
|
lineno, buf, punct);
|
|
}
|
|
|
|
int
|
|
config_error_done(void)
|
|
{
|
|
int n;
|
|
struct _config_error_frame *tmp = config_error_data;
|
|
|
|
if (!config_error_data)
|
|
return 0;
|
|
n = config_error_data->num_errors;
|
|
#ifndef USER_SOUNDS
|
|
if (gn.no_sound_notified > 0) {
|
|
/* no USER_SOUNDS; config_error_add() was called once for first
|
|
SOUND or SOUNDDIR entry seen, then skipped for any others;
|
|
include those skipped ones in the total error count */
|
|
n += (gn.no_sound_notified - 1);
|
|
gn.no_sound_notified = 0;
|
|
}
|
|
#endif
|
|
if (n) {
|
|
boolean cmdline = !strcmp(config_error_data->source, "command line");
|
|
|
|
pline("\n%d error%s %s %s.\n", n, plur(n), cmdline ? "on" : "in",
|
|
*config_error_data->source ? config_error_data->source
|
|
: configfile);
|
|
wait_synch();
|
|
}
|
|
config_error_data = tmp->next;
|
|
free(tmp);
|
|
program_state.config_error_ready = (config_error_data != 0);
|
|
return n;
|
|
}
|
|
|
|
boolean
|
|
read_config_file(const char *filename, int src)
|
|
{
|
|
FILE *fp;
|
|
boolean rv = TRUE;
|
|
|
|
if (!(fp = fopen_config_file(filename, src)))
|
|
return FALSE;
|
|
#ifndef SFCTOOL
|
|
/* begin detection of duplicate configfile options */
|
|
reset_duplicate_opt_detection();
|
|
#endif /* SFCTOOL */
|
|
free_config_sections();
|
|
iflags.parse_config_file_src = src;
|
|
|
|
rv = parse_conf_file(fp, parse_config_line);
|
|
(void) fclose(fp);
|
|
|
|
free_config_sections();
|
|
#ifndef SFCTOOL
|
|
/* turn off detection of duplicate configfile options */
|
|
reset_duplicate_opt_detection();
|
|
#endif /* SFCTOOL */
|
|
return rv;
|
|
}
|
|
|
|
struct _cnf_parser_state {
|
|
char *inbuf;
|
|
unsigned inbufsz;
|
|
int rv;
|
|
char *ep;
|
|
char *buf;
|
|
boolean skip, morelines;
|
|
boolean cont;
|
|
boolean pbreak;
|
|
};
|
|
|
|
/* Initialize config parser data */
|
|
staticfn void
|
|
cnf_parser_init(struct _cnf_parser_state *parser)
|
|
{
|
|
parser->rv = TRUE; /* assume successful parse */
|
|
parser->ep = parser->buf = (char *) 0;
|
|
parser->skip = FALSE;
|
|
parser->morelines = FALSE;
|
|
parser->inbufsz = 4 * BUFSZ;
|
|
parser->inbuf = (char *) alloc(parser->inbufsz);
|
|
parser->cont = FALSE;
|
|
parser->pbreak = FALSE;
|
|
memset(parser->inbuf, 0, parser->inbufsz);
|
|
}
|
|
|
|
/* caller has finished with 'parser' (except for 'rv' so leave that intact) */
|
|
staticfn void
|
|
cnf_parser_done(struct _cnf_parser_state *parser)
|
|
{
|
|
parser->ep = 0; /* points into parser->inbuf, so becoming stale */
|
|
if (parser->inbuf)
|
|
free(parser->inbuf), parser->inbuf = 0;
|
|
if (parser->buf)
|
|
free(parser->buf), parser->buf = 0;
|
|
}
|
|
|
|
/*
|
|
* Parse config buffer, handling comments, empty lines, config sections,
|
|
* CHOOSE, and line continuation, calling proc for every valid line.
|
|
*
|
|
* Continued lines are merged together with one space in between.
|
|
*/
|
|
staticfn void
|
|
parse_conf_buf(struct _cnf_parser_state *p, boolean (*proc)(char *arg))
|
|
{
|
|
p->cont = FALSE;
|
|
p->pbreak = FALSE;
|
|
p->ep = strchr(p->inbuf, '\n');
|
|
if (p->skip) { /* in case previous line was too long */
|
|
if (p->ep)
|
|
p->skip = FALSE; /* found newline; next line is normal */
|
|
} else {
|
|
if (!p->ep) { /* newline missing */
|
|
if (strlen(p->inbuf) < (p->inbufsz - 2)) {
|
|
/* likely the last line of file is just
|
|
missing a newline; process it anyway */
|
|
p->ep = eos(p->inbuf);
|
|
} else {
|
|
config_error_add("Line too long, skipping");
|
|
p->skip = TRUE; /* discard next fgets */
|
|
}
|
|
} else {
|
|
*p->ep = '\0'; /* remove newline */
|
|
}
|
|
if (p->ep) {
|
|
char *tmpbuf = (char *) 0;
|
|
int len;
|
|
boolean ignoreline = FALSE;
|
|
boolean oldline = FALSE;
|
|
|
|
/* line continuation (trailing '\') */
|
|
p->morelines = (--p->ep >= p->inbuf && *p->ep == '\\');
|
|
if (p->morelines)
|
|
*p->ep = '\0';
|
|
|
|
/* trim off spaces at end of line */
|
|
while (p->ep >= p->inbuf
|
|
&& (*p->ep == ' ' || *p->ep == '\t' || *p->ep == '\r'))
|
|
*p->ep-- = '\0';
|
|
|
|
if (!config_error_nextline(p->inbuf)) {
|
|
p->rv = FALSE;
|
|
if (p->buf)
|
|
free(p->buf), p->buf = (char *) 0;
|
|
p->pbreak = TRUE;
|
|
return;
|
|
}
|
|
|
|
p->ep = p->inbuf;
|
|
while (*p->ep == ' ' || *p->ep == '\t')
|
|
++p->ep;
|
|
|
|
/* ignore empty lines and full-line comment lines */
|
|
if (!*p->ep || *p->ep == '#')
|
|
ignoreline = TRUE;
|
|
|
|
if (p->buf)
|
|
oldline = TRUE;
|
|
|
|
/* merge now read line with previous ones, if necessary */
|
|
if (!ignoreline) {
|
|
len = (int) strlen(p->ep) + 1; /* +1: final '\0' */
|
|
if (p->buf)
|
|
len += (int) strlen(p->buf) + 1; /* +1: space */
|
|
tmpbuf = (char *) alloc(len);
|
|
*tmpbuf = '\0';
|
|
if (p->buf) {
|
|
Strcat(strcpy(tmpbuf, p->buf), " ");
|
|
free(p->buf), p->buf = 0;
|
|
}
|
|
p->buf = strcat(tmpbuf, p->ep);
|
|
if (strlen(p->buf) >= p->inbufsz)
|
|
p->buf[p->inbufsz - 1] = '\0';
|
|
}
|
|
|
|
if (p->morelines || (ignoreline && !oldline))
|
|
return;
|
|
|
|
if (handle_config_section(p->buf)) {
|
|
free(p->buf), p->buf = (char *) 0;
|
|
return;
|
|
}
|
|
|
|
/* from here onwards, we'll handle buf only */
|
|
|
|
if (match_varname(p->buf, "CHOOSE", 6)) {
|
|
char *section;
|
|
char *bufp = find_optparam(p->buf);
|
|
|
|
if (!bufp) {
|
|
config_error_add("Format is CHOOSE=section1"
|
|
",section2,...");
|
|
p->rv = FALSE;
|
|
free(p->buf), p->buf = (char *) 0;
|
|
return;
|
|
}
|
|
bufp++;
|
|
if (gc.config_section_chosen)
|
|
free(gc.config_section_chosen),
|
|
gc.config_section_chosen = 0;
|
|
section = choose_random_part(bufp, ',');
|
|
if (section) {
|
|
gc.config_section_chosen = dupstr(section);
|
|
} else {
|
|
config_error_add("No config section to choose");
|
|
p->rv = FALSE;
|
|
}
|
|
free(p->buf), p->buf = (char *) 0;
|
|
return;
|
|
}
|
|
|
|
if (!(*proc)(p->buf))
|
|
p->rv = FALSE;
|
|
|
|
free(p->buf), p->buf = (char *) 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
boolean
|
|
parse_conf_str(const char *str, boolean (*proc)(char *arg))
|
|
{
|
|
size_t len;
|
|
struct _cnf_parser_state parser;
|
|
|
|
cnf_parser_init(&parser);
|
|
free_config_sections();
|
|
config_error_init(FALSE, "parse_conf_str", FALSE);
|
|
while (str && *str) {
|
|
len = 0;
|
|
while (*str && len < (parser.inbufsz-1)) {
|
|
parser.inbuf[len] = *str;
|
|
len++;
|
|
str++;
|
|
if (parser.inbuf[len-1] == '\n')
|
|
break;
|
|
}
|
|
parser.inbuf[len] = '\0';
|
|
parse_conf_buf(&parser, proc);
|
|
if (parser.pbreak)
|
|
break;
|
|
}
|
|
cnf_parser_done(&parser);
|
|
|
|
free_config_sections();
|
|
config_error_done();
|
|
return parser.rv;
|
|
}
|
|
|
|
/* parse_conf_file
|
|
*
|
|
* Read from file fp, calling parse_conf_buf for each line.
|
|
*/
|
|
boolean
|
|
parse_conf_file(FILE *fp, boolean (*proc)(char *arg))
|
|
{
|
|
struct _cnf_parser_state parser;
|
|
|
|
cnf_parser_init(&parser);
|
|
free_config_sections();
|
|
|
|
while (fgets(parser.inbuf, parser.inbufsz, fp)) {
|
|
parse_conf_buf(&parser, proc);
|
|
if (parser.pbreak)
|
|
break;
|
|
}
|
|
cnf_parser_done(&parser);
|
|
|
|
free_config_sections();
|
|
return parser.rv;
|
|
}
|
|
|
|
DISABLE_WARNING_FORMAT_NONLITERAL
|
|
|
|
void
|
|
config_error_add(const char *str, ...)
|
|
{
|
|
va_list the_args;
|
|
|
|
va_start(the_args, str);
|
|
vconfig_error_add(str, the_args);
|
|
va_end(the_args);
|
|
}
|
|
|
|
staticfn void
|
|
vconfig_error_add(const char *str, va_list the_args)
|
|
{ /* start of vconf...() or of nested block in USE_OLDARG's conf...() */
|
|
int vlen = 0;
|
|
char buf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */
|
|
|
|
vlen = vsnprintf(buf, sizeof buf, str, the_args);
|
|
#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) && defined(DEBUG)
|
|
if (vlen >= (int) sizeof buf)
|
|
panic("%s: truncation of buffer at %zu of %d bytes",
|
|
"config_error_add", sizeof buf, vlen);
|
|
#else
|
|
nhUse(vlen);
|
|
#endif
|
|
buf[BUFSZ - 1] = '\0';
|
|
config_erradd(buf);
|
|
}
|
|
|
|
#ifndef SFCTOOL
|
|
void
|
|
rcfile(void)
|
|
{
|
|
char *opts = 0, *xtraopts = 0;
|
|
const char *envname, *namesrc, *nameval;
|
|
|
|
go.opt_phase = environ_opt;
|
|
/* getenv() instead of nhgetenv(): let total length of options be long;
|
|
parseoptions() will check each individually */
|
|
envname = "NETHACKOPTIONS";
|
|
opts = getenv(envname);
|
|
if (!opts) {
|
|
/* fall back to original name; discouraged */
|
|
envname = "HACKOPTIONS";
|
|
opts = getenv(envname);
|
|
}
|
|
|
|
if (gc.cmdline_rcfile) {
|
|
namesrc = "command line";
|
|
nameval = gc.cmdline_rcfile;
|
|
xtraopts = opts;
|
|
if (opts && (*opts == '/' || *opts == '\\' || *opts == '@'))
|
|
xtraopts = 0; /* NETHACKOPTIONS is a file name; ignore it */
|
|
} else if (opts && (*opts == '/' || *opts == '\\' || *opts == '@')) {
|
|
/* NETHACKOPTIONS is a file name; use that instead of the default */
|
|
if (*opts == '@')
|
|
++opts; /* @filename */
|
|
namesrc = envname;
|
|
nameval = opts;
|
|
xtraopts = 0;
|
|
} else {
|
|
/* either no NETHACKOPTIONS or it wasn't a file name;
|
|
read the default configuration file */
|
|
nameval = namesrc = 0;
|
|
xtraopts = opts;
|
|
}
|
|
|
|
go.opt_phase = rc_file_opt;
|
|
/* seemingly arbitrary name length restriction is to prevent error
|
|
messages, if any were to be delivered while accessing the file,
|
|
from potentially overflowing buffers */
|
|
if (nameval && (int) strlen(nameval) >= BUFSZ / 2) {
|
|
config_error_init(TRUE, namesrc, FALSE);
|
|
config_error_add(
|
|
"nethackrc file name \"%.40s\"... too long; using default",
|
|
nameval);
|
|
config_error_done();
|
|
nameval = namesrc = 0; /* revert to default nethackrc */
|
|
}
|
|
|
|
config_error_init(TRUE, nameval, nameval ? CONFIG_ERROR_SECURE : FALSE);
|
|
(void) read_config_file(nameval, set_in_config);
|
|
config_error_done();
|
|
if (xtraopts) {
|
|
/* NETHACKOPTIONS is present and not a file name */
|
|
go.opt_phase = environ_opt;
|
|
config_error_init(FALSE, envname, FALSE);
|
|
(void) parseoptions(xtraopts, TRUE, FALSE);
|
|
config_error_done();
|
|
}
|
|
|
|
if (gc.cmdline_rcfile)
|
|
free((genericptr_t) gc.cmdline_rcfile), gc.cmdline_rcfile = 0;
|
|
/*[end of nethackrc handling]*/
|
|
}
|
|
|
|
void
|
|
rcfile_interface_options(void)
|
|
{
|
|
allopt_array_init();
|
|
disregard_all_options();
|
|
disregard_all_config_statements();
|
|
heed_this_option(opt_windowtype);
|
|
heed_this_option(opt_soundlib);
|
|
set_ignore_errors_on_unmatched();
|
|
ignore_statement_errors = TRUE;
|
|
rcfile();
|
|
heed_all_config_statements();
|
|
heed_all_options();
|
|
disregard_this_option(opt_windowtype);
|
|
disregard_this_option(opt_soundlib);
|
|
clear_ignore_errors_on_unmatched();
|
|
ignore_statement_errors = FALSE;
|
|
}
|
|
|
|
void
|
|
heed_all_config_statements(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < SIZE(disregarded_config_lines); i++) {
|
|
disregarded_config_lines[i] = FALSE;
|
|
}
|
|
}
|
|
void
|
|
disregard_all_config_statements(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < SIZE(disregarded_config_lines); i++) {
|
|
disregarded_config_lines[i] = TRUE;
|
|
}
|
|
}
|
|
void
|
|
heed_this_config_statement(int statement_idx)
|
|
{
|
|
if (statement_idx >= 0 && statement_idx < SIZE(disregarded_config_lines))
|
|
disregarded_config_lines[statement_idx] = FALSE;
|
|
}
|
|
void
|
|
disregard_this_config_statement(int statement_idx)
|
|
{
|
|
if (statement_idx >= 0 && statement_idx < SIZE(disregarded_config_lines))
|
|
disregarded_config_lines[statement_idx] = TRUE;
|
|
}
|
|
|
|
void
|
|
clear_ignore_errors_on_unmatched(void)
|
|
{
|
|
ignore_errors_on_unmatched = FALSE;
|
|
}
|
|
void
|
|
set_ignore_errors_on_unmatched(void)
|
|
{
|
|
ignore_errors_on_unmatched = TRUE;
|
|
}
|
|
boolean
|
|
config_unmatched_ignored(void)
|
|
{
|
|
if (ignore_errors_on_unmatched)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
#endif /* SFCTOOL */
|
|
|
|
#ifdef SYSCF
|
|
#ifdef SYSCF_FILE
|
|
void
|
|
assure_syscf_file(void)
|
|
{
|
|
int fd;
|
|
|
|
#ifdef WIN32
|
|
/* We are checking that the sysconf exists ... lock the path */
|
|
fqn_prefix_locked[SYSCONFPREFIX] = TRUE;
|
|
#endif
|
|
/*
|
|
* All we really care about is the end result - can we read the file?
|
|
* So just check that directly.
|
|
*
|
|
* Not tested on most of the old platforms (which don't attempt
|
|
* to implement SYSCF).
|
|
* Some ports don't like open()'s optional third argument;
|
|
* VMS overrides open() usage with a macro which requires it.
|
|
*/
|
|
#ifndef VMS
|
|
#if defined(NOCWD_ASSUMPTIONS) && defined(WIN32)
|
|
fd = open(fqname(SYSCF_FILE, SYSCONFPREFIX, 0), O_RDONLY);
|
|
#else
|
|
fd = open(SYSCF_FILE, O_RDONLY);
|
|
#endif
|
|
#else /* VMS */
|
|
fd = open(SYSCF_FILE, O_RDONLY, 0);
|
|
#endif /* VMS */
|
|
if (fd >= 0) {
|
|
/* readable */
|
|
close(fd);
|
|
return;
|
|
}
|
|
#ifndef SFCTOOL
|
|
if (gd.deferred_showpaths)
|
|
do_deferred_showpaths(1); /* does not return */
|
|
#endif
|
|
raw_printf("Unable to open SYSCF_FILE.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
#endif /* SYSCF_FILE */
|
|
#endif /* SYSCF */
|
|
|
|
/* ---------- END CONFIG FILE HANDLING ----------- */
|
|
|
|
/*cfgfiles.c*/
|
|
|