Change Windows startup

remove the safeproc pseudo-windowport routines from
almost a decade ago.

A very early pass is made through the config file,
seeking out just the interface-related OPTIONS=windowport
and OPTIONS=soundlib and ignoring all other options in the
config file during that early pass, so the windowport
can be activated without the NetHack core initialization
in place that some of the other rcfile OPTIONS require.

Bundles the existing rcfile processing code into rcfile().

New functions to control which rcfile options will be
disregarded in the early config file pass, and which will be
processed:

    set_all_options_disregarded();
    set_all_options_heeded();
    disregard_this_option(opt_xx);
    heed_this_option(opt_xx);

Windows calls rcfile_interface_options(), which is
a bundling of a series of function calls to achieve
the desired result.

    void
    rcfile_interface_options(void)
    {
        allopt_array_init();
        set_all_options_disregarded();
        heed_this_option(opt_windowtype);
        heed_this_option(opt_soundlib);
        rcfile();
        set_all_options_heeded();
        disregard_this_option(opt_windowtype);
        disregard_this_option(opt_soundlib);
    }
This commit is contained in:
nhmall
2026-04-05 11:36:19 -04:00
parent 92340a6827
commit acb85b18cf
23 changed files with 450 additions and 959 deletions

View File

@@ -1881,6 +1881,85 @@ vconfig_error_add(const char *str, va_list the_args)
config_erradd(buf);
}
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();
set_all_options_disregarded();
heed_this_option(opt_windowtype);
heed_this_option(opt_soundlib);
rcfile();
set_all_options_heeded();
disregard_this_option(opt_windowtype);
disregard_this_option(opt_soundlib);
}
#ifdef SYSCF
#ifdef SYSCF_FILE
void

View File

@@ -592,7 +592,7 @@ static const struct instance_globals_o g_init_o = {
/* o_init.c */
DUMMY, /* oclass_prob_totals */
/* options.c */
0, /* opt_phase */
phase_not_set, /* opt_phase */
FALSE, /* opt_initial */
FALSE, /* opt_from_file */
FALSE, /* opt_need_redraw */

View File

@@ -56,21 +56,13 @@ NEARDATA struct accessibility_data a11y;
#include "optlist.h"
#undef NHOPT_PROTO
#define NHOPT_ENUM
enum opt {
opt_prefix_only = -1,
#include "optlist.h"
OPTCOUNT
};
#undef NHOPT_ENUM
#define NHOPT_PARSE
static struct allopt_t allopt_init[] = {
#include "optlist.h"
{(const char *) 0, OptS_Advanced, 0, 0, 0, set_in_sysconf, BoolOpt,
No, No, No, No, Term_False, 0, (boolean *) 0,
(int (*)(int, int, boolean, char *, char *)) 0,
(char *) 0, (const char *) 0, (const char *) 0, 0, 0, 0 }
(char *) 0, (const char *) 0, (const char *) 0, 0, 0, 0, TRUE }
};
#undef NHOPT_PARSE
@@ -94,16 +86,6 @@ enum optn_result {
enum requests {
do_nothing, do_init, do_set, do_handler, get_val, get_cnf_val
};
/* these aren't the same as set_xxx in optlist.h */
enum option_phases {
builtin_opt=1,/* compiled-in default value of an option */
syscf_opt, /* sysconf setting of an option, overrides builtin */
rc_file_opt, /* player's run-time config file setting, overrides syscf */
environ_opt, /* player's environment NETHACKOPTIONS, overrides rc_file */
cmdline_opt, /* program invocation command-line, overrides environ */
play_opt, /* 'O' command, interactively set so overrides all */
num_opt_phases
};
static struct allopt_t allopt[SIZE(allopt_init)];
@@ -124,6 +106,7 @@ extern char ttycolors[CLR_MAX]; /* in sys/msdos/video.c */
static char empty_optstr[] = { '\0' };
static boolean duplicate, using_alias;
static boolean give_opt_msg = TRUE;
static boolean restricted_options_mode = FALSE;
enum { MAX_ROLEOPT = 4 }; /* 4: role,race,gend,algn */
static boolean opt_set_in_config[OPTCOUNT];
@@ -357,6 +340,7 @@ static const menu_cmd_t default_menu_cmd_info[] = {
static const char n_currently_set[] = "(%d currently set)";
staticfn void allopt_array_init(void);
staticfn void nmcpy(char *, const char *, int);
staticfn void escapes(const char *, char *);
staticfn void rejectoption(const char *);
@@ -595,7 +579,7 @@ parseoptions(
* placed that number into each option's allopt[n].minmatch.
*
*/
if (!got_match)
if (!got_match && allopt[i].name)
got_match = match_optname(opts, allopt[i].name,
allopt[i].minmatch, TRUE);
if (got_match) {
@@ -634,7 +618,8 @@ parseoptions(
/* allow optfn's to test whether they were called from parseoptions() */
program_state.in_parseoptions++;
if (got_match && matchidx >= 0) {
if (got_match && (matchidx >= 0 && matchidx < OPTCOUNT)
&& !allopt[matchidx].disregarded) {
duplicate = duplicate_opt_detection(matchidx);
if (duplicate && !allopt[matchidx].dupeok)
complain_about_duplicate(matchidx);
@@ -684,7 +669,7 @@ parseoptions(
}
}
if (optresult == optn_silenterr)
if (optresult == optn_silenterr || restricted_options_mode)
return FALSE;
if (pfx_match && optresult == optn_err) {
char pfxbuf[BUFSZ], *pfxp;
@@ -4971,17 +4956,20 @@ optfn_windowtype(
* _end_ because comma-separated option strings are processed from
* right to left.
*/
if (iflags.windowtype_locked)
return optn_ok;
if (!iflags.window_inited) {
if (iflags.windowtype_locked)
return optn_ok;
if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
!= empty_optstr) {
nmcpy(gc.chosen_windowtype, op, WINTYPELEN);
if (!iflags.windowtype_deferred) {
choose_windows(gc.chosen_windowtype);
if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
!= empty_optstr) {
nmcpy(gc.chosen_windowtype, op, WINTYPELEN);
if (!iflags.windowtype_deferred) {
choose_windows(gc.chosen_windowtype);
}
} else {
return optn_err;
}
} else
return optn_err;
}
return optn_ok;
}
if (req == get_val || req == get_cnf_val) {
@@ -7066,29 +7054,14 @@ txt2key(char *txt)
void
initoptions(void)
{
int i;
/*
* Most places that call initoptions_init()/initoptions() would
* have the calls next to each other, so instead of adding
* initoptions_init() everywhere, just add it where it's needed in
* a non-adjacent place and call it here for all the other cases.
*/
if(go.opt_phase != builtin_opt)
if (go.opt_phase != builtin_opt)
initoptions_init();
/*
* Call each option function with an init flag and give it a chance
* to make any preparations that it might require. We do this
* whether or not the option itself is ever specified; that's
* irrelevant for the init call. Doing this allows the prep code for
* option settings to remain adjacent to, and in the same function as,
* the code that processes those options.
*/
for (i = 0; i < OPTCOUNT; ++i) {
if (allopt[i].optfn)
(*allopt[i].optfn)(i, do_init, FALSE, empty_optstr, empty_optstr);
}
#ifdef SYSCF
/* someday there may be other SYSCF alternatives besides text file */
#ifdef SYSCF_FILE
@@ -7130,9 +7103,7 @@ initoptions_init(void)
go.opt_phase = builtin_opt; /* Did I need to move this here? */
/* initialize the function pointers for saving the game */
sf_init();
memcpy(allopt, allopt_init, sizeof(allopt));
determine_ambiguities();
allopt_array_init();
/* if windowtype has been specified on the command line, set it up
early so windowtype-specific options use it as their base */
if (gc.cmdline_windowsys) {
@@ -7286,6 +7257,28 @@ initoptions_init(void)
/* since this is done before init_objects(), do partial init here */
objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD;
nmcpy(svp.pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
#ifdef SYSCF
/* someday there may be other SYSCF alternatives besides text file */
#ifdef SYSCF_FILE
/* If SYSCF_FILE is specified, it _must_ exist... */
assure_syscf_file();
config_error_init(TRUE, SYSCF_FILE, FALSE);
/* ... and _must_ parse correctly. */
go.opt_phase = syscf_opt;
if (!read_config_file(SYSCF_FILE, set_in_sysconf)) {
if (config_error_done() && !iflags.initoptions_noterminate)
nh_terminate(EXIT_FAILURE);
}
config_error_done();
/*
* TODO [maybe]: parse the sysopt entries which are space-separated
* lists of usernames into arrays with one name per element.
*/
#endif
#endif /* SYSCF */
initoptions_finish();
}
/*
@@ -7306,72 +7299,69 @@ initoptions_init(void)
*/
void
initoptions_finish(void)
{
nhsym sym = 0;
char *opts = 0, *xtraopts = 0;
#ifndef MAC
const char *envname, *namesrc, *nameval;
{ nhsym sym = 0;
/* 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);
rcfile();
(void) fruitadd(svp.pl_fruit, (struct fruit *) 0);
/*
* Remove "slime mold" from list of object names. This will
* prevent it from being wished unless it's actually present
* as a named (or default) fruit. Wishing for "fruit" will
* result in the player's preferred fruit. [Once upon a time
* the override value used was "\033" which prevented wishing
* for the slime mold object at all except by asking for a
* specific named fruit.] Note that there are multiple fruit
* object types (apple, melon, &c) but the "fruit" object is
* slime mold or whatever custom name player assigns to that.
*/
obj_descr[SLIME_MOLD].oc_name = "fruit";
sym = get_othersym(SYM_BOULDER,
Is_rogue_level(&u.uz) ? ROGUESET : PRIMARYSET);
if (sym)
gs.showsyms[SYM_BOULDER + SYM_OFF_X] = sym;
reglyph_darkroom();
reset_glyphmap(gm_optionchange);
#ifdef STATUS_HILITES
/*
* A multi-interface binary might only support status highlighting
* for some of the interfaces; check whether we asked for it but are
* using one which doesn't.
*
* Option processing can take place before a user-decided WindowPort
* is even initialized, so check for that too.
*/
if (!WINDOWPORT(safestartup)) {
if (iflags.hilite_delta && !wc2_supported("statushilites")) {
raw_printf("Status highlighting not supported for %s interface.",
windowprocs.name);
iflags.hilite_delta = 0;
}
}
#endif
update_rest_on_space();
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
#endif /* !MAC */
/*else*/ {
/* either no NETHACKOPTIONS or it wasn't a file name;
read the default configuration file */
nameval = namesrc = 0;
xtraopts = opts;
}
/* these can't rely on compile-time initialization for their defaults
because a multi-interface binary might need different values for
different interfaces; if neither tiled_map nor ascii_map pass the
wc_supported() test, assume ascii_map */
if (iflags.wc_tiled_map && !wc_supported("tiled_map"))
iflags.wc_tiled_map = FALSE, iflags.wc_ascii_map = TRUE;
else if (iflags.wc_ascii_map && !wc_supported("ascii_map")
&& wc_supported("tiled_map"))
iflags.wc_ascii_map = FALSE, iflags.wc_tiled_map = TRUE;
/* 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) {
go.opt_phase = rc_file_opt;
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]*/
#ifdef ENHANCED_SYMBOLS
if (glyphid_cache_status())
free_glyphid_cache();
apply_customizations(gc.currentgraphics, do_custom_symbols);
#endif
go.opt_initial = FALSE;
return;
}
#if 0
(void) fruitadd(svp.pl_fruit, (struct fruit *) 0);
/*
* Remove "slime mold" from list of object names. This will
@@ -7449,6 +7439,37 @@ initoptions_finish(void)
}
return;
}
#endif
void
allopt_array_init(void)
{
int i;
static boolean options_array_inited_already = FALSE;
if (!options_array_inited_already) {
memcpy(allopt, allopt_init, sizeof(allopt));
determine_ambiguities();
for (i = 0; allopt[i].name; i++) {
if (allopt[i].addr)
*(allopt[i].addr) = allopt[i].initval;
}
set_all_options_heeded();
/*
* Call each option function with an init flag and give it a chance
* to make any preparations that it might require. We do this
* whether or not the option itself is ever specified; that's
* irrelevant for the init call. Doing this allows the prep code for
* option settings to remain adjacent to, and in the same function as,
* the code that processes those options.
*/
for (i = 0; i < OPTCOUNT; ++i) {
if (allopt[i].optfn)
(*allopt[i].optfn)(i, do_init, FALSE, empty_optstr,
empty_optstr);
}
options_array_inited_already = TRUE;
}
}
/*
*******************************************
@@ -8966,6 +8987,7 @@ doset(void) /* changing options via menu by Per Liboriussen */
if (opt_indx < -1)
opt_indx++; /* -1 offset for select_menu() */
opt_indx -= indexoffset;
assert(IndexOk(opt_indx, allopt));
if (allopt[opt_indx].opttyp == BoolOpt) {
/* boolean option */
Sprintf(buf, "%s%s", *allopt[opt_indx].addr ? "!" : "",
@@ -10220,6 +10242,41 @@ enhance_menu_text(
return;
}
void
set_all_options_heeded(void)
{
int i;
for (i = 0; i < OPTCOUNT; i++)
allopt[i].disregarded = FALSE;
restricted_options_mode = FALSE;
}
void
set_all_options_disregarded(void)
{
int i;
for (i = 0; i < OPTCOUNT ; i++)
allopt[i].disregarded = TRUE;
restricted_options_mode = TRUE;
}
void
heed_this_option(enum opt optidx)
{
if (optidx >= 0 && optidx < (enum opt) OPTCOUNT)
allopt[optidx].disregarded = FALSE;
}
void
disregard_this_option(enum opt optidx)
{
if (optidx >= 0 && optidx < (enum opt) OPTCOUNT)
allopt[optidx].disregarded = TRUE;
if (!restricted_options_mode)
restricted_options_mode = TRUE;
}
#undef OPTIONS_HEADING
#undef CONFIG_SLOT