]> granicus.if.org Git - nethack/commitdiff
February 2020 options.c overhaul
authornhmall <nhmall@nethack.org>
Wed, 26 Feb 2020 05:19:08 +0000 (00:19 -0500)
committernhmall <nhmall@nethack.org>
Wed, 26 Feb 2020 05:24:37 +0000 (00:24 -0500)
combine boolean and compound options into a single allopt[] array for
processing in options.c.

move the definitions of the options into new include/optlist.h file which
uses a set of macros to define them appropriately.

during compile of options.c each option described in include/optlist.h:
   1. automatically results in a function prototype for an optfn called
      optfn_xxxx (xxxx is the option name).
   2. automatically results in an opt_xxxx enum value for referencing
      its index throughout options.c (xxxx is the option name).
   3. is used to initialize an element of the allopt[] array at index
      opt_xxxx (xxxx is the option name) based on the settings in the
      NHOPTB, NHOPTC, NHOPTP macros. Those macros only live during the
      compilation of include/optlist.h.

each optfn_xxxx() function can be called with a req id of: do_init, do_set,
get_val or do_handler.

req do_init is called from options_init, and if initialization or memory
allocation or other initialization for that particular option is needed,
it can be done in response to the init req.

req do_set is called from parseoptions() for each option it encounters
and the optfn_xxxx() function is expected to react and set the option
based on the string values that parseoptions() passes to it.

req get_val expects each optfn_xxxx() function to write the current
option value into the buffer it is passed.

req do_handler is called during doset() operations in response to player
selections most likely from the 'O' option-setting menu, but only if the
option is identified as having do_handler support in the allopts[]
'has_handler' boolean flag. Not every optfn_xxxx() does.

function special_handling() is eliminated. It's code has been redistributed
to individual handler functions for the option or purpose that they serve.

moved reglyph_darkroom() function from options.c to display.c

21 files changed:
doc/window.doc
include/botl.h
include/decl.h
include/extern.h
include/global.h
include/hack.h
include/optlist.h [new file with mode: 0644]
src/display.c
src/files.c
src/options.c
sys/unix/Makefile.src
sys/wince/mswproc.c
sys/winnt/Makefile.msc
sys/winnt/nttty.c
sys/winnt/windmain.c
win/X11/winX.c
win/curses/cursinit.c
win/curses/cursmain.c
win/gem/wingem.c
win/tty/wintty.c
win/win32/mswproc.c

index a2ddc4a9074c82b4f8378f1cd3e5de8f98a98ef8..73caf932238a9a9fc94665a9b64c92c1507c1956 100644 (file)
@@ -691,8 +691,8 @@ Two things control whether any preference setting appears in the
 'O' command options menu during the game:
  1. The option must be marked as being supported by having its 
     bit set in the window_procs wincap or wincap2 mask.
- 2. The option must have its optflag field set to SET_IN_GAME in order
-    to be able to set the option, or marked DISP_IN_GAME if you just
+ 2. The option must have its optflag field set to set_in_game in order
+    to be able to set the option, or marked set_gameview if you just
     want to reveal what the option is set to. 
 Both conditions must be true to be able to see or set the option from
 within NetHack.  
@@ -703,8 +703,8 @@ the wc_ options can be altered by calling
        set_wc_option_mod_status(optmask, status)
 The default value for the wc2_ options can be altered by calling 
        set_wc2_option_mod_status(optmask, status)
-In each case, set the option modification status to one of SET_IN_FILE
-DISP_IN_GAME, or SET_IN_GAME.
+In each case, set the option modification status to one of set_in_config
+set_gameview, or set_in_game.
 
 The setting of any wincap or wincap2 option is handled by the NetHack 
 core option processing code. You do not have to provide a parser in 
@@ -874,7 +874,7 @@ the option's bit in the port's wincap mask.  The port can choose to adjust
 for the change to an option that it receives notification about, or ignore it.
 The former approach is recommended.  If you don't want to deal with a
 user-initiated setting change, then the port should call 
-set_wc_option_mod_status(mask, SET_IN_FILE) to make the option invisible to 
+set_wc_option_mod_status(mask, set_in_config) to make the option invisible to 
 the user.
 
 Functions available for the window port to call:
@@ -883,16 +883,16 @@ set_wc_option_mod_status(optmask, status)
                -- Adjust the optflag field for a set of wincap options to 
                   specify whether the port wants the option to appear 
                   in the 'O' command options menu, The second parameter,
-                  "status" can be set to SET_IN_FILE, DISP_IN_GAME,
-                  or SET_IN_GAME (SET_IN_FILE implies that the option
+                  "status" can be set to set_in_config, set_gameview,
+                  or set_in_game (set_in_config implies that the option
                   is completely hidden during the game).
 
 set_wc2_option_mod_status(optmask, status)
                -- Adjust the optflag field for a set of wincap2 options to 
                   specify whether the port wants the option to appear 
                   in the 'O' command options menu, The second parameter,
-                  "status" can be set to SET_IN_FILE, DISP_IN_GAME,
-                  or SET_IN_GAME (SET_IN_FILE implies that the option
+                  "status" can be set to set_in_config, set_gameview,
+                  or set_in_game (set_in_config implies that the option
                   is completely hidden during the game).
 
 set_option_mod_status(optnam, status)
index 92167a9f2d388519d5f77013be451dd8b09ae8bc..c91d76db6705cc30081fbd6a577c97dcb0ec3c49 100644 (file)
@@ -127,11 +127,10 @@ struct conditions_t {
 };
 extern const struct conditions_t conditions[CONDITION_COUNT];
 
-enum condchoice { opt_in, opt_out};
 struct condtests_t {
     enum blconditions c;
     const char *useroption;
-    enum condchoice opt;
+    enum optchoice opt;
     boolean enabled;
     boolean choice;
     boolean test;
index 729e1e8b9359057d05455a0a0c6718970eb61581..09830460a3a85c031f62eda93e4758ef8dcdea49 100644 (file)
@@ -107,6 +107,8 @@ struct sinfo {
     int in_moveloop;
     int in_impossible;
     int in_self_recover;
+    int in_parseoptions;        /* in parseoptions */
+    int config_error_ready;     /* config_error_add is ready, available */
 #ifdef PANICLOG
     int in_paniclog;
 #endif
index ec291048078ad9342645c7d4429020fdeddb21ca..b59522caa0e7d3167530d8085a9a935e4a176d7c 100644 (file)
@@ -384,6 +384,7 @@ E void FDECL(flush_screen, (int));
 E int FDECL(back_to_glyph, (XCHAR_P, XCHAR_P));
 E int FDECL(zapdir_to_glyph, (int, int, int));
 E int FDECL(glyph_at, (XCHAR_P, XCHAR_P));
+E void NDECL(reglyph_darkroom);
 E void NDECL(set_wall_state);
 E void FDECL(unset_seenv, (struct rm *, int, int, int, int));
 E int FDECL(warning_of, (struct monst *));
@@ -1847,7 +1848,6 @@ E int FDECL(shiny_obj, (CHAR_P));
 
 /* ### options.c ### */
 
-E void NDECL(reglyph_darkroom);
 E boolean FDECL(match_optname, (const char *, const char *, int, BOOLEAN_P));
 E void NDECL(initoptions);
 E void NDECL(initoptions_init);
index 35c336e23ae3d30f15d7d116ca801696fa8f19af..bf0ed4b164a86f4e1e8127a34bf6ba239cdfaf06 100644 (file)
@@ -86,6 +86,8 @@ typedef xchar boolean; /* 0 or 1 */
 #define FALSE ((boolean) 0)
 #endif
 
+enum optchoice { opt_in, opt_out};
+
 /*
  * type nhsym: loadable symbols go into this type
  */
index 87b5e7784adbe5dba417119827ee7fdca951b8a6..8c83195bf8c9b7d60e257af19c1dd21b1442e16e 100644 (file)
@@ -497,19 +497,19 @@ enum bodypart_types {
 #define TELEDS_TELEPORT   2
 
 /*
- * Option flags
- * Each higher number includes the characteristics of the numbers
- * below it.
+ * option setting restrictions
  */
-/* XXX This should be replaced with a bitmap. */
-#define SET_IN_SYS 0   /* system config file option only */
-#define SET_IN_FILE 1  /* config file option only */
-#define SET_VIA_PROG 2 /* may be set via extern program, not seen in game */
-#define DISP_IN_GAME 3 /* may be set via extern program, displayed in game \
-                          */
-#define SET_IN_GAME 4  /* may be set via extern program or set in the game */
-#define SET_IN_WIZGAME 5  /* may be set set in the game if wizmode */
-#define SET__IS_VALUE_VALID(s) ((s < SET_IN_SYS) || (s > SET_IN_WIZGAME))
+
+enum optset_restrictions {
+    set_in_sysconf = 0, /* system config file option only */
+    set_in_config  = 1, /* config file option only */
+    set_viaprog    = 2, /* may be set via extern program, not seen in game */
+    set_gameview   = 3, /* may be set via extern program, displayed in game */
+    set_in_game    = 4, /* may be set set in the game if wizmode */
+    set_wizonly    = 5, /* may be set via extern program or set in the game */
+    set_hidden     = 6  /* placeholder for prefixed entries, never show it  */
+};
+#define SET__IS_VALUE_VALID(s) ((s < set_in_sysconf) || (s > set_wizonly))
 
 #define FEATURE_NOTICE_VER(major, minor, patch)                    \
     (((unsigned long) major << 24) | ((unsigned long) minor << 16) \
diff --git a/include/optlist.h b/include/optlist.h
new file mode 100644 (file)
index 0000000..25c2af7
--- /dev/null
@@ -0,0 +1,566 @@
+/* NetHack 3.7 optlist.h */
+/* NetHack may be freely redistributed.  See license for details. */
+
+#ifndef OPTLIST_H
+#define OPTLIST_H
+
+/*
+ *  NOTE:  If you add (or delete) an option, please review:
+ *             doc/options.doc
+ *
+ *         It contains how-to info and outlines some required/suggested
+ *         updates that should accompany your change.
+ */
+
+extern int FDECL(optfn_boolean, (int, int, BOOLEAN_P, char *, char *));
+enum OptType {BoolOpt, CompOpt};
+enum Y_N {No, Yes};
+enum Off_On {Off, On};
+
+struct allopt_t {
+    const char *name;
+    int minmatch;
+    int expectedbuf;
+    int idx;
+    enum optset_restrictions setwhere;
+    enum OptType opttyp;
+    enum Y_N negateok;
+    enum Y_N valok;
+    enum Y_N dupeok;
+    enum Y_N pfx;
+    boolean opt_in_out, *addr;
+    int FDECL((*optfn), (int, int, BOOLEAN_P, char *, char *));
+    const char *alias;
+    const char *descr;
+    const char *prefixgw;
+    boolean initval, has_handler, unused;
+};
+
+#endif /* OPTLIST_H */
+
+#if defined(NHOPT_PROTO) || defined(NHOPT_ENUM) || defined(NHOPT_PARSE)
+
+#define NoAlias ((const char *) 0)
+
+#if defined(NHOPT_PROTO)
+#define NHOPTB(a, b, c, s, i, n, v, d, al, bp)
+#define NHOPTC(a, b, c, s, n, v, d, h, al, z) \
+int FDECL(optfn_##a, (int, int, BOOLEAN_P, char *, char *));
+#define NHOPTP(a, b, c, s, n, v, d, h, al, z) \
+int FDECL(pfxfn_##a, (int, int, BOOLEAN_P, char *, char *));
+
+#elif defined(NHOPT_ENUM)
+#define NHOPTB(a, b, c, s, i, n, v, d, al, bp) \
+opt_##a,
+#define NHOPTC(a, b, c, s, n, v, d, h, al, z) \
+opt_##a,
+#define NHOPTP(a, b, c, s, n, v, d, h, al, z) \
+pfx_##a,
+
+#elif defined(NHOPT_PARSE)
+#define NHOPTB(a, b, c, s, i, n, v, d, al, bp) \
+{ #a, 0, b, opt_##a, s, BoolOpt, n, v, d, No, c, bp, &optfn_boolean, \
+ al, (const char *) 0, (const char *) 0, i, 0, 0 },
+#define NHOPTC(a, b, c, s, n, v, d, h, al, z) \
+{ #a, 0, b, opt_##a, s, CompOpt, n, v, d, No, c, (boolean *) 0, &optfn_##a, \
+ al, z, (const char *) 0, Off, h, 0 },
+#define NHOPTP(a, b, c, s, n, v, d, h, al, z) \
+{ #a, 0, b, pfx_##a, s, CompOpt, n, v, d, Yes, c, (boolean *) 0, &pfxfn_##a, \
+ al, z, #a, Off, h, 0 },
+#endif
+
+/* B:nm, ln, opt_*, setwhere?, on?, negat?, val?, dup?, hndlr? Alias, boolptr */
+/* C:nm, ln, opt_*, setwhere?, on?, negat?, val?, dup?, hndlr? Alias, descr  */
+/* P:pfx, ln, opt_*, setwhere?, on?, negat?, val?, dup?, hndlr? Alias, descr */
+
+    NHOPTB(acoustics, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+               &flags.acoustics)
+    NHOPTC(align, 8, opt_in, set_gameview, No, Yes, No, No, NoAlias,
+               "your starting alignment (lawful, neutral, or chaotic)")
+    NHOPTC(align_message, 20, opt_in, set_gameview, Yes, Yes, No, Yes, NoAlias,
+               "message window alignment")
+    NHOPTC(align_status, 20, opt_in, set_gameview, No, Yes, No, Yes, NoAlias,
+               "status window alignment")
+    NHOPTC(altkeyhandler, 20, opt_in, set_in_game, No, Yes, No, No, NoAlias,
+               "alternate key handler")
+#if defined(SYSFLAGS) && defined(AMIGA)
+    NHOPTB(altmeta, 0, opt_out, set_in_config, On, No, No, No, NoAlias,
+               &sysflags.altmeta)
+#else
+#ifdef ALTMETA
+    NHOPTB(altmeta, 0, opt_out, set_in_config, Off, No, No, No, NoAlias,
+               &iflags.altmeta)
+#else
+    NHOPTB(altmeta, 0, opt_out, set_in_config, Off, No, No, No, NoAlias,
+               (boolean *) 0)
+#endif
+#endif
+    NHOPTB(ascii_map, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+               &iflags.wc_ascii_map)
+#if defined(SYSFLAGS) && defined(MFLOPPY)
+    NHOPTB(asksavedisk, 0, opt_in, set_gameview, Off, No, No, No, NoAlias,
+               &sysflags.asksavedisk)
+#else
+    NHOPTB(asksavedisk, 0, opt_in, set_gameview, Off, No, No, No, NoAlias,
+               (boolean *) 0)
+#endif
+    NHOPTB(autodescribe, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &iflags.autodescribe)
+    NHOPTB(autodig, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.autodig)
+    NHOPTB(autoopen, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.autoopen)
+    NHOPTB(autopickup, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.pickup)
+    NHOPTB(autoquiver, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.autoquiver)
+    NHOPTB(autounlock, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.autounlock)
+#if defined(MICRO) && !defined(AMIGA)
+    NHOPTB(BIOS, 0, opt_in, set_in_config, Off, Yes, No, No, NoAlias,
+                &iflags.BIOS)
+#else
+    NHOPTB(BIOS, 0, opt_in, set_in_config, Off, No, No, No, NoAlias,
+                (boolean *) 0)
+#endif
+    NHOPTB(blind, 0, opt_in, set_in_config, Off, Yes, No, No, NoAlias,
+                &u.uroleplay.blind)
+    NHOPTB(bones, 0, opt_out, set_in_config, On, Yes, No, No, NoAlias,
+                &flags.bones)
+#ifdef BACKWARD_COMPAT
+    NHOPTC(boulder, 1, opt_in, set_in_game , No, Yes, No, No, NoAlias,
+                "deprecated (use S_boulder in sym file instead)")
+#endif
+    NHOPTC(catname, PL_PSIZ, opt_in, set_gameview, No, Yes, No, No, NoAlias,
+                "the name of your (first) cat (e.g., catname:Tabby)")
+#ifdef INSURANCE
+    NHOPTB(checkpoint, 0, opt_out, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.ins_chkpt)
+#else
+    NHOPTB(checkpoint, 0, opt_out, set_in_game, Off, No, No, No, NoAlias,
+                (boolean *) 0)
+#endif
+    NHOPTB(clicklook, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &iflags.clicklook)
+    NHOPTB(cmdassist, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &iflags.cmdassist)
+    NHOPTB(color, 0, opt_in, set_in_game, Off, Yes, No, No, "colour",
+                &iflags.wc_color)
+    NHOPTB(confirm, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.confirm)
+#ifdef CURSES_GRAPHICS
+    NHOPTC(cursesgraphics, 70, opt_in, set_in_config, No, Yes, No, No, NoAlias,
+                "load curses display symbols")
+#endif
+    NHOPTB(dark_room, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.dark_room)
+#ifdef BACKWARD_COMPAT
+    NHOPTC(DECgraphics, 70, opt_in, set_in_config, Yes, Yes, No, No, NoAlias,
+                "load DECGraphics display symbols")
+#endif
+    NHOPTC(disclose, sizeof flags.end_disclose * 2,
+                opt_in, set_in_game, Yes, Yes, No, Yes, NoAlias,
+                "the kinds of information to disclose at end of game")
+    NHOPTC(dogname, PL_PSIZ, opt_in, set_gameview, No, Yes, No, No, NoAlias,
+                "the name of your (first) dog (e.g., dogname:Fang)")
+    NHOPTC(dungeon, MAXDCHARS + 1,opt_in, set_in_config, No, Yes, No, No, NoAlias,
+                "the symbols to use in drawing the dungeon map")
+    NHOPTC(effects, MAXECHARS + 1, opt_in, set_in_config, No, Yes, No, No, NoAlias,
+                "the symbols to use in drawing special effects")
+    NHOPTB(eight_bit_tty, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &iflags.wc_eight_bit_input)
+    NHOPTB(extmenu, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &iflags.extmenu)
+    NHOPTB(female, 0, opt_in, set_in_config, Off, Yes, No, No, "male",
+                &flags.female)
+    NHOPTB(fixinv, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.invlet_constant)
+    NHOPTC(font_map, 40, opt_in, set_gameview, Yes, Yes, Yes, No, NoAlias,
+                "the font to use in the map window")
+    NHOPTC(font_menu, 40, opt_in, set_gameview, Yes, Yes, Yes, No, NoAlias,
+                "the font to use in menus")
+    NHOPTC(font_message, 40, opt_in, set_gameview, Yes, Yes, Yes, No, NoAlias,
+                "the font to use in the message window")
+    NHOPTC(font_size_map, 20, opt_in, set_gameview, Yes, Yes, Yes, No, NoAlias,
+                "the size of the map font")
+    NHOPTC(font_size_menu, 20, opt_in, set_gameview, Yes, Yes, Yes, No, NoAlias,
+                "the size of the menu font")
+    NHOPTC(font_size_message, 20, opt_in, set_gameview, Yes, Yes, Yes, No, NoAlias,
+                "the size of the message font")
+    NHOPTC(font_size_status, 20, opt_in, set_gameview, Yes, Yes, Yes, No, NoAlias,
+                "the size of the status font")
+    NHOPTC(font_size_text, 20, opt_in, set_gameview, Yes, Yes, Yes, No, NoAlias,
+                "the size of the text font")
+    NHOPTC(font_status, 40, opt_in, set_gameview, Yes, Yes, Yes, No, NoAlias,
+                "the font to use in status window")
+    NHOPTC(font_text, 40, opt_in, set_gameview, Yes, Yes, Yes, No, NoAlias,
+                "the font to use in text windows")
+    NHOPTB(force_invmenu, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &iflags.force_invmenu)
+    NHOPTC(fruit, PL_FSIZ, opt_in, set_in_game, No, Yes, No, No, NoAlias,
+                "the name of a fruit you enjoy eating")
+    NHOPTB(fullscreen, 0, opt_in, set_in_config, Off, Yes, No, No, NoAlias,
+                &iflags.wc2_fullscreen)
+    NHOPTC(gender, 8, opt_in, set_gameview, No, Yes, No, No, NoAlias,
+                "your starting gender (male or female)")
+    NHOPTB(goldX, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.goldX)
+    NHOPTB(guicolor, 0, opt_out, set_in_game, On, Yes, No, No,  NoAlias,
+                &iflags.wc2_guicolor)
+    NHOPTB(help, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.help)
+    NHOPTB(herecmd_menu, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &iflags.herecmd_menu)
+#if defined(MAC)
+    NHOPTC(hicolor, 15, opt_in, set_in_config, No, Yes, No, No, NoAlias,
+                "same as palette, only order is reversed")
+#endif
+    NHOPTB(hilite_pet, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &iflags.wc_hilite_pet)
+    NHOPTB(hilite_pile, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &iflags.hilite_pile)
+#ifdef STATUS_HILITES
+    NHOPTC(hilite_status, 13, opt_out, set_in_game, Yes, Yes, Yes, No, NoAlias,
+                "hilite_status")
+#endif
+    NHOPTB(hitpointbar, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &iflags.wc2_hitpointbar)
+    NHOPTC(horsename, PL_PSIZ, opt_in, set_gameview, No, Yes, No, No, NoAlias,
+                "the name of your (first) horse (e.g., horsename:Silver)")
+#ifdef BACKWARD_COMPAT
+    NHOPTC(IBMgraphics, 70, opt_in, set_in_config, Yes, Yes, No, No, NoAlias,
+                "load IBMGraphics display symbols")
+#endif
+#ifndef MAC
+    NHOPTB(ignintr, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.ignintr)
+#else
+    NHOPTB(ignintr, 0, opt_in, set_in_config, Off, Yes, No, No, NoAlias,
+                (boolean *) 0)
+#endif
+    NHOPTB(implicit_uncursed, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.implicit_uncursed)
+    NHOPTB(large_font, 0, opt_in, set_in_config, Off, Yes, No, No, NoAlias,
+                &iflags.obsolete)
+    NHOPTB(legacy, 0, opt_out, set_in_config, On, Yes, No, No, NoAlias,
+                &flags.legacy)
+    NHOPTB(lit_corridor, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.lit_corridor)
+    NHOPTB(lootabc, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.lootabc)
+#ifdef BACKWARD_COMPAT
+#ifdef MAC_GRAPHICS_ENV
+    NHOPTC(Macgraphics, 70, opt_in, set_in_config, No, Yes, No, No, NoAlias,
+                "load MACGraphics display symbols")
+#endif
+#endif
+    NHOPTB(mail, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.biff)
+    NHOPTC(map_mode, 20, opt_in, set_gameview, Yes, Yes, No, No, NoAlias,
+                "map display mode under Windows")
+    NHOPTB(mention_decor, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.mention_decor)
+    NHOPTB(mention_walls, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.mention_walls)
+    NHOPTC(menu_deselect_all, 4, opt_in, set_in_config, No, No, No, No, NoAlias,
+                "deselect all items in a menu")
+    NHOPTC(menu_deselect_page, 4, opt_in, set_in_config, No, No, No, No, NoAlias,
+                "deselect all items on this page of a menu")
+    NHOPTC(menu_first_page, 4, opt_in, set_in_config, No, No, No, No, NoAlias,
+                "jump to the first page in a menu")
+    NHOPTC(menu_headings, 4, opt_in, set_in_game, No, Yes, No, Yes, NoAlias,
+                "display style for menu headings")
+    NHOPTC(menu_invert_all, 4, opt_in, set_in_config, No, No, No, No, NoAlias,
+                "invert all items in a menu")
+    NHOPTC(menu_invert_page, 4, opt_in, set_in_config, No, No, No, No, NoAlias,
+                "invert all items on this page of a menu")
+    NHOPTC(menu_last_page, 4, opt_in, set_in_config, No, No, No, No, NoAlias,
+                "jump to the last page in a menu")
+    NHOPTC(menu_next_page, 4, opt_in, set_in_config, No, No, No, No, NoAlias,
+                "goto the next menu page")
+    NHOPTB(menu_objsyms, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &iflags.menu_head_objsym)
+#ifdef TTY_GRAPHICS
+    NHOPTB(menu_overlay, 0, opt_in, set_in_game, On, No, No, No, NoAlias,
+                &iflags.menu_overlay)
+#else
+    NHOPTB(menu_overlay, 0, opt_in, set_in_config, Off, No, No, No, NoAlias,
+                (boolean *) 0)
+#endif
+    NHOPTC(menu_previous_page, 4, opt_in, set_in_config, No, No, No, No, NoAlias,
+                "goto the previous menu page")
+    NHOPTC(menu_search, 4, opt_in, set_in_config, No, No, No, No, NoAlias,
+                "search for a menu item")
+    NHOPTC(menu_select_all, 4, opt_in, set_in_config, No, No, No, No, NoAlias,
+                "select all items in a menu")
+    NHOPTC(menu_select_page, 4, opt_in, set_in_config, No, No, No, No, NoAlias,
+                "select all items on this page of a menu")
+    NHOPTB(menu_tab_sep, 0, opt_in, set_wizonly, Off, Yes, No, No, NoAlias,
+                &iflags.menu_tab_sep)
+    NHOPTB(menucolors, 0, opt_in, set_in_game, Off, Yes, Yes, No, NoAlias,
+                &iflags.use_menu_color)
+    NHOPTC(menuinvertmode, 5, opt_in, set_in_game, No, Yes, No, No, NoAlias,
+                "behaviour of menu iverts")
+    NHOPTC(menustyle, MENUTYPELEN, opt_in, set_in_game, Yes, Yes, No, Yes, NoAlias,
+                "user interface for object selection")
+    NHOPTB(monpolycontrol, 0, opt_in, set_wizonly, Off, Yes, No, No, NoAlias,
+                &iflags.mon_polycontrol)
+    NHOPTC(monsters, MAXMCLASSES, opt_in, set_in_config, No, Yes, No, No, NoAlias,
+                "the symbols to use for monsters")
+    NHOPTC(mouse_support, 0, opt_in, set_in_game, No, Yes, No, No, NoAlias,
+                "game receives click info from mouse")
+#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS)
+    NHOPTC(msg_window, 1, opt_in, set_in_game, Yes, Yes, No, Yes, NoAlias,
+                "the type of message window required")
+#else
+    NHOPTC(msg_window, 1, opt_in, set_in_config, Yes, Yes, No, Yes, NoAlias,
+                "the type of message window required")
+#endif
+    NHOPTC(msghistory, 5, opt_in, set_gameview, Yes, Yes, No, No, NoAlias,
+                "number of top line messages to save")
+    NHOPTC(name, PL_NSIZ, opt_in, set_gameview, No, Yes, No, No, NoAlias,
+                "your character's name (e.g., name:Merlin-W)")
+#ifdef NEWS
+    NHOPTB(news, 0, opt_in, set_in_config, Off, Yes, No, No, NoAlias,
+                &iflags.news)
+#else
+    NHOPTB(news, 0, opt_in, set_in_config, Off, No, No, No, NoAlias,
+                (boolean *) 0)
+#endif
+    NHOPTB(nudist, 0, opt_in, set_in_config, Off, Yes, No, No, NoAlias,
+                &u.uroleplay.nudist)
+    NHOPTB(null, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.null)
+    NHOPTC(number_pad, 1, opt_in, set_in_game, No, Yes, No, Yes, NoAlias,
+                "use the number pad for movement")
+    NHOPTC(objects, MAXOCLASSES, opt_in, set_in_config, No, Yes, No, No, NoAlias,
+                "the symbols to use for objects")
+    NHOPTC(packorder, MAXOCLASSES, opt_in, set_in_game, No, Yes, No, No, NoAlias,
+                "the inventory order of the items in your pack")
+#ifdef CHANGE_COLOR
+#ifndef WIN32
+    NHOPTC(palette, 15, opt_in, set_in_game, No, Yes, No, "hicolor",
+                "palette (00c/880/-fff is blue/yellow/reverse white)")
+#else
+    NHOPTC(palette, 15, opt_in, set_in_config, No, Yes, No, "hicolor",
+                "palette (adjust an RGB color in palette (color-R-G-B)")
+#endif
+#endif
+    NHOPTC(paranoid_confirmation, 28, opt_in, set_in_game, Yes, Yes, Yes, Yes,
+                "prayconfirm", "extra prompting in certain situations")
+    NHOPTB(perm_invent, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &iflags.perm_invent)
+    NHOPTC(petattr, 88, opt_in, set_in_game, No, Yes, No, No, NoAlias,
+                "attributes for highlighting pets")
+    NHOPTC(pettype, 4, opt_in, set_gameview, Yes, Yes, No, No, NoAlias,
+                "your preferred initial pet type")
+    NHOPTC(pickup_burden, 20, opt_in, set_in_game, No, Yes, No, Yes, NoAlias,
+                "maximum burden picked up before prompt")
+    NHOPTB(pickup_thrown, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.pickup_thrown)
+    NHOPTC(pickup_types, MAXOCLASSES, opt_in, set_in_game, No, Yes, No, Yes, NoAlias, 
+                "types of objects to pick up automatically")
+    NHOPTC(pile_limit, 24, opt_in, set_in_game, Yes, Yes, No, No, NoAlias,
+                "threshold for \"there are many objects here\"")
+    NHOPTC(player_selection, 12, opt_in, set_gameview, No, Yes, No, No, NoAlias,
+                "choose character via dialog or prompts")
+    NHOPTC(playmode, 8, opt_in, set_gameview, No, Yes, No, No, NoAlias,
+                "normal play, non-scoring explore mode, or debug mode")
+    NHOPTB(popup_dialog, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &iflags.wc_popup_dialog)
+    NHOPTB(preload_tiles, 0, opt_out, set_in_config, On, Yes, No, No, NoAlias,
+                &iflags.wc_preload_tiles)
+    NHOPTB(pushweapon, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.pushweapon)
+    NHOPTB(quick_farsight, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.quick_farsight)
+    NHOPTC(race, PL_CSIZ, opt_in, set_gameview, No, Yes, No, No, NoAlias,
+                "your starting race (e.g., Human, Elf)")
+#ifdef MICRO
+    NHOPTB(rawio, 0, opt_in, set_in_config, Off, Yes, No, No, NoAlias,
+                &iflags.rawio)
+#else
+    NHOPTB(rawio, 0, opt_in, set_in_config, Off, No, No, No, NoAlias,
+                (boolean *) 0)
+#endif
+    NHOPTB(rest_on_space, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.rest_on_space)
+    NHOPTC(roguesymset, 70, opt_in, set_in_game, No, Yes, No, Yes, NoAlias,
+                "load a set of rogue display symbols from the symbols file")
+    NHOPTC(role, PL_CSIZ, opt_in, set_gameview, No, Yes, No, No, "character",
+                "your starting role (e.g., Barbarian, Valkyrie)")
+    NHOPTC(runmode, sizeof "teleport", opt_in, set_in_game, Yes, Yes, No, Yes,
+                NoAlias, "display frequency when `running' or `travelling'")
+    NHOPTB(safe_pet, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.safe_dog)
+    NHOPTB(sanity_check, 0, opt_in, set_wizonly, Off, Yes, No, No, NoAlias,
+                &iflags.sanity_check)
+    NHOPTC(scores, 32, opt_in, set_in_game, No, Yes, No, No, NoAlias,
+                "the parts of the score list you wish to see")
+    NHOPTC(scroll_amount, 20, opt_in, set_gameview, Yes, Yes, No, No, NoAlias,
+                "amount to scroll map when scroll_margin is reached")
+    NHOPTC(scroll_margin, 20, opt_in, set_gameview, Yes, Yes, No, No, NoAlias,
+                "scroll map when this far from the edge")
+    NHOPTB(selectsaved, 0, opt_out, set_in_config, On, Yes, No, No, NoAlias,
+                &iflags.wc2_selectsaved)
+    NHOPTB(showexp, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.showexp)
+    NHOPTB(showrace, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.showrace)
+#ifdef SCORE_ON_BOTL
+    NHOPTB(showscore, 0, opt_in, set_in_game, Off, No, No, No, NoAlias,
+                &flags.showscore) 
+#else
+    NHOPTB(showscore, 0, opt_in, set_in_config, Off, No, No, No, NoAlias,
+                (boolean *) 0) 
+#endif
+    NHOPTB(silent, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.silent)
+    NHOPTB(softkeyboard, 0, opt_in, set_in_config, Off, Yes, No, No, NoAlias,
+                &iflags.wc2_softkeyboard)
+    NHOPTC(sortloot, 4, opt_in, set_in_game, No, Yes, No, Yes, NoAlias,
+                "sort object selection lists by description")
+    NHOPTB(sortpack, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.sortpack)
+    NHOPTB(sparkle, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.sparkle)
+    NHOPTB(splash_screen, 0, opt_out, set_in_config, On, Yes, No, No, NoAlias,
+                &iflags.wc_splash_screen)
+    NHOPTB(standout, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.standout)
+    NHOPTB(status_updates, 0, opt_out, set_in_config, On, Yes, No, No, NoAlias,
+                &iflags.status_updates)
+#ifdef STATUS_HILITES 
+    NHOPTC(statushilites, 20, opt_in, set_in_game, Yes, Yes, Yes, No, NoAlias,
+                "0=no status highlighting, N=show highlights for N turns")
+#else
+    NHOPTC(statushilites, 20, opt_in, set_in_config, Yes, Yes, Yes, No, NoAlias,
+                "highlight control")
+#endif
+#ifdef CURSES_GRAPHICS
+    NHOPTC(statuslines, 20, opt_in, set_in_game, No, Yes, No, No, NoAlias,
+                "2 or 3 lines for horizontal (bottom or top) status display")
+#else
+    NHOPTC(statuslines, 20, opt_in, set_in_config, No, Yes, No, No, NoAlias,
+                "2 or 3 lines for status display")
+#endif
+#ifdef WIN32
+    NHOPTC(subkeyvalue, 7, opt_in, set_in_config, No, Yes, Yes, No, NoAlias,
+                "override keystroke value")
+#endif
+    NHOPTC(suppress_alert, 8, opt_in, set_in_game, No, Yes, Yes, No, NoAlias,
+                "suppress alerts about version-specific features")
+    NHOPTC(symset, 70, opt_in, set_in_game, No, Yes, No, Yes, NoAlias,
+                "load a set of display symbols from the symbols file")
+    NHOPTC(term_cols, 6, opt_in, set_in_config, No, Yes, No, No, "termcolumns",
+                "number of columns")
+    NHOPTC(term_rows, 6, opt_in, set_in_config, No, Yes, No, No, NoAlias,
+                "number of rows")
+    NHOPTC(tile_file, 70, opt_in, set_gameview, No, Yes, No, No, NoAlias,
+                "name of tile file")
+    NHOPTC(tile_height, 20, opt_in, set_gameview, Yes, Yes, No, No, NoAlias,
+                "height of tiles")
+    NHOPTC(tile_width, 20, opt_in, set_gameview, Yes, Yes, No, No, NoAlias,
+                "width of tiles")
+    NHOPTB(tiled_map, 0, opt_in, set_in_config, On, Yes, No, No, NoAlias,
+                &iflags.wc_tiled_map)
+    NHOPTB(time, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.time)
+#ifdef TIMED_DELAY
+    NHOPTB(timed_delay, 0, opt_out, set_in_game, Off, Yes, No, No, NoAlias,
+                &flags.nap)
+#else
+    NHOPTB(timed_delay, 0, opt_in, set_in_game, Off, No, No, No, NoAlias,
+                (boolean *) 0)
+#endif
+    NHOPTB(tombstone, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.tombstone)
+    NHOPTB(toptenwin, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &iflags.toptenwin)
+    NHOPTC(traps, MAXTCHARS + 1, opt_in, set_in_config, No, Yes, No, No, NoAlias,
+                "the symbols to use in drawing traps")
+    NHOPTB(travel, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.travelcmd)
+#ifdef DEBUG
+    NHOPTB(travel_debug, 0, opt_out, set_wizonly, Off, Yes, No, No, NoAlias,
+                &iflags.trav_debug)
+#else
+    NHOPTB(travel_debug, 0, opt_out, set_wizonly, Off, No, No, No, NoAlias,
+                (boolean *) 0)
+#endif
+    NHOPTB(use_darkgray, 0, opt_out, set_in_config, On, Yes, No, No, NoAlias,
+                &iflags.wc2_darkgray)
+#ifdef WIN32
+    NHOPTB(use_inverse, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &iflags.wc_inverse)
+#else
+    NHOPTB(use_inverse, 0, opt_in, set_in_game, On, No, No, No, NoAlias,
+                (boolean *) 0)
+#endif
+    NHOPTC(vary_msgcount, 20, opt_in, set_gameview, No, Yes, No, No, NoAlias,
+                "show more old messages at a time")
+    NHOPTB(verbose, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias,
+                &flags.verbose)
+#ifdef MSDOS
+    NHOPTC(video, 20, opt_in, set_in_config, No, Yes, No, No, NoAlias,
+                "method of video updating")
+#endif
+#ifdef VIDEOSHADES
+    NHOPTC(videocolors, 40, opt_in, set_gameview, No, Yes, No, "videocolours",
+                "color mappings for internal screen routines")
+    NHOPTC(videoshades, 32, opt_in, set_gameview, No, Yes, No, No, NoAlias,
+                "gray shades to map to black/gray/white")
+#endif
+#ifdef MSDOS
+    NHOPTC(video_width, 10, opt_in, set_gameview, No, Yes, No, No, NoAlias,
+                "video width")
+    NHOPTC(video_height, 10, opt_in, set_gameview, No, Yes, No, No, NoAlias,
+                "video height")
+#endif
+    NHOPTC(warnings, 10, opt_in, set_in_config, No, Yes, No, No, NoAlias,
+                "display characters for warnings")
+    NHOPTC(whatis_coord, 1, opt_in, set_in_game, Yes, Yes, No, Yes, NoAlias,
+                "show coordinates when auto-describing cursor position")
+    NHOPTC(whatis_filter, 1, opt_in, set_in_game, Yes, Yes, No, Yes, NoAlias,
+                "filter coordinate locations when targeting next or previous")
+    NHOPTB(whatis_menu, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &iflags.getloc_usemenu)
+    NHOPTB(whatis_moveskip, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &iflags.getloc_moveskip)
+    NHOPTC(windowborders, 9, opt_in, set_in_game, Yes, Yes, No, No, NoAlias,
+                "0 (off), 1 (on), 2 (auto)")
+#ifdef WINCHAIN
+    NHOPTC(windowchain, WINTYPELEN, opt_in, set_in_sysconf, No, Yes, No, No,
+                NoAlias, "window processor to use")
+#endif
+    NHOPTC(windowcolors, 80, opt_in, set_gameview, No, Yes, No, No, NoAlias,
+                "the foreground/background colors of windows")
+    NHOPTC(windowtype, WINTYPELEN, opt_in, set_gameview, No, Yes, No, No, NoAlias,
+                "windowing system to use")
+    NHOPTB(wizweight, 0, opt_in, set_wizonly, Off, Yes, No, No, NoAlias,
+                &iflags.wizweight)
+    NHOPTB(wraptext, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias,
+                &iflags.wc2_wraptext)
+    /*
+     * Prefix-based Options
+     */
+
+    NHOPTP(cond_, 0, opt_in, set_hidden, No, No, Yes, Yes, NoAlias,
+                "prefix for cond_ options")
+    NHOPTP(font, 0, opt_in, set_hidden, Yes, Yes, Yes, No, NoAlias,
+                "prefix for font options")
+#if defined(MICRO) && !defined(AMIGA)
+    /* included for compatibility with old NetHack.cnf files */
+    NHOPTP(IBM_, 0, opt_in, set_hidden, No, No, Yes, NoAlias,
+                "prefix for old micro IBM_ options")
+#endif /* MICRO */
+
+#undef NoAlias
+#undef NHOPTB
+#undef NHOPTC
+#undef NHOPTP
+
+#endif /* NHOPT_PROTO || NHOPT_ENUM || NHOPT_PARSE */
+
+/* end of optlist */
+
index 62ccabe4af9696d4256281bb9af306f60bb46de6..69ed462b55fb7bd380cfeb6323bac2c5487e6cdd 100644 (file)
@@ -1459,6 +1459,38 @@ redraw_map()
     flush_screen(1);
 }
 
+/*
+ * =======================================================
+ */
+void
+reglyph_darkroom()
+{
+    xchar x, y;
+
+    for (x = 0; x < COLNO; x++)
+        for (y = 0; y < ROWNO; y++) {
+            struct rm *lev = &levl[x][y];
+
+            if (!flags.dark_room || !iflags.use_color
+                || Is_rogue_level(&u.uz)) {
+                if (lev->glyph == cmap_to_glyph(S_darkroom))
+                    lev->glyph = lev->waslit ? cmap_to_glyph(S_room)
+                                             : GLYPH_NOTHING;
+            } else {
+                if (lev->glyph == cmap_to_glyph(S_room) && lev->seenv
+                    && lev->waslit && !cansee(x, y))
+                    lev->glyph = cmap_to_glyph(S_darkroom);
+                else if (lev->glyph == GLYPH_NOTHING
+                         && lev->typ == ROOM && lev->seenv && !cansee(x, y))
+                    lev->glyph = cmap_to_glyph(S_darkroom);
+            }
+        }
+    if (flags.dark_room && iflags.use_color)
+        g.showsyms[S_darkroom] = g.showsyms[S_room];
+    else
+        g.showsyms[S_darkroom] = g.showsyms[SYM_NOTHING + SYM_OFF_X];
+}
+
 /* ======================================================================== */
 /* Glyph Buffering (3rd screen) =========================================== */
 
index dd05153b19d17641ee206bb19b261dbf9ecc633d..df0a48916fb11cfd76abe791841e852bd4963f95 100644 (file)
@@ -2099,7 +2099,7 @@ int src;
     char *envp;
 #endif
 
-    if (src == SET_IN_SYS) {
+    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));
@@ -2108,7 +2108,7 @@ int src;
             fp = (FILE *) 0;
         return  fp;
     }
-    /* If src != SET_IN_SYS, "filename" is an environment variable, so it
+    /* 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)
      */
@@ -2556,7 +2556,7 @@ char *origbuf;
         (void) strncpy(g.catname, bufp, PL_PSIZ - 1);
 
 #ifdef SYSCF
-    } else if (src == SET_IN_SYS && match_varname(buf, "WIZARDS", 7)) {
+    } else if (src == set_in_sysconf && match_varname(buf, "WIZARDS", 7)) {
         if (sysopt.wizards)
             free((genericptr_t) sysopt.wizards);
         sysopt.wizards = dupstr(bufp);
@@ -2568,15 +2568,15 @@ char *origbuf;
                 free((genericptr_t) sysopt.fmtd_wizard_list);
             sysopt.fmtd_wizard_list = build_english_list(sysopt.wizards);
         }
-    } else if (src == SET_IN_SYS && match_varname(buf, "SHELLERS", 8)) {
+    } else if (src == set_in_sysconf && match_varname(buf, "SHELLERS", 8)) {
         if (sysopt.shellers)
             free((genericptr_t) sysopt.shellers);
         sysopt.shellers = dupstr(bufp);
-    } else if (src == SET_IN_SYS && match_varname(buf, "EXPLORERS", 7)) {
+    } else if (src == set_in_sysconf && match_varname(buf, "EXPLORERS", 7)) {
         if (sysopt.explorers)
             free((genericptr_t) sysopt.explorers);
         sysopt.explorers = dupstr(bufp);
-    } else if (src == SET_IN_SYS && match_varname(buf, "DEBUGFILES", 5)) {
+    } else if (src == set_in_sysconf && match_varname(buf, "DEBUGFILES", 5)) {
         /* if showdebug() has already been called (perhaps we've added
            some debugpline() calls to option processing) and has found
            a value for getenv("DEBUGFILES"), don't override that */
@@ -2585,17 +2585,17 @@ char *origbuf;
                 free((genericptr_t) sysopt.debugfiles);
             sysopt.debugfiles = dupstr(bufp);
         }
-    } else if (src == SET_IN_SYS && match_varname(buf, "DUMPLOGFILE", 7)) {
+    } else if (src == set_in_sysconf && match_varname(buf, "DUMPLOGFILE", 7)) {
 #ifdef DUMPLOG
         if (sysopt.dumplogfile)
             free((genericptr_t) sysopt.dumplogfile);
         sysopt.dumplogfile = dupstr(bufp);
 #endif
-    } else if (src == SET_IN_SYS && match_varname(buf, "GENERICUSERS", 12)) {
+    } else if (src == set_in_sysconf && match_varname(buf, "GENERICUSERS", 12)) {
         if (sysopt.genericusers)
             free((genericptr_t) sysopt.genericusers);
         sysopt.genericusers = dupstr(bufp);
-    } else if (src == SET_IN_SYS && match_varname(buf, "BONES_POOLS", 10)) {
+    } else if (src == set_in_sysconf && match_varname(buf, "BONES_POOLS", 10)) {
         /* 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 */
         n = atoi(bufp);
@@ -2603,32 +2603,32 @@ char *origbuf;
         /* 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 */
-    } else if (src == SET_IN_SYS && match_varname(buf, "SUPPORT", 7)) {
+    } else if (src == set_in_sysconf && match_varname(buf, "SUPPORT", 7)) {
         if (sysopt.support)
             free((genericptr_t) sysopt.support);
         sysopt.support = dupstr(bufp);
-    } else if (src == SET_IN_SYS && match_varname(buf, "RECOVER", 7)) {
+    } else if (src == set_in_sysconf && match_varname(buf, "RECOVER", 7)) {
         if (sysopt.recover)
             free((genericptr_t) sysopt.recover);
         sysopt.recover = dupstr(bufp);
-    } else if (src == SET_IN_SYS
+    } else if (src == set_in_sysconf
                && match_varname(buf, "CHECK_SAVE_UID", 14)) {
         n = atoi(bufp);
         sysopt.check_save_uid = n;
-    } else if (src == SET_IN_SYS
+    } else if (src == set_in_sysconf
                && match_varname(buf, "CHECK_PLNAME", 12)) {
         n = atoi(bufp);
         sysopt.check_plname = n;
     } else if (match_varname(buf, "SEDUCE", 6)) {
         n = !!atoi(bufp); /* XXX this could be tighter */
         /* allow anyone to turn it off, but only sysconf to turn it on*/
-        if (src != SET_IN_SYS && n != 0) {
+        if (src != set_in_sysconf && n != 0) {
             config_error_add("Illegal value in SEDUCE");
             return FALSE;
         }
         sysopt.seduce = n;
         sysopt_seduce_set(sysopt.seduce);
-    } else if (src == SET_IN_SYS && match_varname(buf, "MAXPLAYERS", 10)) {
+    } else if (src == set_in_sysconf && match_varname(buf, "MAXPLAYERS", 10)) {
         n = atoi(bufp);
         /* XXX to get more than 25, need to rewrite all lock code */
         if (n < 0 || n > 25) {
@@ -2636,35 +2636,35 @@ char *origbuf;
             return FALSE;
         }
         sysopt.maxplayers = n;
-    } else if (src == SET_IN_SYS && match_varname(buf, "PERSMAX", 7)) {
+    } else if (src == set_in_sysconf && match_varname(buf, "PERSMAX", 7)) {
         n = atoi(bufp);
         if (n < 1) {
             config_error_add("Illegal value in PERSMAX (minimum is 1).");
             return FALSE;
         }
         sysopt.persmax = n;
-    } else if (src == SET_IN_SYS && match_varname(buf, "PERS_IS_UID", 11)) {
+    } else if (src == set_in_sysconf && match_varname(buf, "PERS_IS_UID", 11)) {
         n = atoi(bufp);
         if (n != 0 && n != 1) {
             config_error_add("Illegal value in PERS_IS_UID (must be 0 or 1).");
             return FALSE;
         }
         sysopt.pers_is_uid = n;
-    } else if (src == SET_IN_SYS && match_varname(buf, "ENTRYMAX", 8)) {
+    } else if (src == set_in_sysconf && match_varname(buf, "ENTRYMAX", 8)) {
         n = atoi(bufp);
         if (n < 10) {
             config_error_add("Illegal value in ENTRYMAX (minimum is 10).");
             return FALSE;
         }
         sysopt.entrymax = n;
-    } else if ((src == SET_IN_SYS) && match_varname(buf, "POINTSMIN", 9)) {
+    } else if ((src == set_in_sysconf) && match_varname(buf, "POINTSMIN", 9)) {
         n = atoi(bufp);
         if (n < 1) {
             config_error_add("Illegal value in POINTSMIN (minimum is 1).");
             return FALSE;
         }
         sysopt.pointsmin = n;
-    } else if (src == SET_IN_SYS
+    } else if (src == set_in_sysconf
                && match_varname(buf, "MAX_STATUENAME_RANK", 10)) {
         n = atoi(bufp);
         if (n < 1) {
@@ -2675,7 +2675,7 @@ char *origbuf;
         sysopt.tt_oname_maxrank = n;
 
     /* SYSCF PANICTRACE options */
-    } else if (src == SET_IN_SYS
+    } else if (src == set_in_sysconf
                && match_varname(buf, "PANICTRACE_LIBC", 15)) {
         n = atoi(bufp);
 #if defined(PANICTRACE) && defined(PANICTRACE_LIBC)
@@ -2685,7 +2685,7 @@ char *origbuf;
         }
 #endif
         sysopt.panictrace_libc = n;
-    } else if (src == SET_IN_SYS
+    } else if (src == set_in_sysconf
                && match_varname(buf, "PANICTRACE_GDB", 14)) {
         n = atoi(bufp);
 #if defined(PANICTRACE)
@@ -2695,7 +2695,7 @@ char *origbuf;
         }
 #endif
         sysopt.panictrace_gdb = n;
-    } else if (src == SET_IN_SYS && match_varname(buf, "GDBPATH", 7)) {
+    } else if (src == set_in_sysconf && match_varname(buf, "GDBPATH", 7)) {
 #if defined(PANICTRACE) && !defined(VMS)
         if (!file_exists(bufp)) {
             config_error_add("File specified in GDBPATH does not exist.");
@@ -2705,7 +2705,7 @@ char *origbuf;
         if (sysopt.gdbpath)
             free((genericptr_t) sysopt.gdbpath);
         sysopt.gdbpath = dupstr(bufp);
-    } else if (src == SET_IN_SYS && match_varname(buf, "GREPPATH", 7)) {
+    } else if (src == set_in_sysconf && match_varname(buf, "GREPPATH", 7)) {
 #if defined(PANICTRACE) && !defined(VMS)
         if (!file_exists(bufp)) {
             config_error_add("File specified in GREPPATH does not exist.");
@@ -2716,13 +2716,13 @@ char *origbuf;
             free((genericptr_t) sysopt.greppath);
         sysopt.greppath = dupstr(bufp);
     /* SYSCF SAVE and BONES format options */
-    } else if (src == SET_IN_SYS
+    } else if (src == set_in_sysconf
                && match_varname(buf, "SAVEFORMAT", 10)) {
         parseformat(sysopt.saveformat, bufp);
-    } else if (src == SET_IN_SYS
+    } else if (src == set_in_sysconf
                && match_varname(buf, "BONESFORMAT", 11)) {
         parseformat(sysopt.bonesformat, bufp);
-    } else if (src == SET_IN_SYS
+    } else if (src == set_in_sysconf
                && match_varname(buf, "ACCESSIBILITY", 13)) {
         n = atoi(bufp);
         if (n < 0 || n > 1) {
@@ -2731,7 +2731,7 @@ char *origbuf;
         }
         sysopt.accessibility = n;
 #ifdef WIN32
-    } else if (src == SET_IN_SYS
+    } else if (src == set_in_sysconf
                 && match_varname(buf, "portable_device_paths", 8)) {
         n = atoi(bufp);
         if (n < 0 || n > 1) {
@@ -2967,6 +2967,7 @@ boolean secure;
 
     tmp->next = config_error_data;
     config_error_data = tmp;
+    g.program_state.config_error_ready = TRUE;
 }
 
 static boolean
@@ -3042,6 +3043,7 @@ config_error_done()
     }
     config_error_data = tmp->next;
     free(tmp);
+    g.program_state.config_error_ready = FALSE;
     return n;
 }
 
index 3ede3def435a2e4b6cb07c56decea08130c4ebf1..5a86385537a7455101ab20bab2ce5b6d5540cec2 100644 (file)
@@ -21,16 +21,63 @@ NEARDATA struct instance_flags iflags; /* provide linkage */
 
 #define BACKWARD_COMPAT
 
+/*
+ *  NOTE:  If you add (or delete) an option, please review the following:
+ *             doc/options.doc
+ *
+ *         It contains how-to info and outlines some required/suggested
+ *         updates that should accompany your change.
+ */
+
+/*
+ * include/optlist.h is utilized 3 successive times, for 3 different
+ * objectives.
+ *
+ * The first time is with NHOPT_PROTO defined, to produce and include
+ * the prototypes for the individual option processing functions.
+ *
+ * The second time is with NHOPT_ENUM defined, to produce the enum values
+ * for the individual options that are used throughout options processing.
+ * They are generally opt_optname, where optname is the name of the option.
+ *
+ * The third time is with NHOPT_PARSE defined, to produce the initializers
+ * to fill out the allopt[] array of options (both boolean and compound).
+ *
+ */
+
+#define NHOPT_PROTO
+#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, 0, 0, 0, set_in_sysconf, BoolOpt,
+     No, No, No, No, 0, (boolean *) 0,
+     (int FDECL((*), (int, int, BOOLEAN_P, char *, char *))) 0,
+     (char *) 0, (const char *) 0, (const char *) 0, 0, 0, 0}
+};
+#undef NHOPT_PARSE
+
+
 #ifdef DEFAULT_WC_TILED_MAP
 #define PREFER_TILED TRUE
 #else
 #define PREFER_TILED FALSE
 #endif
 
-#ifdef CURSES_GRAPHICS
-extern int curses_read_attrs(const char *attrs);
-extern char *curses_fmt_attrs(char *);
-#endif
+#define PILE_LIMIT_DFLT 5
+#define rolestring(val, array, field) \
+    ((val >= 0) ? array[val].field : (val == ROLE_RANDOM) ? randomrole : none)
+
 
 enum window_option_types {
     MESSAGE_OPTION = 1,
@@ -40,451 +87,81 @@ enum window_option_types {
     TEXT_OPTION
 };
 
-#define PILE_LIMIT_DFLT 5
-
-static char empty_optstr[] = { '\0' };
-
-/*
- *  NOTE:  If you add (or delete) an option, please update the short
- *  options help (option_help()), the long options help (dat/opthelp),
- *  and the current options setting display function (doset()),
- *  and also the Guidebooks.
- *
- *  The order matters.  If an option is a an initial substring of another
- *  option (e.g. time and timed_delay) the shorter one must come first.
- */
-
-static const struct Bool_Opt {
-    const char *name;
-    boolean *addr, initvalue;
-    int optflags;
-} boolopt_init[] = {
-    { "acoustics", &flags.acoustics, TRUE, SET_IN_GAME },
-#if defined(SYSFLAGS) && defined(AMIGA)
-    /* Amiga altmeta causes Alt+key to be converted into Meta+key by
-       low level nethack code; on by default, can be toggled off if
-       Alt+key is needed for some ASCII chars on non-ASCII keyboard */
-    { "altmeta", &sysflags.altmeta, TRUE, DISP_IN_GAME },
-#else
-#ifdef ALTMETA
-    /* non-Amiga altmeta causes nethack's top level command loop to treat
-       two character sequence "ESC c" as M-c, for terminals or emulators
-       which send "ESC c" when Alt+c is pressed; off by default, enabling
-       this can potentially make trouble if user types ESC when nethack
-       is honoring this conversion request (primarily after starting a
-       count prefix prior to a command and then deciding to cancel it) */
-    { "altmeta", &iflags.altmeta, FALSE, SET_IN_GAME },
-#else
-    { "altmeta", (boolean *) 0, TRUE, DISP_IN_GAME },
-#endif
-#endif
-    { "ascii_map", &iflags.wc_ascii_map, !PREFER_TILED, SET_IN_GAME }, /*WC*/
-#if defined(SYSFLAGS) && defined(MFLOPPY)
-    { "asksavedisk", &sysflags.asksavedisk, FALSE, SET_IN_GAME },
-#else
-    { "asksavedisk", (boolean *) 0, FALSE, SET_IN_FILE },
-#endif
-    { "autodescribe", &iflags.autodescribe, TRUE, SET_IN_GAME },
-    { "autodig", &flags.autodig, FALSE, SET_IN_GAME },
-    { "autoopen", &flags.autoopen, TRUE, SET_IN_GAME },
-    { "autopickup", &flags.pickup, TRUE, SET_IN_GAME },
-    { "autoquiver", &flags.autoquiver, FALSE, SET_IN_GAME },
-    { "autounlock", &flags.autounlock, TRUE, SET_IN_GAME },
-#if defined(MICRO) && !defined(AMIGA)
-    { "BIOS", &iflags.BIOS, FALSE, SET_IN_FILE },
-#else
-    { "BIOS", (boolean *) 0, FALSE, SET_IN_FILE },
-#endif
-    { "blind", &u.uroleplay.blind, FALSE, DISP_IN_GAME },
-    { "bones", &flags.bones, TRUE, SET_IN_FILE },
-#ifdef INSURANCE
-    { "checkpoint", &flags.ins_chkpt, TRUE, SET_IN_GAME },
-#else
-    { "checkpoint", (boolean *) 0, FALSE, SET_IN_FILE },
-#endif
-#ifdef MFLOPPY
-    { "checkspace", &iflags.checkspace, TRUE, SET_IN_GAME },
-#else
-    { "checkspace", (boolean *) 0, FALSE, SET_IN_FILE },
-#endif
-    { "clicklook", &iflags.clicklook, FALSE, SET_IN_GAME },
-    { "cmdassist", &iflags.cmdassist, TRUE, SET_IN_GAME },
-#if defined(MICRO) || defined(WIN32) || defined(CURSES_GRAPHICS)
-    { "color", &iflags.wc_color, TRUE, SET_IN_GAME }, /* on/off: use WC or not */
-#else /* systems that support multiple terminals, many monochrome */
-    { "color", &iflags.wc_color, FALSE, SET_IN_GAME },
-#endif
-    { "confirm", &flags.confirm, TRUE, SET_IN_GAME },
-    { "dark_room", &flags.dark_room, TRUE, SET_IN_GAME },
-    { "eight_bit_tty", &iflags.wc_eight_bit_input, FALSE, SET_IN_GAME }, /*WC*/
-#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) || defined(X11_GRAPHICS)
-    { "extmenu", &iflags.extmenu, FALSE, SET_IN_GAME },
-#else
-    { "extmenu", (boolean *) 0, FALSE, SET_IN_FILE },
-#endif
-#ifdef OPT_DISPMAP
-    { "fast_map", &flags.fast_map, TRUE, SET_IN_GAME },
-#else
-    { "fast_map", (boolean *) 0, TRUE, SET_IN_FILE },
-#endif
-    { "female", &flags.female, FALSE, DISP_IN_GAME },
-    { "fixinv", &flags.invlet_constant, TRUE, SET_IN_GAME },
-#if defined(SYSFLAGS) && defined(AMIFLUSH)
-    { "flush", &sysflags.amiflush, FALSE, SET_IN_GAME },
-#else
-    { "flush", (boolean *) 0, FALSE, SET_IN_FILE },
-#endif
-    { "force_invmenu", &iflags.force_invmenu, FALSE, SET_IN_GAME },
-    { "fullscreen", &iflags.wc2_fullscreen, FALSE, SET_IN_FILE }, /*WC2*/
-    { "goldX", &flags.goldX, FALSE, SET_IN_GAME },
-    { "guicolor", &iflags.wc2_guicolor, TRUE, SET_IN_GAME}, /*WC2*/
-    { "help", &flags.help, TRUE, SET_IN_GAME },
-    { "herecmd_menu", &iflags.herecmd_menu, FALSE, SET_IN_GAME },
-    { "hilite_pet", &iflags.wc_hilite_pet, FALSE, SET_IN_GAME }, /*WC*/
-    { "hilite_pile", &iflags.hilite_pile, FALSE, SET_IN_GAME },
-    { "hitpointbar", &iflags.wc2_hitpointbar, FALSE, SET_IN_GAME }, /*WC2*/
-#ifndef MAC
-    { "ignintr", &flags.ignintr, FALSE, SET_IN_GAME },
-#else
-    { "ignintr", (boolean *) 0, FALSE, SET_IN_FILE },
-#endif
-    { "implicit_uncursed", &flags.implicit_uncursed, TRUE, SET_IN_GAME },
-    { "large_font", &iflags.obsolete, FALSE, SET_IN_FILE }, /* OBSOLETE */
-    { "legacy", &flags.legacy, TRUE, DISP_IN_GAME },
-    { "lit_corridor", &flags.lit_corridor, FALSE, SET_IN_GAME },
-    { "lootabc", &flags.lootabc, FALSE, SET_IN_GAME },
-#ifdef MAIL_STRUCTURES
-    { "mail", &flags.biff, TRUE, SET_IN_GAME },
-#else
-    { "mail", (boolean *) 0, TRUE, SET_IN_FILE },
-#endif
-    { "mention_decor", &flags.mention_decor, FALSE, SET_IN_GAME },
-    { "mention_walls", &flags.mention_walls, FALSE, SET_IN_GAME },
-    { "menucolors", &iflags.use_menu_color, FALSE, SET_IN_GAME },
-    /* for menu debugging only*/
-    { "menu_tab_sep", &iflags.menu_tab_sep, FALSE, SET_IN_WIZGAME },
-    { "menu_objsyms", &iflags.menu_head_objsym, FALSE, SET_IN_GAME },
-#ifdef TTY_GRAPHICS
-    { "menu_overlay", &iflags.menu_overlay, TRUE, SET_IN_GAME },
-#else
-    { "menu_overlay", (boolean *) 0, FALSE, SET_IN_FILE },
-#endif
-    { "monpolycontrol", &iflags.mon_polycontrol, FALSE, SET_IN_WIZGAME },
-#ifdef NEWS
-    { "news", &iflags.news, TRUE, DISP_IN_GAME },
-#else
-    { "news", (boolean *) 0, FALSE, SET_IN_FILE },
-#endif
-    { "nudist", &u.uroleplay.nudist, FALSE, DISP_IN_GAME },
-    { "null", &flags.null, TRUE, SET_IN_GAME },
-#if defined(SYSFLAGS) && defined(MAC)
-    { "page_wait", &sysflags.page_wait, TRUE, SET_IN_GAME },
-#else
-    { "page_wait", (boolean *) 0, FALSE, SET_IN_FILE },
-#endif
-    /* moved perm_invent from flags to iflags and out of save file in 3.6.2 */
-    { "perm_invent", &iflags.perm_invent, FALSE, SET_IN_GAME },
-    { "pickup_thrown", &flags.pickup_thrown, TRUE, SET_IN_GAME },
-    { "popup_dialog", &iflags.wc_popup_dialog, FALSE, SET_IN_GAME },   /*WC*/
-    { "preload_tiles", &iflags.wc_preload_tiles, TRUE, DISP_IN_GAME }, /*WC*/
-    { "pushweapon", &flags.pushweapon, FALSE, SET_IN_GAME },
-    { "quick_farsight", &flags.quick_farsight, FALSE, SET_IN_GAME },
-#if defined(MICRO) && !defined(AMIGA)
-    { "rawio", &iflags.rawio, FALSE, DISP_IN_GAME },
-#else
-    { "rawio", (boolean *) 0, FALSE, SET_IN_FILE },
-#endif
-    { "rest_on_space", &flags.rest_on_space, FALSE, SET_IN_GAME },
-#ifdef RLECOMP
-    { "rlecomp", &iflags.rlecomp,
-#if defined(COMPRESS) || defined(ZLIB_COMP)
-      FALSE,
-#else
-      TRUE,
-#endif
-      DISP_IN_GAME },
-#endif
-    { "safe_pet", &flags.safe_dog, TRUE, SET_IN_GAME },
-    { "sanity_check", &iflags.sanity_check, FALSE, SET_IN_WIZGAME },
-    { "selectsaved", &iflags.wc2_selectsaved, TRUE, DISP_IN_GAME }, /*WC*/
-    { "showexp", &flags.showexp, FALSE, SET_IN_GAME },
-    { "showrace", &flags.showrace, FALSE, SET_IN_GAME },
-#ifdef SCORE_ON_BOTL
-    { "showscore", &flags.showscore, FALSE, SET_IN_GAME },
-#else
-    { "showscore", (boolean *) 0, FALSE, SET_IN_FILE },
-#endif
-    { "silent", &flags.silent, TRUE, SET_IN_GAME },
-    { "softkeyboard", &iflags.wc2_softkeyboard, FALSE, SET_IN_FILE }, /*WC2*/
-    { "sortpack", &flags.sortpack, TRUE, SET_IN_GAME },
-    { "sparkle", &flags.sparkle, TRUE, SET_IN_GAME },
-    { "splash_screen", &iflags.wc_splash_screen, TRUE, DISP_IN_GAME }, /*WC*/
-    { "standout", &flags.standout, FALSE, SET_IN_GAME },
-    { "status_updates", &iflags.status_updates, TRUE, DISP_IN_GAME },
-    { "tiled_map", &iflags.wc_tiled_map, PREFER_TILED, DISP_IN_GAME }, /*WC*/
-    { "time", &flags.time, FALSE, SET_IN_GAME },
-#ifdef TIMED_DELAY
-    { "timed_delay", &flags.nap, TRUE, SET_IN_GAME },
-#else
-    { "timed_delay", (boolean *) 0, FALSE, SET_IN_GAME },
-#endif
-    { "tombstone", &flags.tombstone, TRUE, SET_IN_GAME },
-    { "toptenwin", &iflags.toptenwin, FALSE, SET_IN_GAME },
-    { "travel", &flags.travelcmd, TRUE, SET_IN_GAME },
-#ifdef DEBUG
-    { "travel_debug", &iflags.trav_debug, FALSE, SET_IN_WIZGAME }, /*hack.c*/
-#endif
-    { "use_darkgray", &iflags.wc2_darkgray, TRUE, SET_IN_FILE }, /*WC2*/
-#ifdef WIN32
-    { "use_inverse", &iflags.wc_inverse, TRUE, SET_IN_GAME }, /*WC*/
-#else
-    { "use_inverse", &iflags.wc_inverse, FALSE, SET_IN_GAME }, /*WC*/
-#endif
-    { "verbose", &flags.verbose, TRUE, SET_IN_GAME },
-#ifdef TTY_TILES_ESCCODES
-    { "vt_tiledata", &iflags.vt_tiledata, FALSE, SET_IN_FILE },
-#else
-    { "vt_tiledata", (boolean *) 0, FALSE, SET_IN_FILE },
-#endif
-    { "whatis_menu", &iflags.getloc_usemenu, FALSE, SET_IN_GAME },
-    { "whatis_moveskip", &iflags.getloc_moveskip, FALSE, SET_IN_GAME },
-    { "wizweight", &iflags.wizweight, FALSE, SET_IN_WIZGAME },
-    { "wraptext", &iflags.wc2_wraptext, FALSE, SET_IN_GAME }, /*WC2*/
-#ifdef ZEROCOMP
-    { "zerocomp", &iflags.zerocomp,
-#if defined(COMPRESS) || defined(ZLIB_COMP)
-      FALSE,
-#else
-      TRUE,
-#endif
-      DISP_IN_GAME },
-#endif
-    { (char *) 0, (boolean *) 0, FALSE, 0 }
-};
-
-/* compound options, for option_help() and external programs like Amiga
- * frontend */
-static struct Comp_Opt {
-    const char *name, *descr;
-    int size; /* for frontends and such allocating space --
-               * usually allowed size of data in game, but
-               * occasionally maximum reasonable size for
-               * typing when game maintains information in
-               * a different format */
-    int optflags;
-} compopt[] = {
-    { "align", "your starting alignment (lawful, neutral, or chaotic)", 8,
-      DISP_IN_GAME },
-    { "align_message", "message window alignment", 20, DISP_IN_GAME }, /*WC*/
-    { "align_status", "status window alignment", 20, DISP_IN_GAME },   /*WC*/
-    { "altkeyhandler", "alternate key handler", 20, SET_IN_GAME },
-#ifdef BACKWARD_COMPAT
-    { "boulder", "deprecated (use S_boulder in sym file instead)", 1,
-      SET_IN_GAME },
-#endif
-    { "catname", "the name of your (first) cat (e.g., catname:Tabby)",
-      PL_PSIZ, DISP_IN_GAME },
-    { "disclose", "the kinds of information to disclose at end of game",
-      sizeof flags.end_disclose * 2, SET_IN_GAME },
-    { "dogname", "the name of your (first) dog (e.g., dogname:Fang)", PL_PSIZ,
-      DISP_IN_GAME },
-    { "dungeon", "the symbols to use in drawing the dungeon map",
-      MAXDCHARS + 1, SET_IN_FILE },
-    { "effects", "the symbols to use in drawing special effects",
-      MAXECHARS + 1, SET_IN_FILE },
-    { "font_map", "the font to use in the map window", 40,
-      DISP_IN_GAME },                                              /*WC*/
-    { "font_menu", "the font to use in menus", 40, DISP_IN_GAME }, /*WC*/
-    { "font_message", "the font to use in the message window", 40,
-      DISP_IN_GAME },                                                  /*WC*/
-    { "font_size_map", "the size of the map font", 20, DISP_IN_GAME }, /*WC*/
-    { "font_size_menu", "the size of the menu font", 20,
-      DISP_IN_GAME }, /*WC*/
-    { "font_size_message", "the size of the message font", 20,
-      DISP_IN_GAME }, /*WC*/
-    { "font_size_status", "the size of the status font", 20,
-      DISP_IN_GAME }, /*WC*/
-    { "font_size_text", "the size of the text font", 20,
-      DISP_IN_GAME }, /*WC*/
-    { "font_status", "the font to use in status window", 40,
-      DISP_IN_GAME }, /*WC*/
-    { "font_text", "the font to use in text windows", 40,
-      DISP_IN_GAME }, /*WC*/
-    { "fruit", "the name of a fruit you enjoy eating", PL_FSIZ, SET_IN_GAME },
-    { "gender", "your starting gender (male or female)", 8, DISP_IN_GAME },
-    { "horsename", "the name of your (first) horse (e.g., horsename:Silver)",
-      PL_PSIZ, DISP_IN_GAME },
-    { "map_mode", "map display mode under Windows", 20, DISP_IN_GAME }, /*WC*/
-    { "menuinvertmode", "behaviour of menu iverts", 5, SET_IN_GAME },
-    { "menustyle", "user interface for object selection", MENUTYPELEN,
-      SET_IN_GAME },
-    { "menu_deselect_all", "deselect all items in a menu", 4, SET_IN_FILE },
-    { "menu_deselect_page", "deselect all items on this page of a menu", 4,
-      SET_IN_FILE },
-    { "menu_first_page", "jump to the first page in a menu", 4, SET_IN_FILE },
-    { "menu_headings", "text attribute for menu headings", 9, SET_IN_GAME },
-    { "menu_invert_all", "invert all items in a menu", 4, SET_IN_FILE },
-    { "menu_invert_page", "invert all items on this page of a menu", 4,
-      SET_IN_FILE },
-    { "menu_last_page", "jump to the last page in a menu", 4, SET_IN_FILE },
-    { "menu_next_page", "goto the next menu page", 4, SET_IN_FILE },
-    { "menu_previous_page", "goto the previous menu page", 4, SET_IN_FILE },
-    { "menu_search", "search for a menu item", 4, SET_IN_FILE },
-    { "menu_select_all", "select all items in a menu", 4, SET_IN_FILE },
-    { "menu_select_page", "select all items on this page of a menu", 4,
-      SET_IN_FILE },
-    { "monsters", "the symbols to use for monsters", MAXMCLASSES,
-      SET_IN_FILE },
-    { "msghistory", "number of top line messages to save", 5, DISP_IN_GAME },
-#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS)
-    { "msg_window", "the type of message window required", 1, SET_IN_GAME },
-#else
-    { "msg_window", "the type of message window required", 1, SET_IN_FILE },
-#endif
-    { "name", "your character's name (e.g., name:Merlin-W)", PL_NSIZ,
-      DISP_IN_GAME },
-    { "mouse_support", "game receives click info from mouse", 0, SET_IN_GAME },
-    { "number_pad", "use the number pad for movement", 1, SET_IN_GAME },
-    { "objects", "the symbols to use for objects", MAXOCLASSES, SET_IN_FILE },
-    { "packorder", "the inventory order of the items in your pack",
-      MAXOCLASSES, SET_IN_GAME },
-#ifdef CHANGE_COLOR
-    { "palette",
-#ifndef WIN32
-      "palette (00c/880/-fff is blue/yellow/reverse white)", 15, SET_IN_GAME
-#else
-      "palette (adjust an RGB color in palette (color-R-G-B)", 15, SET_IN_FILE
-#endif
-    },
-#if defined(MAC)
-    { "hicolor", "same as palette, only order is reversed", 15, SET_IN_FILE },
-#endif
-#endif
-    { "paranoid_confirmation", "extra prompting in certain situations", 28,
-      SET_IN_GAME },
-    { "petattr",  "attributes for highlighting pets", 88, SET_IN_GAME },
-    { "pettype", "your preferred initial pet type", 4, DISP_IN_GAME },
-    { "pickup_burden", "maximum burden picked up before prompt", 20,
-      SET_IN_GAME },
-    { "pickup_types", "types of objects to pick up automatically",
-      MAXOCLASSES, SET_IN_GAME },
-    { "pile_limit", "threshold for \"there are many objects here\"", 24,
-      SET_IN_GAME },
-    { "playmode", "normal play, non-scoring explore mode, or debug mode", 8,
-      DISP_IN_GAME },
-    { "player_selection", "choose character via dialog or prompts", 12,
-      DISP_IN_GAME },
-    { "race", "your starting race (e.g., Human, Elf)", PL_CSIZ,
-      DISP_IN_GAME },
-    { "role", "your starting role (e.g., Barbarian, Valkyrie)", PL_CSIZ,
-      DISP_IN_GAME },
-    { "runmode", "display frequency when `running' or `travelling'",
-      sizeof "teleport", SET_IN_GAME },
-    { "scores", "the parts of the score list you wish to see", 32,
-      SET_IN_GAME },
-    { "scroll_amount", "amount to scroll map when scroll_margin is reached",
-      20, DISP_IN_GAME }, /*WC*/
-    { "scroll_margin", "scroll map when this far from the edge", 20,
-      DISP_IN_GAME }, /*WC*/
-    { "sortloot", "sort object selection lists by description", 4,
-      SET_IN_GAME },
-    { "statushilites",
-#ifdef STATUS_HILITES
-      "0=no status highlighting, N=show highlights for N turns",
-      20, SET_IN_GAME
-#else
-    "highlight control", 20, SET_IN_FILE
-#endif
-    },
-    { "statuslines",
-#ifdef CURSES_GRAPHICS
-      "2 or 3 lines for horizontal (bottom or top) status display",
-      20, SET_IN_GAME
-#else
-      "2 or 3 lines for status display",
-      20, SET_IN_FILE
-#endif
-    }, /*WC2*/
-    { "symset", "load a set of display symbols from the symbols file", 70,
-      SET_IN_GAME },
-    { "roguesymset",
-      "load a set of rogue display symbols from the symbols file", 70,
-      SET_IN_GAME },
-#ifdef WIN32
-    { "subkeyvalue", "override keystroke value", 7, SET_IN_FILE },
-#endif
-    { "suppress_alert", "suppress alerts about version-specific features", 8,
-      SET_IN_GAME },
-    /* term_cols,term_rows -> WC2_TERM_SIZE (6: room to format 1..32767) */
-    { "term_cols", "number of columns", 6, SET_IN_FILE }, /*WC2*/
-    { "term_rows", "number of rows", 6, SET_IN_FILE }, /*WC2*/
-    { "tile_width", "width of tiles", 20, DISP_IN_GAME },   /*WC*/
-    { "tile_height", "height of tiles", 20, DISP_IN_GAME }, /*WC*/
-    { "tile_file", "name of tile file", 70, DISP_IN_GAME }, /*WC*/
-    { "traps", "the symbols to use in drawing traps", MAXTCHARS + 1,
-      SET_IN_FILE },
-    { "vary_msgcount", "show more old messages at a time", 20,
-      DISP_IN_GAME }, /*WC*/
-#ifdef MSDOS
-    { "video", "method of video updating", 20, SET_IN_FILE },
-#endif
-#ifdef VIDEOSHADES
-    { "videocolors", "color mappings for internal screen routines", 40,
-      DISP_IN_GAME },
-    { "videoshades", "gray shades to map to black/gray/white", 32,
-      DISP_IN_GAME },
-#endif
-    { "whatis_coord", "show coordinates when auto-describing cursor position",
-      1, SET_IN_GAME },
-    { "whatis_filter",
-      "filter coordinate locations when targeting next or previous",
-      1, SET_IN_GAME },
-    { "windowborders", "0 (off), 1 (on), 2 (auto)", 9, SET_IN_GAME }, /*WC2*/
-    { "windowcolors", "the foreground/background colors of windows", /*WC*/
-      80, DISP_IN_GAME },
-    { "windowtype", "windowing system to use", WINTYPELEN, DISP_IN_GAME },
-#ifdef WINCHAIN
-    { "windowchain", "window processor to use", WINTYPELEN, SET_IN_SYS },
-#endif
-#ifdef BACKWARD_COMPAT
-    { "DECgraphics", "load DECGraphics display symbols", 70, SET_IN_FILE },
-    { "IBMgraphics", "load IBMGraphics display symbols", 70, SET_IN_FILE },
-#ifdef CURSES_GRAPHICS
-    { "cursesgraphics", "load curses display symbols", 70, SET_IN_FILE },
-#endif
-#ifdef MAC_GRAPHICS_ENV
-    { "Macgraphics", "load MACGraphics display symbols", 70, SET_IN_FILE },
-#endif
-#endif
-    { (char *) 0, (char *) 0, 0, 0 }
-};
+enum {optn_err = 0, optn_ok};
+enum requests {do_nothing, do_init, do_set, do_handler, get_val};
 
-static struct Bool_Opt boolopt[SIZE(boolopt_init)];
+static struct allopt_t allopt[SIZE(allopt_init)];
 
-#ifdef OPTION_LISTS_ONLY
-#undef static
+#ifndef OPTION_LISTS_ONLY
 
-#else /* use rest of file */
+/* use rest of file */
 
 extern char configfile[]; /* for messages */
-
 extern struct symparse loadsyms[];
-
 #if defined(TOS) && defined(TEXTCOLOR)
 extern boolean colors_changed;  /* in tos.c */
 #endif
-
 #ifdef VIDEOSHADES
 extern char *shade[3];          /* in sys/msdos/video.c */
 extern char ttycolors[CLR_MAX]; /* in sys/msdos/video.c */
 #endif
 
+static char empty_optstr[] = { '\0' };
+boolean duplicate;
+
 static const char def_inv_order[MAXOCLASSES] = {
     COIN_CLASS, AMULET_CLASS, WEAPON_CLASS, ARMOR_CLASS, FOOD_CLASS,
     SCROLL_CLASS, SPBOOK_CLASS, POTION_CLASS, RING_CLASS, WAND_CLASS,
     TOOL_CLASS, GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, 0,
 };
 
+static const char none[] = "(none)", randomrole[] = "random",
+                  to_be_done[] = "(to be done)",
+                  defopt[] = "default", defbrief[] = "def";
+
+/* paranoia[] - used by parseoptions() and handler_paranoid_confirmation() */
+static const struct paranoia_opts {
+    int flagmask;        /* which paranoid option */
+    const char *argname; /* primary name */
+    int argMinLen;       /* minimum number of letters to match */
+    const char *synonym; /* alternate name (optional) */
+    int synMinLen;
+    const char *explain; /* for interactive menu */
+} paranoia[] = {
+    /* there are some initial-letter conflicts: "a"ttack vs "a"ll, "attack"
+       takes precedence and "all" isn't present in the interactive menu,
+       and "d"ie vs "d"eath, synonyms for each other so doesn't matter;
+       (also "p"ray vs "P"aranoia, "pray" takes precedence since "Paranoia"
+       is just a synonym for "Confirm"); "b"ones vs "br"eak-wand, the
+       latter requires at least two letters; "e"at vs "ex"plore,
+       "cont"inue eating vs "C"onfirm; "wand"-break vs "Were"-change,
+       both require at least two letters during config processing and use
+       case-senstivity for 'O's interactive menu */
+    { PARANOID_CONFIRM, "Confirm", 1, "Paranoia", 2,
+      "for \"yes\" confirmations, require \"no\" to reject" },
+    { PARANOID_QUIT, "quit", 1, "explore", 2,
+      "yes vs y to quit or to enter explore mode" },
+    { PARANOID_DIE, "die", 1, "death", 2,
+      "yes vs y to die (explore mode or debug mode)" },
+    { PARANOID_BONES, "bones", 1, 0, 0,
+      "yes vs y to save bones data when dying in debug mode" },
+    { PARANOID_HIT, "attack", 1, "hit", 1,
+      "yes vs y to attack a peaceful monster" },
+    { PARANOID_BREAKWAND, "wand-break", 2, "break-wand", 2,
+      "yes vs y to break a wand via (a)pply" },
+    { PARANOID_EATING, "eat", 1, "continue", 4,
+      "yes vs y to continue eating after first bite when satiated" },
+    { PARANOID_WERECHANGE, "Were-change", 2, (const char *) 0, 0,
+      "yes vs y to change form when lycanthropy is controllable" },
+    { PARANOID_PRAY, "pray", 1, 0, 0,
+      "y to pray (supersedes old \"prayconfirm\" option)" },
+    { PARANOID_REMOVE, "Remove", 1, "Takeoff", 1,
+      "always pick from inventory for Remove and Takeoff" },
+    /* for config file parsing; interactive menu skips these */
+    { 0, "none", 4, 0, 0, 0 }, /* require full word match */
+    { ~0, "all", 3, 0, 0, 0 }, /* ditto */
+};
+
 /*
  * Default menu manipulation command accelerators.  These may _not_ be:
  *
@@ -517,6 +194,18 @@ typedef struct {
     const char *desc;
 } menu_cmd_t;
 
+static NEARDATA const char *menutype[] = { "traditional", "combination",
+                                           "full", "partial" };
+
+static NEARDATA const char *burdentype[] = { "unencumbered", "burdened",
+                                             "stressed",     "strained",
+                                             "overtaxed",    "overloaded" };
+
+static NEARDATA const char *runmodes[] = { "teleport", "run", "walk",
+                                           "crawl" };
+
+static NEARDATA const char *sortltype[] = { "none", "loot", "full" };
+
 static const menu_cmd_t default_menu_cmd_info[] = {
  { "menu_first_page", MENU_FIRST_PAGE, "Go to first page" },
  { "menu_last_page", MENU_LAST_PAGE, "Go to last page" },
@@ -543,6 +232,10 @@ static boolean FDECL(warning_opts, (char *, const char *));
 static int FDECL(feature_alert_opts, (char *, const char *));
 static boolean FDECL(duplicate_opt_detection, (const char *, int));
 static void FDECL(complain_about_duplicate, (const char *, int));
+static int FDECL(length_without_val, (const char *, int len));
+static void NDECL(determine_ambiguities);
+static int FDECL(check_misc_menu_command, (char *, char *));
+int FDECL(spcfn_misc_menu_cmd, (int, BOOLEAN_P, BOOLEAN_P, char *, char *));
 
 static const char *FDECL(attr2attrname, (int));
 static const char * FDECL(msgtype2name, (int));
@@ -556,16 +249,34 @@ static void FDECL(free_one_menu_coloring, (int));
 static int NDECL(count_menucolors);
 static boolean FDECL(parse_role_opts, (BOOLEAN_P, const char *,
                                            char *, char **));
-
-static void FDECL(doset_add_menu, (winid, const char *, int));
+static void FDECL(doset_add_menu, (winid, const char *, int, int));
 static void FDECL(opts_add_others, (winid, const char *, int,
                                         char *, int));
 static int FDECL(handle_add_list_remove, (const char *, int));
-static boolean FDECL(special_handling, (const char *,
-                                            BOOLEAN_P, BOOLEAN_P));
-static const char *FDECL(get_compopt_value, (const char *, char *));
 static void FDECL(remove_autopickup_exception,
                       (struct autopickup_exception *));
+static int NDECL(count_apes);
+static int NDECL(count_cond);
+
+static int FDECL(handler_align_misc, (int));
+static int NDECL(handler_disclose);
+static int NDECL(handler_menu_headings);
+static int NDECL(handler_menustyle);
+static int NDECL(handler_msg_window);
+static int NDECL(handler_number_pad);
+static int NDECL(handler_paranoid_confirmation);
+static int NDECL(handler_pickup_burden);
+static int NDECL(handler_pickup_types);
+static int NDECL(handler_runmode);
+static int NDECL(handler_sortloot);
+static int FDECL(handler_symset, (int));
+static int NDECL(handler_whatis_coord);
+static int NDECL(handler_whatis_filter);
+/* next few are not allopts[] entries, so will only be called
+   directly from doset, not from individual optfn's */
+static int NDECL(handler_autopickup_exception);
+static int NDECL(handler_menu_colors);
+static int NDECL(handler_msgtype);
 
 static boolean FDECL(is_wc_option, (const char *));
 static boolean FDECL(wc_supported, (const char *));
@@ -573,1701 +284,1932 @@ static boolean FDECL(is_wc2_option, (const char *));
 static boolean FDECL(wc2_supported, (const char *));
 static void FDECL(wc_set_font_name, (int, char *));
 static int FDECL(wc_set_window_colors, (char *));
+static boolean FDECL(illegal_menu_cmd_key, (CHAR_P));
+#ifndef CHANGE_COLOR
+int FDECL(optfn_palette, (int, BOOLEAN_P, BOOLEAN_P, char *, char *));
+#endif
+#ifdef CURSES_GRAPHICS
+extern int curses_read_attrs(const char *attrs);
+extern char *curses_fmt_attrs(char *);
+#endif
 
-void
-reglyph_darkroom()
+/*
+ **********************************
+ *
+ *   parseoptions
+ *
+ **********************************
+ */
+boolean
+parseoptions(opts, tinitial, tfrom_file)
+register char *opts;
+boolean tinitial, tfrom_file;
 {
-    xchar x, y;
+    char *op;
+    boolean negated, got_match, has_val = FALSE;
+    int i, matchidx = -1, optresult = optn_err, optlen, optlen_wo_val;
+    boolean retval = TRUE;
+
+    duplicate = FALSE;
+    g.opt_initial = tinitial;
+    g.opt_from_file = tfrom_file;
+    if ((op = index(opts, ',')) != 0) {
+        *op++ = 0;
+        if (!parseoptions(op, g.opt_initial, g.opt_from_file))
+            retval = FALSE;
+    }
+    if (strlen(opts) > BUFSZ / 2) {
+        config_error_add("Option too long, max length is %i characters",
+                         (BUFSZ / 2));
+        return FALSE;
+    }
 
-    for (x = 0; x < COLNO; x++)
-        for (y = 0; y < ROWNO; y++) {
-            struct rm *lev = &levl[x][y];
+    /* strip leading and trailing white space */
+    while (isspace((uchar) *opts))
+        opts++;
+    op = eos(opts);
+    while (--op >= opts && isspace((uchar) *op))
+        *op = '\0';
 
-            if (!flags.dark_room || !iflags.use_color
-                || Is_rogue_level(&u.uz)) {
-                if (lev->glyph == cmap_to_glyph(S_darkroom))
-                    lev->glyph = lev->waslit ? cmap_to_glyph(S_room)
-                                             : GLYPH_NOTHING;
-            } else {
-                if (lev->glyph == cmap_to_glyph(S_room) && lev->seenv
-                    && lev->waslit && !cansee(x, y))
-                    lev->glyph = cmap_to_glyph(S_darkroom);
-                else if (lev->glyph == GLYPH_NOTHING
-                         && lev->typ == ROOM && lev->seenv && !cansee(x, y))
-                    lev->glyph = cmap_to_glyph(S_darkroom);
+    if (!*opts) {
+        config_error_add("Empty statement");
+        return FALSE;
+    }
+    negated = FALSE;
+    while ((*opts == '!') || !strncmpi(opts, "no", 2)) {
+        if (*opts == '!')
+            opts++;
+        else
+            opts += 2;
+        negated = !negated;
+    }
+    optlen = (int) strlen(opts);
+    optlen_wo_val = length_without_val(opts, optlen);
+    if (optlen_wo_val < optlen) {
+        has_val = TRUE;
+        optlen = optlen_wo_val;
+    }
+
+    for (i = 0; i < OPTCOUNT; ++i) {
+        got_match = FALSE;
+
+        if (allopt[i].pfx) {
+            if (!strncmpi(opts, allopt[i].name, strlen(allopt[i].name))) {
+                matchidx = i;
+                got_match = TRUE;
             }
         }
-    if (flags.dark_room && iflags.use_color)
-        g.showsyms[S_darkroom] = g.showsyms[S_room];
-    else
-        g.showsyms[S_darkroom] = g.showsyms[SYM_NOTHING + SYM_OFF_X];
-}
-
-/* check whether a user-supplied option string is a proper leading
-   substring of a particular option name; option string might have
-   a colon or equals sign and arbitrary value appended to it */
-boolean
-match_optname(user_string, opt_name, min_length, val_allowed)
-const char *user_string, *opt_name;
-int min_length;
-boolean val_allowed;
-{
-    int len = (int) strlen(user_string);
 
-    if (val_allowed) {
-        const char *p = index(user_string, ':'),
-                   *q = index(user_string, '=');
+        if (!got_match) {
+            if (has_val && !allopt[i].valok)
+                continue;
+        }
 
-        if (!p || (q && q < p))
-            p = q;
-        if (p) {
-            /* 'user_string' hasn't necessarily been through mungspaces()
-               so might have tabs or consecutive spaces */
-            while (p > user_string && isspace((uchar) *(p - 1)))
-                p--;
-            len = (int) (p - user_string);
+        /*
+         * During option initialization, the function
+         *     determine_ambiguities()
+         * figured out exactly how many characters are required to
+         * unambiguously differentiate one option from all others, and it
+         * placed that number into each option's alloption[n].minmatch.
+         *
+         */
+        if (!got_match)
+            got_match = match_optname(opts, allopt[i].name, allopt[i].minmatch,
+                                      allopt[i].valok);
+        if (got_match) {
+            if (!allopt[i].pfx && optlen < allopt[i].minmatch) {
+                config_error_add(
+              "Ambiguous option %s, %d characters are needed to differentiate",
+                             opts, allopt[i].minmatch);
+                break;
+            }
+            matchidx = i;
+            break;
         }
     }
 
-    return (boolean) (len >= min_length
-                      && !strncmpi(opt_name, user_string, len));
-}
+    if (!got_match) {
+        /* spin through the aliases to see if there's a match in those.
+           Note that if multiple delimited aliases for the same option
+           becomes desireable in the future, this is where you'll need
+           to split a delimited allopt[i].alias field into each
+           individual alias */
 
-/* most environment variables will eventually be printed in an error
- * message if they don't work, and most error message paths go through
- * BUFSZ buffers, which could be overflowed by a maliciously long
- * environment variable.  If a variable can legitimately be long, or
- * if it's put in a smaller buffer, the responsible code will have to
- * bounds-check itself.
- */
-char *
-nh_getenv(ev)
-const char *ev;
-{
-    char *getev = getenv(ev);
-
-    if (getev && strlen(getev) <= (BUFSZ / 2))
-        return getev;
-    else
-        return (char *) 0;
-}
-
-/* process options, possibly including SYSCF */
-void
-initoptions()
-{
-    initoptions_init();
-#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. */
-    if (!read_config_file(SYSCF_FILE, SET_IN_SYS)) {
-        if (config_error_done() && !iflags.initoptions_noterminate)
-            nh_terminate(EXIT_FAILURE);
+        for (i = 0; i < OPTCOUNT; ++i) {
+            if (!allopt[i].alias)
+                continue;
+            got_match = match_optname(opts, allopt[i].alias,
+                                      (int) strlen(allopt[i].alias),
+                                      allopt[i].valok);
+            if (got_match) {
+                matchidx = i;
+                break;
+            }
+        }
     }
-    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();
-}
 
-void
-initoptions_init()
-{
-#if (defined(UNIX) || defined(VMS)) && defined(TTY_GRAPHICS)
-    char *opts;
-#endif
-    int i;
+    /* allow optfn's to test whether they were called from parseoptions() */
+    g.program_state.in_parseoptions++;
 
-    memcpy(boolopt, boolopt_init, sizeof(boolopt));
+    if (got_match && matchidx >= 0) {
+        duplicate = duplicate_opt_detection(opts, 1);
+        if (duplicate && !allopt[matchidx].dupeok)
+            complain_about_duplicate(opts, 1);
 
-    /* set up the command parsing */
-    reset_commands(TRUE); /* init */
+        /* check for bad negation, so option functions don't have to */
+        if (negated && !allopt[matchidx].negateok) {
+            bad_negation(allopt[matchidx].name, TRUE);
+            return optn_err;
+        }
 
-    /* initialize the random number generator(s) */
-    init_random(rn2);
-    init_random(rn2_on_display_rng);
+        /*
+         * Now call the option's associated function via the
+         * function pointer for it in the allopt[] array.
+         */
+        if (allopt[matchidx].optfn) {
+            op = string_for_opt(opts, TRUE);
+            optresult = (*allopt[matchidx].optfn)(allopt[matchidx].idx, do_set,
+                                                  negated, opts, op);
+        }
+    }
 
-    /* for detection of configfile options specified multiple times */
-    iflags.opt_booldup = iflags.opt_compdup = (int *) 0;
+    if (g.program_state.in_parseoptions > 0)
+        g.program_state.in_parseoptions--;
 
-    for (i = 0; boolopt[i].name; i++) {
-        if (boolopt[i].addr)
-            *(boolopt[i].addr) = boolopt[i].initvalue;
+    if (!got_match) {
+        int res = check_misc_menu_command(opts, op);
+
+        if (res >= 0)
+            optresult = spcfn_misc_menu_cmd(res, FALSE, negated, opts, op);
+        if (optresult == optn_ok)
+            got_match = TRUE;
     }
-    condopt(0, (boolean *) 0, 0);  /* make the choices match defaults */
-#ifdef SYSFLAGS
-    Strcpy(sysflags.sysflagsid, "sysflags");
-    sysflags.sysflagsid[9] = (char) sizeof (struct sysflag);
-#endif
-    flags.end_own = FALSE;
-    flags.end_top = 3;
-    flags.end_around = 2;
-    flags.paranoia_bits = PARANOID_PRAY; /* old prayconfirm=TRUE */
-    flags.pile_limit = PILE_LIMIT_DFLT;  /* 5 */
-    flags.runmode = RUN_LEAP;
-    iflags.msg_history = 20;
-    /* msg_window has conflicting defaults for multi-interface binary */
-#ifdef TTY_GRAPHICS
-    iflags.prevmsg_window = 's';
-#else
-#ifdef CURSES_GRAPHICS
-    iflags.prevmsg_window = 'r';
-#endif
-#endif
 
-    iflags.menu_headings = ATR_INVERSE;
-    iflags.getpos_coords = GPCOORDS_NONE;
+    if (!got_match) {
+        /* Is it a symbol? */
+        if (strstr(opts, "S_") == opts && parsesymbols(opts, PRIMARY)) {
+            switch_symbols(TRUE);
+            check_gold_symbol();
+            optresult = optn_ok;
+        }
+    }
 
-    /* hero's role, race, &c haven't been chosen yet */
-    flags.initrole = flags.initrace = flags.initgend = flags.initalign
-        = ROLE_NONE;
+    if (optresult == optn_ok)
+        return retval;
 
-    init_ov_primary_symbols();
-    init_ov_rogue_symbols();
-    /* Set the default monster and object class symbols. */
-    init_symbols();
-    for (i = 0; i < WARNCOUNT; i++)
-        g.warnsyms[i] = def_warnsyms[i].sym;
+    /* out of valid options */
+    config_error_add("Unknown option '%s'", opts);
+    return FALSE;
+}
 
-    /* assert( sizeof flags.inv_order == sizeof def_inv_order ); */
-    (void) memcpy((genericptr_t) flags.inv_order,
-                  (genericptr_t) def_inv_order, sizeof flags.inv_order);
-    flags.pickup_types[0] = '\0';
-    flags.pickup_burden = MOD_ENCUMBER;
-    flags.sortloot = 'l'; /* sort only loot by default */
+int
+check_misc_menu_command(opts, op)
+char *opts;
+char *op UNUSED;
+{
+    int i;
+    const char *name_to_check;
 
-    for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++)
-        flags.end_disclose[i] = DISCLOSE_PROMPT_DEFAULT_NO;
-    switch_symbols(FALSE); /* set default characters */
-    init_rogue_symbols();
-#if defined(UNIX) && defined(TTY_GRAPHICS)
-    /*
-     * Set defaults for some options depending on what we can
-     * detect about the environment's capabilities.
-     * This has to be done after the global initialization above
-     * and before reading user-specific initialization via
-     * config file/environment variable below.
-     */
-    /* this detects the IBM-compatible console on most 386 boxes */
-    if ((opts = nh_getenv("TERM")) && !strncmp(opts, "AT", 2)) {
-        if (!g.symset[PRIMARY].explicitly)
-            load_symset("IBMGraphics", PRIMARY);
-        if (!g.symset[ROGUESET].explicitly)
-            load_symset("RogueIBM", ROGUESET);
-        switch_symbols(TRUE);
-#ifdef TEXTCOLOR
-        iflags.use_color = TRUE;
-#endif
-    }
-#endif /* UNIX && TTY_GRAPHICS */
-#if defined(UNIX) || defined(VMS)
-#ifdef TTY_GRAPHICS
-    /* detect whether a "vt" terminal can handle alternate charsets */
-    if ((opts = nh_getenv("TERM"))
-        /* [could also check "xterm" which emulates vtXXX by default] */
-        && !strncmpi(opts, "vt", 2)
-        && AS && AE && index(AS, '\016') && index(AE, '\017')) {
-        if (!g.symset[PRIMARY].explicitly)
-            load_symset("DECGraphics", PRIMARY);
-        switch_symbols(TRUE);
+    /* check for menu command mapping */
+    for (i = 0; i < SIZE(default_menu_cmd_info); i++) {
+        name_to_check = default_menu_cmd_info[i].name;
+        if (match_optname(opts, name_to_check,
+                          (int) strlen(name_to_check), TRUE))
+            return i;
     }
-#endif
-#endif /* UNIX || VMS */
-
-#if defined(MSDOS) || defined(WIN32)
-    /* Use IBM defaults. Can be overridden via config file */
-    if (!g.symset[PRIMARY].explicitly)
-        load_symset("IBMGraphics_2", PRIMARY);
-    if (!g.symset[ROGUESET].explicitly)
-        load_symset("RogueEpyx", ROGUESET);
-#endif
-#ifdef MAC_GRAPHICS_ENV
-    if (!g.symset[PRIMARY].explicitly)
-        load_symset("MACGraphics", PRIMARY);
-    switch_symbols(TRUE);
-#endif /* MAC_GRAPHICS_ENV */
-    flags.menu_style = MENU_FULL;
+    return -1;
+}
 
-    iflags.wc_align_message = ALIGN_TOP;
-    iflags.wc_align_status = ALIGN_BOTTOM;
-    /* used by tty and curses */
-    iflags.wc2_statuslines = 2;
-    /* only used by curses */
-    iflags.wc2_windowborders = 2; /* 'Auto' */
+/*
+ **********************************
+ *
+ *   Per-option Functions
+ *
+ **********************************
+ */
 
-    /* since this is done before init_objects(), do partial init here */
-    objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD;
-    nmcpy(g.pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
+int
+optfn_align(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        if (parse_role_opts(negated, allopt[optidx].name, opts, &op)) {
+            if ((flags.initalign = str2align(op)) == ROLE_NONE) {
+                config_error_add("Unknown %s '%s'", allopt[optidx].name, op);
+                return optn_err;
+            }
+        } else
+            return optn_err;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", rolestring(flags.initalign, aligns, adj));
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-void
-initoptions_finish()
+
+int
+optfn_align_message(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
 {
-    nhsym sym = 0;
-#ifndef MAC
-    char *opts = getenv("NETHACKOPTIONS");
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* WINCAP align_message:[left|top|right|bottom] */
 
-    if (!opts)
-        opts = getenv("HACKOPTIONS");
-    if (opts) {
-        if (*opts == '/' || *opts == '\\' || *opts == '@') {
-            if (*opts == '@')
-                opts++; /* @filename */
-            /* looks like a filename */
-            if (strlen(opts) < BUFSZ / 2) {
-                config_error_init(TRUE, opts, CONFIG_ERROR_SECURE);
-                read_config_file(opts, SET_IN_FILE);
-                config_error_done();
+        op = string_for_opt(opts, negated);
+        if ((op != empty_optstr) && !negated) {
+            if (!strncmpi(op, "left", sizeof "left" - 1))
+                iflags.wc_align_message = ALIGN_LEFT;
+            else if (!strncmpi(op, "top", sizeof "top" - 1))
+                iflags.wc_align_message = ALIGN_TOP;
+            else if (!strncmpi(op, "right", sizeof "right" - 1))
+                iflags.wc_align_message = ALIGN_RIGHT;
+            else if (!strncmpi(op, "bottom", sizeof "bottom" - 1))
+                iflags.wc_align_message = ALIGN_BOTTOM;
+            else {
+                config_error_add("Unknown %s parameter '%s'",
+                                 allopt[optidx].name, op);
+                return optn_err;
             }
-        } else {
-            config_error_init(TRUE, (char *) 0, FALSE);
-            read_config_file((char *) 0, SET_IN_FILE);
-            config_error_done();
-            /* let the total length of options be long;
-             * parseoptions() will check each individually
-             */
-            config_error_init(FALSE, "NETHACKOPTIONS", FALSE);
-            (void) parseoptions(opts, TRUE, FALSE);
-            config_error_done();
+        } else if (negated) {
+            bad_negation(allopt[optidx].name, TRUE);
+            return optn_err;
         }
-    } else
-#endif /* !MAC */
-    /*else*/ {
-        config_error_init(TRUE, (char *) 0, FALSE);
-        read_config_file((char *) 0, SET_IN_FILE);
-        config_error_done();
+        return optn_ok;
     }
+    if (req == get_val) {
+        int which;
 
-    (void) fruitadd(g.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 [better than "\033"].
-     */
-    obj_descr[SLIME_MOLD].oc_name = "fruit";
-
-    sym = get_othersym(SYM_BOULDER,
-                Is_rogue_level(&u.uz) ? ROGUESET : PRIMARY);
-    if (sym)
-        g.showsyms[SYM_BOULDER + SYM_OFF_X] = sym;
-    reglyph_darkroom();
-
-#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("safe-startup")) {
-        if (iflags.hilite_delta && !wc2_supported("statushilites")) {
-            raw_printf("Status highlighting not supported for %s interface.",
-                       windowprocs.name);
-            iflags.hilite_delta = 0;
-        }
+        if (!opts)
+            return optn_err;
+        which = iflags.wc_align_message;
+        Sprintf(opts, "%s",
+                (which == ALIGN_TOP) ? "top"
+                : (which == ALIGN_LEFT) ? "left"
+                  : (which == ALIGN_BOTTOM) ? "bottom"
+                    : (which == ALIGN_RIGHT) ? "right"
+                      : defopt);
+        return optn_ok;
     }
-#endif
-    return;
-}
-
-/* copy up to maxlen-1 characters; 'dest' must be able to hold maxlen;
-   treat comma as alternate end of 'src' */
-static void
-nmcpy(dest, src, maxlen)
-char *dest;
-const char *src;
-int maxlen;
-{
-    int count;
-
-    for (count = 1; count < maxlen; count++) {
-        if (*src == ',' || *src == '\0')
-            break; /*exit on \0 terminator*/
-        *dest++ = *src++;
+    if (req == do_handler) {
+        return handler_align_misc(optidx);
     }
-    *dest = '\0';
+    return optn_ok;
 }
 
-/*
- * escapes(): escape expansion for showsyms.  C-style escapes understood
- * include \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal).
- * (Note: unlike in C, leading digit 0 is not used to indicate octal;
- * the letter o (either upper or lower case) is used for that.
- * The ^-prefix for control characters is also understood, and \[mM]
- * has the effect of 'meta'-ing the value which follows (so that the
- * alternate character set will be enabled).
- *
- * X     normal key X
- * ^X    control-X
- * \mX   meta-X
- *
- * For 3.4.3 and earlier, input ending with "\M", backslash, or caret
- * prior to terminating '\0' would pull that '\0' into the output and then
- * keep processing past it, potentially overflowing the output buffer.
- * Now, trailing \ or ^ will act like \\ or \^ and add '\\' or '^' to the
- * output and stop there; trailing \M will fall through to \<other> and
- * yield 'M', then stop.  Any \X or \O followed by something other than
- * an appropriate digit will also fall through to \<other> and yield 'X'
- * or 'O', plus stop if the non-digit is end-of-string.
- */
-static void
-escapes(cp, tp)
-const char *cp; /* might be 'tp', updating in place */
-char *tp; /* result is never longer than 'cp' */
+int
+optfn_align_status(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
 {
-    static NEARDATA const char oct[] = "01234567", dec[] = "0123456789",
-                               hex[] = "00112233445566778899aAbBcCdDeEfF";
-    const char *dp;
-    int cval, meta, dcount;
-
-    while (*cp) {
-        /* \M has to be followed by something to do meta conversion,
-           otherwise it will just be \M which ultimately yields 'M' */
-        meta = (*cp == '\\' && (cp[1] == 'm' || cp[1] == 'M') && cp[2]);
-        if (meta)
-            cp += 2;
-
-        cval = dcount = 0; /* for decimal, octal, hexadecimal cases */
-        if ((*cp != '\\' && *cp != '^') || !cp[1]) {
-            /* simple character, or nothing left for \ or ^ to escape */
-            cval = *cp++;
-        } else if (*cp == '^') { /* expand control-character syntax */
-            cval = (*++cp & 0x1f);
-            ++cp;
-
-        /* remaining cases are all for backslash; we know cp[1] is not \0 */
-        } else if (index(dec, cp[1])) {
-            ++cp; /* move past backslash to first digit */
-            do {
-                cval = (cval * 10) + (*cp - '0');
-            } while (*++cp && index(dec, *cp) && ++dcount < 3);
-        } else if ((cp[1] == 'o' || cp[1] == 'O') && cp[2]
-                   && index(oct, cp[2])) {
-            cp += 2; /* move past backslash and 'O' */
-            do {
-                cval = (cval * 8) + (*cp - '0');
-            } while (*++cp && index(oct, *cp) && ++dcount < 3);
-        } else if ((cp[1] == 'x' || cp[1] == 'X') && cp[2]
-                   && (dp = index(hex, cp[2])) != 0) {
-            cp += 2; /* move past backslash and 'X' */
-            do {
-                cval = (cval * 16) + ((int) (dp - hex) / 2);
-            } while (*++cp && (dp = index(hex, *cp)) != 0 && ++dcount < 2);
-        } else { /* C-style character escapes */
-            switch (*++cp) {
-            case '\\':
-                cval = '\\';
-                break;
-            case 'n':
-                cval = '\n';
-                break;
-            case 't':
-                cval = '\t';
-                break;
-            case 'b':
-                cval = '\b';
-                break;
-            case 'r':
-                cval = '\r';
-                break;
-            default:
-                cval = *cp;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* WINCAP align_status:[left|top|right|bottom] */
+        op = string_for_opt(opts, negated);
+        if ((op != empty_optstr) && !negated) {
+            if (!strncmpi(op, "left", sizeof "left" - 1))
+                iflags.wc_align_status = ALIGN_LEFT;
+            else if (!strncmpi(op, "top", sizeof "top" - 1))
+                iflags.wc_align_status = ALIGN_TOP;
+            else if (!strncmpi(op, "right", sizeof "right" - 1))
+                iflags.wc_align_status = ALIGN_RIGHT;
+            else if (!strncmpi(op, "bottom", sizeof "bottom" - 1))
+                iflags.wc_align_status = ALIGN_BOTTOM;
+            else {
+                config_error_add("Unknown %s parameter '%s'",
+                                 allopt[optidx].name, op);
+                return optn_err;
             }
-            ++cp;
+        } else if (negated) {
+            bad_negation(allopt[optidx].name, TRUE);
+            return optn_err;
         }
-
-        if (meta)
-            cval |= 0x80;
-        *tp++ = (char) cval;
+        return optn_ok;
     }
-    *tp = '\0';
-}
+    if (req == get_val) {
+        int which;
 
-static void
-rejectoption(optname)
-const char *optname;
-{
-#ifdef MICRO
-    pline("\"%s\" settable only from %s.", optname, configfile);
-#else
-    pline("%s can be set only from NETHACKOPTIONS or %s.", optname,
-          configfile);
-#endif
+        if (!opts)
+            return optn_err;
+        which = iflags.wc_align_status;
+        Sprintf(opts, "%s",
+                (which == ALIGN_TOP) ? "top"
+                : (which == ALIGN_LEFT) ? "left"
+                  : (which == ALIGN_BOTTOM) ? "bottom"
+                    : (which == ALIGN_RIGHT) ? "right"
+                      : defopt);
+        return optn_ok;
+    }
+    if (req == do_handler) {
+        return handler_align_misc(optidx);
+    }
+    return optn_ok;
 }
 
-/*
-
-# errors:
-OPTIONS=aaaaaaaaaa[ more than 247 (255 - 8 for 'OPTIONS=') total ]aaaaaaaaaa
-OPTIONS
-OPTIONS=
-MSGTYPE=stop"You swap places with "
-MSGTYPE=st.op "You swap places with "
-MSGTYPE=stop "You swap places with \"
-MENUCOLOR=" blessed "green&none
-MENUCOLOR=" holy " = green&reverse
-MENUCOLOR=" cursed " = red&uline
-MENUCOLOR=" unholy " = reed
-OPTIONS=!legacy:true,fooo
-OPTIONS=align:!pin
-OPTIONS=gender
-
-*/
-
-static char *
-string_for_opt(opts, val_optional)
+int
+optfn_altkeyhandler(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
 char *opts;
-boolean val_optional;
+char *op UNUSED;
 {
-    char *colon, *equals;
-
-    colon = index(opts, ':');
-    equals = index(opts, '=');
-    if (!colon || (equals && equals < colon))
-        colon = equals;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* altkeyhandler:string */
 
-    if (!colon || !*++colon) {
-        if (!val_optional)
-            config_error_add("Missing parameter for '%s'", opts);
-        return empty_optstr;
+#if defined(WIN32) && defined(TTY_GRAPHICS)
+        if (op != empty_optstr) {
+            set_altkeyhandler(op);
+        } else {
+            return optn_err;
+        }
+#endif
+        return optn_ok;
     }
-    return colon;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+#ifdef WIN32
+        Sprintf(opts, "%s",
+                iflags.altkeyhandler[0] ? iflags.altkeyhandler : "default");
+#endif
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-static char *
-string_for_env_opt(optname, opts, val_optional)
-const char *optname;
+int
+optfn_altmeta(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
 char *opts;
-boolean val_optional;
+char *op UNUSED;
 {
-    if (!g.opt_initial) {
-        rejectoption(optname);
-        return empty_optstr;
-    }
-    return string_for_opt(opts, val_optional);
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* Amiga altmeta causes Alt+key to be converted into Meta+key by
+           low level nethack code; on by default, can be toggled off if
+           Alt+key is needed for some ASCII chars on non-ASCII keyboard */
+
+        /* non-Amiga altmeta causes nethack's top level command loop to treat
+           two character sequence "ESC c" as M-c, for terminals or emulators
+           which send "ESC c" when Alt+c is pressed; off by default, enabling
+           this can potentially make trouble if user types ESC when nethack
+           is honoring this conversion request (primarily after starting a
+           count prefix prior to a command and then deciding to cancel it) */
+
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+        return optn_err;
+    }
+    return optn_ok;
 }
 
-static void
-bad_negation(optname, with_parameter)
-const char *optname;
-boolean with_parameter;
+int
+optfn_boulder(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
 {
-    pline_The("%s option may not %sbe negated.", optname,
-              with_parameter ? "both have a value and " : "");
-}
+#ifdef BACKWARD_COMPAT
+    int clash = 0;
+#endif
 
-/*
- * Change the inventory order, using the given string as the new order.
- * Missing characters in the new order are filled in at the end from
- * the current inv_order, except for gold, which is forced to be first
- * if not explicitly present.
- *
- * This routine returns 1 unless there is a duplicate or bad char in
- * the string.
- */
-static int
-change_inv_order(op)
-char *op;
-{
-    int oc_sym, num;
-    char *sp, buf[QBUFSZ];
-    int retval = 1;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* boulder:symbol */
 
-    num = 0;
-    if (!index(op, GOLD_SYM))
-        buf[num++] = COIN_CLASS;
+#ifdef BACKWARD_COMPAT
 
-    for (sp = op; *sp; sp++) {
-        boolean fail = FALSE;
-        oc_sym = def_char_to_objclass(*sp);
-        /* reject bad or duplicate entries */
-        if (oc_sym == MAXOCLASSES) { /* not an object class char */
-            config_error_add("Not an object class '%c'", *sp);
-            retval = 0;
-            fail = TRUE;
-        } else if (!index(flags.inv_order, oc_sym)) {
-            /* VENOM_CLASS, RANDOM_CLASS, and ILLOBJ_CLASS are excluded
-               because they aren't in def_inv_order[] so don't make it
-               into flags.inv_order, hence always fail this index() test */
-            config_error_add("Object class '%c' not allowed", *sp);
-            retval = 0;
-            fail = TRUE;
-        } else if (index(sp + 1, *sp)) {
-            config_error_add("Duplicate object class '%c'", *sp);
-            retval = 0;
-            fail = TRUE;
+        /* if ((opts = string_for_env_opt(allopt[optidx].name, opts, FALSE))
+                                                                          ==
+           empty_optstr)
+         */
+        if ((opts = string_for_opt(opts, FALSE)) == empty_optstr)
+            return FALSE;
+        escapes(opts, opts);
+        /* note: dummy monclass #0 has symbol value '\0'; we allow that--
+           attempting to set bouldersym to '^@'/'\0' will reset to default */
+        if (def_char_to_monclass(opts[0]) != MAXMCLASSES)
+            clash = opts[0] ? 1 : 0;
+        else if (opts[0] >= '1' && opts[0] < WARNCOUNT + '0')
+            clash = 2;
+        if (clash) {
+            /* symbol chosen matches a used monster or warning
+               symbol which is not good - reject it */
+            config_error_add("Badoption - boulder symbol '%s' would conflict "
+                             "with a %s symbol",
+                             visctrl(opts[0]),
+                             (clash == 1) ? "monster" : "warning");
+        } else {
+            /*
+             * Override the default boulder symbol.
+             */
+            g.ov_primary_syms[SYM_BOULDER + SYM_OFF_X] = (nhsym) opts[0];
+            g.ov_rogue_syms[SYM_BOULDER + SYM_OFF_X] = (nhsym) opts[0];
+            /* for 'initial', update of BOULDER symbol is done in
+               initoptions_finish(), after all symset options
+               have been processed */
+            if (!g.opt_initial) {
+                nhsym sym = get_othersym(
+                    SYM_BOULDER, Is_rogue_level(&u.uz) ? ROGUESET : PRIMARY);
+
+                if (sym)
+                    g.showsyms[SYM_BOULDER + SYM_OFF_X] = sym;
+                g.opt_need_redraw = TRUE;
+            }
         }
-        /* retain good ones */
-        if (!fail)
-            buf[num++] = (char) oc_sym;
+        return optn_ok;
+#else
+        config_error_add("'%s' no longer supported; use S_boulder:c instead",
+                         allopt[optidx].name);
+        return optn_err;
+#endif
     }
-    buf[num] = '\0';
-
-    /* fill in any omitted classes, using previous ordering */
-    for (sp = flags.inv_order; *sp; sp++)
-        if (!index(buf, *sp))
-            (void) strkitten(&buf[num++], *sp);
-    buf[MAXOCLASSES - 1] = '\0';
-
-    Strcpy(flags.inv_order, buf);
-    return retval;
-}
-
-static boolean
-warning_opts(opts, optype)
-register char *opts;
-const char *optype;
-{
-    uchar translate[WARNCOUNT];
-    int length, i;
-
-    if ((opts = string_for_env_opt(optype, opts, FALSE)) == empty_optstr)
-        return FALSE;
-    escapes(opts, opts);
-
-    length = (int) strlen(opts);
-    /* match the form obtained from PC configuration files */
-    for (i = 0; i < WARNCOUNT; i++)
-        translate[i] = (i >= length) ? 0
-                                     : opts[i] ? (uchar) opts[i]
-                                               : def_warnsyms[i].sym;
-    assign_warnings(translate);
-    return TRUE;
-}
-
-void
-assign_warnings(graph_chars)
-register uchar *graph_chars;
-{
-    int i;
-
-    for (i = 0; i < WARNCOUNT; i++)
-        if (graph_chars[i])
-            g.warnsyms[i] = graph_chars[i];
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+#ifdef BACKWARD_COMPAT
+        Sprintf(opts, "%c",
+                g.ov_primary_syms[SYM_BOULDER + SYM_OFF_X]
+                    ? g.ov_primary_syms[SYM_BOULDER + SYM_OFF_X]
+                    : g.showsyms[(int) objects[BOULDER].oc_class + SYM_OFF_O]);
+#endif
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-static int
-feature_alert_opts(op, optn)
+int
+optfn_catname(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated UNUSED;
+char *opts;
 char *op;
-const char *optn;
 {
-    char buf[BUFSZ];
-    unsigned long fnv = get_feature_notice_ver(op); /* version.c */
-
-    if (fnv == 0L)
-        return 0;
-    if (fnv > get_current_feature_ver()) {
-        if (!g.opt_initial) {
-            You_cant("disable new feature alerts for future versions.");
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
+            != empty_optstr) {
+            nmcpy(g.catname, op, PL_PSIZ);
         } else {
-            config_error_add(
-                        "%s=%s Invalid reference to a future version ignored",
-                             optn, op);
+            return optn_err;
         }
-        return 0;
+        sanitize_name(g.catname);
+        return optn_ok;
     }
-
-    flags.suppress_alert = fnv;
-    if (!g.opt_initial) {
-        Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
-                FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
-        pline(
-          "Feature change alerts disabled for NetHack %s features and prior.",
-              buf);
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", g.catname[0] ? g.catname : none);
+        return optn_ok;
     }
-    return 1;
+    return optn_ok;
 }
 
-void
-set_duplicate_opt_detection(on_or_off)
-int on_or_off;
+int
+optfn_cursesgraphics(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op UNUSED;
 {
-    int k, *optptr;
+#ifdef BACKWARD_COMPAT
+    boolean badflag = FALSE;
+#endif
 
-    if (on_or_off != 0) {
-        /*-- ON --*/
-        if (iflags.opt_booldup)
-            impossible("iflags.opt_booldup already on (memory leak)");
-        iflags.opt_booldup = (int *) alloc(SIZE(boolopt) * sizeof (int));
-        optptr = iflags.opt_booldup;
-        for (k = 0; k < SIZE(boolopt); ++k)
-            *optptr++ = 0;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* "cursesgraphics" */
 
-        if (iflags.opt_compdup)
-            impossible("iflags.opt_compdup already on (memory leak)");
-        iflags.opt_compdup = (int *) alloc(SIZE(compopt) * sizeof (int));
-        optptr = iflags.opt_compdup;
-        for (k = 0; k < SIZE(compopt); ++k)
-            *optptr++ = 0;
-    } else {
-        /*-- OFF --*/
-        if (iflags.opt_booldup)
-            free((genericptr_t) iflags.opt_booldup);
-        iflags.opt_booldup = (int *) 0;
-        if (iflags.opt_compdup)
-            free((genericptr_t) iflags.opt_compdup);
-        iflags.opt_compdup = (int *) 0;
+#ifdef BACKWARD_COMPAT
+        if (!negated) {
+            /* There is no rogue level cursesgraphics-specific set */
+            if (g.symset[PRIMARY].name) {
+                badflag = TRUE;
+            } else {
+                g.symset[PRIMARY].name = dupstr(allopt[optidx].name);
+                if (!read_sym_file(PRIMARY)) {
+                    badflag = TRUE;
+                    clear_symsetentry(PRIMARY, TRUE);
+                } else
+                    switch_symbols(TRUE);
+            }
+            if (badflag) {
+                config_error_add("Failure to load symbol set %s.",
+                                 allopt[optidx].name);
+                return optn_err;
+            }
+        }
+        return optn_ok;
+#else
+        config_error_add("'%s' no longer supported; use 'symset:%s' instead",
+                         allopt[optidx].name, allopt[optidx].name);
+        return optn_err;
+#endif
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+        return optn_ok;
     }
+    return optn_ok;
 }
 
-static boolean
-duplicate_opt_detection(opts, iscompound)
-const char *opts;
-int iscompound; /* 0 == boolean option, 1 == compound */
+int
+optfn_DECgraphics(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op UNUSED;
 {
-    int i, *optptr;
+#ifdef BACKWARD_COMPAT
+    boolean badflag = FALSE;
+#endif
 
-    if (!iscompound && iflags.opt_booldup && g.opt_initial && g.opt_from_file) {
-        for (i = 0; boolopt[i].name; i++) {
-            if (match_optname(opts, boolopt[i].name, 3, FALSE)) {
-                optptr = iflags.opt_booldup + i;
-                *optptr += 1;
-                if (*optptr > 1)
-                    return TRUE;
-                else
-                    return FALSE;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* "DECgraphics" */
+
+#ifdef BACKWARD_COMPAT
+        if (!negated) {
+            /* There is no rogue level DECgraphics-specific set */
+            if (g.symset[PRIMARY].name) {
+                badflag = TRUE;
+            } else {
+                g.symset[PRIMARY].name = dupstr(allopt[optidx].name);
+                if (!read_sym_file(PRIMARY)) {
+                    badflag = TRUE;
+                    clear_symsetentry(PRIMARY, TRUE);
+                } else
+                    switch_symbols(TRUE);
             }
-        }
-    } else if (iscompound && iflags.opt_compdup && g.opt_initial && g.opt_from_file) {
-        for (i = 0; compopt[i].name; i++) {
-            if (match_optname(opts, compopt[i].name, strlen(compopt[i].name),
-                              TRUE)) {
-                optptr = iflags.opt_compdup + i;
-                *optptr += 1;
-                if (*optptr > 1)
-                    return TRUE;
-                else
-                    return FALSE;
+            if (badflag) {
+                config_error_add("Failure to load symbol set %s.",
+                                 allopt[optidx].name);
+                return optn_err;
             }
         }
+        return optn_ok;
+#else
+        config_error_add("'%s' no longer supported; use 'symset:%s' instead",
+                         allopt[optidx].name, allopt[optidx].name);
+        return optn_err;
+#endif
     }
-    return FALSE;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-static void
-complain_about_duplicate(opts, iscompound)
-const char *opts;
-int iscompound; /* 0 == boolean option, 1 == compound */
+int
+optfn_disclose(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
 {
-#ifdef MAC
-    /* the Mac has trouble dealing with the output of messages while
-     * processing the config file.  That should get fixed one day.
-     * For now just return.
-     */
-#else /* !MAC */
-    config_error_add("%s option specified multiple times: %s",
-                     iscompound ? "compound" : "boolean", opts);
-#endif /* ?MAC */
-    return;
-}
+    int i, idx, prefix_val;
+    unsigned num;
 
-/* paranoia[] - used by parseoptions() and special_handling() */
-static const struct paranoia_opts {
-    int flagmask;        /* which paranoid option */
-    const char *argname; /* primary name */
-    int argMinLen;       /* minimum number of letters to match */
-    const char *synonym; /* alternate name (optional) */
-    int synMinLen;
-    const char *explain; /* for interactive menu */
-} paranoia[] = {
-    /* there are some initial-letter conflicts: "a"ttack vs "a"ll, "attack"
-       takes precedence and "all" isn't present in the interactive menu,
-       and "d"ie vs "d"eath, synonyms for each other so doesn't matter;
-       (also "p"ray vs "P"aranoia, "pray" takes precedence since "Paranoia"
-       is just a synonym for "Confirm"); "b"ones vs "br"eak-wand, the
-       latter requires at least two letters; "e"at vs "ex"plore,
-       "cont"inue eating vs "C"onfirm; "wand"-break vs "Were"-change,
-       both require at least two letters during config processing and use
-       case-senstivity for 'O's interactive menu */
-    { PARANOID_CONFIRM, "Confirm", 1, "Paranoia", 2,
-      "for \"yes\" confirmations, require \"no\" to reject" },
-    { PARANOID_QUIT, "quit", 1, "explore", 2,
-      "yes vs y to quit or to enter explore mode" },
-    { PARANOID_DIE, "die", 1, "death", 2,
-      "yes vs y to die (explore mode or debug mode)" },
-    { PARANOID_BONES, "bones", 1, 0, 0,
-      "yes vs y to save bones data when dying in debug mode" },
-    { PARANOID_HIT, "attack", 1, "hit", 1,
-      "yes vs y to attack a peaceful monster" },
-    { PARANOID_BREAKWAND, "wand-break", 2, "break-wand", 2,
-      "yes vs y to break a wand via (a)pply" },
-    { PARANOID_EATING, "eat", 1, "continue", 4,
-      "yes vs y to continue eating after first bite when satiated" },
-    { PARANOID_WERECHANGE, "Were-change", 2, (const char *) 0, 0,
-      "yes vs y to change form when lycanthropy is controllable" },
-    { PARANOID_PRAY, "pray", 1, 0, 0,
-      "y to pray (supersedes old \"prayconfirm\" option)" },
-    { PARANOID_REMOVE, "Remove", 1, "Takeoff", 1,
-      "always pick from inventory for Remove and Takeoff" },
-    /* for config file parsing; interactive menu skips these */
-    { 0, "none", 4, 0, 0, 0 }, /* require full word match */
-    { ~0, "all", 3, 0, 0, 0 }, /* ditto */
-};
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* things to disclose at end of game */
 
-static const struct {
-    const char *name;
-    const int color;
-} colornames[] = {
-    { "black", CLR_BLACK },
-    { "red", CLR_RED },
-    { "green", CLR_GREEN },
-    { "brown", CLR_BROWN },
-    { "blue", CLR_BLUE },
-    { "magenta", CLR_MAGENTA },
-    { "cyan", CLR_CYAN },
-    { "gray", CLR_GRAY },
-    { "orange", CLR_ORANGE },
-    { "light green", CLR_BRIGHT_GREEN },
-    { "yellow", CLR_YELLOW },
-    { "light blue", CLR_BRIGHT_BLUE },
-    { "light magenta", CLR_BRIGHT_MAGENTA },
-    { "light cyan", CLR_BRIGHT_CYAN },
-    { "white", CLR_WHITE },
-    { "no color", NO_COLOR },
-    { NULL, CLR_BLACK }, /* everything after this is an alias */
-    { "transparent", NO_COLOR },
-    { "purple", CLR_MAGENTA },
-    { "light purple", CLR_BRIGHT_MAGENTA },
-    { "bright purple", CLR_BRIGHT_MAGENTA },
-    { "grey", CLR_GRAY },
-    { "bright red", CLR_ORANGE },
-    { "bright green", CLR_BRIGHT_GREEN },
-    { "bright blue", CLR_BRIGHT_BLUE },
-    { "bright magenta", CLR_BRIGHT_MAGENTA },
-    { "bright cyan", CLR_BRIGHT_CYAN }
-};
+        /*
+         * The order that the end_disclose options are stored:
+         *      inventory, attribs, vanquished, genocided,
+         *      conduct, overview.
+         * There is an array in flags:
+         *      end_disclose[NUM_DISCLOSURE_OPT];
+         * with option settings for the each of the following:
+         * iagvc [see disclosure_options in decl.c]:
+         * Allowed setting values in that array are:
+         *      DISCLOSE_PROMPT_DEFAULT_YES  ask with default answer yes
+         *      DISCLOSE_PROMPT_DEFAULT_NO   ask with default answer no
+         *      DISCLOSE_YES_WITHOUT_PROMPT  always disclose and don't ask
+         *      DISCLOSE_NO_WITHOUT_PROMPT   never disclose and don't ask
+         *      DISCLOSE_PROMPT_DEFAULT_SPECIAL  for 'vanquished' only...
+         *      DISCLOSE_SPECIAL_WITHOUT_PROMPT  ...to set up sort order.
+         *
+         * Those setting values can be used in the option
+         * string as a prefix to get the desired behaviour.
+         *
+         * For backward compatibility, no prefix is required,
+         * and the presence of a i,a,g,v, or c without a prefix
+         * sets the corresponding value to DISCLOSE_YES_WITHOUT_PROMPT.
+         */
 
-static const struct {
-    const char *name;
-    const int attr;
-} attrnames[] = {
-    { "none", ATR_NONE },
-    { "bold", ATR_BOLD },
-    { "dim", ATR_DIM },
-    { "underline", ATR_ULINE },
-    { "blink", ATR_BLINK },
-    { "inverse", ATR_INVERSE },
-    { NULL, ATR_NONE }, /* everything after this is an alias */
-    { "normal", ATR_NONE },
-    { "uline", ATR_ULINE },
-    { "reverse", ATR_INVERSE },
-};
+        op = string_for_opt(opts, TRUE);
+        if (op != empty_optstr && negated) {
+            bad_negation(allopt[optidx].name, TRUE);
+            return optn_err;
+        }
+        /* "disclose" without a value means "all with prompting"
+           and negated means "none without prompting" */
+        if (op == empty_optstr || !strcmpi(op, "all")
+            || !strcmpi(op, "none")) {
+            if (op != empty_optstr && !strcmpi(op, "none"))
+                negated = TRUE;
+            for (num = 0; num < NUM_DISCLOSURE_OPTIONS; num++)
+                flags.end_disclose[num] = negated
+                                              ? DISCLOSE_NO_WITHOUT_PROMPT
+                                              : DISCLOSE_PROMPT_DEFAULT_YES;
+            return optn_ok;
+        }
 
-const char *
-clr2colorname(clr)
-int clr;
-{
-    int i;
+        num = 0;
+        prefix_val = -1;
+        while (*op && num < sizeof flags.end_disclose - 1) {
+            static char valid_settings[] = { DISCLOSE_PROMPT_DEFAULT_YES,
+                                             DISCLOSE_PROMPT_DEFAULT_NO,
+                                             DISCLOSE_PROMPT_DEFAULT_SPECIAL,
+                                             DISCLOSE_YES_WITHOUT_PROMPT,
+                                             DISCLOSE_NO_WITHOUT_PROMPT,
+                                             DISCLOSE_SPECIAL_WITHOUT_PROMPT,
+                                             '\0' };
+            register char c, *dop;
 
-    for (i = 0; i < SIZE(colornames); i++)
-        if (colornames[i].name && colornames[i].color == clr)
-            return colornames[i].name;
-    return (char *) 0;
+            c = lowc(*op);
+            if (c == 'k')
+                c = 'v'; /* killed -> vanquished */
+            if (c == 'd')
+                c = 'o'; /* dungeon -> overview */
+            dop = index(disclosure_options, c);
+            if (dop) {
+                idx = (int) (dop - disclosure_options);
+                if (idx < 0 || idx > NUM_DISCLOSURE_OPTIONS - 1) {
+                    impossible("bad disclosure index %d %c", idx, c);
+                    continue;
+                }
+                if (prefix_val != -1) {
+                    if (*dop != 'v') {
+                        if (prefix_val == DISCLOSE_PROMPT_DEFAULT_SPECIAL)
+                            prefix_val = DISCLOSE_PROMPT_DEFAULT_YES;
+                        if (prefix_val == DISCLOSE_SPECIAL_WITHOUT_PROMPT)
+                            prefix_val = DISCLOSE_YES_WITHOUT_PROMPT;
+                    }
+                    flags.end_disclose[idx] = prefix_val;
+                    prefix_val = -1;
+                } else
+                    flags.end_disclose[idx] = DISCLOSE_YES_WITHOUT_PROMPT;
+            } else if (index(valid_settings, c)) {
+                prefix_val = c;
+            } else if (c == ' ') {
+                ; /* do nothing */
+            } else {
+                config_error_add("Unknown %s parameter '%c'",
+                                 allopt[optidx].name, *op);
+                return optn_err;
+            }
+            op++;
+        }
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+
+        opts[0] = '\0';
+        for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
+            if (i)
+                (void) strkitten(opts, ' ');
+            (void) strkitten(opts, flags.end_disclose[i]);
+            (void) strkitten(opts, disclosure_options[i]);
+        }
+        return optn_ok;
+    }
+    if (req == do_handler) {
+        return handler_disclose();
+    }
+    return optn_ok;
 }
 
 int
-match_str2clr(str)
-char *str;
+optfn_dogname(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op;
 {
-    int i, c = CLR_MAX;
-
-    /* allow "lightblue", "light blue", and "light-blue" to match "light blue"
-       (also junk like "_l i-gh_t---b l u e" but we won't worry about that);
-       also copes with trailing space; caller has removed any leading space */
-    for (i = 0; i < SIZE(colornames); i++)
-        if (colornames[i].name
-            && fuzzymatch(str, colornames[i].name, " -_", TRUE)) {
-            c = colornames[i].color;
-            break;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        if (op != empty_optstr) {
+            nmcpy(g.dogname, op, PL_PSIZ);
+        } else {
+            return optn_err;
         }
-    if (i == SIZE(colornames) && (*str >= '0' && *str <= '9'))
-        c = atoi(str);
-
-    if (c == CLR_MAX)
-        config_error_add("Unknown color '%s'", str);
+        sanitize_name(g.dogname);
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", g.dogname[0] ? g.dogname : none);
+        return optn_ok;
+    }
+    return optn_ok;
+}
 
-    return c;
+int
+optfn_dungeon(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", to_be_done);
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-static const char *
-attr2attrname(attr)
-int attr;
+int
+optfn_effects(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
 {
-    int i;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", to_be_done);
+        return optn_ok;
+    }
+    return optn_ok;
+}
 
-    for (i = 0; i < SIZE(attrnames); i++)
-        if (attrnames[i].attr == attr)
-            return attrnames[i].name;
-    return (char *) 0;
+int
+optfn_font_map(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
+{
+    /* send them over to the prefix handling for font_ */
+    return pfxfn_font(optidx, req, negated, opts, op);
 }
 
 int
-match_str2attr(str, complain)
-const char *str;
-boolean complain;
+optfn_font_menu(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
 {
-    int i, a = -1;
+    /* send them over to the prefix handling for font_ */
+    return pfxfn_font(optidx, req, negated, opts, op);
+}
 
-    for (i = 0; i < SIZE(attrnames); i++)
-        if (attrnames[i].name
-            && fuzzymatch(str, attrnames[i].name, " -_", TRUE)) {
-            a = attrnames[i].attr;
-            break;
-        }
+int
+optfn_font_message(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
+{
+    /* send them over to the prefix handling for font_ */
+    return pfxfn_font(optidx, req, negated, opts, op);
+}
 
-    if (a == -1 && complain)
-        config_error_add("Unknown text attribute '%s'", str);
+int
+optfn_font_size_map(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
+{
+    /* send them over to the prefix handling for font_ */
+    return pfxfn_font(optidx, req, negated, opts, op);
+}
 
-    return a;
+int
+optfn_font_size_menu(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
+{
+    /* send them over to the prefix handling for font_ */
+    return pfxfn_font(optidx, req, negated, opts, op);
 }
 
 int
-query_color(prompt)
-const char *prompt;
+optfn_font_size_message(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
 {
-    winid tmpwin;
-    anything any;
-    int i, pick_cnt;
-    menu_item *picks = (menu_item *) 0;
+    /* send them over to the prefix handling for font_ */
+    return pfxfn_font(optidx, req, negated, opts, op);
+}
 
-    tmpwin = create_nhwindow(NHW_MENU);
-    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
-    any = cg.zeroany;
-    for (i = 0; i < SIZE(colornames); i++) {
-        if (!colornames[i].name)
-            break;
-        any.a_int = i + 1;
-        add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, colornames[i].name,
-                 (colornames[i].color == NO_COLOR) ? MENU_ITEMFLAGS_SELECTED
-                                                   : MENU_ITEMFLAGS_NONE);
-    }
-    end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick a color");
-    pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
-    destroy_nhwindow(tmpwin);
-    if (pick_cnt > 0) {
-        i = colornames[picks[0].item.a_int - 1].color;
-        /* pick_cnt==2: explicitly picked something other than the
-           preselected entry */
-        if (pick_cnt == 2 && i == NO_COLOR)
-            i = colornames[picks[1].item.a_int - 1].color;
-        free((genericptr_t) picks);
-        return i;
-    } else if (pick_cnt == 0) {
-        /* pick_cnt==0: explicitly picking preselected entry toggled it off */
-        return NO_COLOR;
-    }
-    return -1;
+int
+optfn_font_size_status(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
+{
+    /* send them over to the prefix handling for font_ */
+    return pfxfn_font(optidx, req, negated, opts, op);
 }
 
-/* ask about highlighting attribute; for menu headers and menu
-   coloring patterns, only one attribute at a time is allowed;
-   for status highlighting, multiple attributes are allowed [overkill;
-   life would be much simpler if that were restricted to one also...] */
 int
-query_attr(prompt)
-const char *prompt;
+optfn_font_size_text(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
 {
-    winid tmpwin;
-    anything any;
-    int i, pick_cnt;
-    menu_item *picks = (menu_item *) 0;
-    boolean allow_many = (prompt && !strncmpi(prompt, "Choose", 6));
-    int default_attr = ATR_NONE;
+    /* send them over to the prefix handling for font_ */
+    return pfxfn_font(optidx, req, negated, opts, op);
+}
 
-    if (prompt && strstri(prompt, "menu headings"))
-        default_attr = iflags.menu_headings;
-    tmpwin = create_nhwindow(NHW_MENU);
-    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
-    any = cg.zeroany;
-    for (i = 0; i < SIZE(attrnames); i++) {
-        if (!attrnames[i].name)
-            break;
-        any.a_int = i + 1;
-        add_menu(tmpwin, NO_GLYPH, &any, 0, 0, attrnames[i].attr,
-                 attrnames[i].name,
-                 (attrnames[i].attr == default_attr) ? MENU_ITEMFLAGS_SELECTED
-                                                     : MENU_ITEMFLAGS_NONE);
+int
+optfn_font_status(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
+{
+    /* send them over to the prefix handling for font_ */
+    return pfxfn_font(optidx, req, negated, opts, op);
+}
+
+int
+optfn_font_text(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
+{
+    /* send them over to the prefix handling for font_ */
+    return pfxfn_font(optidx, req, negated, opts, op);
+}
+
+int
+optfn_fruit(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    struct fruit *forig = 0;
+
+    if (req == do_init) {
+        return optn_ok;
     }
-    end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick an attribute");
-    pick_cnt = select_menu(tmpwin, allow_many ? PICK_ANY : PICK_ONE, &picks);
-    destroy_nhwindow(tmpwin);
-    if (pick_cnt > 0) {
-        int j, k = 0;
+    if (req == do_set) {
+        op = string_for_opt(opts, negated || !g.opt_initial);
+        if (negated) {
+            if (op != empty_optstr) {
+                bad_negation("fruit", TRUE);
+                return optn_err;
+            }
+            op = empty_optstr;
+            goto goodfruit;
+        }
+        if (op == empty_optstr)
+            return optn_err;
+        /* strip leading/trailing spaces, condense internal ones (3.6.2) */
+        mungspaces(op);
+        if (!g.opt_initial) {
+            struct fruit *f;
+            int fnum = 0;
 
-        if (allow_many) {
-            /* PICK_ANY, with one preselected entry (ATR_NONE) which
-               should be excluded if any other choices were picked */
-            for (i = 0; i < pick_cnt; ++i) {
-                j = picks[i].item.a_int - 1;
-                if (attrnames[j].attr != ATR_NONE || pick_cnt == 1) {
-                    switch (attrnames[j].attr) {
-                    case ATR_DIM:
-                        k |= HL_DIM;
-                        break;
-                    case ATR_BLINK:
-                        k |= HL_BLINK;
-                        break;
-                    case ATR_ULINE:
-                        k |= HL_ULINE;
-                        break;
-                    case ATR_INVERSE:
-                        k |= HL_INVERSE;
-                        break;
-                    case ATR_BOLD:
-                        k |= HL_BOLD;
-                        break;
-                    case ATR_NONE:
-                        k = HL_NONE;
-                        break;
-                    }
+            /* count number of named fruits; if 'op' is found among them,
+               then the count doesn't matter because we won't be adding it */
+            f = fruit_from_name(op, FALSE, &fnum);
+            if (!f) {
+                if (!flags.made_fruit)
+                    forig = fruit_from_name(g.pl_fruit, FALSE, (int *) 0);
+
+                if (!forig && fnum >= 100) {
+                    config_error_add(
+                        "Doing that so many times isn't very fruitful.");
+                    return optn_ok;
                 }
             }
-        } else {
-            /* PICK_ONE, but might get 0 or 2 due to preselected entry */
-            j = picks[0].item.a_int - 1;
-            /* pick_cnt==2: explicitly picked something other than the
-               preselected entry */
-            if (pick_cnt == 2 && attrnames[j].attr == default_attr)
-                j = picks[1].item.a_int - 1;
-            k = attrnames[j].attr;
         }
-        free((genericptr_t) picks);
-        return k;
-    } else if (pick_cnt == 0 && !allow_many) {
-        /* PICK_ONE, preselected entry explicitly chosen */
-        return default_attr;
+ goodfruit:
+        nmcpy(g.pl_fruit, op, PL_FSIZ);
+        sanitize_name(g.pl_fruit);
+        /* OBJ_NAME(objects[SLIME_MOLD]) won't work for this after
+           initialization; it gets changed to generic "fruit" */
+        if (!*g.pl_fruit)
+            nmcpy(g.pl_fruit, "slime mold", PL_FSIZ);
+        if (!g.opt_initial) {
+            /* if 'forig' is nonNull, we replace it rather than add
+               a new fruit; it can only be nonNull if no fruits have
+               been created since the previous name was put in place */
+            (void) fruitadd(g.pl_fruit, forig);
+            pline("Fruit is now \"%s\".", g.pl_fruit);
+        }
+        /* If initial, then initoptions is allowed to do it instead
+         * of here (initoptions always has to do it even if there's
+         * no fruit option at all.  Also, we don't want people
+         * setting multiple fruits in their options.)
+         */
+        return optn_ok;
     }
-    /* either ESC to explicitly cancel (pick_cnt==-1) or
-       PICK_ANY with preselected entry toggled off and nothing chosen */
-    return -1;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", g.pl_fruit);
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-static const struct {
-    const char *name;
-    xchar msgtyp;
-    const char *descr;
-} msgtype_names[] = {
-    { "show", MSGTYP_NORMAL, "Show message normally" },
-    { "hide", MSGTYP_NOSHOW, "Hide message" },
-    { "noshow", MSGTYP_NOSHOW, NULL },
-    { "stop", MSGTYP_STOP, "Prompt for more after the message" },
-    { "more", MSGTYP_STOP, NULL },
-    { "norep", MSGTYP_NOREP, "Do not repeat the message" }
-};
-
-static const char *
-msgtype2name(typ)
-int typ;
+int
+optfn_gender(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
 {
-    int i;
-
-    for (i = 0; i < SIZE(msgtype_names); i++)
-        if (msgtype_names[i].descr && msgtype_names[i].msgtyp == typ)
-            return msgtype_names[i].name;
-    return (char *) 0;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* gender:string */
+        if (parse_role_opts(negated, allopt[optidx].name, opts, &op)) {
+            if ((flags.initgend = str2gend(op)) == ROLE_NONE) {
+                config_error_add("Unknown %s '%s'", allopt[optidx].name, op);
+                return optn_err;
+            } else
+                flags.female = flags.initgend;
+        } else
+            return optn_err;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", rolestring(flags.initgend, genders, adj));
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-static int
-query_msgtype()
+int
+optfn_hilite_status(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated;
+char *opts;
+char *op;
 {
-    winid tmpwin;
-    anything any;
-    int i, pick_cnt;
-    menu_item *picks = (menu_item *) 0;
-
-    tmpwin = create_nhwindow(NHW_MENU);
-    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
-    any = cg.zeroany;
-    for (i = 0; i < SIZE(msgtype_names); i++)
-        if (msgtype_names[i].descr) {
-            any.a_int = msgtype_names[i].msgtyp + 1;
-            add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
-                 msgtype_names[i].descr, MENU_ITEMFLAGS_NONE);
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* hilite fields in status prompt */
+#ifdef STATUS_HILITES
+        op = string_for_opt(opts, TRUE);
+        if (op != empty_optstr && negated) {
+            clear_status_hilites();
+            return optn_ok;
+        } else if (op == empty_optstr) {
+            config_error_add("Value is mandatory for hilite_status");
+            return optn_err;
         }
-    end_menu(tmpwin, "How to show the message");
-    pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
-    destroy_nhwindow(tmpwin);
-    if (pick_cnt > 0) {
-        i = picks->item.a_int - 1;
-        free((genericptr_t) picks);
-        return i;
+        if (!parse_status_hl1(op, g.opt_from_file))
+            return optn_err;
+        return optn_ok;
+#else
+        config_error_add("'%s' is not supported", allopt[optidx].name);
+        return optn_err;
+#endif
     }
-    return -1;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-static boolean
-msgtype_add(typ, pattern)
-int typ;
-char *pattern;
+int
+optfn_horsename(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated UNUSED;
+char *opts;
+char *op;
 {
-    struct plinemsg_type *tmp = (struct plinemsg_type *) alloc(sizeof *tmp);
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
+            != empty_optstr) {
+            nmcpy(g.horsename, op, PL_PSIZ);
+        } else {
+            return optn_err;
+        }
+        sanitize_name(g.horsename);
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", g.horsename[0] ? g.horsename : none);
+        return optn_ok;
+    }
+    return optn_ok;
+}
 
-    tmp->msgtype = typ;
-    tmp->regex = regex_init();
-    if (!regex_compile(pattern, tmp->regex)) {
-        static const char *re_error = "MSGTYPE regex error";
+int
+optfn_IBMgraphics(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op UNUSED;
+{
+#ifdef BACKWARD_COMPAT
+    const char *sym_name = allopt[optidx].name;
+    boolean badflag = FALSE;
+    int i;
+#endif
 
-        config_error_add("%s: %s", re_error, regex_error_desc(tmp->regex));
-        regex_free(tmp->regex);
-        free((genericptr_t) tmp);
-        return FALSE;
+    if (req == do_init) {
+        return optn_ok;
     }
-    tmp->pattern = dupstr(pattern);
-    tmp->next = g.plinemsg_types;
-    g.plinemsg_types = tmp;
-    return TRUE;
-}
+    if (req == do_set) {
+        /* "IBMgraphics" */
 
-void
-msgtype_free()
-{
-    struct plinemsg_type *tmp, *tmp2 = 0;
+#ifdef BACKWARD_COMPAT
 
-    for (tmp = g.plinemsg_types; tmp; tmp = tmp2) {
-        tmp2 = tmp->next;
-        free((genericptr_t) tmp->pattern);
-        regex_free(tmp->regex);
-        free((genericptr_t) tmp);
+        if (!negated) {
+            for (i = 0; i < NUM_GRAPHICS; ++i) {
+                if (g.symset[i].name) {
+                    badflag = TRUE;
+                } else {
+                    if (i == ROGUESET)
+                        sym_name = "RogueIBM";
+                    g.symset[i].name = dupstr(sym_name);
+                    if (!read_sym_file(i)) {
+                        badflag = TRUE;
+                        clear_symsetentry(i, TRUE);
+                        break;
+                    }
+                }
+            }
+            if (badflag) {
+                config_error_add("Failure to load symbol set %s.", sym_name);
+                return optn_err;
+            } else {
+                switch_symbols(TRUE);
+                if (!g.opt_initial && Is_rogue_level(&u.uz))
+                    assign_graphics(ROGUESET);
+            }
+        }
+        return optn_ok;
+#else
+        config_error_add("'%s' no longer supported; use 'symset:%s' instead",
+                         allopt[optidx].name, allopt[optidx].name);
+        return optn_err;
+#endif
     }
-    g.plinemsg_types = (struct plinemsg_type *) 0;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-static void
-free_one_msgtype(idx)
-int idx; /* 0 .. */
+#ifdef MAC_GRAPHICS_ENV
+int
+optfn_MACgraphics(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
 {
-    struct plinemsg_type *tmp = g.plinemsg_types;
-    struct plinemsg_type *prev = NULL;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* "MACgraphics" */
+        iflags.MACgraphics = !negated;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+        return optn_ok;
+    }
+    return optn_ok;
+}
+#endif
 
-    while (tmp) {
-        if (idx == 0) {
-            struct plinemsg_type *next = tmp->next;
+int
+optfn_map_mode(optidx, req, negated, opts, op)
+int optidx, req;
+boolean negated;
+char *opts;
+char *op;
+{
+    int i;
 
-            regex_free(tmp->regex);
-            free((genericptr_t) tmp->pattern);
-            free((genericptr_t) tmp);
-            if (prev)
-                prev->next = next;
-            else
-                g.plinemsg_types = next;
-            return;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* WINCAP
+         *
+         *  map_mode:[tiles|ascii4x6|ascii6x8|ascii8x8|ascii16x8|ascii7x12
+         *            |ascii8x12|ascii16x12|ascii12x16|ascii10x18|fit_to_screen
+         *            |ascii_fit_to_screen|tiles_fit_to_screen]
+         */
+        op = string_for_opt(opts, negated);
+        if (op != empty_optstr && !negated) {
+            if (!strcmpi(op, "tiles"))
+                iflags.wc_map_mode = MAP_MODE_TILES;
+            else if (!strncmpi(op, "ascii4x6", sizeof "ascii4x6" - 1))
+                iflags.wc_map_mode = MAP_MODE_ASCII4x6;
+            else if (!strncmpi(op, "ascii6x8", sizeof "ascii6x8" - 1))
+                iflags.wc_map_mode = MAP_MODE_ASCII6x8;
+            else if (!strncmpi(op, "ascii8x8", sizeof "ascii8x8" - 1))
+                iflags.wc_map_mode = MAP_MODE_ASCII8x8;
+            else if (!strncmpi(op, "ascii16x8", sizeof "ascii16x8" - 1))
+                iflags.wc_map_mode = MAP_MODE_ASCII16x8;
+            else if (!strncmpi(op, "ascii7x12", sizeof "ascii7x12" - 1))
+                iflags.wc_map_mode = MAP_MODE_ASCII7x12;
+            else if (!strncmpi(op, "ascii8x12", sizeof "ascii8x12" - 1))
+                iflags.wc_map_mode = MAP_MODE_ASCII8x12;
+            else if (!strncmpi(op, "ascii16x12", sizeof "ascii16x12" - 1))
+                iflags.wc_map_mode = MAP_MODE_ASCII16x12;
+            else if (!strncmpi(op, "ascii12x16", sizeof "ascii12x16" - 1))
+                iflags.wc_map_mode = MAP_MODE_ASCII12x16;
+            else if (!strncmpi(op, "ascii10x18", sizeof "ascii10x18" - 1))
+                iflags.wc_map_mode = MAP_MODE_ASCII10x18;
+            else if (!strncmpi(op, "fit_to_screen",
+                               sizeof "fit_to_screen" - 1))
+                iflags.wc_map_mode = MAP_MODE_ASCII_FIT_TO_SCREEN;
+            else if (!strncmpi(op, "ascii_fit_to_screen",
+                               sizeof "ascii_fit_to_screen" - 1))
+                iflags.wc_map_mode = MAP_MODE_ASCII_FIT_TO_SCREEN;
+            else if (!strncmpi(op, "tiles_fit_to_screen",
+                               sizeof "tiles_fit_to_screen" - 1))
+                iflags.wc_map_mode = MAP_MODE_TILES_FIT_TO_SCREEN;
+            else {
+                config_error_add("Unknown %s parameter '%s'",
+                                 allopt[optidx].name, op);
+                return optn_err;
+            }
+        } else if (negated) {
+            bad_negation(allopt[optidx].name, TRUE);
+            return optn_err;
         }
-        idx--;
-        prev = tmp;
-        tmp = tmp->next;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        i = iflags.wc_map_mode;
+        Sprintf(opts, "%s",
+                (i == MAP_MODE_TILES) ? "tiles"
+                : (i == MAP_MODE_ASCII4x6) ? "ascii4x6"
+                  : (i == MAP_MODE_ASCII6x8) ? "ascii6x8"
+                    : (i == MAP_MODE_ASCII8x8) ? "ascii8x8"
+                      : (i == MAP_MODE_ASCII16x8) ? "ascii16x8"
+                        : (i == MAP_MODE_ASCII7x12) ? "ascii7x12"
+                          : (i == MAP_MODE_ASCII8x12) ? "ascii8x12"
+                            : (i == MAP_MODE_ASCII16x12) ? "ascii16x12"
+                              : (i == MAP_MODE_ASCII12x16) ? "ascii12x16"
+                                : (i == MAP_MODE_ASCII10x18) ? "ascii10x18"
+                                  : (i == MAP_MODE_ASCII_FIT_TO_SCREEN)
+                                    ? "fit_to_screen"
+                                    : defopt);
+        return optn_ok;
     }
+    return optn_ok;
 }
 
 int
-msgtype_type(msg, norepeat)
-const char *msg;
-boolean norepeat; /* called from Norep(via pline) */
+optfn_menu_deselect_all(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
 {
-    struct plinemsg_type *tmp = g.plinemsg_types;
-
-    while (tmp) {
-        /* we don't exclude entries with negative msgtype values
-           because then the msg might end up matching a later pattern */
-        if (regex_match(msg, tmp->regex))
-            return tmp->msgtype;
-        tmp = tmp->next;
+    if (req == do_init) {
+        return optn_ok;
     }
-    return norepeat ? MSGTYP_NOREP : MSGTYP_NORMAL;
+    if (req == do_set) {
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", to_be_done);
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-/* negate one or more types of messages so that their type handling will
-   be disabled or re-enabled; MSGTYPE_NORMAL (value 0) is not affected */
-void
-hide_unhide_msgtypes(hide, hide_mask)
-boolean hide;
-int hide_mask;
+int
+optfn_menu_deselect_page(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
 {
-    struct plinemsg_type *tmp;
-    int mt;
-
-    /* negative msgtype value won't be recognized by pline, so does nothing */
-    for (tmp = g.plinemsg_types; tmp; tmp = tmp->next) {
-        mt = tmp->msgtype;
-        if (!hide)
-            mt = -mt; /* unhide: negate negative, yielding positive */
-        if (mt > 0 && ((1 << mt) & hide_mask))
-            tmp->msgtype = -tmp->msgtype;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", to_be_done);
+        return optn_ok;
     }
+    return optn_ok;
 }
 
-static int
-msgtype_count(VOID_ARGS)
+int
+optfn_menu_first_page(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
 {
-    int c = 0;
-    struct plinemsg_type *tmp = g.plinemsg_types;
-
-    while (tmp) {
-        c++;
-        tmp = tmp->next;
+    if (req == do_init) {
+        return optn_ok;
     }
-    return c;
+    if (req == do_set) {
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", to_be_done);
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-boolean
-msgtype_parse_add(str)
-char *str;
+int
+optfn_menu_headings(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
 {
-    char pattern[256];
-    char msgtype[11];
+    int tmpattr;
 
-    if (sscanf(str, "%10s \"%255[^\"]\"", msgtype, pattern) == 2) {
-        int typ = -1;
-        int i;
-
-        for (i = 0; i < SIZE(msgtype_names); i++)
-            if (!strncmpi(msgtype_names[i].name, msgtype, strlen(msgtype))) {
-                typ = msgtype_names[i].msgtyp;
-                break;
-            }
-        if (typ != -1)
-            return msgtype_add(typ, pattern);
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        if ((opts = string_for_env_opt(allopt[optidx].name, opts, FALSE))
+            == empty_optstr) {
+            return optn_err;
+        }
+        tmpattr = match_str2attr(opts, TRUE);
+        if (tmpattr == -1)
+            return optn_err;
         else
-            config_error_add("Unknown message type '%s'", msgtype);
-    } else {
-        config_error_add("Malformed MSGTYPE");
+            iflags.menu_headings = tmpattr;
+        return optn_ok;
     }
-    return FALSE;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", attr2attrname(iflags.menu_headings));
+        return optn_ok;
+    }
+    if (req == do_handler) {
+        return handler_menu_headings();
+    }
+    return optn_ok;
 }
 
-static boolean
-test_regex_pattern(str, errmsg)
-const char *str;
-const char *errmsg;
+int
+optfn_menu_invert_all(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
 {
-    static const char re_error[] = "Regex error";
-    struct nhregex *match;
-    boolean retval = TRUE;
-
-    if (!str)
-        return FALSE;
-
-    match = regex_init();
-    if (!match) {
-        config_error_add("NHregex error");
-        return FALSE;
+    if (req == do_init) {
+        return optn_ok;
     }
-
-    if (!regex_compile(str, match)) {
-        config_error_add("%s: %s", errmsg ? errmsg : re_error,
-                         regex_error_desc(match));
-        retval = FALSE;
+    if (req == do_set) {
+        return optn_ok;
     }
-    regex_free(match);
-    return retval;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", to_be_done);
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-static boolean
-add_menu_coloring_parsed(str, c, a)
-char *str;
-int c, a;
+int
+optfn_menu_invert_page(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
 {
-    static const char re_error[] = "Menucolor regex error";
-    struct menucoloring *tmp;
-
-    if (!str)
-        return FALSE;
-    tmp = (struct menucoloring *) alloc(sizeof *tmp);
-    tmp->match = regex_init();
-    if (!regex_compile(str, tmp->match)) {
-        config_error_add("%s: %s", re_error, regex_error_desc(tmp->match));
-        regex_free(tmp->match);
-        free(tmp);
-        return FALSE;
-    } else {
-        tmp->next = g.menu_colorings;
-        tmp->origstr = dupstr(str);
-        tmp->color = c;
-        tmp->attr = a;
-        g.menu_colorings = tmp;
-        return TRUE;
+    if (req == do_init) {
+        return optn_ok;
     }
+    if (req == do_set) {
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", to_be_done);
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-/* parse '"regex_string"=color&attr' and add it to menucoloring */
-boolean
-add_menu_coloring(tmpstr)
-char *tmpstr; /* never Null but could be empty */
+int
+optfn_menu_last_page(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
 {
-    int c = NO_COLOR, a = ATR_NONE;
-    char *tmps, *cs, *amp;
-    char str[BUFSZ];
-
-    (void) strncpy(str, tmpstr, sizeof str - 1);
-    str[sizeof str - 1] = '\0';
-
-    if ((cs = index(str, '=')) == 0) {
-        config_error_add("Malformed MENUCOLOR");
-        return FALSE;
+    if (req == do_init) {
+        return optn_ok;
     }
-
-    tmps = cs + 1; /* advance past '=' */
-    mungspaces(tmps);
-    if ((amp = index(tmps, '&')) != 0)
-        *amp = '\0';
-
-    c = match_str2clr(tmps);
-    if (c >= CLR_MAX)
-        return FALSE;
-
-    if (amp) {
-        tmps = amp + 1; /* advance past '&' */
-        a = match_str2attr(tmps, TRUE);
-        if (a == -1)
-            return FALSE;
+    if (req == do_set) {
+        return optn_ok;
     }
-
-    /* the regexp portion here has not been condensed by mungspaces() */
-    *cs = '\0';
-    tmps = str;
-    if (*tmps == '"' || *tmps == '\'') {
-        cs--;
-        while (isspace((uchar) *cs))
-            cs--;
-        if (*cs == *tmps) {
-            *cs = '\0';
-            tmps++;
-        }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", to_be_done);
+        return optn_ok;
     }
-    return add_menu_coloring_parsed(tmps, c, a);
+    return optn_ok;
 }
 
-boolean
-get_menu_coloring(str, color, attr)
-const char *str;
-int *color, *attr;
+int
+optfn_menu_next_page(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
 {
-    struct menucoloring *tmpmc;
-
-    if (iflags.use_menu_color)
-        for (tmpmc = g.menu_colorings; tmpmc; tmpmc = tmpmc->next)
-            if (regex_match(str, tmpmc->match)) {
-                *color = tmpmc->color;
-                *attr = tmpmc->attr;
-                return TRUE;
-            }
-    return FALSE;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", to_be_done);
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-void
-free_menu_coloring()
+int
+optfn_menu_previous_page(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
 {
-    struct menucoloring *tmp, *tmp2;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", to_be_done);
+        return optn_ok;
+    }
+    return optn_ok;
+}
 
-    for (tmp = g.menu_colorings; tmp; tmp = tmp2) {
-        tmp2 = tmp->next;
-        regex_free(tmp->match);
-        free((genericptr_t) tmp->origstr);
-        free((genericptr_t) tmp);
+int
+optfn_menu_search(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", to_be_done);
+        return optn_ok;
     }
+    return optn_ok;
 }
 
-static void
-free_one_menu_coloring(idx)
-int idx; /* 0 .. */
+int
+optfn_menu_select_all(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
 {
-    struct menucoloring *tmp = g.menu_colorings;
-    struct menucoloring *prev = NULL;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", to_be_done);
+        return optn_ok;
+    }
+    return optn_ok;
+}
 
-    while (tmp) {
-        if (idx == 0) {
-            struct menucoloring *next = tmp->next;
+int
+optfn_menu_select_page(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", to_be_done);
+        return optn_ok;
+    }
+    return optn_ok;
+}
 
-            regex_free(tmp->match);
-            free((genericptr_t) tmp->origstr);
-            free((genericptr_t) tmp);
-            if (prev)
-                prev->next = next;
-            else
-                g.menu_colorings = next;
-            return;
-        }
-        idx--;
-        prev = tmp;
-        tmp = tmp->next;
+int
+optfn_menucolor(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* menucolor:"regex_string"=color */
+        if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
+            != empty_optstr) {
+            if (!add_menu_coloring(op))
+                return optn_err;
+        } else
+            return optn_err;
+        return optn_ok;
     }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-static int
-count_menucolors(VOID_ARGS)
+int
+optfn_menuinvertmode(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op;
 {
-    struct menucoloring *tmp;
-    int count = 0;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* menuinvertmode=0 or 1 or 2 (2 is experimental) */
+        if (op != empty_optstr) {
+            int mode = atoi(op);
 
-    for (tmp = g.menu_colorings; tmp; tmp = tmp->next)
-        count++;
-    return count;
+            if (mode < 0 || mode > 2) {
+                config_error_add("Illegal %s parameter '%s'",
+                                 allopt[optidx].name, op);
+                return optn_err;
+            }
+            iflags.menuinvertmode = mode;
+        }
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%d", iflags.menuinvertmode);
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-static boolean
-parse_role_opts(negated, fullname, opts, opp)
+int
+optfn_menustyle(optidx, req, negated, opts, op)
+int optidx;
+int req;
 boolean negated;
-const char *fullname;
 char *opts;
-char **opp;
+char *op;
 {
-    char *op = *opp;
+    int tmp;
+    boolean val_required; /* no initializer based on opts because this can be
+                             called with init and invalid opts and op */
 
-    if (negated) {
-        bad_negation(fullname, FALSE);
-    } else if ((op = string_for_env_opt(fullname, opts, FALSE))
-                                        != empty_optstr) {
-        boolean val_negated = FALSE;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* menustyle:traditional or combination or full or partial */
 
-        while ((*op == '!') || !strncmpi(op, "no", 2)) {
-            if (*op == '!')
-                op++;
-            else
-                op += 2;
-            val_negated = !val_negated;
-        }
-        if (val_negated) {
-            if (!setrolefilter(op)) {
-                config_error_add("Unknown negated parameter '%s'", op);
-                return FALSE;
-            }
+        val_required = (strlen(opts) > 5 && !negated);
+        if ((op = string_for_opt(opts, !val_required)) == empty_optstr) {
+            if (val_required)
+                return optn_err; /* string_for_opt gave feedback */
+            tmp = negated ? 'n' : 'f';
         } else {
-            if (duplicate_opt_detection(opts, 1))
-                complain_about_duplicate(opts, 1);
-            *opp = op;
-            return TRUE;
+            tmp = lowc(*op);
+        }
+        switch (tmp) {
+        case 'n': /* none */
+        case 't': /* traditional: prompt for class(es) by symbol,
+                     prompt for each item within class(es) one at a time */
+            flags.menu_style = MENU_TRADITIONAL;
+            break;
+        case 'c': /* combination: prompt for class(es) by symbol,
+                     choose items within selected class(es) by menu */
+            flags.menu_style = MENU_COMBINATION;
+            break;
+        case 'f': /* full: choose class(es) by first menu,
+                     choose items within selected class(es) by second menu */
+            flags.menu_style = MENU_FULL;
+            break;
+        case 'p': /* partial: skip class filtering, choose items among all
+                     classes by menu */
+            flags.menu_style = MENU_PARTIAL;
+            break;
+        default:
+            config_error_add("Unknown %s parameter '%s'", allopt[optidx].name,
+                             op);
+            return optn_err;
         }
+        return optn_ok;
     }
-    return FALSE;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", menutype[(int) flags.menu_style]);
+        return optn_ok;
+    }
+    if (req == do_handler) {
+        return handler_menustyle();
+    }
+    return optn_ok;
 }
 
-/* Check if character c is illegal as a menu command key */
-boolean
-illegal_menu_cmd_key(c)
-char c;
+int
+optfn_monsters(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
 {
-    if (c == 0 || c == '\r' || c == '\n' || c == '\033'
-        || c == ' ' || digit(c) || (letter(c) && c != '@')) {
-        config_error_add("Reserved menu command key '%s'", visctrl(c));
-        return TRUE;
-    } else { /* reject default object class symbols */
-        int j;
-        for (j = 1; j < MAXOCLASSES; j++)
-            if (c == def_oc_syms[j].sym) {
-                config_error_add("Menu command key '%s' is an object class",
-                                 visctrl(c));
-                return TRUE;
-            }
+    if (req == do_init) {
+        return optn_ok;
     }
-    return FALSE;
+    if (req == do_set) {
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+        return optn_ok;
+    }
+    return optn_ok;
 }
 
-#define is_cond_option(x) (!strncmpi((x), "cond_", sizeof "cond_" - 1))
-
-boolean
-parseoptions(opts, tinitial, tfrom_file)
-register char *opts;
-boolean tinitial, tfrom_file;
+int
+optfn_mouse_support(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
 {
-    char *op;
-    unsigned num;
-    boolean negated, duplicate;
-    int i;
-    const char *fullname;
-    boolean retval = TRUE;
+    boolean compat;
 
-    g.opt_initial = tinitial;
-    g.opt_from_file = tfrom_file;
-    if ((op = index(opts, ',')) != 0) {
-        *op++ = 0;
-        if (!parseoptions(op, g.opt_initial, g.opt_from_file))
-            retval = FALSE;
+    if (req == do_init) {
+        return optn_ok;
     }
-    if (strlen(opts) > BUFSZ / 2) {
-        config_error_add("Option too long, max length is %i characters",
-                         (BUFSZ / 2));
-        return FALSE;
-    }
-
-    /* strip leading and trailing white space */
-    while (isspace((uchar) *opts))
-        opts++;
-    op = eos(opts);
-    while (--op >= opts && isspace((uchar) *op))
-        *op = '\0';
+    if (req == do_set) {
+        compat = (strlen(opts) <= 13);
+        op = string_for_opt(opts, (compat || !g.opt_initial));
+        if (op == empty_optstr) {
+            if (compat || negated || g.opt_initial) {
+                /* for backwards compatibility, "mouse_support" without a
+                   value is a synonym for mouse_support:1 */
+                iflags.wc_mouse_support = !negated;
+            }
+        } else {
+            int mode = atoi(op);
 
-    if (!*opts) {
-        config_error_add("Empty statement");
-        return FALSE;
-    }
-    negated = FALSE;
-    while ((*opts == '!') || !strncmpi(opts, "no", 2)) {
-        if (*opts == '!')
-            opts++;
-        else
-            opts += 2;
-        negated = !negated;
+            if (mode < 0 || mode > 2 || (mode == 0 && *op != '0')) {
+                config_error_add("Illegal %s parameter '%s'",
+                                 allopt[optidx].name, op);
+                return optn_err;
+            } else { /* mode >= 0 */
+                iflags.wc_mouse_support = mode;
+            }
+        }
+        return optn_ok;
     }
+    if (req == get_val) {
+#ifdef WIN32
+#define MOUSEFIX1 ", QuickEdit off"
+#define MOUSEFIX2 ", QuickEdit unchanged"
+#else
+#define MOUSEFIX1 ", O/S adjusted"
+#define MOUSEFIX2 ", O/S unchanged"
+#endif
+        static const char *mousemodes[][2] = {
+            { "0=off", "" },
+            { "1=on",  MOUSEFIX1 },
+            { "2=on",  MOUSEFIX2 },
+        };
+#undef MOUSEFIX1
+#undef MOUSEFIX2
+        int ms = iflags.wc_mouse_support;
 
-    /* variant spelling */
-
-    if (match_optname(opts, "colour", 5, FALSE))
-        Strcpy(opts, "color"); /* fortunately this isn't longer */
-
-    /* special boolean options */
-
-    if (match_optname(opts, "female", 3, FALSE)) {
-        if (duplicate_opt_detection(opts, 0))
-            complain_about_duplicate(opts, 0);
-        if (!g.opt_initial && flags.female == negated) {
-            config_error_add("That is not anatomically possible.");
-            return FALSE;
-        } else
-            flags.initgend = flags.female = !negated;
-        return retval;
-    }
+        if (!opts)
+            return optn_err;
 
-    if (match_optname(opts, "male", 4, FALSE)) {
-        if (duplicate_opt_detection(opts, 0))
-            complain_about_duplicate(opts, 0);
-        if (!g.opt_initial && flags.female != negated) {
-            config_error_add("That is not anatomically possible.");
-            return FALSE;
-        } else
-            flags.initgend = flags.female = negated;
-        return retval;
+        if (ms >= 0 && ms <= 2)
+            Sprintf(opts, "%s%s", mousemodes[ms][0], mousemodes[ms][1]);
+        return optn_ok;
     }
+    return optn_ok;
+}
 
-    /* some prefix-based options */
+int
+optfn_msg_window(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    int retval = optn_ok;
+#ifdef TTY_GRAPHICS
+    int tmp;
+#endif
 
-    if (is_cond_option(opts)) {
-        int reslt;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* msg_window:single, combo, full or reversed */
 
-        if ((reslt = parse_cond_option(negated, opts)) != 0) {
-            switch(reslt) {
-              case 3:
-                 config_error_add("Ambiguous condition option %s", opts);
-                 break;
-              case 1:
-              case 2:
-              default:
-                 config_error_add("Unknown condition option %s (%d)",
-                                 opts, reslt);
-                 break;
+/* allow option to be silently ignored by non-tty ports */
+#ifdef TTY_GRAPHICS
+        if (op == empty_optstr) {
+            tmp = negated ? 's' : 'f';
+        } else {
+            if (negated) {
+                bad_negation(allopt[optidx].name, TRUE);
+                return optn_err;
             }
-            return FALSE;
+            tmp = lowc(*op);
         }
-        g.opt_need_redraw = TRUE;
-        return retval;
-    }
-
-#if defined(MICRO) && !defined(AMIGA)
-    /* included for compatibility with old NetHack.cnf files */
-    if (match_optname(opts, "IBM_", 4, FALSE)) {
-        iflags.BIOS = !negated;
+        switch (tmp) {
+        case 's': /* single message history cycle (default if negated) */
+            iflags.prevmsg_window = 's';
+            break;
+        case 'c': /* combination: two singles, then full page */
+            iflags.prevmsg_window = 'c';
+            break;
+        case 'f': /* full page (default if specified without argument) */
+            iflags.prevmsg_window = 'f';
+            break;
+        case 'r': /* full page (reversed) */
+            iflags.prevmsg_window = 'r';
+            break;
+        default:
+            config_error_add("Unknown %s parameter '%s'", allopt[optidx].name,
+                             op);
+            retval = optn_err;
+        }
+#endif
         return retval;
     }
-#endif /* MICRO */
-
-    /* compound options */
-
-    /* This first batch can be duplicated if their values are negated */
-
-    /* align:string */
-    fullname = "align";
-    if (match_optname(opts, fullname, sizeof "align" - 1, TRUE)) {
-        if (parse_role_opts(negated, fullname, opts, &op)) {
-            if ((flags.initalign = str2align(op)) == ROLE_NONE) {
-                config_error_add("Unknown %s '%s'", fullname, op);
-                return FALSE;
-            }
-        } else
-            return FALSE;
-        return retval;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+#ifdef TTY_GRAPHICS
+        Sprintf(opts, "%s", (iflags.prevmsg_window == 's') ? "single"
+                           : (iflags.prevmsg_window == 'c') ? "combination"
+                             : (iflags.prevmsg_window == 'f') ? "full"
+                               : "reversed");
+#endif
+        return optn_ok;
     }
-
-    /* role:string or character:string */
-    fullname = "role";
-    if (match_optname(opts, fullname, 4, TRUE)
-        || match_optname(opts, (fullname = "character"), 4, TRUE)) {
-        if (parse_role_opts(negated, fullname, opts, &op)) {
-            if ((flags.initrole = str2role(op)) == ROLE_NONE) {
-                config_error_add("Unknown %s '%s'", fullname, op);
-                return FALSE;
-            } else /* Backwards compatibility */
-                nmcpy(g.pl_character, op, PL_NSIZ);
-        } else
-            return FALSE;
-        return retval;
+    if (req == do_handler) {
+        return handler_msg_window();
     }
+    return optn_ok;
+}
 
-    /* race:string */
-    fullname = "race";
-    if (match_optname(opts, fullname, 4, TRUE)) {
-        if (parse_role_opts(negated, fullname, opts, &op)) {
-            if ((flags.initrace = str2race(op)) == ROLE_NONE) {
-                config_error_add("Unknown %s '%s'", fullname, op);
-                return FALSE;
-            } else /* Backwards compatibility */
-                g.pl_race = *op;
-        } else
-            return FALSE;
-        return retval;
+int
+optfn_msghistory(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
     }
-
-    /* gender:string */
-    fullname = "gender";
-    if (match_optname(opts, fullname, 4, TRUE)) {
-        if (parse_role_opts(negated, fullname, opts, &op)) {
-            if ((flags.initgend = str2gend(op)) == ROLE_NONE) {
-                config_error_add("Unknown %s '%s'", fullname, op);
-                return FALSE;
-            } else
-                flags.female = flags.initgend;
-        } else
-            return FALSE;
-        return retval;
+    if (req == do_set) {
+        op = string_for_env_opt(allopt[optidx].name, opts, negated);
+        if ((negated && op == empty_optstr)
+            || (!negated && op != empty_optstr)) {
+            iflags.msg_history = negated ? 0 : atoi(op);
+        } else if (negated) {
+            bad_negation(allopt[optidx].name, TRUE);
+            return optn_err;
+        }
+        return optn_ok;
     }
-
-    /* We always check for duplicates on the remaining compound options,
-       although individual option processing can choose to complain or not */
-
-    duplicate = duplicate_opt_detection(opts, 1); /* 1: check compounds */
-
-    fullname = "pettype";
-    if (match_optname(opts, fullname, 3, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if ((op = string_for_env_opt(fullname, opts, negated))
-                                     != empty_optstr) {
-            if (negated) {
-                bad_negation(fullname, TRUE);
-                return FALSE;
-            } else
-                switch (lowc(*op)) {
-                case 'd': /* dog */
-                    g.preferred_pet = 'd';
-                    break;
-                case 'c': /* cat */
-                case 'f': /* feline */
-                    g.preferred_pet = 'c';
-                    break;
-                case 'h': /* horse */
-                case 'q': /* quadruped */
-                    /* avoids giving "unrecognized type of pet" but
-                       pet_type(dog.c) won't actually honor this */
-                    g.preferred_pet = 'h';
-                    break;
-                case 'n': /* no pet */
-                    g.preferred_pet = 'n';
-                    break;
-                case '*': /* random */
-                    g.preferred_pet = '\0';
-                    break;
-                default:
-                    config_error_add("Unrecognized pet type '%s'.", op);
-                    return FALSE;
-                    break;
-                }
-        } else if (negated)
-            g.preferred_pet = 'n';
-        return retval;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%u", iflags.msg_history);
+        return optn_ok;
     }
+    return optn_ok;
+}
 
-    fullname = "catname";
-    if (match_optname(opts, fullname, 3, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if ((op = string_for_env_opt(fullname, opts, FALSE))
-                                            != empty_optstr) {
-            nmcpy(g.catname, op, PL_PSIZ);
-        } else
-            return FALSE;
-        sanitize_name(g.catname);
-        return retval;
+int
+optfn_name(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
     }
+    if (req == do_set) {
+        /* name:string */
 
-    fullname = "dogname";
-    if (match_optname(opts, fullname, 3, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if ((op = string_for_env_opt(fullname, opts, FALSE))
-                                            != empty_optstr) {
-            nmcpy(g.dogname, op, PL_PSIZ);
+        if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
+            != empty_optstr) {
+            nmcpy(g.plname, op, PL_NSIZ);
         } else
-            return FALSE;
-        sanitize_name(g.dogname);
-        return retval;
+            return optn_err;
+        return optn_ok;
     }
-
-    fullname = "horsename";
-    if (match_optname(opts, fullname, 5, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if ((op = string_for_env_opt(fullname, opts, FALSE))
-                                            != empty_optstr) {
-            nmcpy(g.horsename, op, PL_PSIZ);
-        } else
-            return FALSE;
-        sanitize_name(g.horsename);
-        return retval;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", g.plname);
+        return optn_ok;
     }
+    return optn_ok;
+}
 
-    fullname = "mouse_support";
-    if (match_optname(opts, fullname, 13, TRUE)) {
-        boolean compat = (strlen(opts) <= 13);
-
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        op = string_for_opt(opts, (compat || !g.opt_initial));
-        if (op == empty_optstr) {
-            if (compat || negated || g.opt_initial) {
-                /* for backwards compatibility, "mouse_support" without a
-                   value is a synonym for mouse_support:1 */
-                iflags.wc_mouse_support = !negated;
-            }
-        } else if (negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
-        } else {
-            int mode = atoi(op);
+int
+optfn_number_pad(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    boolean compat;
 
-            if (mode < 0 || mode > 2 || (mode == 0 && *op != '0')) {
-                config_error_add("Illegal %s parameter '%s'", fullname, op);
-                return FALSE;
-            } else { /* mode >= 0 */
-                iflags.wc_mouse_support = mode;
-            }
-        }
-        return retval;
+    if (req == do_init) {
+        return optn_ok;
     }
-
-    fullname = "number_pad";
-    if (match_optname(opts, fullname, 10, TRUE)) {
-        boolean compat = (strlen(opts) <= 10);
-
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
+    if (req == do_set) {
+        compat = (strlen(opts) <= 10);
         op = string_for_opt(opts, (compat || !g.opt_initial));
         if (op == empty_optstr) {
             if (compat || negated || g.opt_initial) {
@@ -2277,14 +2219,15 @@ boolean tinitial, tfrom_file;
                 iflags.num_pad_mode = 0;
             }
         } else if (negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
+            bad_negation(allopt[optidx].name, TRUE);
+            return optn_err;
         } else {
             int mode = atoi(op);
 
             if (mode < -1 || mode > 4 || (mode == 0 && *op != '0')) {
-                config_error_add("Illegal %s parameter '%s'", fullname, op);
-                return FALSE;
+                config_error_add("Illegal %s parameter '%s'",
+                                 allopt[optidx].name, op);
+                return optn_err;
             } else if (mode <= 0) {
                 iflags.num_pad = FALSE;
                 /* German keyboard; y and z keys swapped */
@@ -2302,288 +2245,141 @@ boolean tinitial, tfrom_file;
         }
         reset_commands(FALSE);
         number_pad(iflags.num_pad ? 1 : 0);
-        return retval;
+        return optn_ok;
     }
+    if (req == get_val) {
+        static const char *numpadmodes[] = {
+            "0=off", "1=on", "2=on, MSDOS compatible",
+            "3=on, phone-style layout",
+            "4=on, phone layout, MSDOS compatible",
+            "-1=off, y & z swapped", /*[5]*/
+        };
+        int indx = g.Cmd.num_pad
+                       ? (g.Cmd.phone_layout ? (g.Cmd.pcHack_compat ? 4 : 3)
+                                           : (g.Cmd.pcHack_compat ? 2 : 1))
+                       : g.Cmd.swap_yz ? 5 : 0;
 
-    fullname = "roguesymset";
-    if (match_optname(opts, fullname, 7, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
-            g.symset[ROGUESET].name = dupstr(op);
-            if (!read_sym_file(ROGUESET)) {
-                clear_symsetentry(ROGUESET, TRUE);
-                config_error_add(
-                               "Unable to load symbol set \"%s\" from \"%s\"",
-                                 op, SYMBOLS);
-                return FALSE;
-            } else {
-                if (!g.opt_initial && Is_rogue_level(&u.uz))
-                    assign_graphics(ROGUESET);
-                g.opt_need_redraw = TRUE;
-            }
-        } else
-            return FALSE;
-        return retval;
+        if (!opts)
+            return optn_err;
+        Strcpy(opts, numpadmodes[indx]);
+        return optn_ok;
     }
-
-    fullname = "symset";
-    if (match_optname(opts, fullname, 6, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
-            g.symset[PRIMARY].name = dupstr(op);
-            if (!read_sym_file(PRIMARY)) {
-                clear_symsetentry(PRIMARY, TRUE);
-                config_error_add(
-                               "Unable to load symbol set \"%s\" from \"%s\"",
-                                 op, SYMBOLS);
-                return FALSE;
-            } else {
-                switch_symbols(g.symset[PRIMARY].name != (char *) 0);
-                g.opt_need_redraw = TRUE;
-            }
-        } else
-            return FALSE;
-        return retval;
+    if (req == do_handler) {
+        return handler_number_pad();
     }
+    return optn_ok;
+}
 
-    fullname = "runmode";
-    if (match_optname(opts, fullname, 4, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            flags.runmode = RUN_TPORT;
-        } else if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
-            if (!strncmpi(op, "teleport", strlen(op)))
-                flags.runmode = RUN_TPORT;
-            else if (!strncmpi(op, "run", strlen(op)))
-                flags.runmode = RUN_LEAP;
-            else if (!strncmpi(op, "walk", strlen(op)))
-                flags.runmode = RUN_STEP;
-            else if (!strncmpi(op, "crawl", strlen(op)))
-                flags.runmode = RUN_CRAWL;
-            else {
-                config_error_add("Unknown %s parameter '%s'", fullname, op);
-                return FALSE;
-            }
-        } else
-            return FALSE;
-        return retval;
+int
+optfn_objects(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
+{
+    if (req == do_init) {
+        return optn_ok;
     }
-
-    /* menucolor:"regex_string"=color */
-    fullname = "menucolor";
-    if (match_optname(opts, fullname, 9, TRUE)) {
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if ((op = string_for_env_opt(fullname, opts, FALSE))
-                                            != empty_optstr) {
-            if (!add_menu_coloring(op))
-                return FALSE;
-        } else
-            return FALSE;
-        return retval;
+    if (req == do_set) {
+        return optn_ok;
     }
-
-    /* menuinvertmode=0 or 1 or 2 (2 is experimental) */
-    fullname = "menuinvertmode";
-    if (match_optname(opts, fullname, 5, TRUE)) {
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else {
-            int mode = atoi(op);
-
-            if (mode < 0 || mode > 2) {
-                config_error_add("Illegal %s parameter '%s'", fullname, op);
-                return FALSE;
-            }
-            iflags.menuinvertmode = mode;
-        }
-        return retval;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", to_be_done);
+        return optn_ok;
     }
+    return optn_ok;
+}
 
-    fullname = "msghistory";
-    if (match_optname(opts, fullname, 3, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        op = string_for_env_opt(fullname, opts, negated);
-        if ((negated && op == empty_optstr)
-            || (!negated && op != empty_optstr)) {
-            iflags.msg_history = negated ? 0 : atoi(op);
-        } else if (negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
-        }
-        return retval;
+int
+optfn_packorder(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
     }
-
-    fullname = "msg_window";
-    /* msg_window:single, combo, full or reversed */
-    if (match_optname(opts, fullname, 4, TRUE)) {
-/* allow option to be silently ignored by non-tty ports */
-#ifdef TTY_GRAPHICS
-        int tmp;
-
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if ((op = string_for_opt(opts, TRUE)) == empty_optstr) {
-            tmp = negated ? 's' : 'f';
-        } else {
-            if (negated) {
-                bad_negation(fullname, TRUE);
-                return FALSE;
-            }
-            tmp = lowc(*op);
-        }
-        switch (tmp) {
-        case 's': /* single message history cycle (default if negated) */
-            iflags.prevmsg_window = 's';
-            break;
-        case 'c': /* combination: two singles, then full page */
-            iflags.prevmsg_window = 'c';
-            break;
-        case 'f': /* full page (default if specified without argument) */
-            iflags.prevmsg_window = 'f';
-            break;
-        case 'r': /* full page (reversed) */
-            iflags.prevmsg_window = 'r';
-            break;
-        default:
-            config_error_add("Unknown %s parameter '%s'", fullname, op);
-            retval = FALSE;
-        }
-#endif
-        return retval;
+    if (req == do_set) {
+        if (op == empty_optstr)
+            return optn_err;
+        if (!change_inv_order(op))
+            return optn_err;
+        return optn_ok;
     }
+    if (req == get_val) {
+        char ocl[MAXOCLASSES + 1];
 
-    /* WINCAP
-     * setting font options  */
-    fullname = "font";
-    if (!strncmpi(opts, fullname, 4)) {
-        int opttype = -1;
-        char *fontopts = opts + 4;
+        if (!opts)
+            return optn_err;
+        oc_to_str(flags.inv_order, ocl);
+        Sprintf(opts, "%s", ocl);
+        return optn_ok;
+    }
+    return optn_ok;
+}
 
-        if (!strncmpi(fontopts, "map", 3) || !strncmpi(fontopts, "_map", 4))
-            opttype = MAP_OPTION;
-        else if (!strncmpi(fontopts, "message", 7)
-                 || !strncmpi(fontopts, "_message", 8))
-            opttype = MESSAGE_OPTION;
-        else if (!strncmpi(fontopts, "text", 4)
-                 || !strncmpi(fontopts, "_text", 5))
-            opttype = TEXT_OPTION;
-        else if (!strncmpi(fontopts, "menu", 4)
-                 || !strncmpi(fontopts, "_menu", 5))
-            opttype = MENU_OPTION;
-        else if (!strncmpi(fontopts, "status", 6)
-                 || !strncmpi(fontopts, "_status", 7))
-            opttype = STATUS_OPTION;
-        else if (!strncmpi(fontopts, "_size", 5)) {
-            if (!strncmpi(fontopts, "_size_map", 8))
-                opttype = MAP_OPTION;
-            else if (!strncmpi(fontopts, "_size_message", 12))
-                opttype = MESSAGE_OPTION;
-            else if (!strncmpi(fontopts, "_size_text", 9))
-                opttype = TEXT_OPTION;
-            else if (!strncmpi(fontopts, "_size_menu", 9))
-                opttype = MENU_OPTION;
-            else if (!strncmpi(fontopts, "_size_status", 11))
-                opttype = STATUS_OPTION;
-            else {
-                config_error_add("Unknown %s parameter '%s'", fullname, opts);
-                return FALSE;
-            }
-            if (duplicate)
-                complain_about_duplicate(opts, 1);
-            if (opttype > 0 && !negated
-                && (op = string_for_opt(opts, FALSE)) != empty_optstr) {
-                switch (opttype) {
-                case MAP_OPTION:
-                    iflags.wc_fontsiz_map = atoi(op);
-                    break;
-                case MESSAGE_OPTION:
-                    iflags.wc_fontsiz_message = atoi(op);
-                    break;
-                case TEXT_OPTION:
-                    iflags.wc_fontsiz_text = atoi(op);
-                    break;
-                case MENU_OPTION:
-                    iflags.wc_fontsiz_menu = atoi(op);
-                    break;
-                case STATUS_OPTION:
-                    iflags.wc_fontsiz_status = atoi(op);
-                    break;
-                }
-            }
-            return retval;
-        } else {
-            config_error_add("Unknown %s parameter '%s'", fullname, opts);
-            return FALSE;
-        }
-        if (opttype > 0
-            && (op = string_for_opt(opts, FALSE)) != empty_optstr) {
-            wc_set_font_name(opttype, op);
-#ifdef MAC
-            set_font_name(opttype, op);
+int
+optfn_palette(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op;
+{
+#ifndef WIN32
+#ifdef CHANGE_COLOR
+    int cnt, tmp, reverse;
+    char *pt = op;
+    long rgb;
 #endif
-            return retval;
-        } else if (negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
-        }
-        return retval;
+#endif
+
+    if (req == do_init) {
+        return optn_ok;
     }
+    if (req == do_set) {
+        if (op == empty_optstr)
+            return optn_err;
+        /*
+          Non-WIN32 variant
+                 palette (00c/880/-fff is blue/yellow/reverse white)
+          WIN32 variant
+                 palette (adjust an RGB color in palette (color-R-G-B)
+        */
 
 #ifdef CHANGE_COLOR
-    if (match_optname(opts, "palette", 3, TRUE)
+        if (match_optname(opts, "palette", 3, TRUE)
 #ifdef MAC
-        || match_optname(opts, "hicolor", 3, TRUE)
+            || match_optname(opts, "hicolor", 3, TRUE)
 #endif
-        ) {
-        int color_number, color_incr;
+                                                      ) {
+            int color_number, color_incr;
 
 #ifndef WIN32
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
+            if (duplicate)
+                complain_about_duplicate(opts, 1);
 #endif
 #ifdef MAC
-        if (match_optname(opts, "hicolor", 3, TRUE)) {
-            if (negated) {
-                bad_negation("hicolor", FALSE);
-                return FALSE;
-            }
-            color_number = CLR_MAX + 4; /* HARDCODED inverse number */
-            color_incr = -1;
-        } else
+            if (match_optname(opts, "hicolor", 3, TRUE)) {
+                color_number = CLR_MAX + 4; /* HARDCODED inverse number */
+                color_incr = -1;
+            } else
 #endif
-        {
-            if (negated) {
-                bad_negation("palette", FALSE);
-                return FALSE;
+            {
+                color_number = 0;
+                color_incr = 1;
             }
-            color_number = 0;
-            color_incr = 1;
-        }
 #ifdef WIN32
-        op = string_for_opt(opts, TRUE);
-        if (op == empty_optstr || !alternative_palette(op)) {
-            config_error_add("Error in palette parameter '%s'", op);
-            return FALSE;
-        }
+            if (!alternative_palette(op)) {
+                config_error_add("Error in palette parameter '%s'", op);
+                return optn_err;
+            }
 #else
-        if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
-            char *pt = op;
-            int cnt, tmp, reverse;
-            long rgb;
-
             while (*pt && color_number >= 0) {
                 cnt = 3;
                 rgb = 0L;
@@ -2618,376 +2414,261 @@ boolean tinitial, tfrom_file;
                 change_color(color_number, rgb, reverse);
                 color_number += color_incr;
             }
-        }
 #endif /* !WIN32 */
-        if (!g.opt_initial) {
-            g.opt_need_redraw = TRUE;
-        }
-        return retval;
-    }
+            if (!g.opt_initial) {
+                g.opt_need_redraw = TRUE;
+            }
 #endif /* CHANGE_COLOR */
+            return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+#ifdef CHANGE_COLOR
+        Sprintf(opts, "%s", get_color_string());
+#endif
+        return optn_ok;
+    }
+    return optn_ok;
+}
 
-    if (match_optname(opts, "fruit", 2, TRUE)) {
-        struct fruit *forig = 0;
-
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        op = string_for_opt(opts, negated || !g.opt_initial);
-        if (negated) {
-            if (op != empty_optstr) {
-                bad_negation("fruit", TRUE);
-                return FALSE;
-            }
-            op = empty_optstr;
-            goto goodfruit;
-        }
-        if (op == empty_optstr)
-            return FALSE;
-        /* strip leading/trailing spaces, condense internal ones (3.6.2) */
-        mungspaces(op);
-        if (!g.opt_initial) {
-            struct fruit *f;
-            int fnum = 0;
+int optfn_paranoid_confirmation(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    int i;
 
-            /* count number of named fruits; if 'op' is found among them,
-               then the count doesn't matter because we won't be adding it */
-            f = fruit_from_name(op, FALSE, &fnum);
-            if (!f) {
-                if (!flags.made_fruit)
-                    forig = fruit_from_name(g.pl_fruit, FALSE, (int *) 0);
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* user can change required response for some prompts (quit, die,
+           hit), or add an extra prompt (pray, Remove) that isn't
+           ordinarily there */
 
-                if (!forig && fnum >= 100) {
-                    config_error_add(
-                             "Doing that so many times isn't very fruitful.");
-                    return retval;
-                }
-            }
-        }
- goodfruit:
-        nmcpy(g.pl_fruit, op, PL_FSIZ);
-        sanitize_name(g.pl_fruit);
-        /* OBJ_NAME(objects[SLIME_MOLD]) won't work for this after
-           initialization; it gets changed to generic "fruit" */
-        if (!*g.pl_fruit)
-            nmcpy(g.pl_fruit, "slime mold", PL_FSIZ);
-        if (!g.opt_initial) {
-            /* if 'forig' is nonNull, we replace it rather than add
-               a new fruit; it can only be nonNull if no fruits have
-               been created since the previous name was put in place */
-            (void) fruitadd(g.pl_fruit, forig);
-            pline("Fruit is now \"%s\".", g.pl_fruit);
+        if (strncmpi(opts, "prayconfirm", 4) != 0) { /* not prayconfirm */
+            /* at present we don't complain about duplicates for this
+               option, but we do throw away the old settings whenever
+               we process a new one [clearing old flags is essential
+               for handling default paranoid_confirm:pray sanely] */
+            flags.paranoia_bits = 0; /* clear all */
+            if (negated) {
+                flags.paranoia_bits = 0; /* [now redundant...] */
+            } else if (op != empty_optstr) {
+                char *pp, buf[BUFSZ];
+
+                strncpy(buf, op, sizeof buf - 1);
+                buf[sizeof buf - 1] = '\0';
+                op = mungspaces(buf);
+                for (;;) {
+                    /* We're looking to parse
+                       "paranoid_confirm:whichone wheretwo whothree"
+                       and "paranoid_confirm:" prefix has already
+                       been stripped off by the time we get here */
+                    pp = index(op, ' ');
+                    if (pp)
+                        *pp = '\0';
+                    /* we aren't matching option names but match_optname()
+                       does what we want once we've broken the space
+                       delimited aggregate into separate tokens */
+                    for (i = 0; i < SIZE(paranoia); ++i) {
+                        if (match_optname(op, paranoia[i].argname,
+                                          paranoia[i].argMinLen, FALSE)
+                            || (paranoia[i].synonym
+                                && match_optname(op, paranoia[i].synonym,
+                                                 paranoia[i].synMinLen,
+                                                 FALSE))) {
+                            if (paranoia[i].flagmask)
+                                flags.paranoia_bits |= paranoia[i].flagmask;
+                            else /* 0 == "none", so clear all */
+                                flags.paranoia_bits = 0;
+                            break;
+                        }
+                    }
+                    if (i == SIZE(paranoia)) {
+                        /* didn't match anything, so arg is bad;
+                           any flags already set will stay set */
+                        config_error_add("Unknown %s parameter '%s'",
+                                         allopt[optidx].name, op);
+                        return optn_err;
+                    }
+                    /* move on to next token */
+                    if (pp)
+                        op = pp + 1;
+                    else
+                        break; /* no next token */
+                }              /* for(;;) */
+            } else
+                return optn_err;
+            return optn_ok;
+        } else { /* prayconfirm */
+            if (negated)
+                flags.paranoia_bits &= ~PARANOID_PRAY;
+            else
+                flags.paranoia_bits |= PARANOID_PRAY;
+            return optn_ok;
         }
-        /* If initial, then initoptions is allowed to do it instead
-         * of here (initoptions always has to do it even if there's
-         * no fruit option at all.  Also, we don't want people
-         * setting multiple fruits in their options.)
-         */
-        return retval;
+        return optn_ok;
     }
+    if (req == get_val) {
+        char tmpbuf[QBUFSZ];
 
-    fullname = "whatis_coord";
-    if (match_optname(opts, fullname, 8, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            iflags.getpos_coords = GPCOORDS_NONE;
-            return retval;
-        } else if ((op = string_for_env_opt(fullname, opts, FALSE))
-                                            != empty_optstr) {
-            static char gpcoords[] = { GPCOORDS_NONE, GPCOORDS_COMPASS,
-                                       GPCOORDS_COMFULL, GPCOORDS_MAP,
-                                       GPCOORDS_SCREEN, '\0' };
-            char c = lowc(*op);
-
-            if (c && index(gpcoords, c))
-                iflags.getpos_coords = c;
-            else {
-                config_error_add("Unknown %s parameter '%s'", fullname, op);
-                return FALSE;
-            }
-        } else
-            return FALSE;
-        return retval;
+        if (!opts)
+            return optn_err;
+        tmpbuf[0] = '\0';
+        for (i = 0; paranoia[i].flagmask != 0; ++i)
+            if (flags.paranoia_bits & paranoia[i].flagmask)
+                Sprintf(eos(tmpbuf), " %s", paranoia[i].argname);
+        Strcpy(opts, tmpbuf[0] ? &tmpbuf[1] : "none");
+        return optn_ok;
     }
-
-    fullname = "whatis_filter";
-    if (match_optname(opts, fullname, 8, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            iflags.getloc_filter = GFILTER_NONE;
-            return retval;
-        } else if ((op = string_for_env_opt(fullname, opts, FALSE))
-                                            != empty_optstr) {
-            char c = lowc(*op);
-
-            switch (c) {
-            case 'n':
-                iflags.getloc_filter = GFILTER_NONE;
-                break;
-            case 'v':
-                iflags.getloc_filter = GFILTER_VIEW;
-                break;
-            case 'a':
-                iflags.getloc_filter = GFILTER_AREA;
-                break;
-            default: {
-                config_error_add("Unknown %s parameter '%s'", fullname, op);
-                return FALSE;
-            }
-            }
-        } else
-            return FALSE;
-        return retval;
+    if (req == do_handler) {
+        return handler_paranoid_confirmation();
     }
+    return optn_ok;
+}
 
-    fullname = "warnings";
-    if (match_optname(opts, fullname, 5, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        }
-        return warning_opts(opts, fullname);
+int
+optfn_petattr(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    int retval = optn_ok;
+
+    if (req == do_init) {
+        return optn_ok;
     }
+    if (req == do_set) {
+        /* WINCAP2 petattr:string */
 
-    /* boulder:symbol */
-    fullname = "boulder";
-    if (match_optname(opts, fullname, 7, TRUE)) {
-#ifdef BACKWARD_COMPAT
-        int clash = 0;
+        op = string_for_opt(opts, negated);
+        if (op != empty_optstr && negated) {
+            bad_negation(allopt[optidx].name, TRUE);
+            retval = optn_err;
+        } else if (op != empty_optstr) {
+#ifdef CURSES_GRAPHICS
+            int itmp = curses_read_attrs(op);
 
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
+            if (itmp == -1) {
+                config_error_add("Unknown %s parameter '%s'",
+                                 allopt[optidx].name, opts);
+                retval = optn_err;
+            } else
+                iflags.wc2_petattr = itmp;
+#else
+            /* non-curses windowports will not use this flag anyway
+             * but the above will not compile if we don't have curses.
+             * Just set it to a sensible default: */
+            iflags.wc2_petattr = ATR_INVERSE;
+#endif
+        } else if (negated) {
+            iflags.wc2_petattr = ATR_NONE;
         }
-        /* if ((opts = string_for_env_opt(fullname, opts, FALSE))
-                                          == empty_optstr)
-         */
-        if ((opts = string_for_opt(opts, FALSE)) == empty_optstr)
-            return FALSE;
-        escapes(opts, opts);
-        /* note: dummy monclass #0 has symbol value '\0'; we allow that--
-           attempting to set bouldersym to '^@'/'\0' will reset to default */
-        if (def_char_to_monclass(opts[0]) != MAXMCLASSES)
-            clash = opts[0] ? 1 : 0;
-        else if (opts[0] >= '1' && opts[0] < WARNCOUNT + '0')
-            clash = 2;
-        if (clash) {
-            /* symbol chosen matches a used monster or warning
-               symbol which is not good - reject it */
-            config_error_add(
-            "Badoption - boulder symbol '%s' would conflict with a %s symbol",
-                             visctrl(opts[0]),
-                             (clash == 1) ? "monster" : "warning");
-        } else {
-            /*
-             * Override the default boulder symbol.
-             */
-            g.ov_primary_syms[SYM_BOULDER + SYM_OFF_X] = (nhsym) opts[0];
-            g.ov_rogue_syms[SYM_BOULDER + SYM_OFF_X] = (nhsym) opts[0];
-            /* for 'initial', update of BOULDER symbol is done in
-               initoptions_finish(), after all symset options
-               have been processed */
-            if (!g.opt_initial) {
-                nhsym sym = get_othersym(SYM_BOULDER,
-                                Is_rogue_level(&u.uz) ? ROGUESET : PRIMARY);
-                if (sym)
-                    g.showsyms[SYM_BOULDER + SYM_OFF_X] = sym;
+        if (retval != optn_err) {
+            iflags.hilite_pet = (iflags.wc2_petattr != ATR_NONE);
+            if (!g.opt_initial)
                 g.opt_need_redraw = TRUE;
-            }
         }
         return retval;
-#else
-        config_error_add("'%s' no longer supported; use S_boulder:c instead",
-                         fullname);
-        return FALSE;
-#endif
     }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
 
-    /* name:string */
-    fullname = "name";
-    if (match_optname(opts, fullname, 4, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if ((op = string_for_env_opt(fullname, opts, FALSE))
-                                            != empty_optstr) {
-            nmcpy(g.plname, op, PL_NSIZ);
-        } else
-            return FALSE;
-        return retval;
-    }
+#ifdef CURSES_GRAPHICS
+        if (WINDOWPORT("curses")) {
+            char tmpbuf[QBUFSZ];
 
-    /* altkeyhandler:string */
-    fullname = "altkeyhandler";
-    if (match_optname(opts, fullname, 4, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if ((op = string_for_opt(opts, negated)) != empty_optstr) {
-#if defined(WIN32) && defined(TTY_GRAPHICS)
-            set_altkeyhandler(op);
-#endif
+            Strcpy(opts, curses_fmt_attrs(tmpbuf));
         } else
-            return FALSE;
-        return retval;
+#endif
+        if (iflags.wc2_petattr != 0)
+            Sprintf(opts, "0x%08x", iflags.wc2_petattr);
+        else
+            Strcpy(opts, defopt);
     }
+    return optn_ok;
+}
 
-    /* WINCAP
-     * align_status:[left|top|right|bottom] */
-    fullname = "align_status";
-    if (match_optname(opts, fullname, sizeof "align_status" - 1, TRUE)) {
-        op = string_for_opt(opts, negated);
-        if ((op != empty_optstr) && !negated) {
-            if (!strncmpi(op, "left", sizeof "left" - 1))
-                iflags.wc_align_status = ALIGN_LEFT;
-            else if (!strncmpi(op, "top", sizeof "top" - 1))
-                iflags.wc_align_status = ALIGN_TOP;
-            else if (!strncmpi(op, "right", sizeof "right" - 1))
-                iflags.wc_align_status = ALIGN_RIGHT;
-            else if (!strncmpi(op, "bottom", sizeof "bottom" - 1))
-                iflags.wc_align_status = ALIGN_BOTTOM;
-            else {
-                config_error_add("Unknown %s parameter '%s'", fullname, op);
-                return FALSE;
-            }
-        } else if (negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
-        }
-        return retval;
+int
+optfn_pettype(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
     }
-
-    /* WINCAP
-     * align_message:[left|top|right|bottom] */
-    fullname = "align_message";
-    if (match_optname(opts, fullname, sizeof "align_message" - 1, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        op = string_for_opt(opts, negated);
-        if ((op != empty_optstr) && !negated) {
-            if (!strncmpi(op, "left", sizeof "left" - 1))
-                iflags.wc_align_message = ALIGN_LEFT;
-            else if (!strncmpi(op, "top", sizeof "top" - 1))
-                iflags.wc_align_message = ALIGN_TOP;
-            else if (!strncmpi(op, "right", sizeof "right" - 1))
-                iflags.wc_align_message = ALIGN_RIGHT;
-            else if (!strncmpi(op, "bottom", sizeof "bottom" - 1))
-                iflags.wc_align_message = ALIGN_BOTTOM;
-            else {
-                config_error_add("Unknown %s parameter '%s'", fullname, op);
-                return FALSE;
+    if (req == do_set) {
+        if ((op = string_for_env_opt(allopt[optidx].name, opts, negated))
+            != empty_optstr) {
+            switch (lowc(*op)) {
+            case 'd': /* dog */
+                g.preferred_pet = 'd';
+                break;
+            case 'c': /* cat */
+            case 'f': /* feline */
+                g.preferred_pet = 'c';
+                break;
+            case 'h': /* horse */
+            case 'q': /* quadruped */
+                /* avoids giving "unrecognized type of pet" but
+                   pet_type(dog.c) won't actually honor this */
+                g.preferred_pet = 'h';
+                break;
+            case 'n': /* no pet */
+                g.preferred_pet = 'n';
+                break;
+            case '*': /* random */
+                g.preferred_pet = '\0';
+                break;
+            default:
+                config_error_add("Unrecognized pet type '%s'.", op);
+                return optn_err;
+                break;
             }
-        } else if (negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
-        }
-        return retval;
-    }
-
-    /* the order to list inventory */
-    fullname = "packorder";
-    if (match_optname(opts, fullname, 4, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if ((op = string_for_opt(opts, FALSE)) == empty_optstr)
-            return FALSE;
-
-        if (!change_inv_order(op))
-            return FALSE;
-        return retval;
+        } else if (negated)
+            g.preferred_pet = 'n';
+        return optn_ok;
     }
-
-    /* user can change required response for some prompts (quit, die, hit),
-       or add an extra prompt (pray, Remove) that isn't ordinarily there */
-    fullname = "paranoid_confirmation";
-    if (match_optname(opts, fullname, 8, TRUE)) {
-        /* at present we don't complain about duplicates for this
-           option, but we do throw away the old settings whenever
-           we process a new one [clearing old flags is essential
-           for handling default paranoid_confirm:pray sanely] */
-        flags.paranoia_bits = 0; /* clear all */
-        if (negated) {
-            flags.paranoia_bits = 0; /* [now redundant...] */
-        } else if ((op = string_for_opt(opts, TRUE)) != empty_optstr) {
-            char *pp, buf[BUFSZ];
-
-            strncpy(buf, op, sizeof buf - 1);
-            buf[sizeof buf - 1] = '\0';
-            op = mungspaces(buf);
-            for (;;) {
-                /* We're looking to parse
-                   "paranoid_confirm:whichone wheretwo whothree"
-                   and "paranoid_confirm:" prefix has already
-                   been stripped off by the time we get here */
-                pp = index(op, ' ');
-                if (pp)
-                    *pp = '\0';
-                /* we aren't matching option names but match_optname()
-                   does what we want once we've broken the space
-                   delimited aggregate into separate tokens */
-                for (i = 0; i < SIZE(paranoia); ++i) {
-                    if (match_optname(op, paranoia[i].argname,
-                                      paranoia[i].argMinLen, FALSE)
-                        || (paranoia[i].synonym
-                            && match_optname(op, paranoia[i].synonym,
-                                             paranoia[i].synMinLen, FALSE))) {
-                        if (paranoia[i].flagmask)
-                            flags.paranoia_bits |= paranoia[i].flagmask;
-                        else /* 0 == "none", so clear all */
-                            flags.paranoia_bits = 0;
-                        break;
-                    }
-                }
-                if (i == SIZE(paranoia)) {
-                    /* didn't match anything, so arg is bad;
-                       any flags already set will stay set */
-                    config_error_add("Unknown %s parameter '%s'",
-                                     fullname, op);
-                    return FALSE;
-                }
-                /* move on to next token */
-                if (pp)
-                    op = pp + 1;
-                else
-                    break; /* no next token */
-            } /* for(;;) */
-        } else
-            return FALSE;
-        return retval;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", (g.preferred_pet == 'c') ? "cat"
+                           : (g.preferred_pet == 'd') ? "dog"
+                             : (g.preferred_pet == 'h') ? "horse"
+                               : (g.preferred_pet == 'n') ? "none"
+                                 : "random");
+        return optn_ok;
     }
+    return optn_ok;
+}
 
-    /* accept deprecated boolean; superseded by paranoid_confirm:pray */
-    fullname = "prayconfirm";
-    if (match_optname(opts, fullname, 4, FALSE)) {
-        if (negated)
-            flags.paranoia_bits &= ~PARANOID_PRAY;
-        else
-            flags.paranoia_bits |= PARANOID_PRAY;
-        return retval;
+int
+optfn_pickup_burden(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
     }
+    if (req == do_set) {
+        /* maximum burden picked up before prompt (Warren Cheung) */
 
-    /* maximum burden picked up before prompt (Warren Cheung) */
-    fullname = "pickup_burden";
-    if (match_optname(opts, fullname, 8, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if ((op = string_for_env_opt(fullname, opts, FALSE))
-                                            != empty_optstr) {
+        if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
+            != empty_optstr) {
             switch (lowc(*op)) {
             case 'u': /* Unencumbered */
                 flags.pickup_burden = UNENCUMBERED;
@@ -3009,24 +2690,46 @@ boolean tinitial, tfrom_file;
                 flags.pickup_burden = OVERLOADED;
                 break;
             default:
-                config_error_add("Unknown %s parameter '%s'", fullname, op);
-                return FALSE;
+                config_error_add("Unknown %s parameter '%s'",
+                                 allopt[optidx].name, op);
+                return optn_err;
             }
         } else
-            return FALSE;
-        return retval;
+            return optn_err;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", burdentype[flags.pickup_burden]);
+        return optn_ok;
     }
+    if (req == do_handler) {
+        return handler_pickup_burden();
+    }
+    return optn_ok;
+}
+
+int
+optfn_pickup_types(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    char ocl[MAXOCLASSES + 1], tbuf[MAXOCLASSES + 1], qbuf[QBUFSZ],
+        abuf[BUFSZ];
+    int oc_sym;
+    unsigned num;
+    boolean badopt = FALSE, compat = (strlen(opts) <= 6), use_menu;
 
-    /* types of objects to pick up automatically */
-    fullname = "pickup_types";
-    if (match_optname(opts, fullname, 8, TRUE)) {
-        char ocl[MAXOCLASSES + 1], tbuf[MAXOCLASSES + 1],
-             qbuf[QBUFSZ], abuf[BUFSZ];
-        int oc_sym;
-        boolean badopt = FALSE, compat = (strlen(opts) <= 6), use_menu;
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* types of objects to pick up automatically */
 
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
         oc_to_str(flags.pickup_types, tbuf);
         flags.pickup_types[0] = '\0'; /* all */
         op = string_for_opt(opts, (compat || !g.opt_initial));
@@ -3036,7 +2739,7 @@ boolean tinitial, tfrom_file;
                    value is a synonym for autopickup of all types
                    (and during initialization, we can't prompt yet) */
                 flags.pickup = !negated;
-                return retval;
+                return optn_ok;
             }
             oc_to_str(flags.inv_order, ocl);
             use_menu = TRUE;
@@ -3045,8 +2748,8 @@ boolean tinitial, tfrom_file;
                 boolean wasspace;
 
                 use_menu = FALSE;
-                Sprintf(qbuf, "New %s: [%s am] (%s)", fullname, ocl,
-                        *tbuf ? tbuf : "all");
+                Sprintf(qbuf, "New %s: [%s am] (%s)", allopt[optidx].name,
+                        ocl, *tbuf ? tbuf : "all");
                 abuf[0] = '\0';
                 getlin(qbuf, abuf);
                 wasspace = (abuf[0] == ' '); /* before mungspaces */
@@ -3069,8 +2772,8 @@ boolean tinitial, tfrom_file;
             }
         }
         if (negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
+            bad_negation(allopt[optidx].name, TRUE);
+            return optn_err;
         }
         while (*op == ' ')
             op++;
@@ -3088,66 +2791,79 @@ boolean tinitial, tfrom_file;
                 op++;
             }
             if (badopt) {
-                config_error_add("Unknown %s parameter '%s'", fullname, op);
-                return FALSE;
+                config_error_add("Unknown %s parameter '%s'",
+                                 allopt[optidx].name, op);
+                return optn_err;
             }
         }
-        return retval;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        oc_to_str(flags.pickup_types, ocl);
+        Sprintf(opts, "%s", ocl[0] ? ocl : "all");
+        return optn_ok;
     }
+    if (req == do_handler) {
+        return handler_pickup_types();
+    }
+    return optn_ok;
+}
+
+int
+optfn_pile_limit(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* pile limit: when walking over objects, number which triggers
+           "there are several/many objects here" instead of listing them
+         */
 
-    /* pile limit: when walking over objects, number which triggers
-       "there are several/many objects here" instead of listing them */
-    fullname = "pile_limit";
-    if (match_optname(opts, fullname, 4, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
         op = string_for_opt(opts, negated);
         if ((negated && op == empty_optstr)
             || (!negated && op != empty_optstr))
             flags.pile_limit = negated ? 0 : atoi(op);
         else if (negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
+            bad_negation(allopt[optidx].name, TRUE);
+            return optn_err;
         } else /* op == empty_optstr */
             flags.pile_limit = PILE_LIMIT_DFLT;
         /* sanity check */
         if (flags.pile_limit < 0)
             flags.pile_limit = PILE_LIMIT_DFLT;
-        return retval;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%d", flags.pile_limit);
+        return optn_ok;
     }
+    return optn_ok;
+}
 
-    /* play mode: normal, explore/discovery, or debug/wizard */
-    fullname = "playmode";
-    if (match_optname(opts, fullname, 4, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated)
-            bad_negation(fullname, FALSE);
-        if (duplicate || negated)
-            return FALSE;
-        op = string_for_opt(opts, FALSE);
-        if (op == empty_optstr)
-            return FALSE;
-        if (!strncmpi(op, "normal", 6) || !strcmpi(op, "play")) {
-            wizard = discover = FALSE;
-        } else if (!strncmpi(op, "explore", 6)
-                   || !strncmpi(op, "discovery", 6)) {
-            wizard = FALSE, discover = TRUE;
-        } else if (!strncmpi(op, "debug", 5) || !strncmpi(op, "wizard", 6)) {
-            wizard = TRUE, discover = FALSE;
-        } else {
-            config_error_add("Invalid value for \"%s\":%s", fullname, op);
-            return FALSE;
-        }
-        return retval;
+int
+optfn_player_selection(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
     }
+    if (req == do_set) {
+        /* WINCAP player_selection: dialog | prompt/prompts/prompting */
 
-    /* WINCAP
-     * player_selection: dialog | prompt/prompts/prompting */
-    fullname = "player_selection";
-    if (match_optname(opts, fullname, sizeof "player_selection" - 1, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
         op = string_for_opt(opts, negated);
         if (op != empty_optstr && !negated) {
             if (!strncmpi(op, "dialog", sizeof "dialog" - 1)) {
@@ -3155,122 +2871,230 @@ boolean tinitial, tfrom_file;
             } else if (!strncmpi(op, "prompt", sizeof "prompt" - 1)) {
                 iflags.wc_player_selection = VIA_PROMPTS;
             } else {
-                config_error_add("Unknown %s parameter '%s'", fullname, op);
-                return FALSE;
+                config_error_add("Unknown %s parameter '%s'",
+                                 allopt[optidx].name, op);
+                return optn_err;
             }
-        } else if (negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
         }
-        return retval;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", iflags.wc_player_selection ? "prompts" : "dialog");
+        return optn_ok;
     }
+    return optn_ok;
+}
 
-    /* things to disclose at end of game */
-    fullname = "disclose";
-    if (match_optname(opts, fullname, 7, TRUE)) {
-        /*
-         * The order that the end_disclose options are stored:
-         *      inventory, attribs, vanquished, genocided,
-         *      conduct, overview.
-         * There is an array in flags:
-         *      end_disclose[NUM_DISCLOSURE_OPT];
-         * with option settings for the each of the following:
-         * iagvc [see disclosure_options in decl.c]:
-         * Allowed setting values in that array are:
-         *      DISCLOSE_PROMPT_DEFAULT_YES  ask with default answer yes
-         *      DISCLOSE_PROMPT_DEFAULT_NO   ask with default answer no
-         *      DISCLOSE_YES_WITHOUT_PROMPT  always disclose and don't ask
-         *      DISCLOSE_NO_WITHOUT_PROMPT   never disclose and don't ask
-         *      DISCLOSE_PROMPT_DEFAULT_SPECIAL  for 'vanquished' only...
-         *      DISCLOSE_SPECIAL_WITHOUT_PROMPT  ...to set up sort order.
-         *
-         * Those setting values can be used in the option
-         * string as a prefix to get the desired behaviour.
-         *
-         * For backward compatibility, no prefix is required,
-         * and the presence of a i,a,g,v, or c without a prefix
-         * sets the corresponding value to DISCLOSE_YES_WITHOUT_PROMPT.
-         */
-        int idx, prefix_val;
+int
+optfn_playmode(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* play mode: normal, explore/discovery, or debug/wizard */
 
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        op = string_for_opt(opts, TRUE);
-        if (op != empty_optstr && negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
-        }
-        /* "disclose" without a value means "all with prompting"
-           and negated means "none without prompting" */
-        if (op == empty_optstr
-            || !strcmpi(op, "all") || !strcmpi(op, "none")) {
-            if (op != empty_optstr && !strcmpi(op, "none"))
-                negated = TRUE;
-            for (num = 0; num < NUM_DISCLOSURE_OPTIONS; num++)
-                flags.end_disclose[num] = negated
-                                              ? DISCLOSE_NO_WITHOUT_PROMPT
-                                              : DISCLOSE_PROMPT_DEFAULT_YES;
-            return retval;
+        if (duplicate || negated)
+            return optn_err;
+        if (op == empty_optstr)
+            return optn_err;
+        if (!strncmpi(op, "normal", 6) || !strcmpi(op, "play")) {
+            wizard = discover = FALSE;
+        } else if (!strncmpi(op, "explore", 6)
+                   || !strncmpi(op, "discovery", 6)) {
+            wizard = FALSE, discover = TRUE;
+        } else if (!strncmpi(op, "debug", 5) || !strncmpi(op, "wizard", 6)) {
+            wizard = TRUE, discover = FALSE;
+        } else {
+            config_error_add("Invalid value for \"%s\":%s",
+                             allopt[optidx].name, op);
+            return optn_err;
         }
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Strcpy(opts, wizard ? "debug" : discover ? "explore" : "normal");
+        return optn_ok;
+    }
+    return optn_ok;
+}
 
-        num = 0;
-        prefix_val = -1;
-        while (*op && num < sizeof flags.end_disclose - 1) {
-            static char valid_settings[] = {
-                DISCLOSE_PROMPT_DEFAULT_YES, DISCLOSE_PROMPT_DEFAULT_NO,
-                DISCLOSE_PROMPT_DEFAULT_SPECIAL,
-                DISCLOSE_YES_WITHOUT_PROMPT, DISCLOSE_NO_WITHOUT_PROMPT,
-                DISCLOSE_SPECIAL_WITHOUT_PROMPT, '\0'
-            };
-            register char c, *dop;
+int
+optfn_race(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* race:string */
+        if (parse_role_opts(negated, allopt[optidx].name, opts, &op)) {
+            if ((flags.initrace = str2race(op)) == ROLE_NONE) {
+                config_error_add("Unknown %s '%s'", allopt[optidx].name, op);
+                return optn_err;
+            } else /* Backwards compatibility */
+                g.pl_race = *op;
+        } else
+            return optn_err;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", rolestring(flags.initrace, races, noun));
+        return optn_ok;
+    }
+    return optn_ok;
+}
 
-            c = lowc(*op);
-            if (c == 'k')
-                c = 'v'; /* killed -> vanquished */
-            if (c == 'd')
-                c = 'o'; /* dungeon -> overview */
-            dop = index(disclosure_options, c);
-            if (dop) {
-                idx = (int) (dop - disclosure_options);
-                if (idx < 0 || idx > NUM_DISCLOSURE_OPTIONS - 1) {
-                    impossible("bad disclosure index %d %c", idx, c);
-                    continue;
-                }
-                if (prefix_val != -1) {
-                    if (*dop != 'v') {
-                        if (prefix_val == DISCLOSE_PROMPT_DEFAULT_SPECIAL)
-                            prefix_val = DISCLOSE_PROMPT_DEFAULT_YES;
-                        if (prefix_val == DISCLOSE_SPECIAL_WITHOUT_PROMPT)
-                            prefix_val = DISCLOSE_YES_WITHOUT_PROMPT;
-                    }
-                    flags.end_disclose[idx] = prefix_val;
-                    prefix_val = -1;
-                } else
-                    flags.end_disclose[idx] = DISCLOSE_YES_WITHOUT_PROMPT;
-            } else if (index(valid_settings, c)) {
-                prefix_val = c;
-            } else if (c == ' ') {
-                ; /* do nothing */
+int
+optfn_roguesymset(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        if (op != empty_optstr) {
+            g.symset[ROGUESET].name = dupstr(op);
+            if (!read_sym_file(ROGUESET)) {
+                clear_symsetentry(ROGUESET, TRUE);
+                config_error_add(
+                    "Unable to load symbol set \"%s\" from \"%s\"", op,
+                    SYMBOLS);
+                return optn_err;
             } else {
-                config_error_add("Unknown %s parameter '%c'", fullname, *op);
-                return FALSE;
+                if (!g.opt_initial && Is_rogue_level(&u.uz))
+                    assign_graphics(ROGUESET);
+                g.opt_need_redraw = TRUE;
             }
-            op++;
-        }
-        return retval;
+        } else
+            return optn_err;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s",
+                g.symset[ROGUESET].name ? g.symset[ROGUESET].name : "default");
+        if (g.currentgraphics == ROGUESET && g.symset[ROGUESET].name)
+            Strcat(opts, ", active");
+        return optn_ok;
+    }
+    if (req == do_handler) {
+        return handler_symset(optidx);
     }
+    return optn_ok;
+}
 
-    /* scores:5t[op] 5a[round] o[wn] */
-    fullname = "scores";
-    if (match_optname(opts, fullname, 4, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
+int
+optfn_role(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        if (parse_role_opts(negated, allopt[optidx].name, opts, &op)) {
+            if ((flags.initrole = str2role(op)) == ROLE_NONE) {
+                config_error_add("Unknown %s '%s'", allopt[optidx].name, op);
+                return optn_err;
+            } else /* Backwards compatibility */
+                nmcpy(g.pl_character, op, PL_NSIZ);
+        } else
+            return optn_err;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", rolestring(flags.initrole, roles, name.m));
+        return optn_ok;
+    }
+    return optn_ok;
+}
+
+int
+optfn_runmode(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
         if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        }
+            flags.runmode = RUN_TPORT;
+        } else if (op != empty_optstr) {
+            if (!strncmpi(op, "teleport", strlen(op)))
+                flags.runmode = RUN_TPORT;
+            else if (!strncmpi(op, "run", strlen(op)))
+                flags.runmode = RUN_LEAP;
+            else if (!strncmpi(op, "walk", strlen(op)))
+                flags.runmode = RUN_STEP;
+            else if (!strncmpi(op, "crawl", strlen(op)))
+                flags.runmode = RUN_CRAWL;
+            else {
+                config_error_add("Unknown %s parameter '%s'",
+                                 allopt[optidx].name, op);
+                return optn_err;
+            }
+        } else
+            return optn_err;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", runmodes[flags.runmode]);
+        return optn_ok;
+    }
+    if (req == do_handler) {
+        return handler_runmode();
+    }
+    return optn_ok;
+}
+
+int
+optfn_scores(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* scores:5t[op] 5a[round] o[wn] */
+
         if ((op = string_for_opt(opts, FALSE)) == empty_optstr)
-            return FALSE;
+            return optn_err;
 
         while (*op) {
             int inum = 1;
@@ -3300,24 +3124,119 @@ boolean tinitial, tfrom_file;
                 flags.end_own = !negated;
                 break;
             default:
-                config_error_add("Unknown %s parameter '%s'", fullname, op);
-                return FALSE;
+                config_error_add("Unknown %s parameter '%s'",
+                                 allopt[optidx].name, op);
+                return optn_err;
             }
-            /* "3a" is sufficient but accept "3around" (or "3abracadabra") */
+            /* "3a" is sufficient but accept "3around" (or "3abracadabra")
+             */
             while (letter(*op))
                 op++;
-            /* t, a, and o can be separated by space(s) or slash or both */
+            /* t, a, and o can be separated by space(s) or slash or both
+             */
             while (*op == ' ')
                 op++;
             if (*op == '/')
                 op++;
         }
-        return retval;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%d top/%d around%s", flags.end_top, flags.end_around,
+                flags.end_own ? "/own" : "");
+        return optn_ok;
+    }
+    return optn_ok;
+}
+
+int
+optfn_scroll_amount(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* WINCAP scroll_amount:nn */
+
+        op = string_for_opt(opts, negated);
+        if ((negated && op == empty_optstr)
+            || (!negated && op != empty_optstr)) {
+            iflags.wc_scroll_amount = negated ? 1 : atoi(op);
+        } else if (negated) {
+            bad_negation(allopt[optidx].name, TRUE);
+            return optn_err;
+        }
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        if (iflags.wc_scroll_amount)
+            Sprintf(opts, "%d", iflags.wc_scroll_amount);
+        else
+            Strcpy(opts, defopt);
+        return optn_ok;
+    }
+    return optn_ok;
+}
+
+int
+optfn_scroll_margin(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* WINCAP scroll_margin:nn */
+        op = string_for_opt(opts, negated);
+        if ((negated && op == empty_optstr)
+            || (!negated && op != empty_optstr)) {
+            iflags.wc_scroll_margin = negated ? 5 : atoi(op);
+        } else if (negated) {
+            bad_negation(allopt[optidx].name, TRUE);
+            return optn_err;
+        }
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        if (iflags.wc_scroll_margin)
+            Sprintf(opts, "%d", iflags.wc_scroll_margin);
+        else
+            Strcpy(opts, defopt);
+        return optn_ok;
     }
+    return optn_ok;
+}
+
+int
+optfn_sortloot(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op;
+{
+    int i;
 
-    fullname = "sortloot";
-    if (match_optname(opts, fullname, 4, TRUE)) {
-        op = string_for_env_opt(fullname, opts, FALSE);
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        op = string_for_env_opt(allopt[optidx].name, opts, FALSE);
         if (op != empty_optstr) {
             char c = lowc(*op);
 
@@ -3328,435 +3247,791 @@ boolean tinitial, tfrom_file;
                 flags.sortloot = c;
                 break;
             default:
-                config_error_add("Unknown %s parameter '%s'", fullname, op);
-                return FALSE;
+                config_error_add("Unknown %s parameter '%s'",
+                                 allopt[optidx].name, op);
+                return optn_err;
             }
         } else
-            return FALSE;
-        return retval;
+            return optn_err;
+        return optn_ok;
     }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        for (i = 0; i < SIZE(sortltype); i++)
+            if (flags.sortloot == sortltype[i][0]) {
+                Strcpy(opts, sortltype[i]);
+                break;
+            }
+        return optn_ok;
+    }
+    if (req == do_handler) {
+        return handler_sortloot();
+    }
+    return optn_ok;
+}
 
-    fullname = "suppress_alert";
-    if (match_optname(opts, fullname, 4, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        op = string_for_opt(opts, negated);
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if (op != empty_optstr)
-            (void) feature_alert_opts(op, fullname);
-        return retval;
+int
+optfn_statushilites(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
     }
+    if (req == do_set) {
+        /* control over whether highlights should be displayed, and for
+         * how long */
 
-#ifdef VIDEOSHADES
-    /* videocolors:string */
-    fullname = "videocolors";
-    if (match_optname(opts, fullname, 6, TRUE)
-        || match_optname(opts, "videocolours", 10, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
+#ifdef STATUS_HILITES
         if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if ((opts = string_for_env_opt(fullname, opts, FALSE))
-                                              == empty_optstr) {
-            return FALSE;
-        }
-        if (!assign_videocolors(opts)) {
-            config_error_add("Unknown error handling '%s'", fullname);
-            return FALSE;
+            iflags.hilite_delta = 0L;
+        } else {
+            op = string_for_opt(opts, TRUE);
+            iflags.hilite_delta =
+                (op == empty_optstr || !*op) ? 3L : atol(op);
+            if (iflags.hilite_delta < 0L)
+                iflags.hilite_delta = 1L;
         }
-        return retval;
+        if (!g.opt_from_file)
+            reset_status_hilites();
+        return optn_ok;
+#else
+        config_error_add("'%s' is not supported", allopt[optidx].name);
+        return optn_err;
+#endif
     }
-    /* videoshades:string */
-    fullname = "videoshades";
-    if (match_optname(opts, fullname, 6, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+#ifdef STATUS_HILITES
+        if (!iflags.hilite_delta)
+            Strcpy(opts, "0 (off: don't highlight status fields)");
+        else
+            Sprintf(opts, "%ld (on: highlight status for %ld turns)",
+                    iflags.hilite_delta, iflags.hilite_delta);
+#else
+        Strcpy(opts, "unsupported");
+#endif
+        return optn_ok;
+    }
+    return optn_ok;
+}
+
+int
+optfn_statuslines(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    int retval = optn_ok, itmp = 0;
+
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* WINCAP2
+         * statuslines:n */
+
+        op = string_for_opt(opts, negated);
         if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if ((opts = string_for_env_opt(fullname, opts, FALSE))
-                                              == empty_optstr) {
-            return FALSE;
+            bad_negation(allopt[optidx].name, TRUE);
+            itmp = 2;
+            retval = optn_err;
+        } else if (op != empty_optstr) {
+            itmp = atoi(op);
         }
-        if (!assign_videoshades(opts)) {
-            config_error_add("Unknown error handling '%s'", fullname);
-            return FALSE;
+        if (itmp < 2 || itmp > 3) {
+            config_error_add("'%s' requires a value of 2 or 3",
+                             allopt[optidx].name);
+            retval = optn_err;
+        } else {
+            iflags.wc2_statuslines = itmp;
+            if (!g.opt_initial)
+                g.opt_need_redraw = TRUE;
         }
         return retval;
     }
-#endif /* VIDEOSHADES */
-#ifdef MSDOS
-    fullname = "video_width";
-    if (match_optname(opts, fullname, 7, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        }
-        if ((op = string_for_opt(opts, negated)) != empty_optstr)
-            iflags.wc_video_width = strtol(op, NULL, 10);
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        if (wc2_supported(allopt[optidx].name))
+            Strcpy(opts, (iflags.wc2_statuslines < 3) ? "2" : "3");
         else
-            return FALSE;
-        return retval;
+            Strcpy(opts, "unknown");
+        return optn_ok;
     }
-    fullname = "video_height";
-    if (match_optname(opts, fullname, 7, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
+    return optn_ok;
+}
+
+int
+optfn_subkeyvalue(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+#if defined(WIN32)
+        if (op == empty_optstr)
+            return optn_err;
+#ifdef TTY_GRAPHICS
+        map_subkeyvalue(op);
+#endif
+#endif
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+        return optn_ok;
+    }
+    return optn_ok;
+}
+
+int
+optfn_suppress_alert(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
         if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        }
-        if ((op = string_for_opt(opts, negated)) != empty_optstr)
-            iflags.wc_video_height = strtol(op, NULL, 10);
+            bad_negation(allopt[optidx].name, FALSE);
+            return optn_err;
+        } else if (op != empty_optstr)
+            (void) feature_alert_opts(op, allopt[optidx].name);
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        if (flags.suppress_alert == 0L)
+            Strcpy(opts, none);
         else
-            return FALSE;
-        return retval;
+            Sprintf(opts, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
+                    FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
+        return optn_ok;
     }
-#ifdef NO_TERMS
-    /* video:string -- must be after longer tests */
-    fullname = "video";
-    if (match_optname(opts, fullname, 5, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if ((opts = string_for_env_opt(fullname, opts, FALSE))
-                                              == empty_optstr) {
-            return FALSE;
-        }
-        if (!assign_video(opts)) {
-            config_error_add("Unknown error handling '%s'", fullname);
-            return FALSE;
+    return optn_ok;
+}
+
+int
+optfn_symset(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        if (op != empty_optstr) {
+            g.symset[PRIMARY].name = dupstr(op);
+            if (!read_sym_file(PRIMARY)) {
+                clear_symsetentry(PRIMARY, TRUE);
+                config_error_add(
+                    "Unable to load symbol set \"%s\" from \"%s\"", op,
+                    SYMBOLS);
+                return optn_err;
+            } else {
+                switch_symbols(g.symset[PRIMARY].name != (char *) 0);
+                g.opt_need_redraw = TRUE;
+            }
+        } else
+            return optn_err;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s",
+                g.symset[PRIMARY].name ? g.symset[PRIMARY].name : "default");
+        if (g.currentgraphics == PRIMARY && g.symset[PRIMARY].name)
+            Strcat(opts, ", active");
+        return optn_ok;
+    }
+    return optn_ok;
+}
+
+int
+optfn_term_cols(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    int retval = optn_ok;
+    long ltmp;
+
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* WINCAP2
+         * term_cols:amount */
+
+        if ((op = string_for_opt(opts, negated)) != empty_optstr) {
+            ltmp = atol(op);
+            /* just checks atol() sanity, not logical window size sanity
+             */
+            if (ltmp <= 0L || ltmp >= (long) LARGEST_INT) {
+                config_error_add("Invalid %s: %ld", allopt[optidx].name,
+                                 ltmp);
+                retval = optn_err;
+            } else {
+                iflags.wc2_term_cols = (int) ltmp;
+            }
         }
         return retval;
     }
-#endif /* NO_TERMS */
-#endif /* MSDOS */
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        if (iflags.wc2_term_cols)
+            Sprintf(opts, "%d", iflags.wc2_term_cols);
+        else
+            Strcpy(opts, defopt);
+        return optn_ok;
+    }
+    return optn_ok;
+}
 
-    /* WINCAP
-     *
-     *  map_mode:[tiles|ascii4x6|ascii6x8|ascii8x8|ascii16x8|ascii7x12
-     *            |ascii8x12|ascii16x12|ascii12x16|ascii10x18|fit_to_screen
-     *            |ascii_fit_to_screen|tiles_fit_to_screen]
-     */
-    fullname = "map_mode";
-    if (match_optname(opts, fullname, sizeof "map_mode" - 1, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        op = string_for_opt(opts, negated);
-        if (op != empty_optstr && !negated) {
-            if (!strcmpi(op, "tiles"))
-                iflags.wc_map_mode = MAP_MODE_TILES;
-            else if (!strncmpi(op, "ascii4x6", sizeof "ascii4x6" - 1))
-                iflags.wc_map_mode = MAP_MODE_ASCII4x6;
-            else if (!strncmpi(op, "ascii6x8", sizeof "ascii6x8" - 1))
-                iflags.wc_map_mode = MAP_MODE_ASCII6x8;
-            else if (!strncmpi(op, "ascii8x8", sizeof "ascii8x8" - 1))
-                iflags.wc_map_mode = MAP_MODE_ASCII8x8;
-            else if (!strncmpi(op, "ascii16x8", sizeof "ascii16x8" - 1))
-                iflags.wc_map_mode = MAP_MODE_ASCII16x8;
-            else if (!strncmpi(op, "ascii7x12", sizeof "ascii7x12" - 1))
-                iflags.wc_map_mode = MAP_MODE_ASCII7x12;
-            else if (!strncmpi(op, "ascii8x12", sizeof "ascii8x12" - 1))
-                iflags.wc_map_mode = MAP_MODE_ASCII8x12;
-            else if (!strncmpi(op, "ascii16x12", sizeof "ascii16x12" - 1))
-                iflags.wc_map_mode = MAP_MODE_ASCII16x12;
-            else if (!strncmpi(op, "ascii12x16", sizeof "ascii12x16" - 1))
-                iflags.wc_map_mode = MAP_MODE_ASCII12x16;
-            else if (!strncmpi(op, "ascii10x18", sizeof "ascii10x18" - 1))
-                iflags.wc_map_mode = MAP_MODE_ASCII10x18;
-            else if (!strncmpi(op, "fit_to_screen",
-                               sizeof "fit_to_screen" - 1))
-                iflags.wc_map_mode = MAP_MODE_ASCII_FIT_TO_SCREEN;
-            else if (!strncmpi(op, "ascii_fit_to_screen",
-                               sizeof "ascii_fit_to_screen" - 1))
-                iflags.wc_map_mode = MAP_MODE_ASCII_FIT_TO_SCREEN;
-            else if (!strncmpi(op, "tiles_fit_to_screen",
-                               sizeof "tiles_fit_to_screen" - 1))
-                iflags.wc_map_mode = MAP_MODE_TILES_FIT_TO_SCREEN;
-            else {
-                config_error_add("Unknown %s parameter '%s'", fullname, op);
-                return FALSE;
+int
+optfn_term_rows(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    int retval = optn_ok;
+    long ltmp;
+
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* WINCAP2
+         * term_rows:amount */
+
+        if ((op = string_for_opt(opts, negated)) != empty_optstr) {
+            ltmp = atol(op);
+            /* just checks atol() sanity, not logical window size sanity
+             */
+            if (ltmp <= 0L || ltmp >= (long) LARGEST_INT) {
+                config_error_add("Invalid %s: %ld", allopt[optidx].name,
+                                 ltmp);
+                retval = optn_err;
+            } else {
+                iflags.wc2_term_rows = (int) ltmp;
             }
-        } else if (negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
         }
         return retval;
     }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        if (iflags.wc2_term_rows)
+            Sprintf(opts, "%d", iflags.wc2_term_rows);
+        else
+            Strcpy(opts, defopt);
+        return optn_ok;
+    }
+    return optn_ok;
+}
 
-    /* WINCAP
-     * scroll_amount:nn */
-    fullname = "scroll_amount";
-    if (match_optname(opts, fullname, sizeof "scroll_amount" - 1, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
+int
+optfn_tile_file(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* WINCAP tile_file:name */
+        if (op != empty_optstr) {
+            if (iflags.wc_tile_file)
+                free(iflags.wc_tile_file);
+            iflags.wc_tile_file = dupstr(op);
+        } else
+            return optn_err;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s",
+                iflags.wc_tile_file ? iflags.wc_tile_file : defopt);
+        return optn_ok;
+    }
+    return optn_ok;
+}
+
+int
+optfn_tile_height(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* WINCAP tile_height:nn */
         op = string_for_opt(opts, negated);
         if ((negated && op == empty_optstr)
             || (!negated && op != empty_optstr)) {
-            iflags.wc_scroll_amount = negated ? 1 : atoi(op);
+            iflags.wc_tile_height = negated ? 0 : atoi(op);
         } else if (negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
+            bad_negation(allopt[optidx].name, TRUE);
+            return optn_err;
         }
-        return retval;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        if (iflags.wc_tile_height)
+            Sprintf(opts, "%d", iflags.wc_tile_height);
+        else
+            Strcpy(opts, defopt);
+        return optn_ok;
     }
+    return optn_ok;
+}
 
-    /* WINCAP
-     * scroll_margin:nn */
-    fullname = "scroll_margin";
-    if (match_optname(opts, fullname, sizeof "scroll_margin" - 1, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
+int
+optfn_tile_width(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* WINCAP tile_width:nn */
         op = string_for_opt(opts, negated);
         if ((negated && op == empty_optstr)
             || (!negated && op != empty_optstr)) {
-            iflags.wc_scroll_margin = negated ? 5 : atoi(op);
+            iflags.wc_tile_width = negated ? 0 : atoi(op);
         } else if (negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
+            bad_negation(allopt[optidx].name, TRUE);
+            return optn_err;
         }
-        return retval;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        if (iflags.wc_tile_width)
+            Sprintf(opts, "%d", iflags.wc_tile_width);
+        else
+            Strcpy(opts, defopt);
+        return optn_ok;
     }
+    return optn_ok;
+}
 
-    fullname = "subkeyvalue";
-    if (match_optname(opts, fullname, 5, TRUE)) {
-        /* no duplicate complaint here */
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-#if defined(WIN32)
-        } else {
-            op = string_for_opt(opts, 0);
-            if (op == empty_optstr)
-                return FALSE;
-#ifdef TTY_GRAPHICS
-            map_subkeyvalue(op);
-#endif
-#endif
-        }
-        return retval;
+int
+optfn_traps(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", to_be_done);
+        return optn_ok;
     }
+    return optn_ok;
+}
 
-    /* WINCAP
-     * tile_width:nn */
-    fullname = "tile_width";
-    if (match_optname(opts, fullname, sizeof "tile_width" - 1, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
+int
+optfn_vary_msgcount(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* WINCAP vary_msgcount:nn */
         op = string_for_opt(opts, negated);
         if ((negated && op == empty_optstr)
             || (!negated && op != empty_optstr)) {
-            iflags.wc_tile_width = negated ? 0 : atoi(op);
+            iflags.wc_vary_msgcount = negated ? 0 : atoi(op);
         } else if (negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
+            bad_negation(allopt[optidx].name, TRUE);
+            return optn_err;
         }
-        return retval;
+        return optn_ok;
     }
-    /* WINCAP
-     * tile_file:name */
-    fullname = "tile_file";
-    if (match_optname(opts, fullname, sizeof "tile_file" - 1, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
-            if (iflags.wc_tile_file)
-                free(iflags.wc_tile_file);
-            iflags.wc_tile_file = dupstr(op);
-        } else
-            return FALSE;
-        return retval;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        if (iflags.wc_vary_msgcount)
+            Sprintf(opts, "%d", iflags.wc_vary_msgcount);
+        else
+            Strcpy(opts, defopt);
+        return optn_ok;
     }
-    /* WINCAP
-     * tile_height:nn */
-    fullname = "tile_height";
-    if (match_optname(opts, fullname, sizeof "tile_height" - 1, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        op = string_for_opt(opts, negated);
-        if ((negated && op == empty_optstr)
-            || (!negated && op != empty_optstr)) {
-            iflags.wc_tile_height = negated ? 0 : atoi(op);
-        } else if (negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
-        }
-        return retval;
+    return optn_ok;
+}
+
+#ifdef VIDEOSHADES
+int
+optfn_videocolors(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
     }
+    if (req == do_set) {
+        /* videocolors:string */
 
-    /* WINCAP
-     * vary_msgcount:nn */
-    fullname = "vary_msgcount";
-    if (match_optname(opts, fullname, sizeof "vary_msgcount" - 1, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        op = string_for_opt(opts, negated);
-        if ((negated && op == empty_optstr)
-            || (!negated && op != empty_optstr)) {
-            iflags.wc_vary_msgcount = negated ? 0 : atoi(op);
-        } else if (negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
+        if ((opts = string_for_env_opt(allopt[optidx].name, opts, FALSE))
+            == empty_optstr) {
+            return optn_err;
         }
-        return retval;
+        if (!assign_videocolors(opts)) {
+            config_error_add("Unknown error handling '%s'",
+                             allopt[optidx].name);
+            return optn_err;
+        }
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d",
+                ttycolors[CLR_RED], ttycolors[CLR_GREEN],
+                ttycolors[CLR_BROWN], ttycolors[CLR_BLUE],
+                ttycolors[CLR_MAGENTA], ttycolors[CLR_CYAN],
+                ttycolors[CLR_ORANGE], ttycolors[CLR_BRIGHT_GREEN],
+                ttycolors[CLR_YELLOW], ttycolors[CLR_BRIGHT_BLUE],
+                ttycolors[CLR_BRIGHT_MAGENTA], ttycolors[CLR_BRIGHT_CYAN]);
+        return optn_ok;
     }
+    return optn_ok;
+}
 
-    /*
-     * windowtype:  option to choose the interface for binaries built
-     * with support for more than one interface (tty + X11, for instance).
-     *
-     * Ideally, 'windowtype' should be processed first, because it
-     * causes the wc_ and wc2_ flags to be set up.
-     * For user, making it be first in a config file is trivial, use
-     * OPTIONS=windowtype:Foo
-     * as the first non-comment line of the file.
-     * Making it first in NETHACKOPTIONS requires it to be at the _end_
-     * because comma-separated option strings are processed from right
-     * to left.
-     */
-    fullname = "windowtype";
-    if (match_optname(opts, fullname, 3, TRUE)) {
-        if (iflags.windowtype_locked)
-            return retval;
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if ((op = string_for_env_opt(fullname, opts, FALSE))
-                                            != empty_optstr) {
-            if (!iflags.windowtype_deferred) {
-                char buf[WINTYPELEN];
+int
+optfn_videoshades(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* videoshades:string */
 
-                nmcpy(buf, op, WINTYPELEN);
-                choose_windows(buf);
-            } else {
-                nmcpy(g.chosen_windowtype, op, WINTYPELEN);
-            }
-        } else
-            return FALSE;
-        return retval;
+        if ((opts = string_for_env_opt(allopt[optidx].name, opts, FALSE))
+            == empty_optstr) {
+            return optn_err;
+        }
+        if (!assign_videoshades(opts)) {
+            config_error_add("Unknown error handling '%s'",
+                             allopt[optidx].name);
+            return optn_err;
+        }
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s-%s-%s", shade[0], shade[1], shade[2]);
+        return optn_ok;
     }
+    return optn_ok;
+}
+#endif /* VIDEOSHADES */
 
-#ifdef WINCHAIN
-    fullname = "windowchain";
-    if (match_optname(opts, fullname, 3, TRUE)) {
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if ((op = string_for_env_opt(fullname, opts, FALSE))
-                                            != empty_optstr) {
-            char buf[WINTYPELEN];
+#ifdef MSDOS
+int
+optfn_video_width(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        if ((op = string_for_opt(opts, negated)) != empty_optstr)
+            iflags.wc_video_width = strtol(op, NULL, 10);
+        else
+            return optn_err;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+    }
+    return optn_ok;
+}
 
-            nmcpy(buf, op, WINTYPELEN);
-            addto_windowchain(buf);
-        } else
-            return FALSE;
-        return retval;
+int
+optfn_video_height(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
     }
-#endif
+    if (req == do_set) {
+        if ((op = string_for_opt(opts, negated)) != empty_optstr)
+            iflags.wc_video_height = strtol(op, NULL, 10);
+        else
+            return optn_err;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+    }
+    return optn_ok;
+}
 
-    /* WINCAP
-     * setting window colors
-     * syntax: windowcolors=menu foregrnd/backgrnd text foregrnd/backgrnd
-     */
-    fullname = "windowcolors";
-    if (match_optname(opts, fullname, 7, TRUE)) {
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
-            if (!wc_set_window_colors(op)) {
-                config_error_add("Could not set %s '%s'", fullname, op);
-                return FALSE;
-            }
-        } else if (negated) {
-            bad_negation(fullname, TRUE);
-            return FALSE;
+#ifdef NO_TERMS
+int
+optfn_video(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* video:string */
+        if ((opts = string_for_env_opt(allopt[optidx].name, opts, FALSE))
+            == empty_optstr) {
+            return optn_err;
         }
-        return retval;
+        if (!assign_video(opts)) {
+            config_error_add("Unknown error handling '%s'",
+                             allopt[optidx].name);
+            return optn_err;
+        }
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", to_be_done);
+        return optn_ok;
     }
+    return optn_ok;
+}
 
-#ifdef CURSES_GRAPHICS
-    /* WINCAP2
-     * term_cols:amount or term_rows:amount */
-    fullname = "term_cols";
-    if (match_optname(opts, fullname, 8, TRUE)
-        /* alternate spelling */
-        || match_optname(opts, "term_columns", 9, TRUE)
-        /* different option but identical handlng */
-        || (fullname = "term_rows", match_optname(opts, fullname, 8, TRUE))) {
-        long ltmp;
+#endif /* NO_TERMS */
+#endif /* MSDOS */
 
-        if ((op = string_for_opt(opts, negated)) != empty_optstr) {
-            ltmp = atol(op);
-            if (negated) {
-                bad_negation(fullname, FALSE);
-                retval = FALSE;
+int
+optfn_warnings(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op UNUSED;
+{
+    int reslt;
+
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        reslt = warning_opts(opts, allopt[optidx].name);
+        return reslt ? optn_ok : optn_err;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+        return optn_ok;
+    }
+    return optn_ok;
+}
 
-            /* just checks atol() sanity, not logical window size sanity */
-            } else if (ltmp <= 0L || ltmp >= (long) LARGEST_INT) {
-                config_error_add("Invalid %s: %ld", fullname, ltmp);
-                retval = FALSE;
+int
+optfn_whatis_coord(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        if (negated) {
+            iflags.getpos_coords = GPCOORDS_NONE;
+            return optn_ok;
+        } else if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
+                   != empty_optstr) {
+            static char gpcoords[] = { GPCOORDS_NONE,    GPCOORDS_COMPASS,
+                                       GPCOORDS_COMFULL, GPCOORDS_MAP,
+                                       GPCOORDS_SCREEN,  '\0' };
+            char c = lowc(*op);
 
-            } else {
-                if (!strcmp(fullname, "term_rows"))
-                    iflags.wc2_term_rows = (int) ltmp;
-                else /* !strcmp(fullname, "term_cols") */
-                    iflags.wc2_term_cols = (int) ltmp;
+            if (c && index(gpcoords, c))
+                iflags.getpos_coords = c;
+            else {
+                config_error_add("Unknown %s parameter '%s'",
+                                 allopt[optidx].name, op);
+                return optn_err;
             }
-        }
-        return retval;
+        } else
+            return optn_err;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s",
+                (iflags.getpos_coords == GPCOORDS_MAP) ? "map"
+                : (iflags.getpos_coords == GPCOORDS_COMPASS) ? "compass"
+                : (iflags.getpos_coords == GPCOORDS_COMFULL) ? "full compass"
+                : (iflags.getpos_coords == GPCOORDS_SCREEN) ? "screen"
+                : "none");
+        return optn_ok;
     }
+    if (req == do_handler) {
+        return handler_whatis_coord();
+    }
+    return optn_ok;
+}
 
-    /* WINCAP2
-     * petattr:string */
-    fullname = "petattr";
-    if (match_optname(opts, fullname, sizeof "petattr" - 1, TRUE)) {
-        op = string_for_opt(opts, negated);
-        if (op != empty_optstr && negated) {
-            bad_negation(fullname, TRUE);
-            retval = FALSE;
-        } else if (op != empty_optstr) {
-#ifdef CURSES_GRAPHICS
-            int itmp = curses_read_attrs(op);
+int
+optfn_whatis_filter(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        if (negated) {
+            iflags.getloc_filter = GFILTER_NONE;
+            return optn_ok;
+        } else if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
+                   != empty_optstr) {
+            char c = lowc(*op);
 
-            if (itmp == -1) {
-                config_error_add("Unknown %s parameter '%s'", fullname, opts);
-                retval = FALSE;
-            } else
-                iflags.wc2_petattr = itmp;
-#else
-            /* non-curses windowports will not use this flag anyway
-             * but the above will not compile if we don't have curses.
-             * Just set it to a sensible default: */
-            iflags.wc2_petattr = ATR_INVERSE;
-#endif
-        } else if (negated) {
-            iflags.wc2_petattr = ATR_NONE;
-        }
-        if (retval) {
-            iflags.hilite_pet = (iflags.wc2_petattr != ATR_NONE);
-            if (!g.opt_initial)
-                g.opt_need_redraw = TRUE;
-        }
-        return retval;
+            switch (c) {
+            case 'n':
+                iflags.getloc_filter = GFILTER_NONE;
+                break;
+            case 'v':
+                iflags.getloc_filter = GFILTER_VIEW;
+                break;
+            case 'a':
+                iflags.getloc_filter = GFILTER_AREA;
+                break;
+            default: {
+                config_error_add("Unknown %s parameter '%s'",
+                                 allopt[optidx].name, op);
+                return optn_err;
+            }
+            }
+        } else
+            return optn_err;
+        return optn_ok;
     }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s",
+                (iflags.getloc_filter == GFILTER_VIEW) ? "view"
+                : (iflags.getloc_filter == GFILTER_AREA) ? "area"
+                : "none");
+        return optn_ok;
+    }
+    if (req == do_handler) {
+        return handler_whatis_filter();
+    }
+    return optn_ok;
+}
+
+int
+optfn_windowborders(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    int retval = optn_ok;
 
-    /* WINCAP2
-     * windowborders:n */
-    fullname = "windowborders";
-    if (match_optname(opts, fullname, 10, TRUE)) {
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
         op = string_for_opt(opts, negated);
         if (negated && op != empty_optstr) {
-            bad_negation(fullname, TRUE);
-            retval = FALSE;
+            bad_negation(allopt[optidx].name, TRUE);
+            retval = optn_err;
         } else {
             int itmp;
 
@@ -3764,2236 +4039,3695 @@ boolean tinitial, tfrom_file;
                 itmp = 0; /* Off */
             else if (op == empty_optstr)
                 itmp = 1; /* On */
-            else    /* Value supplied; expect 0 (off), 1 (on), or 2 (auto) */
+            else /* Value supplied; expect 0 (off), 1 (on), or 2 (auto) */
                 itmp = atoi(op);
 
             if (itmp < 0 || itmp > 2) {
                 config_error_add("Invalid %s (should be 0, 1, or 2): %s",
-                                 fullname, opts);
-                retval = FALSE;
+                                 allopt[optidx].name, opts);
+                retval = optn_err;
             } else {
                 iflags.wc2_windowborders = itmp;
             }
         }
         return retval;
     }
-#endif /* CURSES_GRAPHICS */
-
-    /* WINCAP2
-     * statuslines:n */
-    fullname = "statuslines";
-    if (match_optname(opts, fullname, 11, TRUE)) {
-        int itmp = 0;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s",
+                (iflags.wc2_windowborders == 0) ? "0=off"
+                : (iflags.wc2_windowborders == 1) ? "1=on"
+                  : (iflags.wc2_windowborders == 2) ? "2=auto"
+                    : defopt);
+        return optn_ok;
+    }
+    return optn_ok;
+}
 
-        op = string_for_opt(opts, negated);
-        if (negated) {
-            bad_negation(fullname, TRUE);
-            itmp = 2;
-            retval = FALSE;
-        } else if (op != empty_optstr) {
-            itmp = atoi(op);
-        }
-        if (itmp < 2 || itmp > 3) {
-            config_error_add("'%s' requires a value of 2 or 3", fullname);
-            retval = FALSE;
-        } else {
-            iflags.wc2_statuslines = itmp;
-            if (!g.opt_initial)
-                g.opt_need_redraw = TRUE;
-        }
-        return retval;
+#ifdef WINCHAIN
+int
+optfn_windowchain(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
     }
+    if (req == do_set) {
+        if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
+            != empty_optstr) {
+            char buf[WINTYPELEN];
 
-    /* menustyle:traditional or combination or full or partial */
-    fullname = "menustyle";
-    if (match_optname(opts, fullname, 4, TRUE)) {
-        int tmp;
-        boolean val_required = (strlen(opts) > 5 && !negated);
+            nmcpy(buf, op, WINTYPELEN);
+            addto_windowchain(buf);
+        } else
+            return optn_err;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+        return optn_ok;
+    }
+    return optn_ok;
+}
+#endif
 
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if ((op = string_for_opt(opts, !val_required)) == empty_optstr) {
-            if (val_required)
-                return FALSE; /* string_for_opt gave feedback */
-            tmp = negated ? 'n' : 'f';
-        } else {
-            tmp = lowc(*op);
-        }
-        switch (tmp) {
-        case 'n': /* none */
-        case 't': /* traditional: prompt for class(es) by symbol,
-                     prompt for each item within class(es) one at a time */
-            flags.menu_style = MENU_TRADITIONAL;
-            break;
-        case 'c': /* combination: prompt for class(es) by symbol,
-                     choose items within selected class(es) by menu */
-            flags.menu_style = MENU_COMBINATION;
-            break;
-        case 'f': /* full: choose class(es) by first menu,
-                     choose items within selected class(es) by second menu */
-            flags.menu_style = MENU_FULL;
-            break;
-        case 'p': /* partial: skip class filtering,
-                     choose items among all classes by menu */
-            flags.menu_style = MENU_PARTIAL;
-            break;
-        default:
-            config_error_add("Unknown %s parameter '%s'", fullname, op);
-            return FALSE;
+int
+optfn_windowcolors(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        /* WINCAP
+         * setting window colors
+         * syntax: windowcolors=menu foregrnd/backgrnd text
+         * foregrnd/backgrnd
+         */
+        if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
+            if (!wc_set_window_colors(op)) {
+                config_error_add("Could not set %s '%s'", allopt[optidx].name,
+                                 op);
+                return optn_err;
+            }
         }
-        return retval;
+        return optn_ok;
     }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(
+            opts, "%s/%s %s/%s %s/%s %s/%s",
+            iflags.wc_foregrnd_menu ? iflags.wc_foregrnd_menu : defbrief,
+            iflags.wc_backgrnd_menu ? iflags.wc_backgrnd_menu : defbrief,
+            iflags.wc_foregrnd_message ? iflags.wc_foregrnd_message
+                                       : defbrief,
+            iflags.wc_backgrnd_message ? iflags.wc_backgrnd_message
+                                       : defbrief,
+            iflags.wc_foregrnd_status ? iflags.wc_foregrnd_status : defbrief,
+            iflags.wc_backgrnd_status ? iflags.wc_backgrnd_status : defbrief,
+            iflags.wc_foregrnd_text ? iflags.wc_foregrnd_text : defbrief,
+            iflags.wc_backgrnd_text ? iflags.wc_backgrnd_text : defbrief);
+        return optn_ok;
+    }
+    return optn_ok;
+}
 
-    fullname = "menu_headings";
-    if (match_optname(opts, fullname, 12, TRUE)) {
-        int tmpattr;
-
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (negated) {
-            bad_negation(fullname, FALSE);
-            return FALSE;
-        } else if ((opts = string_for_env_opt(fullname, opts, FALSE))
-                                              == empty_optstr) {
-            return FALSE;
-        }
-        tmpattr = match_str2attr(opts, TRUE);
-        if (tmpattr == -1)
-            return FALSE;
-        else
-            iflags.menu_headings = tmpattr;
-        return retval;
+int
+optfn_windowtype(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated UNUSED;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
     }
+    if (req == do_set) {
+        /*
+         * windowtype:  option to choose the interface for binaries built
+         * with support for more than one interface (tty + X11, for
+         * instance).
+         *
+         * Ideally, 'windowtype' should be processed first, because it
+         * causes the wc_ and wc2_ flags to be set up.
+         * For user, making it be first in a config file is trivial, use
+         * OPTIONS=windowtype:Foo
+         * as the first non-comment line of the file.
+         * Making it first in NETHACKOPTIONS requires it to be at the
+         * _end_ because comma-separated option strings are processed from
+         * right to left.
+         */
+        if (iflags.windowtype_locked)
+            return optn_ok;
 
-    /* check for menu command mapping */
-    for (i = 0; i < SIZE(default_menu_cmd_info); i++) {
-        fullname = default_menu_cmd_info[i].name;
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (match_optname(opts, fullname, (int) strlen(fullname), TRUE)) {
-            if (negated) {
-                bad_negation(fullname, FALSE);
-                return FALSE;
-            } else if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
-                char c, op_buf[BUFSZ];
+        if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
+            != empty_optstr) {
+            if (!iflags.windowtype_deferred) {
+                char buf[WINTYPELEN];
 
-                escapes(op, op_buf);
-                c = *op_buf;
+                nmcpy(buf, op, WINTYPELEN);
+                choose_windows(buf);
+            } else {
+                nmcpy(g.chosen_windowtype, op, WINTYPELEN);
+            }
+        } else
+            return optn_err;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        Sprintf(opts, "%s", windowprocs.name);
+        return optn_ok;
+    }
+    return optn_ok;
+}
 
-                if (illegal_menu_cmd_key(c))
-                    return FALSE;
+/*
+ *    Prefix-handling functions
+ */
 
-                add_menu_cmd_alias(c, default_menu_cmd_info[i].cmd);
+int pfxfn_cond_(optidx, req, negated, opts, op)
+int optidx UNUSED;
+int req;
+boolean negated;
+char *opts;
+char *op UNUSED;
+{
+    int reslt;
+
+    if (req == do_init) {
+        condopt(0, (boolean *) 0, 0); /* make the choices match defaults */
+        return optn_ok;
+    }
+    if (req == do_set) {
+        if ((reslt = parse_cond_option(negated, opts)) != 0) {
+            switch (reslt) {
+            case 3:
+                config_error_add("Ambiguous condition option %s", opts);
+                break;
+            case 1:
+            case 2:
+            default:
+                config_error_add("Unknown condition option %s (%d)", opts,
+                                 reslt);
+                break;
             }
-            return retval;
+            return optn_err;
         }
+        g.opt_need_redraw = TRUE;
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+        return optn_ok;
+    }
+    if (req == do_handler) {
+        cond_menu();    /* in botl.c */
+        return optn_ok;
     }
+    return optn_ok;
+}
 
-    /* hilite fields in status prompt */
-    fullname = "hilite_status";
-    if (match_optname(opts, fullname, 13, TRUE)) {
-#ifdef STATUS_HILITES
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        op = string_for_opt(opts, TRUE);
-        if (op != empty_optstr && negated) {
-            clear_status_hilites();
-            return retval;
-        } else if (op == empty_optstr) {
-            config_error_add("Value is mandatory for hilite_status");
+int pfxfn_font(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    int opttype = -1;
+
+    if (req == do_init) {
+        return optn_ok;
+    }
+
+    if (req == do_set) {
+        /* WINCAP setting font options  */
+        if (optidx == opt_font_map)
+            opttype = MAP_OPTION;
+        else if (optidx == opt_font_message)
+            opttype = MESSAGE_OPTION;
+        else if (optidx == opt_font_text)
+            opttype = TEXT_OPTION;
+        else if (optidx == opt_font_menu)
+            opttype = MENU_OPTION;
+        else if (optidx == opt_font_status)
+            opttype = STATUS_OPTION;
+        else if (optidx == opt_font_size_map
+                || optidx == opt_font_size_message
+                || optidx == opt_font_size_text
+                || optidx == opt_font_size_menu
+                || optidx == opt_font_size_status) {
+            if (optidx == opt_font_size_map)
+                opttype = MAP_OPTION;
+            else if (optidx == opt_font_size_message)
+                opttype = MESSAGE_OPTION;
+            else if (optidx == opt_font_size_text)
+                opttype = TEXT_OPTION;
+            else if (optidx == opt_font_size_menu)
+                opttype = MENU_OPTION;
+            else if (optidx == opt_font_size_status)
+                opttype = STATUS_OPTION;
+            else {
+                config_error_add("Unknown %s parameter '%s'",
+                             allopt[optidx].name, opts);
+                return optn_err;
+            }
+            if (duplicate)
+                complain_about_duplicate(opts, 1);
+            if (opttype > 0 && !negated
+                && (op = string_for_opt(opts, FALSE)) != empty_optstr) {
+                switch (opttype) {
+                case MAP_OPTION:
+                    iflags.wc_fontsiz_map = atoi(op);
+                    break;
+                case MESSAGE_OPTION:
+                    iflags.wc_fontsiz_message = atoi(op);
+                    break;
+                case TEXT_OPTION:
+                    iflags.wc_fontsiz_text = atoi(op);
+                    break;
+                case MENU_OPTION:
+                    iflags.wc_fontsiz_menu = atoi(op);
+                    break;
+                case STATUS_OPTION:
+                    iflags.wc_fontsiz_status = atoi(op);
+                    break;
+                }
+            }
+            return optn_ok;
+        } else {
+            config_error_add("Unknown %s parameter '%s'",
+                             "font", opts);
             return FALSE;
         }
-        if (!parse_status_hl1(op, tfrom_file))
-            return FALSE;
-        return retval;
-#else
-        config_error_add("'%s' is not supported", fullname);
-        return FALSE;
+        if (opttype > 0
+            && (op = string_for_opt(opts, FALSE)) != empty_optstr) {
+            wc_set_font_name(opttype, op);
+#ifdef MAC
+            set_font_name(opttype, op);
 #endif
+            return optn_ok;
+        } else if (negated) {
+            bad_negation(allopt[optidx].name, TRUE);
+            return optn_err;
+        }
+        return optn_ok;
     }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
 
-    /* control over whether highlights should be displayed, and for how long */
-    fullname = "statushilites";
-    if (match_optname(opts, fullname, 9, TRUE)) {
-#ifdef STATUS_HILITES
-        if (negated) {
-            iflags.hilite_delta = 0L;
-        } else {
-            op = string_for_opt(opts, TRUE);
-            iflags.hilite_delta = (op == empty_optstr || !*op) ? 3L : atol(op);
-            if (iflags.hilite_delta < 0L)
-                iflags.hilite_delta = 1L;
+        if (optidx == opt_font_map) {
+            Sprintf(opts, "%s", iflags.wc_font_map ? iflags.wc_font_map : defopt);
+        } else if (optidx == opt_font_message) {
+            Sprintf(opts, "%s",
+                iflags.wc_font_message ? iflags.wc_font_message : defopt);
+        } else if (optidx == opt_font_status) {
+            Sprintf(opts, "%s",
+                iflags.wc_font_status ? iflags.wc_font_status : defopt);
+        } else if (optidx == opt_font_menu) {
+            Sprintf(opts, "%s",
+                iflags.wc_font_menu ? iflags.wc_font_menu : defopt);
+        } else if (optidx == opt_font_text) {
+            Sprintf(opts, "%s",
+                iflags.wc_font_text ? iflags.wc_font_text : defopt);
+        } else if (optidx == opt_font_size_map) {
+            if (iflags.wc_fontsiz_map)
+                Sprintf(opts, "%d", iflags.wc_fontsiz_map);
+            else
+                Strcpy(opts, defopt);
+        } else if (optidx == opt_font_size_message) {
+            if (iflags.wc_fontsiz_message)
+                Sprintf(opts, "%d", iflags.wc_fontsiz_message);
+            else
+                Strcpy(opts, defopt);
+        } else if (optidx == opt_font_size_status) {
+            if (iflags.wc_fontsiz_status)
+                Sprintf(opts, "%d", iflags.wc_fontsiz_status);
+            else
+                Strcpy(opts, defopt);
+        } else if (optidx == opt_font_size_menu) {
+            if (iflags.wc_fontsiz_menu)
+                Sprintf(opts, "%d", iflags.wc_fontsiz_menu);
+            else
+                Strcpy(opts, defopt);
+        } else if (optidx == opt_font_size_text) {
+            if (iflags.wc_fontsiz_text)
+                Sprintf(opts, "%d", iflags.wc_fontsiz_text);
+            else
+                Strcpy(opts, defopt);
         }
-        if (!tfrom_file)
-            reset_status_hilites();
-        return retval;
-#else
-        config_error_add("'%s' is not supported", fullname);
-        return FALSE;
+        return optn_ok;
+    }
+    return optn_ok;
+}
+
+#if defined(MICRO) && !defined(AMIGA)
+int pfxfn_IBM_(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    if (req == do_init) {
+        return optn_ok;
+    }
+    if (req == do_set) {
+        return optn_ok;
+    }
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+        return optn_ok;
+    }
+    return optn_ok;
+}
 #endif
+
+/*
+ *    General boolean option handler
+ *    (Use optidx to reference the specific option)
+ */
+
+int
+optfn_boolean(optidx, req, negated, opts, op)
+int optidx;
+int req;
+boolean negated;
+char *opts;
+char *op;
+{
+    int i;
+
+    if (req == do_init) {
+        return optn_ok;
     }
+    if (req == do_set) {
+        if (!allopt[optidx].addr)
+            return optn_ok; /* silent retreat */
 
-    fullname = "DECgraphics";
-    if (match_optname(opts, fullname, 3, FALSE)
-        || match_optname(opts, "cursesgraphics", 4, FALSE)) {
-#ifdef BACKWARD_COMPAT
-        boolean badflag = FALSE;
+        if (optidx == opt_status_updates)
+            i = 1;
 
-        if (lowc(*opts) == 'c')
-            fullname = "curses"; /* symbol set name is shorter than optname */
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (!negated) {
-            /* There is no rogue level DECgraphics-specific set */
-            if (g.symset[PRIMARY].name) {
-                badflag = TRUE;
-            } else {
-                g.symset[PRIMARY].name = dupstr(fullname);
-                if (!read_sym_file(PRIMARY)) {
-                    badflag = TRUE;
-                    clear_symsetentry(PRIMARY, TRUE);
-                } else
-                    switch_symbols(TRUE);
+        /* option that must come from config file? */
+        if (!g.opt_initial && (allopt[optidx].setwhere == set_in_config))
+            return optn_err;
+
+        /* 0 means boolean opts */
+        if (duplicate_opt_detection(allopt[optidx].name, 0))
+            complain_about_duplicate(allopt[optidx].name, 0);
+
+        op = string_for_opt(opts, TRUE);
+        if (op != empty_optstr) {
+            if (negated) {
+                config_error_add(
+                    "Negated boolean '%s' should not have a parameter",
+                    allopt[optidx].name);
+                return optn_err;
             }
-            if (badflag) {
-                config_error_add("Failure to load symbol set %s.", fullname);
-                return FALSE;
+            if (!strcmp(op, "true") || !strcmp(op, "yes")) {
+                negated = FALSE;
+            } else if (!strcmp(op, "false") || !strcmp(op, "no")) {
+                negated = TRUE;
+            } else if (!allopt[optidx].valok) {
+                config_error_add("Illegal parameter for a boolean");
+                return optn_err;
             }
         }
-        return retval;
-#else
-        config_error_add("'%s' no longer supported; use 'symset:%s' instead",
-                         fullname, fullname);
-        return FALSE;
-#endif
-    } /* "DECgraphics" */
-
-    fullname = "IBMgraphics";
-    if (match_optname(opts, fullname, 3, TRUE)) {
-#ifdef BACKWARD_COMPAT
-        const char *sym_name = fullname;
-        boolean badflag = FALSE;
-
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (!negated) {
-            for (i = 0; i < NUM_GRAPHICS; ++i) {
-                if (g.symset[i].name) {
-                    badflag = TRUE;
+        if (iflags.debug_fuzzer && !g.opt_initial) {
+            /* don't randomly toggle this/these */
+            if (optidx == opt_silent)
+                return optn_ok;
+        }
+        /* Before the change */
+        switch (optidx) {
+        case opt_female:
+            if (!strncmpi(opts, "female", 3)) {
+                if (!g.opt_initial && flags.female == negated) {
+                    config_error_add("That is not anatomically possible.");
+                    return optn_err;
                 } else {
-                    if (i == ROGUESET)
-                        sym_name = "RogueIBM";
-                    g.symset[i].name = dupstr(sym_name);
-                    if (!read_sym_file(i)) {
-                        badflag = TRUE;
-                        clear_symsetentry(i, TRUE);
-                        break;
-                    }
+                    flags.initgend = flags.female = !negated;
+                    return optn_ok;
                 }
             }
-            if (badflag) {
-                config_error_add("Failure to load symbol set %s.", sym_name);
-                return FALSE;
-            } else {
-                switch_symbols(TRUE);
-                if (!g.opt_initial && Is_rogue_level(&u.uz))
-                    assign_graphics(ROGUESET);
-            }
-        }
-        return retval;
-#else
-        config_error_add("'%s' no longer supported; use 'symset:%s' instead",
-                         fullname, fullname);
-        return FALSE;
-#endif
-    } /* "IBMgraphics" */
-
-    fullname = "MACgraphics";
-    if (match_optname(opts, fullname, 3, TRUE)) {
-#if defined(MAC_GRAPHICS_ENV) && defined(BACKWARD_COMPAT)
-        boolean badflag = FALSE;
-
-        if (duplicate)
-            complain_about_duplicate(opts, 1);
-        if (!negated) {
-            if (g.symset[PRIMARY].name) {
-                badflag = TRUE;
-            } else {
-                g.symset[PRIMARY].name = dupstr(fullname);
-                if (!read_sym_file(PRIMARY)) {
-                    badflag = TRUE;
-                    clear_symsetentry(PRIMARY, TRUE);
+            if (!strncmpi(opts, "male", 3)) {
+                if (!g.opt_initial && flags.female != negated) {
+                    config_error_add("That is not anatomically possible.");
+                    return optn_err;
+                } else {
+                    flags.initgend = flags.female = negated;
+                    return optn_ok;
                 }
             }
-            if (badflag) {
-                config_error_add("Failure to load symbol set %s.", fullname);
-                return FALSE;
-            } else {
-                switch_symbols(TRUE);
-                if (!g.opt_initial && Is_rogue_level(&u.uz))
-                    assign_graphics(ROGUESET);
-            }
+            break;
         }
-        return retval;
-#else   /* !(MAC_GRAPHICS_ENV && BACKWARD_COMPAT) */
-        config_error_add("'%s' %s; use 'symset:%s' instead",
-                         fullname,
-#ifdef MAC_GRAPHICS_ENV /* implies BACKWARD_COMPAT is not defined */
-                         "no longer supported",
-#else
-                         "is not supported",
-#endif
-                         fullname);
-        return FALSE;
-#endif  /* ?(MAC_GRAPHICS_ENV && BACKWARD_COMPAT) */
-    } /* "MACgraphics" */
 
-    /*
-     * OK, if we still haven't recognized the option, check the boolean
-     * options list.
-     */
-    for (i = 0; boolopt[i].name; i++) {
-        if (match_optname(opts, boolopt[i].name, 3, TRUE)) {
-            /* options that don't exist */
-            if (!boolopt[i].addr) {
-                if (!g.opt_initial && !negated)
-                    pline_The("\"%s\" option is not available.",
-                              boolopt[i].name);
-                return retval;
-            }
-            /* options that must come from config file */
-            if (!g.opt_initial && (boolopt[i].optflags == SET_IN_FILE)) {
-                rejectoption(boolopt[i].name);
-                return retval;
-            }
-
-            op = string_for_opt(opts, TRUE);
-            if (op != empty_optstr) {
-                if (negated) {
-                    config_error_add(
-                           "Negated boolean '%s' should not have a parameter",
-                                     boolopt[i].name);
-                    return FALSE;
-                }
-                if (!strcmp(op, "true") || !strcmp(op, "yes")) {
-                    negated = FALSE;
-                } else if (!strcmp(op, "false") || !strcmp(op, "no")) {
-                    negated = TRUE;
-                } else {
-                    config_error_add("Illegal parameter for a boolean");
-                    return FALSE;
-                }
-            }
-            if (iflags.debug_fuzzer && !g.opt_initial) {
-                /* don't randomly toggle this/these */
-                if (boolopt[i].addr == &flags.silent)
-                    return TRUE;
-            }
+        *(allopt[optidx].addr) = !negated;    /* <==== SET IT HERE */
 
-            *(boolopt[i].addr) = !negated;
+        /* After the change */
+        switch (optidx) {
+        case opt_ascii_map:
+            iflags.wc_tiled_map = negated;
+            break;
+        case opt_tiled_map:
+            iflags.wc_ascii_map = negated;
+            break;
+        }
 
-            /* 0 means boolean opts */
-            if (duplicate_opt_detection(boolopt[i].name, 0))
-                complain_about_duplicate(boolopt[i].name, 0);
-#ifdef RLECOMP
-            if (boolopt[i].addr == &iflags.rlecomp)
-                set_savepref(iflags.rlecomp ? "rlecomp" : "!rlecomp");
-#endif
-#ifdef ZEROCOMP
-            if (boolopt[i].addr == &iflags.zerocomp)
-                set_savepref(iflags.zerocomp ? "zerocomp" : "externalcomp");
-#endif
-            if (boolopt[i].addr == &iflags.wc_ascii_map) {
-                /* toggling ascii_map; set tiled_map to its opposite;
-                   what does it mean to turn off ascii map if tiled map
-                   isn't supported? -- right now, we do nothing */
-                iflags.wc_tiled_map = negated;
-            } else if (boolopt[i].addr == &iflags.wc_tiled_map) {
-                /* toggling tiled_map; set ascii_map to its opposite;
-                   as with ascii_map, what does it mean to turn off tiled
-                   map if ascii map isn't supported? */
-                iflags.wc_ascii_map = negated;
-            }
+        /* only do processing below if setting with doset() */
 
-            /* only do processing below if setting with doset() */
-            if (g.opt_initial)
-                return retval;
+        if (g.opt_initial)
+            return optn_ok;
 
-            if (boolopt[i].addr == &flags.time
+        switch (optidx) {
+        case opt_time:
 #ifdef SCORE_ON_BOTL
-                || boolopt[i].addr == &flags.showscore
+        case opt_showsore:
 #endif
-                || boolopt[i].addr == &flags.showexp) {
-                if (VIA_WINDOWPORT())
-                    status_initialize(REASSESS_ONLY);
-                g.context.botl = TRUE;
-            } else if (boolopt[i].addr == &flags.invlet_constant
-                       || boolopt[i].addr == &flags.sortpack
-                       || boolopt[i].addr == &flags.implicit_uncursed) {
-                if (!flags.invlet_constant)
-                    reassign();
-                update_inventory();
-            } else if (boolopt[i].addr == &flags.lit_corridor
-                       || boolopt[i].addr == &flags.dark_room) {
-                /*
-                 * All corridor squares seen via night vision or
-                 * candles & lamps change.  Update them by calling
-                 * newsym() on them.  Don't do this if we are
-                 * initializing the options --- the vision system
-                 * isn't set up yet.
-                 */
-                vision_recalc(2);       /* shut down vision */
-                g.vision_full_recalc = 1; /* delayed recalc */
-                if (iflags.use_color)
-                    g.opt_need_redraw = TRUE; /* darkroom refresh */
-            } else if (boolopt[i].addr == &flags.showrace
-                       || boolopt[i].addr == &iflags.use_inverse
-                       || boolopt[i].addr == &iflags.hilite_pile
-                       || boolopt[i].addr == &iflags.perm_invent
-                       || boolopt[i].addr == &iflags.wc_ascii_map
-                       || boolopt[i].addr == &iflags.wc_tiled_map) {
-                g.opt_need_redraw = TRUE;
-            } else if (boolopt[i].addr == &iflags.hilite_pet) {
+        case opt_showexp:
+            if (VIA_WINDOWPORT())
+                status_initialize(REASSESS_ONLY);
+            g.context.botl = TRUE;
+            break;
+        case opt_fixinv:
+        case opt_sortpack:
+        case opt_implicit_uncursed:
+            if (!flags.invlet_constant)
+                reassign();
+            update_inventory();
+            break;
+        case opt_lit_corridor:
+        case opt_dark_room:
+            /*
+             * All corridor squares seen via night vision or
+             * candles & lamps change.  Update them by calling
+             * newsym() on them.  Don't do this if we are
+             * initializing the options --- the vision system
+             * isn't set up yet.
+             */
+            vision_recalc(2);         /* shut down vision */
+            g.vision_full_recalc = 1; /* delayed recalc */
+            if (iflags.use_color)
+                g.opt_need_redraw = TRUE; /* darkroom refresh */
+            break;
+        case opt_showrace:
+        case opt_use_inverse:
+        case opt_hilite_pile:
+        case opt_perm_invent:
+        case opt_ascii_map:
+        case opt_tiled_map:
+            g.opt_need_redraw = TRUE;
+            break;
+        case opt_hilite_pet:
 #ifdef CURSES_GRAPHICS
-                if (WINDOWPORT("curses")) {
-                    /* if we're enabling hilite_pet and petattr isn't set,
-                       set it to Inverse; if we're disabling, leave petattr
-                       alone so that re-enabling will get current value back */
-                    if (iflags.hilite_pet && !iflags.wc2_petattr)
-                        iflags.wc2_petattr = curses_read_attrs("I");
-                }
+            if (WINDOWPORT("curses")) {
+                /* if we're enabling hilite_pet and petattr isn't set,
+                   set it to Inverse; if we're disabling, leave petattr
+                   alone so that re-enabling will get current value back
+                 */
+                if (iflags.hilite_pet && !iflags.wc2_petattr)
+                    iflags.wc2_petattr = curses_read_attrs("I");
+            }
 #endif
-            } else if (boolopt[i].addr == &iflags.wc2_hitpointbar) {
-                if (VIA_WINDOWPORT()) {
-                    /* [is reassessment really needed here?] */
-                    status_initialize(REASSESS_ONLY);
-                    g.opt_need_redraw = TRUE;
-                }
-#ifdef TEXTCOLOR
-            } else if (boolopt[i].addr == &iflags.use_color) {
+            break;
+        case opt_hitpointbar:
+            if (VIA_WINDOWPORT()) {
+                /* [is reassessment really needed here?] */
+                status_initialize(REASSESS_ONLY);
                 g.opt_need_redraw = TRUE;
+            }
+            break;
+        case opt_color:
 #ifdef TOS
-                if (iflags.BIOS) {
-                    if (colors_changed)
-                        restore_colors();
-                    else
-                        set_colors();
-                }
-#endif
-            } else if (boolopt[i].addr == &iflags.use_menu_color
-                       || boolopt[i].addr == &iflags.wc2_guicolor) {
-                update_inventory();
-#endif /* TEXTCOLOR */
-            } else if (boolopt[i].addr == &flags.mention_decor) {
-                iflags.prev_decor = STONE;
+            if (iflags.BIOS) {
+                if (colors_changed)
+                    restore_colors();
+                else
+                    set_colors();
             }
-            return retval;
+#endif
+            g.opt_need_redraw = TRUE;
+            break;
+        case opt_menucolors:
+        case opt_guicolor:
+            update_inventory();
+            break;
+        case opt_mention_decor:
+            iflags.prev_decor = STONE;
+            break;
         }
+        return optn_ok;
     }
-
-    /* Is it a symbol? */
-    if (strstr(opts, "S_") == opts && parsesymbols(opts, PRIMARY)) {
-        switch_symbols(TRUE);
-        check_gold_symbol();
-        return retval;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+        return optn_ok;
     }
-
-    /* out of valid options */
-    config_error_add("Unknown option '%s'", opts);
-    return FALSE;
+    return optn_ok;
 }
 
-/* parse key:command */
-boolean
-parsebindings(bindings)
-char* bindings;
+int spcfn_misc_menu_cmd(midx, req, negated, opts, op)
+int midx, req;
+boolean negated;
+char *opts;
+char *op;
 {
-    char *bind;
-    char key;
-    int i;
-    boolean ret = FALSE;
-
-    /* break off first binding from the rest; parse the rest */
-    if ((bind = index(bindings, ',')) != 0) {
-        *bind++ = 0;
-        ret |= parsebindings(bind);
+    if (req == do_init) {
+        return optn_ok;
     }
+    if (req == do_set) {
+        if (negated) {
+            bad_negation(default_menu_cmd_info[midx].name, FALSE);
+            return optn_err;
+        } else if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
+            char c, op_buf[BUFSZ];
 
-    /* parse a single binding: first split around : */
-    if (! (bind = index(bindings, ':')))
-        return FALSE; /* it's not a binding */
-    *bind++ = 0;
-
-    /* read the key to be bound */
-    key = txt2key(bindings);
-    if (!key) {
-        config_error_add("Unknown key binding key '%s'", bindings);
-        return FALSE;
-    }
-
-    bind = trimspaces(bind);
-
-    /* is it a special key? */
-    if (bind_specialkey(key, bind))
-        return TRUE;
+            escapes(op, op_buf);
+            c = *op_buf;
 
-    /* is it a menu command? */
-    for (i = 0; i < SIZE(default_menu_cmd_info); i++) {
-        if (!strcmp(default_menu_cmd_info[i].name, bind)) {
-            if (illegal_menu_cmd_key(key)) {
-                config_error_add("Bad menu key %s:%s", visctrl(key), bind);
-                return FALSE;
-            } else
-                add_menu_cmd_alias(key, default_menu_cmd_info[i].cmd);
-            return TRUE;
+            if (illegal_menu_cmd_key(c))
+                return optn_err;
+            add_menu_cmd_alias(c, default_menu_cmd_info[midx].cmd);
         }
+        return optn_ok;
     }
-
-    /* extended command? */
-    if (!bind_key(key, bind)) {
-        config_error_add("Unknown key binding command '%s'", bind);
-        return FALSE;
+    if (req == get_val) {
+        if (!opts)
+            return optn_err;
+        opts[0] = '\0';
+        return optn_ok;
     }
-    return TRUE;
+    return optn_ok;
 }
 
-static NEARDATA const char *menutype[] = { "traditional", "combination",
-                                           "full", "partial" };
-
-static NEARDATA const char *burdentype[] = { "unencumbered", "burdened",
-                                             "stressed",     "strained",
-                                             "overtaxed",    "overloaded" };
-
-static NEARDATA const char *runmodes[] = { "teleport", "run", "walk",
-                                           "crawl" };
-
-static NEARDATA const char *sortltype[] = { "none", "loot", "full" };
 
 /*
- * Convert the given string of object classes to a string of default object
- * symbols.
+ **********************************
+ *
+ *   Special per-option handlers
+ *
+ **********************************
  */
-void
-oc_to_str(src, dest)
-char *src, *dest;
+
+static int
+handler_menustyle()
 {
+    winid tmpwin;
+    anything any;
     int i;
+    const char *style_name;
+    menu_item *style_pick = (menu_item *) 0;
 
-    while ((i = (int) *src++) != 0) {
-        if (i < 0 || i >= MAXOCLASSES)
-            impossible("oc_to_str:  illegal object class %d", i);
-        else
-            *dest++ = def_oc_syms[i].sym;
+    tmpwin = create_nhwindow(NHW_MENU);
+    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
+    any = cg.zeroany;
+    for (i = 0; i < SIZE(menutype); i++) {
+        style_name = menutype[i];
+        /* note: separate `style_name' variable used
+           to avoid an optimizer bug in VAX C V2.3 */
+        any.a_int = i + 1;
+        add_menu(tmpwin, NO_GLYPH, &any, *style_name, 0, ATR_NONE,
+                 style_name, MENU_ITEMFLAGS_NONE);
     }
-    *dest = '\0';
-}
-
-/*
- * Add the given mapping to the menu command map list.  Always keep the
- * maps valid C strings.
- */
-void
-add_menu_cmd_alias(from_ch, to_ch)
-char from_ch, to_ch;
-{
-    if (g.n_menu_mapped >= MAX_MENU_MAPPED_CMDS) {
-        pline("out of menu map space.");
-    } else {
-        g.mapped_menu_cmds[g.n_menu_mapped] = from_ch;
-        g.mapped_menu_op[g.n_menu_mapped] = to_ch;
-        g.n_menu_mapped++;
-        g.mapped_menu_cmds[g.n_menu_mapped] = '\0';
-        g.mapped_menu_op[g.n_menu_mapped] = '\0';
+    end_menu(tmpwin, "Select menustyle:");
+    if (select_menu(tmpwin, PICK_ONE, &style_pick) > 0) {
+        flags.menu_style = style_pick->item.a_int - 1;
+        free((genericptr_t) style_pick);
     }
+    destroy_nhwindow(tmpwin);
+    return optn_ok;
 }
 
-char
-get_menu_cmd_key(ch)
-char ch;
+static int
+handler_align_misc(optidx)
+int optidx;
 {
-    char *found = index(g.mapped_menu_op, ch);
-
-    if (found) {
-        int idx = (int) (found - g.mapped_menu_op);
+    winid tmpwin;
+    anything any;
+    menu_item *window_pick = (menu_item *) 0;
+    char abuf[BUFSZ];
 
-        ch = g.mapped_menu_cmds[idx];
+    tmpwin = create_nhwindow(NHW_MENU);
+    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
+    any = cg.zeroany;
+    any.a_int = ALIGN_TOP;
+    add_menu(tmpwin, NO_GLYPH, &any, 't', 0, ATR_NONE, "top",
+             MENU_ITEMFLAGS_NONE);
+    any.a_int = ALIGN_BOTTOM;
+    add_menu(tmpwin, NO_GLYPH, &any, 'b', 0, ATR_NONE, "bottom",
+             MENU_ITEMFLAGS_NONE);
+    any.a_int = ALIGN_LEFT;
+    add_menu(tmpwin, NO_GLYPH, &any, 'l', 0, ATR_NONE, "left",
+             MENU_ITEMFLAGS_NONE);
+    any.a_int = ALIGN_RIGHT;
+    add_menu(tmpwin, NO_GLYPH, &any, 'r', 0, ATR_NONE, "right",
+             MENU_ITEMFLAGS_NONE);
+    Sprintf(abuf, "Select %s window placement relative to the map:",
+            (optidx == opt_align_message) ? "message" : "status");
+    end_menu(tmpwin, abuf);
+    if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) {
+        if (optidx == opt_align_message)
+            iflags.wc_align_message = window_pick->item.a_int;
+        else
+            iflags.wc_align_status = window_pick->item.a_int;
+        free((genericptr_t) window_pick);
     }
-    return ch;
+    destroy_nhwindow(tmpwin);
+    return optn_ok;
 }
 
-/*
- * Map the given character to its corresponding menu command.  If it
- * doesn't match anything, just return the original.
- */
-char
-map_menu_cmd(ch)
-char ch;
+static int
+handler_disclose(VOID_ARGS)
 {
-    char *found = index(g.mapped_menu_cmds, ch);
-
-    if (found) {
-        int idx = (int) (found - g.mapped_menu_cmds);
+    winid tmpwin;
+    anything any;
+    int i, n;
+    char buf[BUFSZ];
+    /* order of disclose_names[] must correspond to
+       disclosure_options in decl.c */
+    static const char *disclosure_names[] = {
+        "inventory", "attributes", "vanquished",
+        "genocides", "conduct",    "overview",
+    };
+    int disc_cat[NUM_DISCLOSURE_OPTIONS];
+    int pick_cnt, pick_idx, opt_idx;
+    char c;
+    menu_item *disclosure_pick = (menu_item *) 0;
 
-        ch = g.mapped_menu_op[idx];
+    tmpwin = create_nhwindow(NHW_MENU);
+    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
+    any = cg.zeroany;
+    for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
+        Sprintf(buf, "%-12s[%c%c]", disclosure_names[i],
+                flags.end_disclose[i], disclosure_options[i]);
+        any.a_int = i + 1;
+        add_menu(tmpwin, NO_GLYPH, &any, disclosure_options[i], 0,
+                 ATR_NONE, buf, MENU_ITEMFLAGS_NONE);
+        disc_cat[i] = 0;
     }
-    return ch;
+    end_menu(tmpwin, "Change which disclosure options categories:");
+    pick_cnt = select_menu(tmpwin, PICK_ANY, &disclosure_pick);
+    if (pick_cnt > 0) {
+        for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
+            opt_idx = disclosure_pick[pick_idx].item.a_int - 1;
+            disc_cat[opt_idx] = 1;
+        }
+        free((genericptr_t) disclosure_pick);
+        disclosure_pick = (menu_item *) 0;
+    }
+    destroy_nhwindow(tmpwin);
+
+    for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
+        if (disc_cat[i]) {
+            c = flags.end_disclose[i];
+            Sprintf(buf, "Disclosure options for %s:",
+                    disclosure_names[i]);
+            tmpwin = create_nhwindow(NHW_MENU);
+            start_menu(tmpwin, MENU_BEHAVE_STANDARD);
+            any = cg.zeroany;
+            /* 'y','n',and '+' work as alternate selectors; '-' doesn't */
+            any.a_char = DISCLOSE_NO_WITHOUT_PROMPT;
+            add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
+                     "Never disclose, without prompting",
+                     (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
+            any.a_char = DISCLOSE_YES_WITHOUT_PROMPT;
+            add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
+                     "Always disclose, without prompting",
+                     (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
+            if (*disclosure_names[i] == 'v') {
+                any.a_char = DISCLOSE_SPECIAL_WITHOUT_PROMPT; /* '#' */
+                add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
+                         "Always disclose, pick sort order from menu",
+                         (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED
+                                           : MENU_ITEMFLAGS_NONE);
+            }
+            any.a_char = DISCLOSE_PROMPT_DEFAULT_NO;
+            add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
+                     "Prompt, with default answer of \"No\"",
+                     (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
+            any.a_char = DISCLOSE_PROMPT_DEFAULT_YES;
+            add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
+                     "Prompt, with default answer of \"Yes\"",
+                     (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
+            if (*disclosure_names[i] == 'v') {
+                any.a_char = DISCLOSE_PROMPT_DEFAULT_SPECIAL; /* '?' */
+                add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
+            "Prompt, with default answer of \"Ask\" to request sort menu",
+                         (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED
+                                           : MENU_ITEMFLAGS_NONE);
+            }
+            end_menu(tmpwin, buf);
+            n = select_menu(tmpwin, PICK_ONE, &disclosure_pick);
+            if (n > 0) {
+                flags.end_disclose[i] = disclosure_pick[0].item.a_char;
+                if (n > 1 && flags.end_disclose[i] == c)
+                    flags.end_disclose[i] = disclosure_pick[1].item.a_char;
+                free((genericptr_t) disclosure_pick);
+            }
+            destroy_nhwindow(tmpwin);
+        }
+    }
+    return optn_ok;
 }
 
-void
-show_menu_controls(win, dolist)
-winid win;
-boolean dolist;
+static int
+handler_menu_headings(VOID_ARGS)
 {
-    char buf[BUFSZ];
-
-    putstr(win, 0, "Menu control keys:");
-    if (dolist) {
-        int i;
+    int mhattr = query_attr("How to highlight menu headings:");
 
-        for (i = 0; i < SIZE(default_menu_cmd_info); i++) {
-            Sprintf(buf, "%-8s %s",
-                    visctrl(get_menu_cmd_key(default_menu_cmd_info[i].cmd)),
-                    default_menu_cmd_info[i].desc);
-            putstr(win, 0, buf);
-        }
-    } else {
-        putstr(win, 0, "");
-        putstr(win, 0, "          Page    All items");
-        Sprintf(buf, "  Select   %s       %s",
-                visctrl(get_menu_cmd_key(MENU_SELECT_PAGE)),
-                visctrl(get_menu_cmd_key(MENU_SELECT_ALL)));
-        putstr(win, 0, buf);
-        Sprintf(buf, "Deselect   %s       %s",
-                visctrl(get_menu_cmd_key(MENU_UNSELECT_PAGE)),
-                visctrl(get_menu_cmd_key(MENU_UNSELECT_ALL)));
-        putstr(win, 0, buf);
-        Sprintf(buf, "  Invert   %s       %s",
-                visctrl(get_menu_cmd_key(MENU_INVERT_PAGE)),
-                visctrl(get_menu_cmd_key(MENU_INVERT_ALL)));
-        putstr(win, 0, buf);
-        putstr(win, 0, "");
-        Sprintf(buf, "   Go to   %s   Next page",
-                visctrl(get_menu_cmd_key(MENU_NEXT_PAGE)));
-        putstr(win, 0, buf);
-        Sprintf(buf, "           %s   Previous page",
-                visctrl(get_menu_cmd_key(MENU_PREVIOUS_PAGE)));
-        putstr(win, 0, buf);
-        Sprintf(buf, "           %s   First page",
-                visctrl(get_menu_cmd_key(MENU_FIRST_PAGE)));
-        putstr(win, 0, buf);
-        Sprintf(buf, "           %s   Last page",
-                visctrl(get_menu_cmd_key(MENU_LAST_PAGE)));
-        putstr(win, 0, buf);
-        putstr(win, 0, "");
-        Sprintf(buf, "           %s   Search and toggle matching entries",
-                visctrl(get_menu_cmd_key(MENU_SEARCH)));
-        putstr(win, 0, buf);
-    }
+    if (mhattr != -1)
+        iflags.menu_headings = mhattr;
+    return optn_ok;
 }
 
-#if defined(MICRO) || defined(MAC) || defined(WIN32)
-#define OPTIONS_HEADING "OPTIONS"
-#else
-#define OPTIONS_HEADING "NETHACKOPTIONS"
-#endif
+static int
+handler_msg_window(VOID_ARGS)
+{
+    winid tmpwin;
+    anything any;
 
-static char fmtstr_doset[] = "%s%-15s [%s]   ";
-static char fmtstr_doset_tab[] = "%s\t[%s]";
-static char n_currently_set[] = "(%d currently set)";
+#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS)
+    if (WINDOWPORT("tty") || WINDOWPORT("curses")) {
+        /* by Christian W. Cooper */
+        menu_item *window_pick = (menu_item *) 0;
 
-/* doset('O' command) menu entries for compound options */
-static void
-doset_add_menu(win, option, indexoffset)
-winid win;          /* window to add to */
-const char *option; /* option name */
-int indexoffset;    /* value to add to index in compopt[], or zero
-                       if option cannot be changed */
+        tmpwin = create_nhwindow(NHW_MENU);
+        start_menu(tmpwin, MENU_BEHAVE_STANDARD);
+        any = cg.zeroany;
+        if (!WINDOWPORT("curses")) {
+            any.a_char = 's';
+            add_menu(tmpwin, NO_GLYPH, &any, 's', 0, ATR_NONE,
+                     "single", MENU_ITEMFLAGS_NONE);
+            any.a_char = 'c';
+            add_menu(tmpwin, NO_GLYPH, &any, 'c', 0, ATR_NONE,
+                     "combination", MENU_ITEMFLAGS_NONE);
+        }
+        any.a_char = 'f';
+        add_menu(tmpwin, NO_GLYPH, &any, 'f', 0, ATR_NONE, "full",
+                 MENU_ITEMFLAGS_NONE);
+        any.a_char = 'r';
+        add_menu(tmpwin, NO_GLYPH, &any, 'r', 0, ATR_NONE, "reversed",
+                 MENU_ITEMFLAGS_NONE);
+        end_menu(tmpwin, "Select message history display type:");
+        if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) {
+            iflags.prevmsg_window = window_pick->item.a_char;
+            free((genericptr_t) window_pick);
+        }
+        destroy_nhwindow(tmpwin);
+    } else
+#endif /* msg_window for tty or curses */
+        pline("'%s' option is not supported for '%s'.",
+              allopt[opt_msg_window].name, windowprocs.name);
+    return optn_ok;
+}
+
+static int
+handler_number_pad(VOID_ARGS)
 {
-    const char *value = "unknown"; /* current value */
-    char buf[BUFSZ], buf2[BUFSZ];
+    winid tmpwin;
     anything any;
     int i;
+    static const char *npchoices[] = {
+        " 0 (off)", " 1 (on)", " 2 (on, MSDOS compatible)",
+        " 3 (on, phone-style digit layout)",
+        " 4 (on, phone-style layout, MSDOS compatible)",
+        "-1 (off, 'z' to move upper-left, 'y' to zap wands)"
+    };
+    menu_item *mode_pick = (menu_item *) 0;
 
+    tmpwin = create_nhwindow(NHW_MENU);
+    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
     any = cg.zeroany;
-    if (indexoffset == 0) {
-        any.a_int = 0;
-        value = get_compopt_value(option, buf2);
-    } else {
-        for (i = 0; compopt[i].name; i++)
-            if (strcmp(option, compopt[i].name) == 0)
-                break;
-
-        if (compopt[i].name) {
-            any.a_int = i + 1 + indexoffset;
-            value = get_compopt_value(option, buf2);
-        } else {
-            /* We are trying to add an option not found in compopt[].
-               This is almost certainly bad, but we'll let it through anyway
-               (with a zero value, so it can't be selected). */
-            any.a_int = 0;
+    for (i = 0; i < SIZE(npchoices); i++) {
+        any.a_int = i + 1;
+        add_menu(tmpwin, NO_GLYPH, &any, 'a' + i, 0, ATR_NONE,
+                 npchoices[i], MENU_ITEMFLAGS_NONE);
+    }
+    end_menu(tmpwin, "Select number_pad mode:");
+    if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
+        switch (mode_pick->item.a_int - 1) {
+        case 0:
+            iflags.num_pad = FALSE;
+            iflags.num_pad_mode = 0;
+            break;
+        case 1:
+            iflags.num_pad = TRUE;
+            iflags.num_pad_mode = 0;
+            break;
+        case 2:
+            iflags.num_pad = TRUE;
+            iflags.num_pad_mode = 1;
+            break;
+        case 3:
+            iflags.num_pad = TRUE;
+            iflags.num_pad_mode = 2;
+            break;
+        case 4:
+            iflags.num_pad = TRUE;
+            iflags.num_pad_mode = 3;
+            break;
+        /* last menu choice: number_pad == -1 */
+        case 5:
+            iflags.num_pad = FALSE;
+            iflags.num_pad_mode = 1;
+            break;
         }
+        reset_commands(FALSE);
+        number_pad(iflags.num_pad ? 1 : 0);
+        free((genericptr_t) mode_pick);
     }
-    /* "    " replaces "a - " -- assumes menus follow that style */
-    if (!iflags.menu_tab_sep)
-        Sprintf(buf, fmtstr_doset, any.a_int ? "" : "    ", option,
-                value);
-    else
-        Sprintf(buf, fmtstr_doset_tab, option, value);
-    add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_ITEMFLAGS_NONE);
+    destroy_nhwindow(tmpwin);
+    return optn_ok;
 }
 
-static void
-opts_add_others(win, name, id, bufx, nset)
-winid win;
-const char *name;
-int id;
-char *bufx;
-int nset;
+static int
+handler_paranoid_confirmation(VOID_ARGS)
 {
-    char buf[BUFSZ], buf2[BUFSZ];
-    anything any = cg.zeroany;
+    winid tmpwin;
+    anything any;
+    int i;
+    menu_item *paranoia_picks = (menu_item *) 0;
 
-    any.a_int = id;
-    if (!bufx)
-        Sprintf(buf2, n_currently_set, nset);
-    else
-        Sprintf(buf2, "%s", bufx);
-    if (!iflags.menu_tab_sep)
-        Sprintf(buf, fmtstr_doset, any.a_int ? "" : "    ",
-                name, buf2);
-    else
-        Sprintf(buf, fmtstr_doset_tab, name, buf2);
-    add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_ITEMFLAGS_NONE);
+    tmpwin = create_nhwindow(NHW_MENU);
+    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
+    any = cg.zeroany;
+    for (i = 0; paranoia[i].flagmask != 0; ++i) {
+        if (paranoia[i].flagmask == PARANOID_BONES && !wizard)
+            continue;
+        any.a_int = paranoia[i].flagmask;
+        add_menu(tmpwin, NO_GLYPH, &any, *paranoia[i].argname, 0,
+                 ATR_NONE, paranoia[i].explain,
+                 (flags.paranoia_bits & paranoia[i].flagmask)
+                     ? MENU_ITEMFLAGS_SELECTED
+                     : MENU_ITEMFLAGS_NONE);
+    }
+    end_menu(tmpwin, "Actions requiring extra confirmation:");
+    i = select_menu(tmpwin, PICK_ANY, &paranoia_picks);
+    if (i >= 0) {
+        /* player didn't cancel; we reset all the paranoia options
+           here even if there were no items picked, since user
+           could have toggled off preselected ones to end up with 0 */
+        flags.paranoia_bits = 0;
+        if (i > 0) {
+            /* at least 1 item set, either preselected or newly picked */
+            while (--i >= 0)
+                flags.paranoia_bits |= paranoia_picks[i].item.a_int;
+            free((genericptr_t) paranoia_picks);
+        }
+    }
+    destroy_nhwindow(tmpwin);
+    return optn_ok;
 }
 
 static int
-count_cond(VOID_ARGS)
+handler_pickup_burden(VOID_ARGS)
 {
-    int i, cnt = 0;
+    winid tmpwin;
+    anything any;
+    int i;
+    const char *burden_name, *burden_letters = "ubsntl";
+    menu_item *burden_pick = (menu_item *) 0;
 
-    for (i = 0; i < CONDITION_COUNT; ++i) {
-        if (condtests[i].enabled)
-            cnt++;
+    tmpwin = create_nhwindow(NHW_MENU);
+    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
+    any = cg.zeroany;
+    for (i = 0; i < SIZE(burdentype); i++) {
+        burden_name = burdentype[i];
+        any.a_int = i + 1;
+        add_menu(tmpwin, NO_GLYPH, &any, burden_letters[i], 0, ATR_NONE,
+                 burden_name, MENU_ITEMFLAGS_NONE);
     }
-    return cnt;
+    end_menu(tmpwin, "Select encumbrance level:");
+    if (select_menu(tmpwin, PICK_ONE, &burden_pick) > 0) {
+        flags.pickup_burden = burden_pick->item.a_int - 1;
+        free((genericptr_t) burden_pick);
+    }
+    destroy_nhwindow(tmpwin);
+    return optn_ok;
 }
 
-int
-count_apes(VOID_ARGS)
+static int
+handler_pickup_types(VOID_ARGS)
 {
-    int numapes = 0;
-    struct autopickup_exception *ape = g.apelist;
-
-    while (ape) {
-      numapes++;
-      ape = ape->next;
-    }
+    char buf[BUFSZ];
 
-    return numapes;
+    /* parseoptions will prompt for the list of types */
+    (void) parseoptions(strcpy(buf, "pickup_types"),
+                        FALSE, FALSE);
+    return optn_ok;
 }
 
-enum opt_other_enums {
-    OPT_OTHER_COND = -5,
-    OPT_OTHER_MSGTYPE = -4,
-    OPT_OTHER_MENUCOLOR = -3,
-    OPT_OTHER_STATHILITE = -2,
-    OPT_OTHER_APEXC = -1
-    /* these must be < 0 */
-};
+static int
+handler_runmode(VOID_ARGS)
+{
+    winid tmpwin;
+    anything any;
+    int i;
+    const char *mode_name;
+    menu_item *mode_pick = (menu_item *) 0;
 
-static struct other_opts {
-    const char *name;
-    int optflags;
-    enum opt_other_enums code;
-    int NDECL((*othr_count_func));
-} othropt[] = {
-    { "autopickup exceptions", SET_IN_GAME, OPT_OTHER_APEXC, count_apes },
-    { "status condition fields", SET_IN_GAME,
-      OPT_OTHER_COND, count_cond },
-    { "menu colors", SET_IN_GAME, OPT_OTHER_MENUCOLOR, count_menucolors },
-    { "message types", SET_IN_GAME, OPT_OTHER_MSGTYPE, msgtype_count },
-#ifdef STATUS_HILITES
-    { "status hilite rules", SET_IN_GAME, OPT_OTHER_STATHILITE,
-      count_status_hilites },
-#endif
-    { (char *) 0, 0, (enum opt_other_enums) 0 },
-};
+    tmpwin = create_nhwindow(NHW_MENU);
+    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
+    any = cg.zeroany;
+    for (i = 0; i < SIZE(runmodes); i++) {
+        mode_name = runmodes[i];
+        any.a_int = i + 1;
+        add_menu(tmpwin, NO_GLYPH, &any, *mode_name, 0, ATR_NONE,
+                 mode_name, MENU_ITEMFLAGS_NONE);
+    }
+    end_menu(tmpwin, "Select run/travel display mode:");
+    if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
+        flags.runmode = mode_pick->item.a_int - 1;
+        free((genericptr_t) mode_pick);
+    }
+    destroy_nhwindow(tmpwin);
+    return optn_ok;
+}
 
-/* the 'O' command */
-int
-doset() /* changing options via menu by Per Liboriussen */
+static int
+handler_sortloot(VOID_ARGS)
 {
-    static boolean made_fmtstr = FALSE;
-    char buf[BUFSZ];
-    const char *name;
-    int i = 0, pass, boolcount, pick_cnt, pick_idx, opt_indx;
-    boolean *bool_p;
     winid tmpwin;
     anything any;
-    menu_item *pick_list;
-    int indexoffset, startpass, endpass, optflags;
-    boolean setinitial = FALSE, fromfile = FALSE;
-    unsigned longest_name_len;
+    int i, n;
+    const char *sortl_name;
+    menu_item *sortl_pick = (menu_item *) 0;
 
     tmpwin = create_nhwindow(NHW_MENU);
     start_menu(tmpwin, MENU_BEHAVE_STANDARD);
+    any = cg.zeroany;
+    for (i = 0; i < SIZE(sortltype); i++) {
+        sortl_name = sortltype[i];
+        any.a_char = *sortl_name;
+        add_menu(tmpwin, NO_GLYPH, &any, *sortl_name, 0, ATR_NONE,
+                 sortl_name, (flags.sortloot == *sortl_name)
+                                ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
+    }
+    end_menu(tmpwin, "Select loot sorting type:");
+    n = select_menu(tmpwin, PICK_ONE, &sortl_pick);
+    if (n > 0) {
+        char c = sortl_pick[0].item.a_char;
 
-#ifdef notyet /* SYSCF */
-    /* XXX I think this is still fragile.  Fixing initial/from_file and/or
-       changing the SET_* etc to bitmaps will let me make this better. */
-    if (wizard)
-        startpass = SET_IN_SYS;
-    else
-#endif
-        startpass = DISP_IN_GAME;
-    endpass = (wizard) ? SET_IN_WIZGAME : SET_IN_GAME;
+        if (n > 1 && c == flags.sortloot)
+            c = sortl_pick[1].item.a_char;
+        flags.sortloot = c;
+        free((genericptr_t) sortl_pick);
+    }
+    destroy_nhwindow(tmpwin);
+    return optn_ok;
+}
 
-    if (!made_fmtstr && !iflags.menu_tab_sep) {
-        /* spin through the options to find the longest name
-           and adjust the format string accordingly */
-        longest_name_len = 0;
-        for (pass = 0; pass <= 2; pass++)
-            for (i = 0; (name = ((pass == 0) ? boolopt[i].name
-                                 : (pass == 1) ? compopt[i].name
-                                   : othropt[i].name)) != 0; i++) {
-                if (pass == 0 && !boolopt[i].addr)
-                    continue;
-                optflags = (pass == 0) ? boolopt[i].optflags
-                           : (pass == 1) ? compopt[i].optflags
-                             : othropt[i].optflags;
-                if (optflags < startpass || optflags > endpass)
-                    continue;
-                if ((is_wc_option(name) && !wc_supported(name))
-                    || (is_wc2_option(name) && !wc2_supported(name)))
-                    continue;
-
-                if (strlen(name) > longest_name_len)
-                    longest_name_len = strlen(name);
-            }
-        Sprintf(fmtstr_doset, "%%s%%-%us [%%s]", longest_name_len);
-        made_fmtstr = TRUE;
-    }
-
-    any = cg.zeroany;
-    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
-             "Booleans (selecting will toggle value):", MENU_ITEMFLAGS_NONE);
-    any.a_int = 0;
-    /* first list any other non-modifiable booleans, then modifiable ones */
-    for (pass = 0; pass <= 1; pass++)
-        for (i = 0; (name = boolopt[i].name) != 0; i++)
-            if ((bool_p = boolopt[i].addr) != 0
-                && ((boolopt[i].optflags <= DISP_IN_GAME && pass == 0)
-                    || (boolopt[i].optflags >= SET_IN_GAME && pass == 1))) {
-                if (bool_p == &flags.female)
-                    continue; /* obsolete */
-                if (boolopt[i].optflags == SET_IN_WIZGAME && !wizard)
-                    continue;
-                if ((is_wc_option(name) && !wc_supported(name))
-                    || (is_wc2_option(name) && !wc2_supported(name)))
-                    continue;
-
-                any.a_int = (pass == 0) ? 0 : i + 1;
-                if (!iflags.menu_tab_sep)
-                    Sprintf(buf, fmtstr_doset, (pass == 0) ? "    " : "",
-                            name, *bool_p ? "true" : "false");
-                else
-                    Sprintf(buf, fmtstr_doset_tab,
-                            name, *bool_p ? "true" : "false");
-                add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf,
-                         MENU_ITEMFLAGS_NONE);
-            }
-
-    boolcount = i;
-    indexoffset = boolcount;
-    any = cg.zeroany;
-    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_ITEMFLAGS_NONE);
-    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
-             "Compounds (selecting will prompt for new value):",
-             MENU_ITEMFLAGS_NONE);
-
-    /* deliberately put playmode, name, role+race+gender+align first */
-    doset_add_menu(tmpwin, "playmode", 0);
-    doset_add_menu(tmpwin, "name", 0);
-    doset_add_menu(tmpwin, "role", 0);
-    doset_add_menu(tmpwin, "race", 0);
-    doset_add_menu(tmpwin, "gender", 0);
-    doset_add_menu(tmpwin, "align", 0);
-
-    for (pass = startpass; pass <= endpass; pass++)
-        for (i = 0; (name = compopt[i].name) != 0; i++)
-            if (compopt[i].optflags == pass) {
-                if (!strcmp(name, "playmode")  || !strcmp(name, "name")
-                    || !strcmp(name, "role")   || !strcmp(name, "race")
-                    || !strcmp(name, "gender") || !strcmp(name, "align"))
-                    continue;
-                if ((is_wc_option(name) && !wc_supported(name))
-                    || (is_wc2_option(name) && !wc2_supported(name)))
-                    continue;
-
-                doset_add_menu(tmpwin, name,
-                               (pass == DISP_IN_GAME) ? 0 : indexoffset);
-            }
-
-    any = cg.zeroany;
-    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_ITEMFLAGS_NONE);
-    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
-             "Other settings:", MENU_ITEMFLAGS_NONE);
-
-    for (i = 0; (name = othropt[i].name) != 0; i++) {
-        if ((is_wc_option(name) && !wc_supported(name))
-            || (is_wc2_option(name) && !wc2_supported(name)))
-            continue;
-        opts_add_others(tmpwin, name, othropt[i].code,
-                        (char *) 0, othropt[i].othr_count_func());
-    }
+static int
+handler_whatis_coord(VOID_ARGS)
+{
+    winid tmpwin;
+    anything any;
+    char buf[BUFSZ];
+    menu_item *window_pick = (menu_item *) 0;
+    int pick_cnt;
+    char gp = iflags.getpos_coords;
 
-#ifdef PREFIXES_IN_USE
+    tmpwin = create_nhwindow(NHW_MENU);
+    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
     any = cg.zeroany;
+    any.a_char = GPCOORDS_COMPASS;
+    add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_COMPASS, 0, ATR_NONE,
+             "compass ('east' or '3s' or '2n,4w')",
+             (gp == GPCOORDS_COMPASS)
+                ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
+    any.a_char = GPCOORDS_COMFULL;
+    add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_COMFULL, 0, ATR_NONE,
+             "full compass ('east' or '3south' or '2north,4west')",
+             (gp == GPCOORDS_COMFULL)
+                ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
+    any.a_char = GPCOORDS_MAP;
+    add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_MAP, 0, ATR_NONE,
+             "map <x,y>",
+             (gp == GPCOORDS_MAP)
+                ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
+    any.a_char = GPCOORDS_SCREEN;
+    add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_SCREEN, 0, ATR_NONE,
+             "screen [row,column]",
+             (gp == GPCOORDS_SCREEN)
+                ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
+    any.a_char = GPCOORDS_NONE;
+    add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_NONE, 0, ATR_NONE,
+             "none (no coordinates displayed)",
+             (gp == GPCOORDS_NONE)
+                ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
+    any.a_long = 0L;
     add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_ITEMFLAGS_NONE);
-    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
-             "Variable playground locations:", MENU_ITEMFLAGS_NONE);
-    for (i = 0; i < PREFIX_COUNT; i++)
-        doset_add_menu(tmpwin, fqn_prefix_names[i], 0);
-#endif
-    end_menu(tmpwin, "Set what options?");
-    g.opt_need_redraw = FALSE;
-    if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &pick_list)) > 0) {
-        /*
-         * Walk down the selection list and either invert the booleans
-         * or prompt for new values. In most cases, call parseoptions()
-         * to take care of options that require special attention, like
-         * redraws.
-         */
-        for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
-            opt_indx = pick_list[pick_idx].item.a_int - 1;
-            if (opt_indx < -1)
-                opt_indx++; /* -1 offset for select_menu() */
-            if (opt_indx == OPT_OTHER_APEXC) {
-                (void) special_handling("autopickup_exception", setinitial,
-                                        fromfile);
-#ifdef STATUS_HILITES
-            } else if (opt_indx == OPT_OTHER_STATHILITE) {
-                if (!status_hilite_menu()) {
-                    pline("Bad status hilite(s) specified.");
-                } else {
-                    if (wc2_supported("hilite_status"))
-                        preference_update("hilite_status");
-                }
+    Sprintf(buf, "map: upper-left: <%d,%d>, lower-right: <%d,%d>%s",
+            1, 0, COLNO - 1, ROWNO - 1,
+            flags.verbose ? "; column 0 unused, off left edge" : "");
+    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_ITEMFLAGS_NONE);
+    if (strcmp(windowprocs.name, "tty")) /* only show for non-tty */
+        add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
+   "screen: row is offset to accommodate tty interface's use of top line",
+                 MENU_ITEMFLAGS_NONE);
+#if COLNO == 80
+#define COL80ARG flags.verbose ? "; column 80 is not used" : ""
+#else
+#define COL80ARG ""
 #endif
-            } else if (opt_indx == OPT_OTHER_MENUCOLOR) {
-                    (void) special_handling("menu_colors", setinitial,
-                                            fromfile);
-            } else if (opt_indx == OPT_OTHER_COND) {
-                    (void) special_handling("condition_options", setinitial,
-                                            fromfile);
-            } else if (opt_indx == OPT_OTHER_MSGTYPE) {
-                    (void) special_handling("msgtype", setinitial, fromfile);
-            } else if (opt_indx < boolcount) {
-                /* boolean option */
-                Sprintf(buf, "%s%s", *boolopt[opt_indx].addr ? "!" : "",
-                        boolopt[opt_indx].name);
-                (void) parseoptions(buf, setinitial, fromfile);
-                if (wc_supported(boolopt[opt_indx].name)
-                    || wc2_supported(boolopt[opt_indx].name))
-                    preference_update(boolopt[opt_indx].name);
-            } else {
-                /* compound option */
-                opt_indx -= boolcount;
-
-                if (!special_handling(compopt[opt_indx].name, setinitial,
-                                      fromfile)) {
-                    char abuf[BUFSZ];
-
-                    Sprintf(buf, "Set %s to what?", compopt[opt_indx].name);
-                    abuf[0] = '\0';
-                    getlin(buf, abuf);
-                    if (abuf[0] == '\033')
-                        continue;
-                    Sprintf(buf, "%s:", compopt[opt_indx].name);
-                    (void) strncat(eos(buf), abuf,
-                                   (sizeof buf - 1 - strlen(buf)));
-                    /* pass the buck */
-                    (void) parseoptions(buf, setinitial, fromfile);
-                }
-                if (wc_supported(compopt[opt_indx].name)
-                    || wc2_supported(compopt[opt_indx].name))
-                    preference_update(compopt[opt_indx].name);
-            }
-        }
-        free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
+    Sprintf(buf, "screen: upper-left: [%02d,%02d], lower-right: [%d,%d]%s",
+            0 + 2, 1, ROWNO - 1 + 2, COLNO - 1, COL80ARG);
+#undef COL80ARG
+    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_ITEMFLAGS_NONE);
+    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_ITEMFLAGS_NONE);
+    end_menu(tmpwin,
+        "Select coordinate display when auto-describing a map position:");
+    if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &window_pick)) > 0) {
+        iflags.getpos_coords = window_pick[0].item.a_char;
+        /* PICK_ONE doesn't unselect preselected entry when
+           selecting another one */
+        if (pick_cnt > 1 && iflags.getpos_coords == gp)
+            iflags.getpos_coords = window_pick[1].item.a_char;
+        free((genericptr_t) window_pick);
     }
-
     destroy_nhwindow(tmpwin);
-    if (g.opt_need_redraw) {
-        check_gold_symbol();
-        reglyph_darkroom();
-        (void) doredraw();
-    }
-    return 0;
+    return optn_ok;
 }
 
-/* common to msg-types, menu-colors, autopickup-exceptions */
 static int
-handle_add_list_remove(optname, numtotal)
-const char *optname;
-int numtotal;
+handler_whatis_filter(VOID_ARGS)
 {
     winid tmpwin;
     anything any;
-    int i, pick_cnt, opt_idx;
-    menu_item *pick_list = (menu_item *) 0;
-    static const struct action {
-        char letr;
-        const char *desc;
-    } action_titles[] = {
-        { 'a', "add new %s" },         /* [0] */
-        { 'l', "list %s" },            /* [1] */
-        { 'r', "remove existing %s" }, /* [2] */
-        { 'x', "exit this menu" },     /* [3] */
-    };
+    menu_item *window_pick = (menu_item *) 0;
+    int pick_cnt;
+    char gf = iflags.getloc_filter;
 
-    opt_idx = 0;
     tmpwin = create_nhwindow(NHW_MENU);
     start_menu(tmpwin, MENU_BEHAVE_STANDARD);
     any = cg.zeroany;
-    for (i = 0; i < SIZE(action_titles); i++) {
-        char tmpbuf[BUFSZ];
-
-        any.a_int++;
-        /* omit list and remove if there aren't any yet */
-        if (!numtotal && (i == 1 || i == 2))
-            continue;
-        Sprintf(tmpbuf, action_titles[i].desc,
-                (i == 1) ? makeplural(optname) : optname);
-        add_menu(tmpwin, NO_GLYPH, &any, action_titles[i].letr, 0, ATR_NONE,
-                 tmpbuf, (i == 3) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
+    any.a_char = (GFILTER_NONE + 1);
+    add_menu(tmpwin, NO_GLYPH, &any, 'n',
+             0, ATR_NONE, "no filtering",
+             (gf == GFILTER_NONE)
+                ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
+    any.a_char = (GFILTER_VIEW + 1);
+    add_menu(tmpwin, NO_GLYPH, &any, 'v',
+             0, ATR_NONE, "in view only",
+             (gf == GFILTER_VIEW)
+                ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
+    any.a_char = (GFILTER_AREA + 1);
+    add_menu(tmpwin, NO_GLYPH, &any, 'a',
+             0, ATR_NONE, "in same area",
+             (gf == GFILTER_AREA)
+                ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
+    end_menu(tmpwin,
+       "Select location filtering when going for next/previous map position:");
+    if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &window_pick)) > 0) {
+        iflags.getloc_filter = (window_pick[0].item.a_char - 1);
+        /* PICK_ONE doesn't unselect preselected entry when
+           selecting another one */
+        if (pick_cnt > 1 && iflags.getloc_filter == gf)
+            iflags.getloc_filter = (window_pick[1].item.a_char - 1);
+        free((genericptr_t) window_pick);
     }
-    end_menu(tmpwin, "Do what?");
-    if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &pick_list)) > 0) {
-        opt_idx = pick_list[0].item.a_int - 1;
-        if (pick_cnt > 1 && opt_idx == 3)
-            opt_idx = pick_list[1].item.a_int - 1;
-        free((genericptr_t) pick_list);
-    } else
-        opt_idx = 3; /* none selected, exit menu */
     destroy_nhwindow(tmpwin);
-    return opt_idx;
+    return optn_ok;
 }
 
-static boolean
-special_handling(optname, setinitial, setfromfile)
-const char *optname;
-boolean setinitial, setfromfile;
+static int
+handler_symset(optidx)
+int optidx;
 {
     winid tmpwin;
     anything any;
-    int i, n;
+    int n;
     char buf[BUFSZ];
+    menu_item *symset_pick = (menu_item *) 0;
+    boolean rogueflag = (optidx == opt_roguesymset),
+            ready_to_switch = FALSE,
+            nothing_to_do = FALSE;
+    char *symset_name, fmtstr[20];
+    struct symsetentry *sl;
+    int res, which_set, setcount = 0, chosen = -2, defindx = 0;
+
+    which_set = rogueflag ? ROGUESET : PRIMARY;
+    g.symset_list = (struct symsetentry *) 0;
+    /* clear symset[].name as a flag to read_sym_file() to build list */
+    symset_name = g.symset[which_set].name;
+    g.symset[which_set].name = (char *) 0;
+
+    res = read_sym_file(which_set);
+    /* put symset name back */
+    g.symset[which_set].name = symset_name;
+
+    if (res && g.symset_list) {
+        int thissize,
+            biggest = (int) (sizeof "Default Symbols" - sizeof ""),
+            big_desc = 0;
+
+        for (sl = g.symset_list; sl; sl = sl->next) {
+            /* check restrictions */
+            if (rogueflag ? sl->primary : sl->rogue)
+                continue;
+#ifndef MAC_GRAPHICS_ENV
+            if (sl->handling == H_MAC)
+                continue;
+#endif
 
-    /* Special handling of menustyle, pickup_burden, pickup_types,
-     * disclose, runmode, msg_window, menu_headings, sortloot,
-     * and number_pad options.
-     * Also takes care of interactive autopickup_exception_handling changes.
-     */
-    if (!strcmp("menustyle", optname)) {
-        const char *style_name;
-        menu_item *style_pick = (menu_item *) 0;
-
-        tmpwin = create_nhwindow(NHW_MENU);
-        start_menu(tmpwin, MENU_BEHAVE_STANDARD);
-        any = cg.zeroany;
-        for (i = 0; i < SIZE(menutype); i++) {
-            style_name = menutype[i];
-            /* note: separate `style_name' variable used
-               to avoid an optimizer bug in VAX C V2.3 */
-            any.a_int = i + 1;
-            add_menu(tmpwin, NO_GLYPH, &any, *style_name, 0, ATR_NONE,
-                     style_name, MENU_ITEMFLAGS_NONE);
-        }
-        end_menu(tmpwin, "Select menustyle:");
-        if (select_menu(tmpwin, PICK_ONE, &style_pick) > 0) {
-            flags.menu_style = style_pick->item.a_int - 1;
-            free((genericptr_t) style_pick);
+            setcount++;
+            /* find biggest name */
+            thissize = sl->name ? (int) strlen(sl->name) : 0;
+            if (thissize > biggest)
+                biggest = thissize;
+            thissize = sl->desc ? (int) strlen(sl->desc) : 0;
+            if (thissize > big_desc)
+                big_desc = thissize;
+        }
+        if (!setcount) {
+            pline("There are no appropriate %s symbol sets available.",
+                  rogueflag ? "rogue level" : "primary");
+            return TRUE;
         }
-        destroy_nhwindow(tmpwin);
-    } else if (!strcmp("paranoid_confirmation", optname)) {
-        menu_item *paranoia_picks = (menu_item *) 0;
 
+        Sprintf(fmtstr, "%%-%ds %%s", biggest + 2);
         tmpwin = create_nhwindow(NHW_MENU);
         start_menu(tmpwin, MENU_BEHAVE_STANDARD);
         any = cg.zeroany;
-        for (i = 0; paranoia[i].flagmask != 0; ++i) {
-            if (paranoia[i].flagmask == PARANOID_BONES && !wizard)
+        any.a_int = 1; /* -1 + 2 [see 'if (sl->name) {' below]*/
+        if (!symset_name)
+            defindx = any.a_int;
+        add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
+                 "Default Symbols",
+                 (any.a_int == defindx) ? MENU_ITEMFLAGS_SELECTED
+                                        : MENU_ITEMFLAGS_NONE);
+
+        for (sl = g.symset_list; sl; sl = sl->next) {
+            /* check restrictions */
+            if (rogueflag ? sl->primary : sl->rogue)
                 continue;
-            any.a_int = paranoia[i].flagmask;
-            add_menu(tmpwin, NO_GLYPH, &any, *paranoia[i].argname, 0,
-                     ATR_NONE, paranoia[i].explain,
-                     (flags.paranoia_bits & paranoia[i].flagmask)
-                         ? MENU_ITEMFLAGS_SELECTED
-                         : MENU_ITEMFLAGS_NONE);
-        }
-        end_menu(tmpwin, "Actions requiring extra confirmation:");
-        i = select_menu(tmpwin, PICK_ANY, &paranoia_picks);
-        if (i >= 0) {
-            /* player didn't cancel; we reset all the paranoia options
-               here even if there were no items picked, since user
-               could have toggled off preselected ones to end up with 0 */
-            flags.paranoia_bits = 0;
-            if (i > 0) {
-                /* at least 1 item set, either preselected or newly picked */
-                while (--i >= 0)
-                    flags.paranoia_bits |= paranoia_picks[i].item.a_int;
-                free((genericptr_t) paranoia_picks);
+#ifndef MAC_GRAPHICS_ENV
+            if (sl->handling == H_MAC)
+                continue;
+#endif
+            if (sl->name) {
+                /* +2: sl->idx runs from 0 to N-1 for N symsets;
+                   +1 because Defaults are implicitly in slot [0];
+                   +1 again so that valid data is never 0 */
+                any.a_int = sl->idx + 2;
+                if (symset_name && !strcmpi(sl->name, symset_name))
+                    defindx = any.a_int;
+                Sprintf(buf, fmtstr, sl->name, sl->desc ? sl->desc : "");
+                add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf,
+                         (any.a_int == defindx) ? MENU_ITEMFLAGS_SELECTED
+                                                : MENU_ITEMFLAGS_NONE);
             }
         }
+        Sprintf(buf, "Select %ssymbol set:",
+                rogueflag ? "rogue level " : "");
+        end_menu(tmpwin, buf);
+        n = select_menu(tmpwin, PICK_ONE, &symset_pick);
+        if (n > 0) {
+            chosen = symset_pick[0].item.a_int;
+            /* if picking non-preselected entry yields 2, make sure
+               that we're going with the non-preselected one */
+            if (n == 2 && chosen == defindx)
+                chosen = symset_pick[1].item.a_int;
+            chosen -= 2; /* convert menu index to symset index;
+                          * "Default symbols" have index -1 */
+            free((genericptr_t) symset_pick);
+        } else if (n == 0 && defindx > 0) {
+            chosen = defindx - 2;
+        }
         destroy_nhwindow(tmpwin);
-    } else if (!strcmp("pickup_burden", optname)) {
-        const char *burden_name, *burden_letters = "ubsntl";
-        menu_item *burden_pick = (menu_item *) 0;
 
-        tmpwin = create_nhwindow(NHW_MENU);
-        start_menu(tmpwin, MENU_BEHAVE_STANDARD);
-        any = cg.zeroany;
-        for (i = 0; i < SIZE(burdentype); i++) {
-            burden_name = burdentype[i];
-            any.a_int = i + 1;
-            add_menu(tmpwin, NO_GLYPH, &any, burden_letters[i], 0, ATR_NONE,
-                     burden_name, MENU_ITEMFLAGS_NONE);
+        if (chosen > -1) {
+            /* chose an actual symset name from file */
+            for (sl = g.symset_list; sl; sl = sl->next)
+                if (sl->idx == chosen)
+                    break;
+            if (sl) {
+                /* free the now stale attributes */
+                clear_symsetentry(which_set, TRUE);
+
+                /* transfer only the name of the symbol set */
+                g.symset[which_set].name = dupstr(sl->name);
+                ready_to_switch = TRUE;
+            }
+        } else if (chosen == -1) {
+            /* explicit selection of defaults */
+            /* free the now stale symset attributes */
+            clear_symsetentry(which_set, TRUE);
+        } else
+            nothing_to_do = TRUE;
+    } else if (!res) {
+        /* The symbols file could not be accessed */
+        pline("Unable to access \"%s\" file.", SYMBOLS);
+        return TRUE;
+    } else if (!g.symset_list) {
+        /* The symbols file was empty */
+        pline("There were no symbol sets found in \"%s\".", SYMBOLS);
+        return TRUE;
+    }
+
+    /* clean up */
+    while ((sl = g.symset_list) != 0) {
+        g.symset_list = sl->next;
+        if (sl->name)
+            free((genericptr_t) sl->name), sl->name = (char *) 0;
+        if (sl->desc)
+            free((genericptr_t) sl->desc), sl->desc = (char *) 0;
+        free((genericptr_t) sl);
+    }
+
+    if (nothing_to_do)
+        return TRUE;
+
+    /* Set default symbols and clear the handling value */
+    if (rogueflag)
+        init_rogue_symbols();
+    else
+        init_primary_symbols();
+
+    if (g.symset[which_set].name) {
+        /* non-default symbols */
+        if (read_sym_file(which_set)) {
+            ready_to_switch = TRUE;
+        } else {
+            clear_symsetentry(which_set, TRUE);
+            return TRUE;
         }
-        end_menu(tmpwin, "Select encumbrance level:");
-        if (select_menu(tmpwin, PICK_ONE, &burden_pick) > 0) {
-            flags.pickup_burden = burden_pick->item.a_int - 1;
-            free((genericptr_t) burden_pick);
+    }
+
+    if (ready_to_switch)
+        switch_symbols(TRUE);
+
+    if (Is_rogue_level(&u.uz)) {
+        if (rogueflag)
+            assign_graphics(ROGUESET);
+    } else if (!rogueflag)
+        assign_graphics(PRIMARY);
+    preference_update("symset");
+    g.opt_need_redraw = TRUE;
+    return optidx;
+}
+
+static int
+handler_autopickup_exception(VOID_ARGS)
+{
+    winid tmpwin;
+    anything any;
+    int i;
+    int opt_idx, numapes = 0;
+    char apebuf[2 + BUFSZ]; /* so &apebuf[1] is BUFSZ long for getlin() */
+    struct autopickup_exception *ape;
+
+ ape_again:
+    numapes = count_apes();
+    opt_idx = handle_add_list_remove("autopickup exception", numapes);
+    if (opt_idx == 3) { /* done */
+        return TRUE;
+    } else if (opt_idx == 0) { /* add new */
+        /* EDIT_GETLIN:  assume user doesn't user want previous
+           exception used as default input string for this one... */
+        apebuf[0] = apebuf[1] = '\0';
+        getlin("What new autopickup exception pattern?", &apebuf[1]);
+        mungspaces(&apebuf[1]); /* regularize whitespace */
+        if (apebuf[1] == '\033')
+            return TRUE;
+        if (apebuf[1]) {
+            apebuf[0] = '\"';
+            /* guarantee room for \" prefix and \"\0 suffix;
+               -2 is good enough for apebuf[] but -3 makes
+               sure the whole thing fits within normal BUFSZ */
+            apebuf[sizeof apebuf - 2] = '\0';
+            Strcat(apebuf, "\"");
+            add_autopickup_exception(apebuf);
         }
-        destroy_nhwindow(tmpwin);
-    } else if (!strcmp("pickup_types", optname)) {
-        /* parseoptions will prompt for the list of types */
-        (void) parseoptions(strcpy(buf, "pickup_types"),
-                            setinitial, setfromfile);
-    } else if (!strcmp("disclose", optname)) {
-        /* order of disclose_names[] must correspond to
-           disclosure_options in decl.c */
-        static const char *disclosure_names[] = {
-            "inventory", "attributes", "vanquished",
-            "genocides", "conduct",    "overview",
-        };
-        int disc_cat[NUM_DISCLOSURE_OPTIONS];
-        int pick_cnt, pick_idx, opt_idx;
-        char c;
-        menu_item *disclosure_pick = (menu_item *) 0;
+        goto ape_again;
+    } else { /* list (1) or remove (2) */
+        int pick_idx, pick_cnt;
+        menu_item *pick_list = (menu_item *) 0;
 
         tmpwin = create_nhwindow(NHW_MENU);
         start_menu(tmpwin, MENU_BEHAVE_STANDARD);
-        any = cg.zeroany;
-        for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
-            Sprintf(buf, "%-12s[%c%c]", disclosure_names[i],
-                    flags.end_disclose[i], disclosure_options[i]);
-            any.a_int = i + 1;
-            add_menu(tmpwin, NO_GLYPH, &any, disclosure_options[i], 0,
-                     ATR_NONE, buf, MENU_ITEMFLAGS_NONE);
-            disc_cat[i] = 0;
-        }
-        end_menu(tmpwin, "Change which disclosure options categories:");
-        pick_cnt = select_menu(tmpwin, PICK_ANY, &disclosure_pick);
-        if (pick_cnt > 0) {
-            for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
-                opt_idx = disclosure_pick[pick_idx].item.a_int - 1;
-                disc_cat[opt_idx] = 1;
+        if (numapes) {
+            ape = g.apelist;
+            any = cg.zeroany;
+            add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
+                     "Always pickup '<'; never pickup '>'",
+                     MENU_ITEMFLAGS_NONE);
+            for (i = 0; i < numapes && ape; i++) {
+                any.a_void = (opt_idx == 1) ? 0 : ape;
+                /* length of pattern plus quotes (plus '<'/'>') is
+                   less than BUFSZ */
+                Sprintf(apebuf, "\"%c%s\"", ape->grab ? '<' : '>',
+                        ape->pattern);
+                add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, apebuf,
+                         MENU_ITEMFLAGS_NONE);
+                ape = ape->next;
             }
-            free((genericptr_t) disclosure_pick);
-            disclosure_pick = (menu_item *) 0;
+        }
+        Sprintf(apebuf, "%s autopickup exceptions",
+                (opt_idx == 1) ? "List of" : "Remove which");
+        end_menu(tmpwin, apebuf);
+        pick_cnt = select_menu(tmpwin,
+                               (opt_idx == 1) ? PICK_NONE : PICK_ANY,
+                               &pick_list);
+        if (pick_cnt > 0) {
+            for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
+                remove_autopickup_exception(
+                                     (struct autopickup_exception *)
+                                         pick_list[pick_idx].item.a_void);
+            free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
         }
         destroy_nhwindow(tmpwin);
+        if (pick_cnt >= 0)
+            goto ape_again;
+    }
+    return optn_ok;
+}
 
-        for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
-            if (disc_cat[i]) {
-                c = flags.end_disclose[i];
-                Sprintf(buf, "Disclosure options for %s:",
-                        disclosure_names[i]);
-                tmpwin = create_nhwindow(NHW_MENU);
-                start_menu(tmpwin, MENU_BEHAVE_STANDARD);
-                any = cg.zeroany;
-                /* 'y','n',and '+' work as alternate selectors; '-' doesn't */
-                any.a_char = DISCLOSE_NO_WITHOUT_PROMPT;
-                add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
-                         "Never disclose, without prompting",
-                         (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
-                any.a_char = DISCLOSE_YES_WITHOUT_PROMPT;
-                add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
-                         "Always disclose, without prompting",
-                         (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
-                if (*disclosure_names[i] == 'v') {
-                    any.a_char = DISCLOSE_SPECIAL_WITHOUT_PROMPT; /* '#' */
-                    add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
-                             "Always disclose, pick sort order from menu",
-                             (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED
-                                               : MENU_ITEMFLAGS_NONE);
-                }
-                any.a_char = DISCLOSE_PROMPT_DEFAULT_NO;
-                add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
-                         "Prompt, with default answer of \"No\"",
-                         (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
-                any.a_char = DISCLOSE_PROMPT_DEFAULT_YES;
-                add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
-                         "Prompt, with default answer of \"Yes\"",
-                         (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
-                if (*disclosure_names[i] == 'v') {
-                    any.a_char = DISCLOSE_PROMPT_DEFAULT_SPECIAL; /* '?' */
-                    add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
-                "Prompt, with default answer of \"Ask\" to request sort menu",
-                             (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED
-                                               : MENU_ITEMFLAGS_NONE);
-                }
-                end_menu(tmpwin, buf);
-                n = select_menu(tmpwin, PICK_ONE, &disclosure_pick);
-                if (n > 0) {
-                    flags.end_disclose[i] = disclosure_pick[0].item.a_char;
-                    if (n > 1 && flags.end_disclose[i] == c)
-                        flags.end_disclose[i] = disclosure_pick[1].item.a_char;
-                    free((genericptr_t) disclosure_pick);
-                }
-                destroy_nhwindow(tmpwin);
-            }
+static int
+handler_menu_colors(VOID_ARGS)
+{
+    winid tmpwin;
+    anything any;
+    char buf[BUFSZ];
+    int opt_idx, nmc, mcclr, mcattr;
+    char mcbuf[BUFSZ];
+
+ menucolors_again:
+    nmc = count_menucolors();
+    opt_idx = handle_add_list_remove("menucolor", nmc);
+    if (opt_idx == 3) { /* done */
+ menucolors_done:
+        /* in case we've made a change which impacts current persistent
+           inventory window; we don't track whether an actual changed
+           occurred, so just assume there was one and that it matters;
+           if we're wrong, a redundant update is cheap... */
+        if (iflags.use_menu_color)
+            update_inventory();
+
+        /* menu colors aren't being used; if any are defined, remind
+           player how to use them */
+        else if (nmc > 0)
+            pline(
+    "To have menu colors become active, toggle 'menucolors' option to True.");
+        return optn_ok;
+
+    } else if (opt_idx == 0) { /* add new */
+        mcbuf[0] = '\0';
+        getlin("What new menucolor pattern?", mcbuf);
+        if (*mcbuf == '\033')
+            goto menucolors_done;
+        if (*mcbuf
+            && test_regex_pattern(mcbuf, (const char *)0)
+            && (mcclr = query_color((char *) 0)) != -1
+            && (mcattr = query_attr((char *) 0)) != -1
+            && !add_menu_coloring_parsed(mcbuf, mcclr, mcattr)) {
+            pline("Error adding the menu color.");
+            wait_synch();
         }
-    } else if (!strcmp("runmode", optname)) {
-        const char *mode_name;
-        menu_item *mode_pick = (menu_item *) 0;
+        goto menucolors_again;
+
+    } else { /* list (1) or remove (2) */
+        int pick_idx, pick_cnt;
+        int mc_idx;
+        unsigned ln;
+        const char *sattr, *sclr;
+        menu_item *pick_list = (menu_item *) 0;
+        struct menucoloring *tmp = g.menu_colorings;
+        char clrbuf[QBUFSZ];
 
         tmpwin = create_nhwindow(NHW_MENU);
         start_menu(tmpwin, MENU_BEHAVE_STANDARD);
         any = cg.zeroany;
-        for (i = 0; i < SIZE(runmodes); i++) {
-            mode_name = runmodes[i];
-            any.a_int = i + 1;
-            add_menu(tmpwin, NO_GLYPH, &any, *mode_name, 0, ATR_NONE,
-                     mode_name, MENU_ITEMFLAGS_NONE);
+        mc_idx = 0;
+        while (tmp) {
+            sattr = attr2attrname(tmp->attr);
+            sclr = strcpy(clrbuf, clr2colorname(tmp->color));
+            (void) strNsubst(clrbuf, " ", "-", 0);
+            any.a_int = ++mc_idx;
+            /* construct suffix */
+            Sprintf(buf, "\"\"=%s%s%s", sclr,
+                    (tmp->attr != ATR_NONE) ? "&" : "",
+                    (tmp->attr != ATR_NONE) ? sattr : "");
+            /* now main string */
+            ln = sizeof buf - strlen(buf) - 1; /* length available */
+            Strcpy(mcbuf, "\"");
+            if (strlen(tmp->origstr) > ln)
+                Strcat(strncat(mcbuf, tmp->origstr, ln - 3), "...");
+            else
+                Strcat(mcbuf, tmp->origstr);
+            /* combine main string and suffix */
+            Strcat(mcbuf, &buf[1]); /* skip buf[]'s initial quote */
+            add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, mcbuf,
+                     MENU_ITEMFLAGS_NONE);
+            tmp = tmp->next;
         }
-        end_menu(tmpwin, "Select run/travel display mode:");
-        if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
-            flags.runmode = mode_pick->item.a_int - 1;
-            free((genericptr_t) mode_pick);
+        Sprintf(mcbuf, "%s menu colors",
+                (opt_idx == 1) ? "List of" : "Remove which");
+        end_menu(tmpwin, mcbuf);
+        pick_cnt = select_menu(tmpwin,
+                               (opt_idx == 1) ? PICK_NONE : PICK_ANY,
+                               &pick_list);
+        if (pick_cnt > 0) {
+            for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
+                free_one_menu_coloring(pick_list[pick_idx].item.a_int - 1
+                                       - pick_idx);
+            free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
         }
         destroy_nhwindow(tmpwin);
-    } else if (!strcmp("whatis_coord", optname)) {
-        menu_item *window_pick = (menu_item *) 0;
-        int pick_cnt;
-        char gp = iflags.getpos_coords;
-
-        tmpwin = create_nhwindow(NHW_MENU);
-        start_menu(tmpwin, MENU_BEHAVE_STANDARD);
-        any = cg.zeroany;
-        any.a_char = GPCOORDS_COMPASS;
-        add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_COMPASS, 0, ATR_NONE,
-                 "compass ('east' or '3s' or '2n,4w')",
-                 (gp == GPCOORDS_COMPASS)
-                    ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
-        any.a_char = GPCOORDS_COMFULL;
-        add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_COMFULL, 0, ATR_NONE,
-                 "full compass ('east' or '3south' or '2north,4west')",
-                 (gp == GPCOORDS_COMFULL)
-                    ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
-        any.a_char = GPCOORDS_MAP;
-        add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_MAP, 0, ATR_NONE,
-                 "map <x,y>",
-                 (gp == GPCOORDS_MAP)
-                    ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
-        any.a_char = GPCOORDS_SCREEN;
-        add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_SCREEN, 0, ATR_NONE,
-                 "screen [row,column]",
-                 (gp == GPCOORDS_SCREEN)
-                    ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
-        any.a_char = GPCOORDS_NONE;
-        add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_NONE, 0, ATR_NONE,
-                 "none (no coordinates displayed)",
-                 (gp == GPCOORDS_NONE)
-                    ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
-        any.a_long = 0L;
-        add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_ITEMFLAGS_NONE);
-        Sprintf(buf, "map: upper-left: <%d,%d>, lower-right: <%d,%d>%s",
-                1, 0, COLNO - 1, ROWNO - 1,
-                flags.verbose ? "; column 0 unused, off left edge" : "");
-        add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_ITEMFLAGS_NONE);
-        if (strcmp(windowprocs.name, "tty")) /* only show for non-tty */
-            add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
-       "screen: row is offset to accommodate tty interface's use of top line",
-                     MENU_ITEMFLAGS_NONE);
-#if COLNO == 80
-#define COL80ARG flags.verbose ? "; column 80 is not used" : ""
-#else
-#define COL80ARG ""
-#endif
-        Sprintf(buf, "screen: upper-left: [%02d,%02d], lower-right: [%d,%d]%s",
-                0 + 2, 1, ROWNO - 1 + 2, COLNO - 1, COL80ARG);
-#undef COL80ARG
-        add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_ITEMFLAGS_NONE);
-        add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_ITEMFLAGS_NONE);
-        end_menu(tmpwin,
-            "Select coordinate display when auto-describing a map position:");
-        if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &window_pick)) > 0) {
-            iflags.getpos_coords = window_pick[0].item.a_char;
-            /* PICK_ONE doesn't unselect preselected entry when
-               selecting another one */
-            if (pick_cnt > 1 && iflags.getpos_coords == gp)
-                iflags.getpos_coords = window_pick[1].item.a_char;
-            free((genericptr_t) window_pick);
-        }
-        destroy_nhwindow(tmpwin);
-    } else if (!strcmp("whatis_filter", optname)) {
-        menu_item *window_pick = (menu_item *) 0;
-        int pick_cnt;
-        char gf = iflags.getloc_filter;
-
-        tmpwin = create_nhwindow(NHW_MENU);
-        start_menu(tmpwin, MENU_BEHAVE_STANDARD);
-        any = cg.zeroany;
-        any.a_char = (GFILTER_NONE + 1);
-        add_menu(tmpwin, NO_GLYPH, &any, 'n',
-                 0, ATR_NONE, "no filtering",
-                 (gf == GFILTER_NONE)
-                    ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
-        any.a_char = (GFILTER_VIEW + 1);
-        add_menu(tmpwin, NO_GLYPH, &any, 'v',
-                 0, ATR_NONE, "in view only",
-                 (gf == GFILTER_VIEW)
-                    ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
-        any.a_char = (GFILTER_AREA + 1);
-        add_menu(tmpwin, NO_GLYPH, &any, 'a',
-                 0, ATR_NONE, "in same area",
-                 (gf == GFILTER_AREA)
-                    ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
-        end_menu(tmpwin,
-      "Select location filtering when going for next/previous map position:");
-        if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &window_pick)) > 0) {
-            iflags.getloc_filter = (window_pick[0].item.a_char - 1);
-            /* PICK_ONE doesn't unselect preselected entry when
-               selecting another one */
-            if (pick_cnt > 1 && iflags.getloc_filter == gf)
-                iflags.getloc_filter = (window_pick[1].item.a_char - 1);
-            free((genericptr_t) window_pick);
-        }
-        destroy_nhwindow(tmpwin);
-    } else if (!strcmp("msg_window", optname)) {
-#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS)
-        if (WINDOWPORT("tty") || WINDOWPORT("curses")) {
-            /* by Christian W. Cooper */
-            menu_item *window_pick = (menu_item *) 0;
-
-            tmpwin = create_nhwindow(NHW_MENU);
-            start_menu(tmpwin, MENU_BEHAVE_STANDARD);
-            any = cg.zeroany;
-            if (!WINDOWPORT("curses")) {
-                any.a_char = 's';
-                add_menu(tmpwin, NO_GLYPH, &any, 's', 0, ATR_NONE,
-                         "single", MENU_ITEMFLAGS_NONE);
-                any.a_char = 'c';
-                add_menu(tmpwin, NO_GLYPH, &any, 'c', 0, ATR_NONE,
-                         "combination", MENU_ITEMFLAGS_NONE);
-            }
-            any.a_char = 'f';
-            add_menu(tmpwin, NO_GLYPH, &any, 'f', 0, ATR_NONE, "full",
-                     MENU_ITEMFLAGS_NONE);
-            any.a_char = 'r';
-            add_menu(tmpwin, NO_GLYPH, &any, 'r', 0, ATR_NONE, "reversed",
-                     MENU_ITEMFLAGS_NONE);
-            end_menu(tmpwin, "Select message history display type:");
-            if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) {
-                iflags.prevmsg_window = window_pick->item.a_char;
-                free((genericptr_t) window_pick);
-            }
-            destroy_nhwindow(tmpwin);
-        } else
-#endif /* msg_window for tty or curses */
-            pline("'%s' option is not supported for '%s'.",
-                  optname, windowprocs.name);
-    } else if (!strcmp("sortloot", optname)) {
-        const char *sortl_name;
-        menu_item *sortl_pick = (menu_item *) 0;
+        if (pick_cnt >= 0)
+            goto menucolors_again;
+    }
+    return optn_ok;
+}
 
-        tmpwin = create_nhwindow(NHW_MENU);
-        start_menu(tmpwin, MENU_BEHAVE_STANDARD);
-        any = cg.zeroany;
-        for (i = 0; i < SIZE(sortltype); i++) {
-            sortl_name = sortltype[i];
-            any.a_char = *sortl_name;
-            add_menu(tmpwin, NO_GLYPH, &any, *sortl_name, 0, ATR_NONE,
-                     sortl_name, (flags.sortloot == *sortl_name)
-                                    ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
-        }
-        end_menu(tmpwin, "Select loot sorting type:");
-        n = select_menu(tmpwin, PICK_ONE, &sortl_pick);
-        if (n > 0) {
-            char c = sortl_pick[0].item.a_char;
+static int
+handler_msgtype(VOID_ARGS)
+{
+    winid tmpwin;
+    anything any;
+    int opt_idx, nmt, mttyp;
+    char mtbuf[BUFSZ];
 
-            if (n > 1 && c == flags.sortloot)
-                c = sortl_pick[1].item.a_char;
-            flags.sortloot = c;
-            free((genericptr_t) sortl_pick);
+ msgtypes_again:
+    nmt = msgtype_count();
+    opt_idx = handle_add_list_remove("message type", nmt);
+    if (opt_idx == 3) { /* done */
+        return TRUE;
+    } else if (opt_idx == 0) { /* add new */
+        mtbuf[0] = '\0';
+        getlin("What new message pattern?", mtbuf);
+        if (*mtbuf == '\033')
+            return TRUE;
+        if (*mtbuf
+            && test_regex_pattern(mtbuf, (const char *)0)
+            && (mttyp = query_msgtype()) != -1
+            && !msgtype_add(mttyp, mtbuf)) {
+            pline("Error adding the message type.");
+            wait_synch();
         }
-        destroy_nhwindow(tmpwin);
-    } else if (!strcmp("align_message", optname)
-               || !strcmp("align_status", optname)) {
-        menu_item *window_pick = (menu_item *) 0;
-        char abuf[BUFSZ];
-        boolean msg = (*(optname + 6) == 'm');
+        goto msgtypes_again;
+    } else { /* list (1) or remove (2) */
+        int pick_idx, pick_cnt;
+        int mt_idx;
+        unsigned ln;
+        const char *mtype;
+        menu_item *pick_list = (menu_item *) 0;
+        struct plinemsg_type *tmp = g.plinemsg_types;
 
         tmpwin = create_nhwindow(NHW_MENU);
         start_menu(tmpwin, MENU_BEHAVE_STANDARD);
         any = cg.zeroany;
-        any.a_int = ALIGN_TOP;
-        add_menu(tmpwin, NO_GLYPH, &any, 't', 0, ATR_NONE, "top",
-                 MENU_ITEMFLAGS_NONE);
-        any.a_int = ALIGN_BOTTOM;
-        add_menu(tmpwin, NO_GLYPH, &any, 'b', 0, ATR_NONE, "bottom",
-                 MENU_ITEMFLAGS_NONE);
-        any.a_int = ALIGN_LEFT;
-        add_menu(tmpwin, NO_GLYPH, &any, 'l', 0, ATR_NONE, "left",
-                 MENU_ITEMFLAGS_NONE);
-        any.a_int = ALIGN_RIGHT;
-        add_menu(tmpwin, NO_GLYPH, &any, 'r', 0, ATR_NONE, "right",
-                 MENU_ITEMFLAGS_NONE);
-        Sprintf(abuf, "Select %s window placement relative to the map:",
-                msg ? "message" : "status");
-        end_menu(tmpwin, abuf);
-        if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) {
-            if (msg)
-                iflags.wc_align_message = window_pick->item.a_int;
+        mt_idx = 0;
+        while (tmp) {
+            mtype = msgtype2name(tmp->msgtype);
+            any.a_int = ++mt_idx;
+            Sprintf(mtbuf, "%-5s \"", mtype);
+            ln = sizeof mtbuf - strlen(mtbuf) - sizeof "\"";
+            if (strlen(tmp->pattern) > ln)
+                Strcat(strncat(mtbuf, tmp->pattern, ln - 3), "...\"");
             else
-                iflags.wc_align_status = window_pick->item.a_int;
-            free((genericptr_t) window_pick);
+                Strcat(strcat(mtbuf, tmp->pattern), "\"");
+            add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, mtbuf,
+                     MENU_ITEMFLAGS_NONE);
+            tmp = tmp->next;
         }
-        destroy_nhwindow(tmpwin);
-    } else if (!strcmp("number_pad", optname)) {
-        static const char *npchoices[] = {
-            " 0 (off)", " 1 (on)", " 2 (on, MSDOS compatible)",
-            " 3 (on, phone-style digit layout)",
-            " 4 (on, phone-style layout, MSDOS compatible)",
-            "-1 (off, 'z' to move upper-left, 'y' to zap wands)"
-        };
-        menu_item *mode_pick = (menu_item *) 0;
-
-        tmpwin = create_nhwindow(NHW_MENU);
-        start_menu(tmpwin, MENU_BEHAVE_STANDARD);
-        any = cg.zeroany;
-        for (i = 0; i < SIZE(npchoices); i++) {
-            any.a_int = i + 1;
-            add_menu(tmpwin, NO_GLYPH, &any, 'a' + i, 0, ATR_NONE,
-                     npchoices[i], MENU_ITEMFLAGS_NONE);
-        }
-        end_menu(tmpwin, "Select number_pad mode:");
-        if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
-            switch (mode_pick->item.a_int - 1) {
-            case 0:
-                iflags.num_pad = FALSE;
-                iflags.num_pad_mode = 0;
-                break;
-            case 1:
-                iflags.num_pad = TRUE;
-                iflags.num_pad_mode = 0;
-                break;
-            case 2:
-                iflags.num_pad = TRUE;
-                iflags.num_pad_mode = 1;
-                break;
-            case 3:
-                iflags.num_pad = TRUE;
-                iflags.num_pad_mode = 2;
-                break;
-            case 4:
-                iflags.num_pad = TRUE;
-                iflags.num_pad_mode = 3;
-                break;
-            /* last menu choice: number_pad == -1 */
-            case 5:
-                iflags.num_pad = FALSE;
-                iflags.num_pad_mode = 1;
-                break;
-            }
-            reset_commands(FALSE);
-            number_pad(iflags.num_pad ? 1 : 0);
-            free((genericptr_t) mode_pick);
+        Sprintf(mtbuf, "%s message types",
+                (opt_idx == 1) ? "List of" : "Remove which");
+        end_menu(tmpwin, mtbuf);
+        pick_cnt = select_menu(tmpwin,
+                               (opt_idx == 1) ? PICK_NONE : PICK_ANY,
+                               &pick_list);
+        if (pick_cnt > 0) {
+            for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
+                free_one_msgtype(pick_list[pick_idx].item.a_int - 1
+                                       - pick_idx);
+            free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
         }
         destroy_nhwindow(tmpwin);
-    } else if (!strcmp("menu_headings", optname)) {
-        int mhattr = query_attr("How to highlight menu headings:");
-
-        if (mhattr != -1)
-            iflags.menu_headings = mhattr;
-    } else if (!strcmp("msgtype", optname)) {
-        int opt_idx, nmt, mttyp;
-        char mtbuf[BUFSZ];
-
- msgtypes_again:
-        nmt = msgtype_count();
-        opt_idx = handle_add_list_remove("message type", nmt);
-        if (opt_idx == 3) { /* done */
-            return TRUE;
-        } else if (opt_idx == 0) { /* add new */
-            mtbuf[0] = '\0';
-            getlin("What new message pattern?", mtbuf);
-            if (*mtbuf == '\033')
-                return TRUE;
-            if (*mtbuf
-                && test_regex_pattern(mtbuf, (const char *)0)
-                && (mttyp = query_msgtype()) != -1
-                && !msgtype_add(mttyp, mtbuf)) {
-                pline("Error adding the message type.");
-                wait_synch();
-            }
+        if (pick_cnt >= 0)
             goto msgtypes_again;
-        } else { /* list (1) or remove (2) */
-            int pick_idx, pick_cnt;
-            int mt_idx;
-            unsigned ln;
-            const char *mtype;
-            menu_item *pick_list = (menu_item *) 0;
-            struct plinemsg_type *tmp = g.plinemsg_types;
-
-            tmpwin = create_nhwindow(NHW_MENU);
-            start_menu(tmpwin, MENU_BEHAVE_STANDARD);
-            any = cg.zeroany;
-            mt_idx = 0;
-            while (tmp) {
-                mtype = msgtype2name(tmp->msgtype);
-                any.a_int = ++mt_idx;
-                Sprintf(mtbuf, "%-5s \"", mtype);
-                ln = sizeof mtbuf - strlen(mtbuf) - sizeof "\"";
-                if (strlen(tmp->pattern) > ln)
-                    Strcat(strncat(mtbuf, tmp->pattern, ln - 3), "...\"");
-                else
-                    Strcat(strcat(mtbuf, tmp->pattern), "\"");
-                add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, mtbuf,
-                         MENU_ITEMFLAGS_NONE);
-                tmp = tmp->next;
-            }
-            Sprintf(mtbuf, "%s message types",
-                    (opt_idx == 1) ? "List of" : "Remove which");
-            end_menu(tmpwin, mtbuf);
-            pick_cnt = select_menu(tmpwin,
-                                   (opt_idx == 1) ? PICK_NONE : PICK_ANY,
-                                   &pick_list);
-            if (pick_cnt > 0) {
-                for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
-                    free_one_msgtype(pick_list[pick_idx].item.a_int - 1
-                                           - pick_idx);
-                free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
-            }
-            destroy_nhwindow(tmpwin);
-            if (pick_cnt >= 0)
-                goto msgtypes_again;
-        }
-    } else if (!strcmp("menu_colors", optname)) {
-        int opt_idx, nmc, mcclr, mcattr;
-        char mcbuf[BUFSZ];
-
- menucolors_again:
-        nmc = count_menucolors();
-        opt_idx = handle_add_list_remove("menucolor", nmc);
-        if (opt_idx == 3) { /* done */
- menucolors_done:
-            /* in case we've made a change which impacts current persistent
-               inventory window; we don't track whether an actual changed
-               occurred, so just assume there was one and that it matters;
-               if we're wrong, a redundant update is cheap... */
-            if (iflags.use_menu_color)
-                update_inventory();
-
-            /* menu colors aren't being used; if any are defined, remind
-               player how to use them */
-            else if (nmc > 0)
-                pline(
-    "To have menu colors become active, toggle 'menucolors' option to True.");
-            return TRUE;
-
-        } else if (opt_idx == 0) { /* add new */
-            mcbuf[0] = '\0';
-            getlin("What new menucolor pattern?", mcbuf);
-            if (*mcbuf == '\033')
-                goto menucolors_done;
-            if (*mcbuf
-                && test_regex_pattern(mcbuf, (const char *)0)
-                && (mcclr = query_color((char *) 0)) != -1
-                && (mcattr = query_attr((char *) 0)) != -1
-                && !add_menu_coloring_parsed(mcbuf, mcclr, mcattr)) {
-                pline("Error adding the menu color.");
-                wait_synch();
-            }
-            goto menucolors_again;
-
-        } else { /* list (1) or remove (2) */
-            int pick_idx, pick_cnt;
-            int mc_idx;
-            unsigned ln;
-            const char *sattr, *sclr;
-            menu_item *pick_list = (menu_item *) 0;
-            struct menucoloring *tmp = g.menu_colorings;
-            char clrbuf[QBUFSZ];
-
-            tmpwin = create_nhwindow(NHW_MENU);
-            start_menu(tmpwin, MENU_BEHAVE_STANDARD);
-            any = cg.zeroany;
-            mc_idx = 0;
-            while (tmp) {
-                sattr = attr2attrname(tmp->attr);
-                sclr = strcpy(clrbuf, clr2colorname(tmp->color));
-                (void) strNsubst(clrbuf, " ", "-", 0);
-                any.a_int = ++mc_idx;
-                /* construct suffix */
-                Sprintf(buf, "\"\"=%s%s%s", sclr,
-                        (tmp->attr != ATR_NONE) ? "&" : "",
-                        (tmp->attr != ATR_NONE) ? sattr : "");
-                /* now main string */
-                ln = sizeof buf - strlen(buf) - 1; /* length available */
-                Strcpy(mcbuf, "\"");
-                if (strlen(tmp->origstr) > ln)
-                    Strcat(strncat(mcbuf, tmp->origstr, ln - 3), "...");
-                else
-                    Strcat(mcbuf, tmp->origstr);
-                /* combine main string and suffix */
-                Strcat(mcbuf, &buf[1]); /* skip buf[]'s initial quote */
-                add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, mcbuf,
-                         MENU_ITEMFLAGS_NONE);
-                tmp = tmp->next;
-            }
-            Sprintf(mcbuf, "%s menu colors",
-                    (opt_idx == 1) ? "List of" : "Remove which");
-            end_menu(tmpwin, mcbuf);
-            pick_cnt = select_menu(tmpwin,
-                                   (opt_idx == 1) ? PICK_NONE : PICK_ANY,
-                                   &pick_list);
-            if (pick_cnt > 0) {
-                for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
-                    free_one_menu_coloring(pick_list[pick_idx].item.a_int - 1
-                                           - pick_idx);
-                free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
-            }
-            destroy_nhwindow(tmpwin);
-            if (pick_cnt >= 0)
-                goto menucolors_again;
-        }
-    } else if (!strcmp("autopickup_exception", optname)) {
-        int opt_idx, numapes = 0;
-        char apebuf[2 + BUFSZ]; /* so &apebuf[1] is BUFSZ long for getlin() */
-        struct autopickup_exception *ape;
+    }
+    return optn_ok;
+}
 
- ape_again:
-        numapes = count_apes();
-        opt_idx = handle_add_list_remove("autopickup exception", numapes);
-        if (opt_idx == 3) { /* done */
-            return TRUE;
-        } else if (opt_idx == 0) { /* add new */
-            /* EDIT_GETLIN:  assume user doesn't user want previous
-               exception used as default input string for this one... */
-            apebuf[0] = apebuf[1] = '\0';
-            getlin("What new autopickup exception pattern?", &apebuf[1]);
-            mungspaces(&apebuf[1]); /* regularize whitespace */
-            if (apebuf[1] == '\033')
-                return TRUE;
-            if (apebuf[1]) {
-                apebuf[0] = '\"';
-                /* guarantee room for \" prefix and \"\0 suffix;
-                   -2 is good enough for apebuf[] but -3 makes
-                   sure the whole thing fits within normal BUFSZ */
-                apebuf[sizeof apebuf - 2] = '\0';
-                Strcat(apebuf, "\"");
-                add_autopickup_exception(apebuf);
-            }
-            goto ape_again;
-        } else { /* list (1) or remove (2) */
-            int pick_idx, pick_cnt;
-            menu_item *pick_list = (menu_item *) 0;
 
-            tmpwin = create_nhwindow(NHW_MENU);
-            start_menu(tmpwin, MENU_BEHAVE_STANDARD);
-            if (numapes) {
-                ape = g.apelist;
-                any = cg.zeroany;
-                add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
-                         "Always pickup '<'; never pickup '>'",
-                         MENU_ITEMFLAGS_NONE);
-                for (i = 0; i < numapes && ape; i++) {
-                    any.a_void = (opt_idx == 1) ? 0 : ape;
-                    /* length of pattern plus quotes (plus '<'/'>') is
-                       less than BUFSZ */
-                    Sprintf(apebuf, "\"%c%s\"", ape->grab ? '<' : '>',
-                            ape->pattern);
-                    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, apebuf,
-                             MENU_ITEMFLAGS_NONE);
-                    ape = ape->next;
-                }
-            }
-            Sprintf(apebuf, "%s autopickup exceptions",
-                    (opt_idx == 1) ? "List of" : "Remove which");
-            end_menu(tmpwin, apebuf);
-            pick_cnt = select_menu(tmpwin,
-                                   (opt_idx == 1) ? PICK_NONE : PICK_ANY,
-                                   &pick_list);
-            if (pick_cnt > 0) {
-                for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
-                    remove_autopickup_exception(
-                                         (struct autopickup_exception *)
-                                             pick_list[pick_idx].item.a_void);
-                free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
-            }
-            destroy_nhwindow(tmpwin);
-            if (pick_cnt >= 0)
-                goto ape_again;
-        }
-    } else if (!strcmp("symset", optname)
-               || !strcmp("roguesymset", optname)) {
-        menu_item *symset_pick = (menu_item *) 0;
-        boolean rogueflag = (*optname == 'r'),
-                ready_to_switch = FALSE,
-                nothing_to_do = FALSE;
-        char *symset_name, fmtstr[20];
-        struct symsetentry *sl;
-        int res, which_set, setcount = 0, chosen = -2, defindx = 0;
-
-        which_set = rogueflag ? ROGUESET : PRIMARY;
-        g.symset_list = (struct symsetentry *) 0;
-        /* clear symset[].name as a flag to read_sym_file() to build list */
-        symset_name = g.symset[which_set].name;
-        g.symset[which_set].name = (char *) 0;
-
-        res = read_sym_file(which_set);
-        /* put symset name back */
-        g.symset[which_set].name = symset_name;
-
-        if (res && g.symset_list) {
-            int thissize,
-                biggest = (int) (sizeof "Default Symbols" - sizeof ""),
-                big_desc = 0;
-
-            for (sl = g.symset_list; sl; sl = sl->next) {
-                /* check restrictions */
-                if (rogueflag ? sl->primary : sl->rogue)
-                    continue;
-#ifndef MAC_GRAPHICS_ENV
-                if (sl->handling == H_MAC)
-                    continue;
-#endif
+/*
+ **********************************
+ *
+ *   Parsing Support Functions
+ *
+ **********************************
+ */
 
-                setcount++;
-                /* find biggest name */
-                thissize = sl->name ? (int) strlen(sl->name) : 0;
-                if (thissize > biggest)
-                    biggest = thissize;
-                thissize = sl->desc ? (int) strlen(sl->desc) : 0;
-                if (thissize > big_desc)
-                    big_desc = thissize;
-            }
-            if (!setcount) {
-                pline("There are no appropriate %s symbol sets available.",
-                      rogueflag ? "rogue level" : "primary");
-                return TRUE;
-            }
+static char *
+string_for_opt(opts, val_optional)
+char *opts;
+boolean val_optional;
+{
+    char *colon, *equals;
 
-            Sprintf(fmtstr, "%%-%ds %%s", biggest + 2);
-            tmpwin = create_nhwindow(NHW_MENU);
-            start_menu(tmpwin, MENU_BEHAVE_STANDARD);
-            any = cg.zeroany;
-            any.a_int = 1; /* -1 + 2 [see 'if (sl->name) {' below]*/
-            if (!symset_name)
-                defindx = any.a_int;
-            add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
-                     "Default Symbols",
-                     (any.a_int == defindx) ? MENU_ITEMFLAGS_SELECTED
-                                            : MENU_ITEMFLAGS_NONE);
+    colon = index(opts, ':');
+    equals = index(opts, '=');
+    if (!colon || (equals && equals < colon))
+        colon = equals;
 
-            for (sl = g.symset_list; sl; sl = sl->next) {
-                /* check restrictions */
-                if (rogueflag ? sl->primary : sl->rogue)
-                    continue;
-#ifndef MAC_GRAPHICS_ENV
-                if (sl->handling == H_MAC)
-                    continue;
-#endif
-                if (sl->name) {
-                    /* +2: sl->idx runs from 0 to N-1 for N symsets;
-                       +1 because Defaults are implicitly in slot [0];
-                       +1 again so that valid data is never 0 */
-                    any.a_int = sl->idx + 2;
-                    if (symset_name && !strcmpi(sl->name, symset_name))
-                        defindx = any.a_int;
-                    Sprintf(buf, fmtstr, sl->name, sl->desc ? sl->desc : "");
-                    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf,
-                             (any.a_int == defindx) ? MENU_ITEMFLAGS_SELECTED
-                                                    : MENU_ITEMFLAGS_NONE);
-                }
-            }
-            Sprintf(buf, "Select %ssymbol set:",
-                    rogueflag ? "rogue level " : "");
-            end_menu(tmpwin, buf);
-            n = select_menu(tmpwin, PICK_ONE, &symset_pick);
-            if (n > 0) {
-                chosen = symset_pick[0].item.a_int;
-                /* if picking non-preselected entry yields 2, make sure
-                   that we're going with the non-preselected one */
-                if (n == 2 && chosen == defindx)
-                    chosen = symset_pick[1].item.a_int;
-                chosen -= 2; /* convert menu index to symset index;
-                              * "Default symbols" have index -1 */
-                free((genericptr_t) symset_pick);
-            } else if (n == 0 && defindx > 0) {
-                chosen = defindx - 2;
-            }
-            destroy_nhwindow(tmpwin);
+    if (!colon || !*++colon) {
+        if (!val_optional)
+            config_error_add("Missing parameter for '%s'", opts);
+        return empty_optstr;
+    }
+    return colon;
+}
 
-            if (chosen > -1) {
-                /* chose an actual symset name from file */
-                for (sl = g.symset_list; sl; sl = sl->next)
-                    if (sl->idx == chosen)
-                        break;
-                if (sl) {
-                    /* free the now stale attributes */
-                    clear_symsetentry(which_set, TRUE);
+static char *
+string_for_env_opt(optname, opts, val_optional)
+const char *optname;
+char *opts;
+boolean val_optional;
+{
+    if (!g.opt_initial) {
+        rejectoption(optname);
+        return empty_optstr;
+    }
+    return string_for_opt(opts, val_optional);
+}
 
-                    /* transfer only the name of the symbol set */
-                    g.symset[which_set].name = dupstr(sl->name);
-                    ready_to_switch = TRUE;
-                }
-            } else if (chosen == -1) {
-                /* explicit selection of defaults */
-                /* free the now stale symset attributes */
-                clear_symsetentry(which_set, TRUE);
-            } else
-                nothing_to_do = TRUE;
-        } else if (!res) {
-            /* The symbols file could not be accessed */
-            pline("Unable to access \"%s\" file.", SYMBOLS);
-            return TRUE;
-        } else if (!g.symset_list) {
-            /* The symbols file was empty */
-            pline("There were no symbol sets found in \"%s\".", SYMBOLS);
-            return TRUE;
-        }
+static void
+bad_negation(optname, with_parameter)
+const char *optname;
+boolean with_parameter;
+{
+    pline_The("%s option may not %sbe negated.", optname,
+              with_parameter ? "both have a value and " : "");
+}
 
-        /* clean up */
-        while ((sl = g.symset_list) != 0) {
-            g.symset_list = sl->next;
-            if (sl->name)
-                free((genericptr_t) sl->name), sl->name = (char *) 0;
-            if (sl->desc)
-                free((genericptr_t) sl->desc), sl->desc = (char *) 0;
-            free((genericptr_t) sl);
-        }
+/* go through all of the options and set the minmatch value
+   based on what is needed for uniqueness of each individual
+   option. Set a minimum of 3 characters. */
+void
+determine_ambiguities()
+{
+    int i, j, len, tmpneeded, needed[SIZE(allopt)];
+    const char *p1, *p2;
 
-        if (nothing_to_do)
-            return TRUE;
+    for (i = 0; i < SIZE(allopt) - 1; ++i) {
+        needed[i] = 0;
+    }
 
-        /* Set default symbols and clear the handling value */
-        if (rogueflag)
-            init_rogue_symbols();
-        else
-            init_primary_symbols();
+    for (i = 0; i < SIZE(allopt) - 1; ++i) {
+        for (j = 0; j < SIZE(allopt) - 1; ++j) {
+            if (j == i)
+                continue;
 
-        if (g.symset[which_set].name) {
-            /* non-default symbols */
-            if (read_sym_file(which_set)) {
-                ready_to_switch = TRUE;
-            } else {
-                clear_symsetentry(which_set, TRUE);
-                return TRUE;
+            p1 = allopt[i].name; /* back to the start */
+            p2 = allopt[j].name;
+            tmpneeded = 1;
+            while (*p1 && *p2 && lowc(*p1) == lowc(*p2)) {
+                ++tmpneeded;
+                ++p1;
+                ++p2;
             }
+            if (tmpneeded > needed[i])
+                needed[i] = tmpneeded;
+            if (tmpneeded > needed[j])
+                needed[j] = tmpneeded;
         }
+    }
+    for (i = 0; i < SIZE(allopt) - 1; ++i) {
+        len = strlen(allopt[i].name);
+        allopt[i].minmatch = (needed[i] < 3) ? 3
+                                : (needed[i] <= len) ? needed[i] : len;
+    }
+}
 
-        if (ready_to_switch)
-            switch_symbols(TRUE);
+int
+length_without_val(user_string, len)
+const char *user_string;
+int len;
+{
+    const char *p = index(user_string, ':'),
+               *q = index(user_string, '=');
 
-        if (Is_rogue_level(&u.uz)) {
-            if (rogueflag)
-                assign_graphics(ROGUESET);
-        } else if (!rogueflag)
-            assign_graphics(PRIMARY);
-        preference_update("symset");
-        g.opt_need_redraw = TRUE;
-    } else if (!strcmp("condition_options", optname)) {
-        cond_menu();    /* in botl.c */
-    } else {
-        /* didn't match any of the special options */
-        return FALSE;
+    if (!p || (q && q < p))
+        p = q;
+    if (p) {
+        /* 'user_string' hasn't necessarily been through mungspaces()
+           so might have tabs or consecutive spaces */
+        while (p > user_string && isspace((uchar) *(p - 1)))
+            p--;
+        len = (int) (p - user_string);
     }
-    return TRUE;
+    return len;
 }
 
-#define rolestring(val, array, field) \
-    ((val >= 0) ? array[val].field : (val == ROLE_RANDOM) ? randomrole : none)
+/* check whether a user-supplied option string is a proper leading
+   substring of a particular option name; option string might have
+   a colon or equals sign and arbitrary value appended to it */
+boolean
+match_optname(user_string, opt_name, min_length, val_allowed)
+const char *user_string, *opt_name;
+int min_length;
+boolean val_allowed;
+{
+    int len = (int) strlen(user_string);
 
-/* This is ugly. We have all the option names in the compopt[] array,
-   but we need to look at each option individually to get the value. */
-static const char *
-get_compopt_value(optname, buf)
-const char *optname;
-char *buf;
+    if (val_allowed)
+        len = length_without_val(user_string, len);
+
+    return (boolean) (len >= min_length
+                      && !strncmpi(opt_name, user_string, len));
+}
+
+void
+set_duplicate_opt_detection(on_or_off)
+int on_or_off;
 {
-    static const char none[] = "(none)", randomrole[] = "random",
-                      to_be_done[] = "(to be done)",
-                      defopt[] = "default", defbrief[] = "def";
-    char ocl[MAXOCLASSES + 1];
-    int i;
+    int k, *optptr;
 
-    buf[0] = '\0';
-    if (!strcmp(optname, "align_message")
-        || !strcmp(optname, "align_status")) {
-        int which = !strcmp(optname, "align_status") ? iflags.wc_align_status
-                                                     : iflags.wc_align_message;
-        Sprintf(buf, "%s",
-                (which == ALIGN_TOP) ? "top"
-                : (which == ALIGN_LEFT) ? "left"
-                  : (which == ALIGN_BOTTOM) ? "bottom"
-                    : (which == ALIGN_RIGHT) ? "right"
-                      : defopt);
-    } else if (!strcmp(optname, "align"))
-        Sprintf(buf, "%s", rolestring(flags.initalign, aligns, adj));
-#ifdef WIN32
-    else if (!strcmp(optname, "altkeyhandler"))
-        Sprintf(buf, "%s",
-                iflags.altkeyhandler[0] ? iflags.altkeyhandler : "default");
+    if (on_or_off != 0) {
+        /*-- ON --*/
+        if (iflags.opt_booldup)
+            impossible("iflags.opt_booldup already on (memory leak)");
+        iflags.opt_booldup = (int *) alloc(SIZE(allopt) * sizeof (int));
+        optptr = iflags.opt_booldup;
+        for (k = 0; k < SIZE(allopt); ++k)
+            *optptr++ = 0;
+
+        if (iflags.opt_compdup)
+            impossible("iflags.opt_compdup already on (memory leak)");
+        iflags.opt_compdup = (int *) alloc(SIZE(allopt) * sizeof (int));
+        optptr = iflags.opt_compdup;
+        for (k = 0; k < SIZE(allopt); ++k)
+            *optptr++ = 0;
+    } else {
+        /*-- OFF --*/
+        if (iflags.opt_booldup)
+            free((genericptr_t) iflags.opt_booldup);
+        iflags.opt_booldup = (int *) 0;
+        if (iflags.opt_compdup)
+            free((genericptr_t) iflags.opt_compdup);
+        iflags.opt_compdup = (int *) 0;
+    }
+}
+
+static boolean
+duplicate_opt_detection(opts, iscompound)
+const char *opts;
+int iscompound; /* 0 == boolean option, 1 == compound */
+{
+    int i, *optptr;
+
+    if (!iscompound && iflags.opt_booldup && g.opt_initial && g.opt_from_file) {
+        for (i = 0; allopt[i].name; i++) {
+            if (match_optname(opts, allopt[i].name, 3, FALSE)) {
+                optptr = iflags.opt_booldup + i;
+                *optptr += 1;
+                if (*optptr > 1)
+                    return TRUE;
+                else
+                    return FALSE;
+            }
+        }
+    } else if (iscompound && iflags.opt_compdup && g.opt_initial && g.opt_from_file) {
+        for (i = 0; allopt[i].name; i++) {
+            if (match_optname(opts, allopt[i].name, strlen(allopt[i].name),
+                              TRUE)) {
+                optptr = iflags.opt_compdup + i;
+                *optptr += 1;
+                if (*optptr > 1)
+                    return TRUE;
+                else
+                    return FALSE;
+            }
+        }
+    }
+    return FALSE;
+}
+
+static void
+complain_about_duplicate(opts, iscompound)
+const char *opts;
+int iscompound; /* 0 == boolean option, 1 == compound */
+{
+#ifdef MAC
+    /* the Mac has trouble dealing with the output of messages while
+     * processing the config file.  That should get fixed one day.
+     * For now just return.
+     */
+#else /* !MAC */
+    config_error_add("%s option specified multiple times: %s",
+                     iscompound ? "compound" : "boolean", opts);
+#endif /* ?MAC */
+    return;
+}
+
+static void
+rejectoption(optname)
+const char *optname;
+{
+#ifdef MICRO
+    pline("\"%s\" settable only from %s.", optname, configfile);
+#else
+    pline("%s can be set only from NETHACKOPTIONS or %s.", optname,
+          configfile);
 #endif
-#ifdef BACKWARD_COMPAT
-    else if (!strcmp(optname, "boulder"))
-        Sprintf(buf, "%c",
-                g.ov_primary_syms[SYM_BOULDER + SYM_OFF_X]
-                    ? g.ov_primary_syms[SYM_BOULDER + SYM_OFF_X]
-                    : g.showsyms[(int) objects[BOULDER].oc_class + SYM_OFF_O]);
+}
+
+/*
+
+# errors:
+OPTIONS=aaaaaaaaaa[ more than 247 (255 - 8 for 'OPTIONS=') total ]aaaaaaaaaa
+OPTIONS
+OPTIONS=
+MSGTYPE=stop"You swap places with "
+MSGTYPE=st.op "You swap places with "
+MSGTYPE=stop "You swap places with \"
+MENUCOLOR=" blessed "green&none
+MENUCOLOR=" holy " = green&reverse
+MENUCOLOR=" cursed " = red&uline
+MENUCOLOR=" unholy " = reed
+OPTIONS=!legacy:true,fooo
+OPTIONS=align:!pin
+OPTIONS=gender
+
+*/
+/* most environment variables will eventually be printed in an error
+ * message if they don't work, and most error message paths go through
+ * BUFSZ buffers, which could be overflowed by a maliciously long
+ * environment variable.  If a variable can legitimately be long, or
+ * if it's put in a smaller buffer, the responsible code will have to
+ * bounds-check itself.
+ */
+char *
+nh_getenv(ev)
+const char *ev;
+{
+    char *getev = getenv(ev);
+
+    if (getev && strlen(getev) <= (BUFSZ / 2))
+        return getev;
+    else
+        return (char *) 0;
+}
+
+/* copy up to maxlen-1 characters; 'dest' must be able to hold maxlen;
+   treat comma as alternate end of 'src' */
+static void
+nmcpy(dest, src, maxlen)
+char *dest;
+const char *src;
+int maxlen;
+{
+    int count;
+
+    for (count = 1; count < maxlen; count++) {
+        if (*src == ',' || *src == '\0')
+            break; /*exit on \0 terminator*/
+        *dest++ = *src++;
+    }
+    *dest = '\0';
+}
+
+/*
+ * escapes(): escape expansion for showsyms.  C-style escapes understood
+ * include \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal).
+ * (Note: unlike in C, leading digit 0 is not used to indicate octal;
+ * the letter o (either upper or lower case) is used for that.
+ * The ^-prefix for control characters is also understood, and \[mM]
+ * has the effect of 'meta'-ing the value which follows (so that the
+ * alternate character set will be enabled).
+ *
+ * X     normal key X
+ * ^X    control-X
+ * \mX   meta-X
+ *
+ * For 3.4.3 and earlier, input ending with "\M", backslash, or caret
+ * prior to terminating '\0' would pull that '\0' into the output and then
+ * keep processing past it, potentially overflowing the output buffer.
+ * Now, trailing \ or ^ will act like \\ or \^ and add '\\' or '^' to the
+ * output and stop there; trailing \M will fall through to \<other> and
+ * yield 'M', then stop.  Any \X or \O followed by something other than
+ * an appropriate digit will also fall through to \<other> and yield 'X'
+ * or 'O', plus stop if the non-digit is end-of-string.
+ */
+static void
+escapes(cp, tp)
+const char *cp; /* might be 'tp', updating in place */
+char *tp; /* result is never longer than 'cp' */
+{
+    static NEARDATA const char oct[] = "01234567", dec[] = "0123456789",
+                               hex[] = "00112233445566778899aAbBcCdDeEfF";
+    const char *dp;
+    int cval, meta, dcount;
+
+    while (*cp) {
+        /* \M has to be followed by something to do meta conversion,
+           otherwise it will just be \M which ultimately yields 'M' */
+        meta = (*cp == '\\' && (cp[1] == 'm' || cp[1] == 'M') && cp[2]);
+        if (meta)
+            cp += 2;
+
+        cval = dcount = 0; /* for decimal, octal, hexadecimal cases */
+        if ((*cp != '\\' && *cp != '^') || !cp[1]) {
+            /* simple character, or nothing left for \ or ^ to escape */
+            cval = *cp++;
+        } else if (*cp == '^') { /* expand control-character syntax */
+            cval = (*++cp & 0x1f);
+            ++cp;
+
+        /* remaining cases are all for backslash; we know cp[1] is not \0 */
+        } else if (index(dec, cp[1])) {
+            ++cp; /* move past backslash to first digit */
+            do {
+                cval = (cval * 10) + (*cp - '0');
+            } while (*++cp && index(dec, *cp) && ++dcount < 3);
+        } else if ((cp[1] == 'o' || cp[1] == 'O') && cp[2]
+                   && index(oct, cp[2])) {
+            cp += 2; /* move past backslash and 'O' */
+            do {
+                cval = (cval * 8) + (*cp - '0');
+            } while (*++cp && index(oct, *cp) && ++dcount < 3);
+        } else if ((cp[1] == 'x' || cp[1] == 'X') && cp[2]
+                   && (dp = index(hex, cp[2])) != 0) {
+            cp += 2; /* move past backslash and 'X' */
+            do {
+                cval = (cval * 16) + ((int) (dp - hex) / 2);
+            } while (*++cp && (dp = index(hex, *cp)) != 0 && ++dcount < 2);
+        } else { /* C-style character escapes */
+            switch (*++cp) {
+            case '\\':
+                cval = '\\';
+                break;
+            case 'n':
+                cval = '\n';
+                break;
+            case 't':
+                cval = '\t';
+                break;
+            case 'b':
+                cval = '\b';
+                break;
+            case 'r':
+                cval = '\r';
+                break;
+            default:
+                cval = *cp;
+            }
+            ++cp;
+        }
+
+        if (meta)
+            cval |= 0x80;
+        *tp++ = (char) cval;
+    }
+    *tp = '\0';
+}
+
+/*
+ **********************************
+ *
+ *   Options Initialization
+ *
+ **********************************
+ */
+
+/* process options, possibly including SYSCF */
+void
+initoptions()
+{
+    int i;
+
+    initoptions_init();
+#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);
+
+    /* 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);
+    }
+
+    /* ... and _must_ parse correctly. */
+    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();
+}
+
+void
+initoptions_init()
+{
+#if (defined(UNIX) || defined(VMS)) && defined(TTY_GRAPHICS)
+    char *opts;
+#endif
+    int i;
+
+    memcpy(allopt, allopt_init, sizeof(allopt));
+    determine_ambiguities();
+
+    /* set up the command parsing */
+    reset_commands(TRUE); /* init */
+
+    /* initialize the random number generator(s) */
+    init_random(rn2);
+    init_random(rn2_on_display_rng);
+
+    /* for detection of configfile options specified multiple times */
+    iflags.opt_booldup = iflags.opt_compdup = (int *) 0;
+
+    for (i = 0; allopt[i].name; i++) {
+        if (allopt[i].addr)
+            *(allopt[i].addr) = allopt[i].initval;
+    }
+
+#ifdef SYSFLAGS
+    Strcpy(sysflags.sysflagsid, "sysflags");
+    sysflags.sysflagsid[9] = (char) sizeof (struct sysflag);
+#endif
+    flags.end_own = FALSE;
+    flags.end_top = 3;
+    flags.end_around = 2;
+    flags.paranoia_bits = PARANOID_PRAY; /* old prayconfirm=TRUE */
+    flags.pile_limit = PILE_LIMIT_DFLT;  /* 5 */
+    flags.runmode = RUN_LEAP;
+    iflags.msg_history = 20;
+    /* msg_window has conflicting defaults for multi-interface binary */
+#ifdef TTY_GRAPHICS
+    iflags.prevmsg_window = 's';
+#else
+#ifdef CURSES_GRAPHICS
+    iflags.prevmsg_window = 'r';
+#endif
+#endif
+
+    iflags.menu_headings = ATR_INVERSE;
+    iflags.getpos_coords = GPCOORDS_NONE;
+
+    /* hero's role, race, &c haven't been chosen yet */
+    flags.initrole = flags.initrace = flags.initgend = flags.initalign
+        = ROLE_NONE;
+
+    init_ov_primary_symbols();
+    init_ov_rogue_symbols();
+    /* Set the default monster and object class symbols. */
+    init_symbols();
+    for (i = 0; i < WARNCOUNT; i++)
+        g.warnsyms[i] = def_warnsyms[i].sym;
+
+    /* assert( sizeof flags.inv_order == sizeof def_inv_order ); */
+    (void) memcpy((genericptr_t) flags.inv_order,
+                  (genericptr_t) def_inv_order, sizeof flags.inv_order);
+    flags.pickup_types[0] = '\0';
+    flags.pickup_burden = MOD_ENCUMBER;
+    flags.sortloot = 'l'; /* sort only loot by default */
+
+    for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++)
+        flags.end_disclose[i] = DISCLOSE_PROMPT_DEFAULT_NO;
+    switch_symbols(FALSE); /* set default characters */
+    init_rogue_symbols();
+#if defined(UNIX) && defined(TTY_GRAPHICS)
+    /*
+     * Set defaults for some options depending on what we can
+     * detect about the environment's capabilities.
+     * This has to be done after the global initialization above
+     * and before reading user-specific initialization via
+     * config file/environment variable below.
+     */
+    /* this detects the IBM-compatible console on most 386 boxes */
+    if ((opts = nh_getenv("TERM")) && !strncmp(opts, "AT", 2)) {
+        if (!g.symset[PRIMARY].explicitly)
+            load_symset("IBMGraphics", PRIMARY);
+        if (!g.symset[ROGUESET].explicitly)
+            load_symset("RogueIBM", ROGUESET);
+        switch_symbols(TRUE);
+#ifdef TEXTCOLOR
+        iflags.use_color = TRUE;
+#endif
+    }
+#endif /* UNIX && TTY_GRAPHICS */
+#if defined(UNIX) || defined(VMS)
+#ifdef TTY_GRAPHICS
+    /* detect whether a "vt" terminal can handle alternate charsets */
+    if ((opts = nh_getenv("TERM"))
+        /* [could also check "xterm" which emulates vtXXX by default] */
+        && !strncmpi(opts, "vt", 2)
+        && AS && AE && index(AS, '\016') && index(AE, '\017')) {
+        if (!g.symset[PRIMARY].explicitly)
+            load_symset("DECGraphics", PRIMARY);
+        switch_symbols(TRUE);
+    }
+#endif
+#endif /* UNIX || VMS */
+
+#if defined(MSDOS) || defined(WIN32)
+    /* Use IBM defaults. Can be overridden via config file */
+    if (!g.symset[PRIMARY].explicitly)
+        load_symset("IBMGraphics_2", PRIMARY);
+    if (!g.symset[ROGUESET].explicitly)
+        load_symset("RogueEpyx", ROGUESET);
+#endif
+#ifdef MAC_GRAPHICS_ENV
+    if (!g.symset[PRIMARY].explicitly)
+        load_symset("MACGraphics", PRIMARY);
+    switch_symbols(TRUE);
+#endif /* MAC_GRAPHICS_ENV */
+    flags.menu_style = MENU_FULL;
+
+    iflags.wc_align_message = ALIGN_TOP;
+    iflags.wc_align_status = ALIGN_BOTTOM;
+    /* used by tty and curses */
+    iflags.wc2_statuslines = 2;
+    /* only used by curses */
+    iflags.wc2_windowborders = 2; /* 'Auto' */
+
+    /* since this is done before init_objects(), do partial init here */
+    objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD;
+    nmcpy(g.pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
+}
+
+void
+initoptions_finish()
+{
+    nhsym sym = 0;
+#ifndef MAC
+    char *opts = getenv("NETHACKOPTIONS");
+
+    if (!opts)
+        opts = getenv("HACKOPTIONS");
+    if (opts) {
+        if (*opts == '/' || *opts == '\\' || *opts == '@') {
+            if (*opts == '@')
+                opts++; /* @filename */
+            /* looks like a filename */
+            if (strlen(opts) < BUFSZ / 2) {
+                config_error_init(TRUE, opts, CONFIG_ERROR_SECURE);
+                read_config_file(opts, set_in_config);
+                config_error_done();
+            }
+        } else {
+            config_error_init(TRUE, (char *) 0, FALSE);
+            read_config_file((char *) 0, set_in_config);
+            config_error_done();
+            /* let the total length of options be long;
+             * parseoptions() will check each individually
+             */
+            config_error_init(FALSE, "NETHACKOPTIONS", FALSE);
+            (void) parseoptions(opts, TRUE, FALSE);
+            config_error_done();
+        }
+    } else
+#endif /* !MAC */
+    /*else*/ {
+        config_error_init(TRUE, (char *) 0, FALSE);
+        read_config_file((char *) 0, set_in_config);
+        config_error_done();
+    }
+
+    (void) fruitadd(g.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 [better than "\033"].
+     */
+    obj_descr[SLIME_MOLD].oc_name = "fruit";
+
+    sym = get_othersym(SYM_BOULDER,
+                Is_rogue_level(&u.uz) ? ROGUESET : PRIMARY);
+    if (sym)
+        g.showsyms[SYM_BOULDER + SYM_OFF_X] = sym;
+    reglyph_darkroom();
+
+#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("safe-startup")) {
+        if (iflags.hilite_delta && !wc2_supported("statushilites")) {
+            raw_printf("Status highlighting not supported for %s interface.",
+                       windowprocs.name);
+            iflags.hilite_delta = 0;
+        }
+    }
+#endif
+    return;
+}
+
+/*
+ *******************************************
+ *
+ * Support Functions for Individual Options
+ *
+ *******************************************
+ */
+
+/*
+ * Change the inventory order, using the given string as the new order.
+ * Missing characters in the new order are filled in at the end from
+ * the current inv_order, except for gold, which is forced to be first
+ * if not explicitly present.
+ *
+ * This routine returns 1 unless there is a duplicate or bad char in
+ * the string.
+ *
+ * Used by: optfn_packorder()
+ *
+ */
+static int
+change_inv_order(op)
+char *op;
+{
+    int oc_sym, num;
+    char *sp, buf[QBUFSZ];
+    int retval = 1;
+
+    num = 0;
+    if (!index(op, GOLD_SYM))
+        buf[num++] = COIN_CLASS;
+
+    for (sp = op; *sp; sp++) {
+        boolean fail = FALSE;
+        oc_sym = def_char_to_objclass(*sp);
+        /* reject bad or duplicate entries */
+        if (oc_sym == MAXOCLASSES) { /* not an object class char */
+            config_error_add("Not an object class '%c'", *sp);
+            retval = 0;
+            fail = TRUE;
+        } else if (!index(flags.inv_order, oc_sym)) {
+            /* VENOM_CLASS, RANDOM_CLASS, and ILLOBJ_CLASS are excluded
+               because they aren't in def_inv_order[] so don't make it
+               into flags.inv_order, hence always fail this index() test */
+            config_error_add("Object class '%c' not allowed", *sp);
+            retval = 0;
+            fail = TRUE;
+        } else if (index(sp + 1, *sp)) {
+            config_error_add("Duplicate object class '%c'", *sp);
+            retval = 0;
+            fail = TRUE;
+        }
+        /* retain good ones */
+        if (!fail)
+            buf[num++] = (char) oc_sym;
+    }
+    buf[num] = '\0';
+
+    /* fill in any omitted classes, using previous ordering */
+    for (sp = flags.inv_order; *sp; sp++)
+        if (!index(buf, *sp))
+            (void) strkitten(&buf[num++], *sp);
+    buf[MAXOCLASSES - 1] = '\0';
+
+    Strcpy(flags.inv_order, buf);
+    return retval;
+}
+
+
+/*
+ * Support functions for "warning"
+ *
+ * Used by: optfn_warnings()
+ *
+ */
+
+static boolean
+warning_opts(opts, optype)
+register char *opts;
+const char *optype;
+{
+    uchar translate[WARNCOUNT];
+    int length, i;
+
+    if ((opts = string_for_env_opt(optype, opts, FALSE)) == empty_optstr)
+        return FALSE;
+    escapes(opts, opts);
+
+    length = (int) strlen(opts);
+    /* match the form obtained from PC configuration files */
+    for (i = 0; i < WARNCOUNT; i++)
+        translate[i] = (i >= length) ? 0
+                                     : opts[i] ? (uchar) opts[i]
+                                               : def_warnsyms[i].sym;
+    assign_warnings(translate);
+    return TRUE;
+}
+
+void
+assign_warnings(graph_chars)
+register uchar *graph_chars;
+{
+    int i;
+
+    for (i = 0; i < WARNCOUNT; i++)
+        if (graph_chars[i])
+            g.warnsyms[i] = graph_chars[i];
+}
+
+/*
+ * Support functions for "suppress_alert"
+ *
+ * Used by: optfn_suppress_alert()
+ *
+ */
+
+static int
+feature_alert_opts(op, optn)
+char *op;
+const char *optn;
+{
+    char buf[BUFSZ];
+    unsigned long fnv = get_feature_notice_ver(op); /* version.c */
+
+    if (fnv == 0L)
+        return 0;
+    if (fnv > get_current_feature_ver()) {
+        if (!g.opt_initial) {
+            You_cant("disable new feature alerts for future versions.");
+        } else {
+            config_error_add(
+                        "%s=%s Invalid reference to a future version ignored",
+                             optn, op);
+        }
+        return 0;
+    }
+
+    flags.suppress_alert = fnv;
+    if (!g.opt_initial) {
+        Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
+                FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
+        pline(
+          "Feature change alerts disabled for NetHack %s features and prior.",
+              buf);
+    }
+    return 1;
+}
+
+/*
+ * This is used by parse_config_line() in files.c
+ *
+ */
+
+/* parse key:command */
+boolean
+parsebindings(bindings)
+char* bindings;
+{
+    char *bind;
+    char key;
+    int i;
+    boolean ret = FALSE;
+
+    /* break off first binding from the rest; parse the rest */
+    if ((bind = index(bindings, ',')) != 0) {
+        *bind++ = 0;
+        ret |= parsebindings(bind);
+    }
+
+    /* parse a single binding: first split around : */
+    if (! (bind = index(bindings, ':')))
+        return FALSE; /* it's not a binding */
+    *bind++ = 0;
+
+    /* read the key to be bound */
+    key = txt2key(bindings);
+    if (!key) {
+        config_error_add("Unknown key binding key '%s'", bindings);
+        return FALSE;
+    }
+
+    bind = trimspaces(bind);
+
+    /* is it a special key? */
+    if (bind_specialkey(key, bind))
+        return TRUE;
+
+    /* is it a menu command? */
+    for (i = 0; i < SIZE(default_menu_cmd_info); i++) {
+        if (!strcmp(default_menu_cmd_info[i].name, bind)) {
+            if (illegal_menu_cmd_key(key)) {
+                config_error_add("Bad menu key %s:%s", visctrl(key), bind);
+                return FALSE;
+            } else
+                add_menu_cmd_alias(key, default_menu_cmd_info[i].cmd);
+            return TRUE;
+        }
+    }
+
+    /* extended command? */
+    if (!bind_key(key, bind)) {
+        config_error_add("Unknown key binding command '%s'", bind);
+        return FALSE;
+    }
+    return TRUE;
+}
+
+/*
+ * Color support functions and data for "color"
+ *
+ * Used by: optfn_()
+ *
+ */
+
+static const struct {
+    const char *name;
+    const int color;
+} colornames[] = {
+    { "black", CLR_BLACK },
+    { "red", CLR_RED },
+    { "green", CLR_GREEN },
+    { "brown", CLR_BROWN },
+    { "blue", CLR_BLUE },
+    { "magenta", CLR_MAGENTA },
+    { "cyan", CLR_CYAN },
+    { "gray", CLR_GRAY },
+    { "orange", CLR_ORANGE },
+    { "light green", CLR_BRIGHT_GREEN },
+    { "yellow", CLR_YELLOW },
+    { "light blue", CLR_BRIGHT_BLUE },
+    { "light magenta", CLR_BRIGHT_MAGENTA },
+    { "light cyan", CLR_BRIGHT_CYAN },
+    { "white", CLR_WHITE },
+    { "no color", NO_COLOR },
+    { NULL, CLR_BLACK }, /* everything after this is an alias */
+    { "transparent", NO_COLOR },
+    { "purple", CLR_MAGENTA },
+    { "light purple", CLR_BRIGHT_MAGENTA },
+    { "bright purple", CLR_BRIGHT_MAGENTA },
+    { "grey", CLR_GRAY },
+    { "bright red", CLR_ORANGE },
+    { "bright green", CLR_BRIGHT_GREEN },
+    { "bright blue", CLR_BRIGHT_BLUE },
+    { "bright magenta", CLR_BRIGHT_MAGENTA },
+    { "bright cyan", CLR_BRIGHT_CYAN }
+};
+
+static const struct {
+    const char *name;
+    const int attr;
+} attrnames[] = {
+    { "none", ATR_NONE },
+    { "bold", ATR_BOLD },
+    { "dim", ATR_DIM },
+    { "underline", ATR_ULINE },
+    { "blink", ATR_BLINK },
+    { "inverse", ATR_INVERSE },
+    { NULL, ATR_NONE }, /* everything after this is an alias */
+    { "normal", ATR_NONE },
+    { "uline", ATR_ULINE },
+    { "reverse", ATR_INVERSE },
+};
+
+const char *
+clr2colorname(clr)
+int clr;
+{
+    int i;
+
+    for (i = 0; i < SIZE(colornames); i++)
+        if (colornames[i].name && colornames[i].color == clr)
+            return colornames[i].name;
+    return (char *) 0;
+}
+
+int
+match_str2clr(str)
+char *str;
+{
+    int i, c = CLR_MAX;
+
+    /* allow "lightblue", "light blue", and "light-blue" to match "light blue"
+       (also junk like "_l i-gh_t---b l u e" but we won't worry about that);
+       also copes with trailing space; caller has removed any leading space */
+    for (i = 0; i < SIZE(colornames); i++)
+        if (colornames[i].name
+            && fuzzymatch(str, colornames[i].name, " -_", TRUE)) {
+            c = colornames[i].color;
+            break;
+        }
+    if (i == SIZE(colornames) && (*str >= '0' && *str <= '9'))
+        c = atoi(str);
+
+    if (c == CLR_MAX)
+        config_error_add("Unknown color '%s'", str);
+
+    return c;
+}
+
+static const char *
+attr2attrname(attr)
+int attr;
+{
+    int i;
+
+    for (i = 0; i < SIZE(attrnames); i++)
+        if (attrnames[i].attr == attr)
+            return attrnames[i].name;
+    return (char *) 0;
+}
+
+int
+match_str2attr(str, complain)
+const char *str;
+boolean complain;
+{
+    int i, a = -1;
+
+    for (i = 0; i < SIZE(attrnames); i++)
+        if (attrnames[i].name
+            && fuzzymatch(str, attrnames[i].name, " -_", TRUE)) {
+            a = attrnames[i].attr;
+            break;
+        }
+
+    if (a == -1 && complain)
+        config_error_add("Unknown text attribute '%s'", str);
+
+    return a;
+}
+
+int
+query_color(prompt)
+const char *prompt;
+{
+    winid tmpwin;
+    anything any;
+    int i, pick_cnt;
+    menu_item *picks = (menu_item *) 0;
+
+    tmpwin = create_nhwindow(NHW_MENU);
+    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
+    any = cg.zeroany;
+    for (i = 0; i < SIZE(colornames); i++) {
+        if (!colornames[i].name)
+            break;
+        any.a_int = i + 1;
+        add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, colornames[i].name,
+                 (colornames[i].color == NO_COLOR) ? MENU_ITEMFLAGS_SELECTED
+                                                   : MENU_ITEMFLAGS_NONE);
+    }
+    end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick a color");
+    pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
+    destroy_nhwindow(tmpwin);
+    if (pick_cnt > 0) {
+        i = colornames[picks[0].item.a_int - 1].color;
+        /* pick_cnt==2: explicitly picked something other than the
+           preselected entry */
+        if (pick_cnt == 2 && i == NO_COLOR)
+            i = colornames[picks[1].item.a_int - 1].color;
+        free((genericptr_t) picks);
+        return i;
+    } else if (pick_cnt == 0) {
+        /* pick_cnt==0: explicitly picking preselected entry toggled it off */
+        return NO_COLOR;
+    }
+    return -1;
+}
+
+/* ask about highlighting attribute; for menu headers and menu
+   coloring patterns, only one attribute at a time is allowed;
+   for status highlighting, multiple attributes are allowed [overkill;
+   life would be much simpler if that were restricted to one also...] */
+int
+query_attr(prompt)
+const char *prompt;
+{
+    winid tmpwin;
+    anything any;
+    int i, pick_cnt;
+    menu_item *picks = (menu_item *) 0;
+    boolean allow_many = (prompt && !strncmpi(prompt, "Choose", 6));
+    int default_attr = ATR_NONE;
+
+    if (prompt && strstri(prompt, "menu headings"))
+        default_attr = iflags.menu_headings;
+    tmpwin = create_nhwindow(NHW_MENU);
+    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
+    any = cg.zeroany;
+    for (i = 0; i < SIZE(attrnames); i++) {
+        if (!attrnames[i].name)
+            break;
+        any.a_int = i + 1;
+        add_menu(tmpwin, NO_GLYPH, &any, 0, 0, attrnames[i].attr,
+                 attrnames[i].name,
+                 (attrnames[i].attr == default_attr) ? MENU_ITEMFLAGS_SELECTED
+                                                     : MENU_ITEMFLAGS_NONE);
+    }
+    end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick an attribute");
+    pick_cnt = select_menu(tmpwin, allow_many ? PICK_ANY : PICK_ONE, &picks);
+    destroy_nhwindow(tmpwin);
+    if (pick_cnt > 0) {
+        int j, k = 0;
+
+        if (allow_many) {
+            /* PICK_ANY, with one preselected entry (ATR_NONE) which
+               should be excluded if any other choices were picked */
+            for (i = 0; i < pick_cnt; ++i) {
+                j = picks[i].item.a_int - 1;
+                if (attrnames[j].attr != ATR_NONE || pick_cnt == 1) {
+                    switch (attrnames[j].attr) {
+                    case ATR_DIM:
+                        k |= HL_DIM;
+                        break;
+                    case ATR_BLINK:
+                        k |= HL_BLINK;
+                        break;
+                    case ATR_ULINE:
+                        k |= HL_ULINE;
+                        break;
+                    case ATR_INVERSE:
+                        k |= HL_INVERSE;
+                        break;
+                    case ATR_BOLD:
+                        k |= HL_BOLD;
+                        break;
+                    case ATR_NONE:
+                        k = HL_NONE;
+                        break;
+                    }
+                }
+            }
+        } else {
+            /* PICK_ONE, but might get 0 or 2 due to preselected entry */
+            j = picks[0].item.a_int - 1;
+            /* pick_cnt==2: explicitly picked something other than the
+               preselected entry */
+            if (pick_cnt == 2 && attrnames[j].attr == default_attr)
+                j = picks[1].item.a_int - 1;
+            k = attrnames[j].attr;
+        }
+        free((genericptr_t) picks);
+        return k;
+    } else if (pick_cnt == 0 && !allow_many) {
+        /* PICK_ONE, preselected entry explicitly chosen */
+        return default_attr;
+    }
+    /* either ESC to explicitly cancel (pick_cnt==-1) or
+       PICK_ANY with preselected entry toggled off and nothing chosen */
+    return -1;
+}
+
+static const struct {
+    const char *name;
+    xchar msgtyp;
+    const char *descr;
+} msgtype_names[] = {
+    { "show", MSGTYP_NORMAL, "Show message normally" },
+    { "hide", MSGTYP_NOSHOW, "Hide message" },
+    { "noshow", MSGTYP_NOSHOW, NULL },
+    { "stop", MSGTYP_STOP, "Prompt for more after the message" },
+    { "more", MSGTYP_STOP, NULL },
+    { "norep", MSGTYP_NOREP, "Do not repeat the message" }
+};
+
+static const char *
+msgtype2name(typ)
+int typ;
+{
+    int i;
+
+    for (i = 0; i < SIZE(msgtype_names); i++)
+        if (msgtype_names[i].descr && msgtype_names[i].msgtyp == typ)
+            return msgtype_names[i].name;
+    return (char *) 0;
+}
+
+static int
+query_msgtype()
+{
+    winid tmpwin;
+    anything any;
+    int i, pick_cnt;
+    menu_item *picks = (menu_item *) 0;
+
+    tmpwin = create_nhwindow(NHW_MENU);
+    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
+    any = cg.zeroany;
+    for (i = 0; i < SIZE(msgtype_names); i++)
+        if (msgtype_names[i].descr) {
+            any.a_int = msgtype_names[i].msgtyp + 1;
+            add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
+                 msgtype_names[i].descr, MENU_ITEMFLAGS_NONE);
+        }
+    end_menu(tmpwin, "How to show the message");
+    pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
+    destroy_nhwindow(tmpwin);
+    if (pick_cnt > 0) {
+        i = picks->item.a_int - 1;
+        free((genericptr_t) picks);
+        return i;
+    }
+    return -1;
+}
+
+static boolean
+msgtype_add(typ, pattern)
+int typ;
+char *pattern;
+{
+    struct plinemsg_type *tmp = (struct plinemsg_type *) alloc(sizeof *tmp);
+
+    tmp->msgtype = typ;
+    tmp->regex = regex_init();
+    if (!regex_compile(pattern, tmp->regex)) {
+        static const char *re_error = "MSGTYPE regex error";
+
+        config_error_add("%s: %s", re_error, regex_error_desc(tmp->regex));
+        regex_free(tmp->regex);
+        free((genericptr_t) tmp);
+        return FALSE;
+    }
+    tmp->pattern = dupstr(pattern);
+    tmp->next = g.plinemsg_types;
+    g.plinemsg_types = tmp;
+    return TRUE;
+}
+
+void
+msgtype_free()
+{
+    struct plinemsg_type *tmp, *tmp2 = 0;
+
+    for (tmp = g.plinemsg_types; tmp; tmp = tmp2) {
+        tmp2 = tmp->next;
+        free((genericptr_t) tmp->pattern);
+        regex_free(tmp->regex);
+        free((genericptr_t) tmp);
+    }
+    g.plinemsg_types = (struct plinemsg_type *) 0;
+}
+
+static void
+free_one_msgtype(idx)
+int idx; /* 0 .. */
+{
+    struct plinemsg_type *tmp = g.plinemsg_types;
+    struct plinemsg_type *prev = NULL;
+
+    while (tmp) {
+        if (idx == 0) {
+            struct plinemsg_type *next = tmp->next;
+
+            regex_free(tmp->regex);
+            free((genericptr_t) tmp->pattern);
+            free((genericptr_t) tmp);
+            if (prev)
+                prev->next = next;
+            else
+                g.plinemsg_types = next;
+            return;
+        }
+        idx--;
+        prev = tmp;
+        tmp = tmp->next;
+    }
+}
+
+int
+msgtype_type(msg, norepeat)
+const char *msg;
+boolean norepeat; /* called from Norep(via pline) */
+{
+    struct plinemsg_type *tmp = g.plinemsg_types;
+
+    while (tmp) {
+        /* we don't exclude entries with negative msgtype values
+           because then the msg might end up matching a later pattern */
+        if (regex_match(msg, tmp->regex))
+            return tmp->msgtype;
+        tmp = tmp->next;
+    }
+    return norepeat ? MSGTYP_NOREP : MSGTYP_NORMAL;
+}
+
+/* negate one or more types of messages so that their type handling will
+   be disabled or re-enabled; MSGTYPE_NORMAL (value 0) is not affected */
+void
+hide_unhide_msgtypes(hide, hide_mask)
+boolean hide;
+int hide_mask;
+{
+    struct plinemsg_type *tmp;
+    int mt;
+
+    /* negative msgtype value won't be recognized by pline, so does nothing */
+    for (tmp = g.plinemsg_types; tmp; tmp = tmp->next) {
+        mt = tmp->msgtype;
+        if (!hide)
+            mt = -mt; /* unhide: negate negative, yielding positive */
+        if (mt > 0 && ((1 << mt) & hide_mask))
+            tmp->msgtype = -tmp->msgtype;
+    }
+}
+
+static int
+msgtype_count(VOID_ARGS)
+{
+    int c = 0;
+    struct plinemsg_type *tmp = g.plinemsg_types;
+
+    while (tmp) {
+        c++;
+        tmp = tmp->next;
+    }
+    return c;
+}
+
+boolean
+msgtype_parse_add(str)
+char *str;
+{
+    char pattern[256];
+    char msgtype[11];
+
+    if (sscanf(str, "%10s \"%255[^\"]\"", msgtype, pattern) == 2) {
+        int typ = -1;
+        int i;
+
+        for (i = 0; i < SIZE(msgtype_names); i++)
+            if (!strncmpi(msgtype_names[i].name, msgtype, strlen(msgtype))) {
+                typ = msgtype_names[i].msgtyp;
+                break;
+            }
+        if (typ != -1)
+            return msgtype_add(typ, pattern);
+        else
+            config_error_add("Unknown message type '%s'", msgtype);
+    } else {
+        config_error_add("Malformed MSGTYPE");
+    }
+    return FALSE;
+}
+
+static boolean
+test_regex_pattern(str, errmsg)
+const char *str;
+const char *errmsg;
+{
+    static const char re_error[] = "Regex error";
+    struct nhregex *match;
+    boolean retval = TRUE;
+
+    if (!str)
+        return FALSE;
+
+    match = regex_init();
+    if (!match) {
+        config_error_add("NHregex error");
+        return FALSE;
+    }
+
+    if (!regex_compile(str, match)) {
+        config_error_add("%s: %s", errmsg ? errmsg : re_error,
+                         regex_error_desc(match));
+        retval = FALSE;
+    }
+    regex_free(match);
+    return retval;
+}
+
+static boolean
+add_menu_coloring_parsed(str, c, a)
+char *str;
+int c, a;
+{
+    static const char re_error[] = "Menucolor regex error";
+    struct menucoloring *tmp;
+
+    if (!str)
+        return FALSE;
+    tmp = (struct menucoloring *) alloc(sizeof *tmp);
+    tmp->match = regex_init();
+    if (!regex_compile(str, tmp->match)) {
+        config_error_add("%s: %s", re_error, regex_error_desc(tmp->match));
+        regex_free(tmp->match);
+        free(tmp);
+        return FALSE;
+    } else {
+        tmp->next = g.menu_colorings;
+        tmp->origstr = dupstr(str);
+        tmp->color = c;
+        tmp->attr = a;
+        g.menu_colorings = tmp;
+        return TRUE;
+    }
+}
+
+/* parse '"regex_string"=color&attr' and add it to menucoloring */
+boolean
+add_menu_coloring(tmpstr)
+char *tmpstr; /* never Null but could be empty */
+{
+    int c = NO_COLOR, a = ATR_NONE;
+    char *tmps, *cs, *amp;
+    char str[BUFSZ];
+
+    (void) strncpy(str, tmpstr, sizeof str - 1);
+    str[sizeof str - 1] = '\0';
+
+    if ((cs = index(str, '=')) == 0) {
+        config_error_add("Malformed MENUCOLOR");
+        return FALSE;
+    }
+
+    tmps = cs + 1; /* advance past '=' */
+    mungspaces(tmps);
+    if ((amp = index(tmps, '&')) != 0)
+        *amp = '\0';
+
+    c = match_str2clr(tmps);
+    if (c >= CLR_MAX)
+        return FALSE;
+
+    if (amp) {
+        tmps = amp + 1; /* advance past '&' */
+        a = match_str2attr(tmps, TRUE);
+        if (a == -1)
+            return FALSE;
+    }
+
+    /* the regexp portion here has not been condensed by mungspaces() */
+    *cs = '\0';
+    tmps = str;
+    if (*tmps == '"' || *tmps == '\'') {
+        cs--;
+        while (isspace((uchar) *cs))
+            cs--;
+        if (*cs == *tmps) {
+            *cs = '\0';
+            tmps++;
+        }
+    }
+    return add_menu_coloring_parsed(tmps, c, a);
+}
+
+boolean
+get_menu_coloring(str, color, attr)
+const char *str;
+int *color, *attr;
+{
+    struct menucoloring *tmpmc;
+
+    if (iflags.use_menu_color)
+        for (tmpmc = g.menu_colorings; tmpmc; tmpmc = tmpmc->next)
+            if (regex_match(str, tmpmc->match)) {
+                *color = tmpmc->color;
+                *attr = tmpmc->attr;
+                return TRUE;
+            }
+    return FALSE;
+}
+
+void
+free_menu_coloring()
+{
+    struct menucoloring *tmp, *tmp2;
+
+    for (tmp = g.menu_colorings; tmp; tmp = tmp2) {
+        tmp2 = tmp->next;
+        regex_free(tmp->match);
+        free((genericptr_t) tmp->origstr);
+        free((genericptr_t) tmp);
+    }
+}
+
+static void
+free_one_menu_coloring(idx)
+int idx; /* 0 .. */
+{
+    struct menucoloring *tmp = g.menu_colorings;
+    struct menucoloring *prev = NULL;
+
+    while (tmp) {
+        if (idx == 0) {
+            struct menucoloring *next = tmp->next;
+
+            regex_free(tmp->match);
+            free((genericptr_t) tmp->origstr);
+            free((genericptr_t) tmp);
+            if (prev)
+                prev->next = next;
+            else
+                g.menu_colorings = next;
+            return;
+        }
+        idx--;
+        prev = tmp;
+        tmp = tmp->next;
+    }
+}
+
+static int
+count_menucolors(VOID_ARGS)
+{
+    struct menucoloring *tmp;
+    int count = 0;
+
+    for (tmp = g.menu_colorings; tmp; tmp = tmp->next)
+        count++;
+    return count;
+}
+
+static boolean
+parse_role_opts(negated, fullname, opts, opp)
+boolean negated;
+const char *fullname;
+char *opts;
+char **opp;
+{
+    char *op = *opp;
+
+    if (negated) {
+        bad_negation(fullname, FALSE);
+    } else if ((op = string_for_env_opt(fullname, opts, FALSE))
+                                        != empty_optstr) {
+        boolean val_negated = FALSE;
+
+        while ((*op == '!') || !strncmpi(op, "no", 2)) {
+            if (*op == '!')
+                op++;
+            else
+                op += 2;
+            val_negated = !val_negated;
+        }
+        if (val_negated) {
+            if (!setrolefilter(op)) {
+                config_error_add("Unknown negated parameter '%s'", op);
+                return FALSE;
+            }
+        } else {
+            if (duplicate_opt_detection(opts, 1))
+                complain_about_duplicate(opts, 1);
+            *opp = op;
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+/* Check if character c is illegal as a menu command key */
+boolean
+illegal_menu_cmd_key(c)
+char c;
+{
+    if (c == 0 || c == '\r' || c == '\n' || c == '\033'
+        || c == ' ' || digit(c) || (letter(c) && c != '@')) {
+        config_error_add("Reserved menu command key '%s'", visctrl(c));
+        return TRUE;
+    } else { /* reject default object class symbols */
+        int j;
+        for (j = 1; j < MAXOCLASSES; j++)
+            if (c == def_oc_syms[j].sym) {
+                config_error_add("Menu command key '%s' is an object class",
+                                 visctrl(c));
+                return TRUE;
+            }
+    }
+    return FALSE;
+}
+
+
+/*
+ * Convert the given string of object classes to a string of default object
+ * symbols.
+ */
+void
+oc_to_str(src, dest)
+char *src, *dest;
+{
+    int i;
+
+    while ((i = (int) *src++) != 0) {
+        if (i < 0 || i >= MAXOCLASSES)
+            impossible("oc_to_str:  illegal object class %d", i);
+        else
+            *dest++ = def_oc_syms[i].sym;
+    }
+    *dest = '\0';
+}
+
+/*
+ * Add the given mapping to the menu command map list.  Always keep the
+ * maps valid C strings.
+ */
+void
+add_menu_cmd_alias(from_ch, to_ch)
+char from_ch, to_ch;
+{
+    if (g.n_menu_mapped >= MAX_MENU_MAPPED_CMDS) {
+        pline("out of menu map space.");
+    } else {
+        g.mapped_menu_cmds[g.n_menu_mapped] = from_ch;
+        g.mapped_menu_op[g.n_menu_mapped] = to_ch;
+        g.n_menu_mapped++;
+        g.mapped_menu_cmds[g.n_menu_mapped] = '\0';
+        g.mapped_menu_op[g.n_menu_mapped] = '\0';
+    }
+}
+
+char
+get_menu_cmd_key(ch)
+char ch;
+{
+    char *found = index(g.mapped_menu_op, ch);
+
+    if (found) {
+        int idx = (int) (found - g.mapped_menu_op);
+
+        ch = g.mapped_menu_cmds[idx];
+    }
+    return ch;
+}
+
+/*
+ * Map the given character to its corresponding menu command.  If it
+ * doesn't match anything, just return the original.
+ */
+char
+map_menu_cmd(ch)
+char ch;
+{
+    char *found = index(g.mapped_menu_cmds, ch);
+
+    if (found) {
+        int idx = (int) (found - g.mapped_menu_cmds);
+
+        ch = g.mapped_menu_op[idx];
+    }
+    return ch;
+}
+
+/* Returns the fid of the fruit type; if that type already exists, it
+ * returns the fid of that one; if it does not exist, it adds a new fruit
+ * type to the chain and returns the new one.
+ * If replace_fruit is sent in, replace the fruit in the chain rather than
+ * adding a new entry--for user specified fruits only.
+ */
+int
+fruitadd(str, replace_fruit)
+char *str;
+struct fruit *replace_fruit;
+{
+    register int i;
+    register struct fruit *f;
+    int highest_fruit_id = 0, globpfx;
+    char buf[PL_FSIZ], altname[PL_FSIZ];
+    boolean user_specified = (str == g.pl_fruit);
+    /* if not user-specified, then it's a fruit name for a fruit on
+     * a bones level or from orctown raider's loot...
+     */
+
+    /* Note: every fruit has an id (kept in obj->spe) of at least 1;
+     * 0 is an error.
+     */
+    if (user_specified) {
+        boolean found = FALSE, numeric = FALSE;
+
+        /* force fruit to be singular; this handling is not
+           needed--or wanted--for fruits from bones because
+           they already received it in their original game;
+           str==pl_fruit but makesingular() creates a copy
+           so we need to copy that back into pl_fruit */
+        nmcpy(g.pl_fruit, makesingular(str), PL_FSIZ);
+        /* (assertion doesn't matter; we use 'g.pl_fruit' from here on out) */
+        /* assert( str == g.pl_fruit ); */
+
+        /* disallow naming after other foods (since it'd be impossible
+         * to tell the difference); globs might have a size prefix which
+         * needs to be skipped in order to match the object type name
+         */
+        globpfx = (!strncmp(g.pl_fruit, "small ", 6)
+                   || !strncmp(g.pl_fruit, "large ", 6)) ? 6
+                  : (!strncmp(g.pl_fruit, "very large ", 11)) ? 11
+                    : 0;
+        for (i = g.bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS; i++) {
+            if (!strcmp(OBJ_NAME(objects[i]), g.pl_fruit)
+                || (globpfx > 0
+                    && !strcmp(OBJ_NAME(objects[i]), &g.pl_fruit[globpfx]))) {
+                found = TRUE;
+                break;
+            }
+        }
+        if (!found) {
+            char *c;
+
+            for (c = g.pl_fruit; *c >= '0' && *c <= '9'; c++)
+                continue;
+            if (!*c || isspace((uchar) *c))
+                numeric = TRUE;
+        }
+        if (found || numeric
+            /* these checks for applying food attributes to actual items
+               are case sensitive; "glob of foo" is caught by 'found'
+               if 'foo' is a valid glob; when not valid, allow it as-is */
+            || !strncmp(g.pl_fruit, "cursed ", 7)
+            || !strncmp(g.pl_fruit, "uncursed ", 9)
+            || !strncmp(g.pl_fruit, "blessed ", 8)
+            || !strncmp(g.pl_fruit, "partly eaten ", 13)
+            || (!strncmp(g.pl_fruit, "tin of ", 7)
+                && (!strcmp(g.pl_fruit + 7, "spinach")
+                    || name_to_mon(g.pl_fruit + 7) >= LOW_PM))
+            || !strcmp(g.pl_fruit, "empty tin")
+            || (!strcmp(g.pl_fruit, "glob")
+                || (globpfx > 0 && !strcmp("glob", &g.pl_fruit[globpfx])))
+            || ((str_end_is(g.pl_fruit, " corpse")
+                 || str_end_is(g.pl_fruit, " egg"))
+                && name_to_mon(g.pl_fruit) >= LOW_PM)) {
+            Strcpy(buf, g.pl_fruit);
+            Strcpy(g.pl_fruit, "candied ");
+            nmcpy(g.pl_fruit + 8, buf, PL_FSIZ - 8);
+        }
+        *altname = '\0';
+        /* This flag indicates that a fruit has been made since the
+         * last time the user set the fruit.  If it hasn't, we can
+         * safely overwrite the current fruit, preventing the user from
+         * setting many fruits in a row and overflowing.
+         * Possible expansion: check for specific fruit IDs, not for
+         * any fruit.
+         */
+        flags.made_fruit = FALSE;
+        if (replace_fruit) {
+            /* replace_fruit is already part of the fruit chain;
+               update it in place rather than looking it up again */
+            f = replace_fruit;
+            copynchars(f->fname, g.pl_fruit, PL_FSIZ - 1);
+            goto nonew;
+        }
+    } else {
+        /* not user_supplied, so assumed to be from bones (or orc gang) */
+        copynchars(altname, str, PL_FSIZ - 1);
+        sanitize_name(altname);
+        flags.made_fruit = TRUE; /* for safety.  Any fruit name added from a
+                                  * bones level should exist anyway. */
+    }
+    f = fruit_from_name(*altname ? altname : str, FALSE, &highest_fruit_id);
+    if (f)
+        goto nonew;
+
+    /* Maximum number of named fruits is 127, even if obj->spe can
+       handle bigger values.  If adding another fruit would overflow,
+       use a random fruit instead... we've got a lot to choose from.
+       current_fruit remains as is. */
+    if (highest_fruit_id >= 127)
+        return rnd(127);
+
+    f = newfruit();
+    (void) memset((genericptr_t) f, 0, sizeof (struct fruit));
+    copynchars(f->fname, *altname ? altname : str, PL_FSIZ - 1);
+    f->fid = ++highest_fruit_id;
+    /* we used to go out of our way to add it at the end of the list,
+       but the order is arbitrary so use simpler insertion at start */
+    f->nextf = g.ffruit;
+    g.ffruit = f;
+ nonew:
+    if (user_specified)
+        g.context.current_fruit = f->fid;
+    return f->fid;
+}
+
+/*
+ **********************************
+ *
+ * Option-setting, menus,
+ *  displaying option values
+ *
+ **********************************
+ */
+
+
+#if defined(MICRO) || defined(MAC) || defined(WIN32)
+#define OPTIONS_HEADING "OPTIONS"
+#else
+#define OPTIONS_HEADING "NETHACKOPTIONS"
 #endif
-    else if (!strcmp(optname, "catname"))
-        Sprintf(buf, "%s", g.catname[0] ? g.catname : none);
-    else if (!strcmp(optname, "disclose"))
-        for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
-            if (i)
-                (void) strkitten(buf, ' ');
-            (void) strkitten(buf, flags.end_disclose[i]);
-            (void) strkitten(buf, disclosure_options[i]);
-        }
-    else if (!strcmp(optname, "dogname"))
-        Sprintf(buf, "%s", g.dogname[0] ? g.dogname : none);
-    else if (!strcmp(optname, "dungeon"))
-        Sprintf(buf, "%s", to_be_done);
-    else if (!strcmp(optname, "effects"))
-        Sprintf(buf, "%s", to_be_done);
-    else if (!strcmp(optname, "font_map"))
-        Sprintf(buf, "%s", iflags.wc_font_map ? iflags.wc_font_map : defopt);
-    else if (!strcmp(optname, "font_message"))
-        Sprintf(buf, "%s",
-                iflags.wc_font_message ? iflags.wc_font_message : defopt);
-    else if (!strcmp(optname, "font_status"))
-        Sprintf(buf, "%s",
-                iflags.wc_font_status ? iflags.wc_font_status : defopt);
-    else if (!strcmp(optname, "font_menu"))
-        Sprintf(buf, "%s",
-                iflags.wc_font_menu ? iflags.wc_font_menu : defopt);
-    else if (!strcmp(optname, "font_text"))
-        Sprintf(buf, "%s",
-                iflags.wc_font_text ? iflags.wc_font_text : defopt);
-    else if (!strcmp(optname, "font_size_map")) {
-        if (iflags.wc_fontsiz_map)
-            Sprintf(buf, "%d", iflags.wc_fontsiz_map);
-        else
-            Strcpy(buf, defopt);
-    } else if (!strcmp(optname, "font_size_message")) {
-        if (iflags.wc_fontsiz_message)
-            Sprintf(buf, "%d", iflags.wc_fontsiz_message);
-        else
-            Strcpy(buf, defopt);
-    } else if (!strcmp(optname, "font_size_status")) {
-        if (iflags.wc_fontsiz_status)
-            Sprintf(buf, "%d", iflags.wc_fontsiz_status);
-        else
-            Strcpy(buf, defopt);
-    } else if (!strcmp(optname, "font_size_menu")) {
-        if (iflags.wc_fontsiz_menu)
-            Sprintf(buf, "%d", iflags.wc_fontsiz_menu);
-        else
-            Strcpy(buf, defopt);
-    } else if (!strcmp(optname, "font_size_text")) {
-        if (iflags.wc_fontsiz_text)
-            Sprintf(buf, "%d", iflags.wc_fontsiz_text);
-        else
-            Strcpy(buf, defopt);
-    } else if (!strcmp(optname, "fruit"))
-        Sprintf(buf, "%s", g.pl_fruit);
-    else if (!strcmp(optname, "gender"))
-        Sprintf(buf, "%s", rolestring(flags.initgend, genders, adj));
-    else if (!strcmp(optname, "horsename"))
-        Sprintf(buf, "%s", g.horsename[0] ? g.horsename : none);
-    else if (!strcmp(optname, "map_mode")) {
-        i = iflags.wc_map_mode;
-        Sprintf(buf, "%s",
-                (i == MAP_MODE_TILES) ? "tiles"
-                : (i == MAP_MODE_ASCII4x6) ? "ascii4x6"
-                  : (i == MAP_MODE_ASCII6x8) ? "ascii6x8"
-                    : (i == MAP_MODE_ASCII8x8) ? "ascii8x8"
-                      : (i == MAP_MODE_ASCII16x8) ? "ascii16x8"
-                        : (i == MAP_MODE_ASCII7x12) ? "ascii7x12"
-                          : (i == MAP_MODE_ASCII8x12) ? "ascii8x12"
-                            : (i == MAP_MODE_ASCII16x12) ? "ascii16x12"
-                              : (i == MAP_MODE_ASCII12x16) ? "ascii12x16"
-                                : (i == MAP_MODE_ASCII10x18) ? "ascii10x18"
-                                  : (i == MAP_MODE_ASCII_FIT_TO_SCREEN)
-                                    ? "fit_to_screen"
-                                    : defopt);
-    } else if (!strcmp(optname, "menuinvertmode"))
-        Sprintf(buf, "%d", iflags.menuinvertmode);
-    else if (!strcmp(optname, "menustyle"))
-        Sprintf(buf, "%s", menutype[(int) flags.menu_style]);
-    else if (!strcmp(optname, "menu_deselect_all"))
-        Sprintf(buf, "%s", to_be_done);
-    else if (!strcmp(optname, "menu_deselect_page"))
-        Sprintf(buf, "%s", to_be_done);
-    else if (!strcmp(optname, "menu_first_page"))
-        Sprintf(buf, "%s", to_be_done);
-    else if (!strcmp(optname, "menu_invert_all"))
-        Sprintf(buf, "%s", to_be_done);
-    else if (!strcmp(optname, "menu_headings"))
-        Sprintf(buf, "%s", attr2attrname(iflags.menu_headings));
-    else if (!strcmp(optname, "menu_invert_page"))
-        Sprintf(buf, "%s", to_be_done);
-    else if (!strcmp(optname, "menu_last_page"))
-        Sprintf(buf, "%s", to_be_done);
-    else if (!strcmp(optname, "menu_next_page"))
-        Sprintf(buf, "%s", to_be_done);
-    else if (!strcmp(optname, "menu_previous_page"))
-        Sprintf(buf, "%s", to_be_done);
-    else if (!strcmp(optname, "menu_search"))
-        Sprintf(buf, "%s", to_be_done);
-    else if (!strcmp(optname, "menu_select_all"))
-        Sprintf(buf, "%s", to_be_done);
-    else if (!strcmp(optname, "menu_select_page"))
-        Sprintf(buf, "%s", to_be_done);
-    else if (!strcmp(optname, "monsters")) {
-        Sprintf(buf, "%s", to_be_done);
-    } else if (!strcmp(optname, "msghistory")) {
-        Sprintf(buf, "%u", iflags.msg_history);
-#ifdef TTY_GRAPHICS
-    } else if (!strcmp(optname, "msg_window")) {
-        Sprintf(buf, "%s", (iflags.prevmsg_window == 's') ? "single"
-                           : (iflags.prevmsg_window == 'c') ? "combination"
-                             : (iflags.prevmsg_window == 'f') ? "full"
-                               : "reversed");
+
+static char fmtstr_doset[] = "%s%-15s [%s]   ";
+static char fmtstr_doset_tab[] = "%s\t[%s]";
+static char n_currently_set[] = "(%d currently set)";
+
+enum opt_other_enums {
+    OPT_OTHER_COND = -5,
+    OPT_OTHER_MSGTYPE = -4,
+    OPT_OTHER_MENUCOLOR = -3,
+    OPT_OTHER_STATHILITE = -2,
+    OPT_OTHER_APEXC = -1
+    /* these must be < 0 */
+};
+
+static struct other_opts {
+    const char *name;
+    enum optset_restrictions setwhere;
+    enum opt_other_enums code;
+    int NDECL((*othr_count_func));
+} othropt[] = {
+    { "autopickup exceptions", set_in_game, OPT_OTHER_APEXC, count_apes },
+    { "status condition fields", set_in_game,
+      OPT_OTHER_COND, count_cond },
+    { "menu colors", set_in_game, OPT_OTHER_MENUCOLOR, count_menucolors },
+    { "message types", set_in_game, OPT_OTHER_MSGTYPE, msgtype_count },
+#ifdef STATUS_HILITES
+    { "status hilite rules", set_in_game, OPT_OTHER_STATHILITE,
+      count_status_hilites },
 #endif
-    } else if (!strcmp(optname, "name")) {
-        Sprintf(buf, "%s", g.plname);
-    } else if (!strcmp(optname, "mouse_support")) {
-#ifdef WIN32
-#define MOUSEFIX1 ", QuickEdit off"
-#define MOUSEFIX2 ", QuickEdit unchanged"
-#else
-#define MOUSEFIX1 ", O/S adjusted"
-#define MOUSEFIX2 ", O/S unchanged"
+    { (char *) 0, 0, (enum opt_other_enums) 0 },
+};
+
+/* the 'O' command */
+int
+doset() /* changing options via menu by Per Liboriussen */
+{
+    static boolean made_fmtstr = FALSE;
+    char buf[BUFSZ];
+    const char *name;
+    int i = 0, pass, pick_cnt, pick_idx, opt_indx;
+    boolean *bool_p;
+    winid tmpwin;
+    anything any;
+    menu_item *pick_list;
+    int indexoffset, startpass, endpass, optflags;
+    boolean setinitial = FALSE, fromfile = FALSE;
+    unsigned longest_name_len;
+
+    tmpwin = create_nhwindow(NHW_MENU);
+    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
+
+#ifdef notyet /* SYSCF */
+    /* XXX I think this is still fragile.  Fixing initial/from_file and/or
+       changing the SET_* etc to bitmaps will let me make this better. */
+    if (wizard)
+        startpass = set_in_sysconf;
+    else
 #endif
-        static const char *mousemodes[][2] = {
-            { "0=off", "" },
-            { "1=on",  MOUSEFIX1 },
-            { "2=on",  MOUSEFIX2 },
-        };
-#undef MOUSEFIX1
-#undef MOUSEFIX2
-        int ms = iflags.wc_mouse_support;
+        startpass = set_gameview;
+    endpass = (wizard) ? set_wizonly : set_in_game;
+
+    if (!made_fmtstr && !iflags.menu_tab_sep) {
+        /* spin through the options to find the longest name
+           and adjust the format string accordingly */
+        longest_name_len = 0;
+        for (pass = 0; pass <= 2; pass++)
+            for (i = 0; (name = ((pass < 2) ? allopt[i].name
+                                : othropt[i].name)) != 0; i++) {
+                if (pass == 0 &&
+                    (allopt[i].opttyp != BoolOpt || !allopt[i].addr))
+                    continue;
+                optflags = (pass < 2) ? allopt[i].setwhere
+                                      : othropt[i].setwhere;
+                if (optflags < startpass || optflags > endpass)
+                    continue;
+                if ((is_wc_option(name) && !wc_supported(name))
+                    || (is_wc2_option(name) && !wc2_supported(name)))
+                    continue;
 
-        if (ms >= 0 && ms <= 2)
-            Sprintf(buf, "%s%s", mousemodes[ms][0], mousemodes[ms][1]);
-    } else if (!strcmp(optname, "number_pad")) {
-        static const char *numpadmodes[] = {
-            "0=off", "1=on", "2=on, MSDOS compatible",
-            "3=on, phone-style layout",
-            "4=on, phone layout, MSDOS compatible",
-            "-1=off, y & z swapped", /*[5]*/
-        };
-        int indx = g.Cmd.num_pad
-                       ? (g.Cmd.phone_layout ? (g.Cmd.pcHack_compat ? 4 : 3)
-                                           : (g.Cmd.pcHack_compat ? 2 : 1))
-                       : g.Cmd.swap_yz ? 5 : 0;
+                if (strlen(name) > longest_name_len)
+                    longest_name_len = strlen(name);
+            }
+        Sprintf(fmtstr_doset, "%%s%%-%us [%%s]", longest_name_len);
+        made_fmtstr = TRUE;
+    }
 
-        Strcpy(buf, numpadmodes[indx]);
-    } else if (!strcmp(optname, "objects")) {
-        Sprintf(buf, "%s", to_be_done);
-    } else if (!strcmp(optname, "packorder")) {
-        oc_to_str(flags.inv_order, ocl);
-        Sprintf(buf, "%s", ocl);
-#ifdef CHANGE_COLOR
-    } else if (!strcmp(optname, "palette")) {
-        Sprintf(buf, "%s", get_color_string());
-#endif
-    } else if (!strcmp(optname, "paranoid_confirmation")) {
-        char tmpbuf[QBUFSZ];
+    indexoffset = 1;
+    any = cg.zeroany;
+    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
+             "Booleans (selecting will toggle value):", MENU_ITEMFLAGS_NONE);
+    any.a_int = 0;
+    /* first list any other non-modifiable booleans, then modifiable ones */
+    for (pass = 0; pass <= 1; pass++)
+        for (i = 0; (name = allopt[i].name) != 0; i++)
+            if (allopt[i].opttyp == BoolOpt
+                && (bool_p = allopt[i].addr) != 0
+                && ((allopt[i].setwhere <= set_gameview && pass == 0)
+                    || (allopt[i].setwhere >= set_in_game && pass == 1))) {
+                if (bool_p == &flags.female)
+                    continue; /* obsolete */
+                if (allopt[i].setwhere == set_wizonly && !wizard)
+                    continue;
+                if ((is_wc_option(name) && !wc_supported(name))
+                    || (is_wc2_option(name) && !wc2_supported(name)))
+                    continue;
 
-        tmpbuf[0] = '\0';
-        for (i = 0; paranoia[i].flagmask != 0; ++i)
-            if (flags.paranoia_bits & paranoia[i].flagmask)
-                Sprintf(eos(tmpbuf), " %s", paranoia[i].argname);
-        Strcpy(buf, tmpbuf[0] ? &tmpbuf[1] : "none");
-    } else if (!strcmp(optname, "petattr")) {
-#ifdef CURSES_GRAPHICS
-        if (WINDOWPORT("curses")) {
-            char tmpbuf[QBUFSZ];
+                any.a_int = (pass == 0) ? 0 : i + 1 + indexoffset;
+                if (!iflags.menu_tab_sep)
+                    Sprintf(buf, fmtstr_doset, (pass == 0) ? "    " : "",
+                            name, *bool_p ? "true" : "false");
+                else
+                    Sprintf(buf, fmtstr_doset_tab,
+                            name, *bool_p ? "true" : "false");
+                add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf,
+                         MENU_ITEMFLAGS_NONE);
+            }
 
-            Strcpy(buf, curses_fmt_attrs(tmpbuf));
-        } else
-#endif
-        if (iflags.wc2_petattr != 0)
-            Sprintf(buf, "0x%08x", iflags.wc2_petattr);
-        else
-            Strcpy(buf, defopt);
-    } else if (!strcmp(optname, "pettype")) {
-        Sprintf(buf, "%s", (g.preferred_pet == 'c') ? "cat"
-                           : (g.preferred_pet == 'd') ? "dog"
-                             : (g.preferred_pet == 'h') ? "horse"
-                               : (g.preferred_pet == 'n') ? "none"
-                                 : "random");
-    } else if (!strcmp(optname, "pickup_burden")) {
-        Sprintf(buf, "%s", burdentype[flags.pickup_burden]);
-    } else if (!strcmp(optname, "pickup_types")) {
-        oc_to_str(flags.pickup_types, ocl);
-        Sprintf(buf, "%s", ocl[0] ? ocl : "all");
-    } else if (!strcmp(optname, "pile_limit")) {
-        Sprintf(buf, "%d", flags.pile_limit);
-    } else if (!strcmp(optname, "playmode")) {
-        Strcpy(buf, wizard ? "debug" : discover ? "explore" : "normal");
-    } else if (!strcmp(optname, "race")) {
-        Sprintf(buf, "%s", rolestring(flags.initrace, races, noun));
-    } else if (!strcmp(optname, "roguesymset")) {
-        Sprintf(buf, "%s",
-                g.symset[ROGUESET].name ? g.symset[ROGUESET].name : "default");
-        if (g.currentgraphics == ROGUESET && g.symset[ROGUESET].name)
-            Strcat(buf, ", active");
-    } else if (!strcmp(optname, "role")) {
-        Sprintf(buf, "%s", rolestring(flags.initrole, roles, name.m));
-    } else if (!strcmp(optname, "runmode")) {
-        Sprintf(buf, "%s", runmodes[flags.runmode]);
-    } else if (!strcmp(optname, "whatis_coord")) {
-        Sprintf(buf, "%s",
-                (iflags.getpos_coords == GPCOORDS_MAP) ? "map"
-                : (iflags.getpos_coords == GPCOORDS_COMPASS) ? "compass"
-                : (iflags.getpos_coords == GPCOORDS_COMFULL) ? "full compass"
-                : (iflags.getpos_coords == GPCOORDS_SCREEN) ? "screen"
-                : "none");
-    } else if (!strcmp(optname, "whatis_filter")) {
-        Sprintf(buf, "%s",
-                (iflags.getloc_filter == GFILTER_VIEW) ? "view"
-                : (iflags.getloc_filter == GFILTER_AREA) ? "area"
-                : "none");
-    } else if (!strcmp(optname, "scores")) {
-        Sprintf(buf, "%d top/%d around%s", flags.end_top, flags.end_around,
-                flags.end_own ? "/own" : "");
-    } else if (!strcmp(optname, "scroll_amount")) {
-        if (iflags.wc_scroll_amount)
-            Sprintf(buf, "%d", iflags.wc_scroll_amount);
-        else
-            Strcpy(buf, defopt);
-    } else if (!strcmp(optname, "scroll_margin")) {
-        if (iflags.wc_scroll_margin)
-            Sprintf(buf, "%d", iflags.wc_scroll_margin);
-        else
-            Strcpy(buf, defopt);
-    } else if (!strcmp(optname, "sortloot")) {
-        for (i = 0; i < SIZE(sortltype); i++)
-            if (flags.sortloot == sortltype[i][0]) {
-                Strcpy(buf, sortltype[i]);
-                break;
+    any = cg.zeroany;
+    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_ITEMFLAGS_NONE);
+    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
+             "Compounds (selecting will prompt for new value):",
+             MENU_ITEMFLAGS_NONE);
+
+    /* deliberately put playmode, name, role+race+gender+align first */
+    doset_add_menu(tmpwin, "playmode", opt_playmode, 0);
+    doset_add_menu(tmpwin, "name", opt_name, 0);
+    doset_add_menu(tmpwin, "role", opt_role, 0);
+    doset_add_menu(tmpwin, "race", opt_race, 0);
+    doset_add_menu(tmpwin, "gender", opt_gender, 0);
+    doset_add_menu(tmpwin, "align", opt_align, 0);
+
+    for (pass = startpass; pass <= endpass; pass++)
+        for (i = 0; (name = allopt[i].name) != 0; i++) {
+            if (allopt[i].opttyp != CompOpt)
+                continue;
+            if ((int) allopt[i].setwhere == pass) {
+                if (!strcmp(name, "playmode")  || !strcmp(name, "name")
+                    || !strcmp(name, "role")   || !strcmp(name, "race")
+                    || !strcmp(name, "gender") || !strcmp(name, "align"))
+                    continue;
+                if ((is_wc_option(name) && !wc_supported(name))
+                    || (is_wc2_option(name) && !wc2_supported(name)))
+                    continue;
+
+                doset_add_menu(tmpwin, name, i,
+                               (pass == set_gameview) ? 0 : indexoffset);
             }
-    } else if (!strcmp(optname, "player_selection")) {
-        Sprintf(buf, "%s", iflags.wc_player_selection ? "prompts" : "dialog");
+        }
+
+    any = cg.zeroany;
+    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_ITEMFLAGS_NONE);
+    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
+             "Other settings:", MENU_ITEMFLAGS_NONE);
+
+    for (i = 0; (name = othropt[i].name) != 0; i++) {
+        if ((is_wc_option(name) && !wc_supported(name))
+            || (is_wc2_option(name) && !wc2_supported(name)))
+            continue;
+        opts_add_others(tmpwin, name, othropt[i].code,
+                        (char *) 0, othropt[i].othr_count_func());
+    }
+
+#ifdef PREFIXES_IN_USE
+    any = cg.zeroany;
+    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_ITEMFLAGS_NONE);
+    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
+             "Variable playground locations:", MENU_ITEMFLAGS_NONE);
+    for (i = 0; i < PREFIX_COUNT; i++)
+        doset_add_menu(tmpwin, fqn_prefix_names[i], -1, 0);
+#endif
+    end_menu(tmpwin, "Set what options?");
+    g.opt_need_redraw = FALSE;
+    if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &pick_list)) > 0) {
+        /*
+         * Walk down the selection list and either invert the booleans
+         * or prompt for new values. In most cases, call parseoptions()
+         * to take care of options that require special attention, like
+         * redraws.
+         */
+        for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
+            opt_indx = pick_list[pick_idx].item.a_int - 1;
+            if (opt_indx < -1)
+                opt_indx++; /* -1 offset for select_menu() */
+            if (opt_indx == OPT_OTHER_APEXC) {
+                handler_autopickup_exception();
 #ifdef STATUS_HILITES
-    } else if (!strcmp("statushilites", optname)) {
-        if (!iflags.hilite_delta)
-            Strcpy(buf, "0 (off: don't highlight status fields)");
-        else
-            Sprintf(buf, "%ld (on: highlight status for %ld turns)",
-                    iflags.hilite_delta, iflags.hilite_delta);
+            } else if (opt_indx == OPT_OTHER_STATHILITE) {
+                if (!status_hilite_menu()) {
+                    pline("Bad status hilite(s) specified.");
+                } else {
+                    if (wc2_supported("hilite_status"))
+                        preference_update("hilite_status");
+                }
 #endif
-    } else if (!strcmp(optname,"statuslines")) {
-        if (wc2_supported(optname))
-            Strcpy(buf, (iflags.wc2_statuslines < 3) ? "2" : "3");
-        /* else default to "unknown" */
-    } else if (!strcmp(optname, "suppress_alert")) {
-        if (flags.suppress_alert == 0L)
-            Strcpy(buf, none);
-        else
-            Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
-                    FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
-    } else if (!strcmp(optname, "symset")) {
-        Sprintf(buf, "%s",
-                g.symset[PRIMARY].name ? g.symset[PRIMARY].name : "default");
-        if (g.currentgraphics == PRIMARY && g.symset[PRIMARY].name)
-            Strcat(buf, ", active");
-    } else if (!strcmp(optname, "term_cols")) {
-        if (iflags.wc2_term_cols)
-            Sprintf(buf, "%d", iflags.wc2_term_cols);
-        else
-            Strcpy(buf, defopt);
-    } else if (!strcmp(optname, "term_rows")) {
-        if (iflags.wc2_term_rows)
-            Sprintf(buf, "%d", iflags.wc2_term_rows);
-        else
-            Strcpy(buf, defopt);
-    } else if (!strcmp(optname, "tile_file")) {
-        Sprintf(buf, "%s",
-                iflags.wc_tile_file ? iflags.wc_tile_file : defopt);
-    } else if (!strcmp(optname, "tile_height")) {
-        if (iflags.wc_tile_height)
-            Sprintf(buf, "%d", iflags.wc_tile_height);
-        else
-            Strcpy(buf, defopt);
-    } else if (!strcmp(optname, "tile_width")) {
-        if (iflags.wc_tile_width)
-            Sprintf(buf, "%d", iflags.wc_tile_width);
-        else
-            Strcpy(buf, defopt);
-    } else if (!strcmp(optname, "traps")) {
-        Sprintf(buf, "%s", to_be_done);
-    } else if (!strcmp(optname, "vary_msgcount")) {
-        if (iflags.wc_vary_msgcount)
-            Sprintf(buf, "%d", iflags.wc_vary_msgcount);
-        else
-            Strcpy(buf, defopt);
-#ifdef MSDOS
-    } else if (!strcmp(optname, "video")) {
-        Sprintf(buf, "%s", to_be_done);
+            } else if (opt_indx == OPT_OTHER_MENUCOLOR) {
+                handler_menu_colors();
+            } else if (opt_indx == OPT_OTHER_COND) {
+                cond_menu();
+            } else if (opt_indx == OPT_OTHER_MSGTYPE) {
+                handler_msgtype();
+            } else {
+                opt_indx -= indexoffset;
+                if (allopt[opt_indx].opttyp == BoolOpt) {
+                    /* boolean option */
+                    Sprintf(buf, "%s%s", *allopt[opt_indx].addr ? "!" : "",
+                            allopt[opt_indx].name);
+                    (void) parseoptions(buf, setinitial, fromfile);
+                } else {
+                    /* compound option */
+                    int k = opt_indx, reslt;
+
+                    if (allopt[k].has_handler && allopt[k].optfn) {
+                        reslt = (*allopt[k].optfn)(allopt[k].idx, do_handler,
+                                           FALSE, empty_optstr, empty_optstr);
+                    } else {
+                        char abuf[BUFSZ];
+
+                        Sprintf(buf, "Set %s to what?", allopt[opt_indx].name);
+                        abuf[0] = '\0';
+                        getlin(buf, abuf);
+                        if (abuf[0] == '\033')
+                            continue;
+                        Sprintf(buf, "%s:", allopt[opt_indx].name);
+                        (void) strncat(eos(buf), abuf,
+                                   (sizeof buf - 1 - strlen(buf)));
+                        /* pass the buck */
+                        (void) parseoptions(buf, setinitial, fromfile);
+                    }
+                }
+                if (wc_supported(allopt[opt_indx].name)
+                        || wc2_supported(allopt[opt_indx].name))
+                    preference_update(allopt[opt_indx].name);
+            }
+        }
+        free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
+    }
+
+    destroy_nhwindow(tmpwin);
+    if (g.opt_need_redraw) {
+        check_gold_symbol();
+        reglyph_darkroom();
+        (void) doredraw();
+    }
+    return 0;
+}
+
+/* doset('O' command) menu entries for compound options */
+static void
+doset_add_menu(win, option, idx, indexoffset)
+winid win;          /* window to add to */
+const char *option; /* option name */
+int idx;            /* index in allopt[] */
+int indexoffset;    /* value to add to index in allopt[], or zero
+                       if option cannot be changed */
+{
+    const char *value = "unknown"; /* current value */
+    char buf[BUFSZ], buf2[BUFSZ];
+    anything any;
+    int i = idx, reslt = optn_err;
+#ifdef PREFIXES_IN_USE
+    int j;
 #endif
-#ifdef VIDEOSHADES
-    } else if (!strcmp(optname, "videoshades")) {
-        Sprintf(buf, "%s-%s-%s", shade[0], shade[1], shade[2]);
-    } else if (!strcmp(optname, "videocolors")) {
-        Sprintf(buf, "%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d",
-                ttycolors[CLR_RED], ttycolors[CLR_GREEN],
-                ttycolors[CLR_BROWN], ttycolors[CLR_BLUE],
-                ttycolors[CLR_MAGENTA], ttycolors[CLR_CYAN],
-                ttycolors[CLR_ORANGE], ttycolors[CLR_BRIGHT_GREEN],
-                ttycolors[CLR_YELLOW], ttycolors[CLR_BRIGHT_BLUE],
-                ttycolors[CLR_BRIGHT_MAGENTA], ttycolors[CLR_BRIGHT_CYAN]);
-#endif /* VIDEOSHADES */
-    } else if (!strcmp(optname,"windowborders")) {
-        Sprintf(buf, "%s",
-                (iflags.wc2_windowborders == 0) ? "0=off"
-                : (iflags.wc2_windowborders == 1) ? "1=on"
-                  : (iflags.wc2_windowborders == 2) ? "2=auto"
-                    : defopt);
-    } else if (!strcmp(optname, "windowtype")) {
-        Sprintf(buf, "%s", windowprocs.name);
-    } else if (!strcmp(optname, "windowcolors")) {
-        Sprintf(
-            buf, "%s/%s %s/%s %s/%s %s/%s",
-            iflags.wc_foregrnd_menu ? iflags.wc_foregrnd_menu : defbrief,
-            iflags.wc_backgrnd_menu ? iflags.wc_backgrnd_menu : defbrief,
-            iflags.wc_foregrnd_message ? iflags.wc_foregrnd_message
-                                       : defbrief,
-            iflags.wc_backgrnd_message ? iflags.wc_backgrnd_message
-                                       : defbrief,
-            iflags.wc_foregrnd_status ? iflags.wc_foregrnd_status : defbrief,
-            iflags.wc_backgrnd_status ? iflags.wc_backgrnd_status : defbrief,
-            iflags.wc_foregrnd_text ? iflags.wc_foregrnd_text : defbrief,
-            iflags.wc_backgrnd_text ? iflags.wc_backgrnd_text : defbrief);
+
+    buf2[0] = '\0';  /* per opt functs may not guarantee this, so do it */
+    any = cg.zeroany;
+    if (i >= 0 && i < OPTCOUNT && allopt[i].name && allopt[i].optfn) {
+        any.a_int = (indexoffset == 0) ? 0 : i + 1 + indexoffset;
+        if (allopt[i].optfn)
+            reslt = (*allopt[i].optfn)(allopt[i].idx, get_val,
+                                       FALSE, buf2, empty_optstr);
+        if (reslt == optn_ok && buf2[0])
+            value = (const char *) buf2;
+    } else {
+        /* We are trying to add an option not found in allopt[].
+           This is almost certainly bad, but we'll let it through anyway
+           (with a zero value, so it can't be selected). */
+        any.a_int = 0;
 #ifdef PREFIXES_IN_USE
+        for (j = 0; j < PREFIX_COUNT; ++j)
+            if (!strcmp(option, fqn_prefix_names[j]) && g.fqn_prefix[j])
+                Sprintf(buf2, "%s", g.fqn_prefix[j]);
+#endif
+        if (!buf2[0])
+            Strcpy(buf2, "unknown");
+        value = (const char *) buf2;
+    }
+
+    /* "    " replaces "a - " -- assumes menus follow that style */
+    if (!iflags.menu_tab_sep)
+        Sprintf(buf, fmtstr_doset, any.a_int ? "" : "    ", option,
+                value);
+    else
+        Sprintf(buf, fmtstr_doset_tab, option, value);
+    add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_ITEMFLAGS_NONE);
+}
+
+static void
+opts_add_others(win, name, id, bufx, nset)
+winid win;
+const char *name;
+int id;
+char *bufx;
+int nset;
+{
+    char buf[BUFSZ], buf2[BUFSZ];
+    anything any = cg.zeroany;
+
+    any.a_int = id;
+    if (!bufx)
+        Sprintf(buf2, n_currently_set, nset);
+    else
+        Sprintf(buf2, "%s", bufx);
+    if (!iflags.menu_tab_sep)
+        Sprintf(buf, fmtstr_doset, any.a_int ? "" : "    ",
+                name, buf2);
+    else
+        Sprintf(buf, fmtstr_doset_tab, name, buf2);
+    add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_ITEMFLAGS_NONE);
+}
+
+/*
+ * used by cmd.c and pager.c
+ */
+void
+show_menu_controls(win, dolist)
+winid win;
+boolean dolist;
+{
+    char buf[BUFSZ];
+
+    putstr(win, 0, "Menu control keys:");
+    if (dolist) {
+        int i;
+
+        for (i = 0; i < SIZE(default_menu_cmd_info); i++) {
+            Sprintf(buf, "%-8s %s",
+                    visctrl(get_menu_cmd_key(default_menu_cmd_info[i].cmd)),
+                    default_menu_cmd_info[i].desc);
+            putstr(win, 0, buf);
+        }
     } else {
-        for (i = 0; i < PREFIX_COUNT; ++i)
-            if (!strcmp(optname, fqn_prefix_names[i]) && g.fqn_prefix[i])
-                Sprintf(buf, "%s", g.fqn_prefix[i]);
-#endif
+        putstr(win, 0, "");
+        putstr(win, 0, "          Page    All items");
+        Sprintf(buf, "  Select   %s       %s",
+                visctrl(get_menu_cmd_key(MENU_SELECT_PAGE)),
+                visctrl(get_menu_cmd_key(MENU_SELECT_ALL)));
+        putstr(win, 0, buf);
+        Sprintf(buf, "Deselect   %s       %s",
+                visctrl(get_menu_cmd_key(MENU_UNSELECT_PAGE)),
+                visctrl(get_menu_cmd_key(MENU_UNSELECT_ALL)));
+        putstr(win, 0, buf);
+        Sprintf(buf, "  Invert   %s       %s",
+                visctrl(get_menu_cmd_key(MENU_INVERT_PAGE)),
+                visctrl(get_menu_cmd_key(MENU_INVERT_ALL)));
+        putstr(win, 0, buf);
+        putstr(win, 0, "");
+        Sprintf(buf, "   Go to   %s   Next page",
+                visctrl(get_menu_cmd_key(MENU_NEXT_PAGE)));
+        putstr(win, 0, buf);
+        Sprintf(buf, "           %s   Previous page",
+                visctrl(get_menu_cmd_key(MENU_PREVIOUS_PAGE)));
+        putstr(win, 0, buf);
+        Sprintf(buf, "           %s   First page",
+                visctrl(get_menu_cmd_key(MENU_FIRST_PAGE)));
+        putstr(win, 0, buf);
+        Sprintf(buf, "           %s   Last page",
+                visctrl(get_menu_cmd_key(MENU_LAST_PAGE)));
+        putstr(win, 0, buf);
+        putstr(win, 0, "");
+        Sprintf(buf, "           %s   Search and toggle matching entries",
+                visctrl(get_menu_cmd_key(MENU_SEARCH)));
+        putstr(win, 0, buf);
+    }
+}
+static int
+count_cond(VOID_ARGS)
+{
+    int i, cnt = 0;
+
+    for (i = 0; i < CONDITION_COUNT; ++i) {
+        if (condtests[i].enabled)
+            cnt++;
+    }
+    return cnt;
+}
+
+int
+count_apes(VOID_ARGS)
+{
+    int numapes = 0;
+    struct autopickup_exception *ape = g.apelist;
+
+    while (ape) {
+      numapes++;
+      ape = ape->next;
     }
 
-    if (!buf[0])
-        Strcpy(buf, "unknown");
-    return buf;
+    return numapes;
+}
+
+/* common to msg-types, menu-colors, autopickup-exceptions */
+static int
+handle_add_list_remove(optname, numtotal)
+const char *optname;
+int numtotal;
+{
+    winid tmpwin;
+    anything any;
+    int i, pick_cnt, opt_idx;
+    menu_item *pick_list = (menu_item *) 0;
+    static const struct action {
+        char letr;
+        const char *desc;
+    } action_titles[] = {
+        { 'a', "add new %s" },         /* [0] */
+        { 'l', "list %s" },            /* [1] */
+        { 'r', "remove existing %s" }, /* [2] */
+        { 'x', "exit this menu" },     /* [3] */
+    };
+
+    opt_idx = 0;
+    tmpwin = create_nhwindow(NHW_MENU);
+    start_menu(tmpwin, MENU_BEHAVE_STANDARD);
+    any = cg.zeroany;
+    for (i = 0; i < SIZE(action_titles); i++) {
+        char tmpbuf[BUFSZ];
+
+        any.a_int++;
+        /* omit list and remove if there aren't any yet */
+        if (!numtotal && (i == 1 || i == 2))
+            continue;
+        Sprintf(tmpbuf, action_titles[i].desc,
+                (i == 1) ? makeplural(optname) : optname);
+        add_menu(tmpwin, NO_GLYPH, &any, action_titles[i].letr, 0, ATR_NONE,
+                 tmpbuf, (i == 3) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
+    }
+    end_menu(tmpwin, "Do what?");
+    if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &pick_list)) > 0) {
+        opt_idx = pick_list[0].item.a_int - 1;
+        if (pick_cnt > 1 && opt_idx == 3)
+            opt_idx = pick_list[1].item.a_int - 1;
+        free((genericptr_t) pick_list);
+    } else
+        opt_idx = 3; /* none selected, exit menu */
+    destroy_nhwindow(tmpwin);
+    return opt_idx;
 }
 
 int
@@ -6286,23 +8020,23 @@ option_help()
         putstr(datawin, 0, opt_intro[i]);
 
     /* Boolean options */
-    for (i = 0; boolopt[i].name; i++) {
-        if (boolopt[i].addr) {
-            if (boolopt[i].addr == &iflags.sanity_check && !wizard)
+    for (i = 0; allopt[i].name; i++) {
+        if (allopt[i].addr) {
+            if (allopt[i].addr == &iflags.sanity_check && !wizard)
                 continue;
-            if (boolopt[i].addr == &iflags.menu_tab_sep && !wizard)
+            if (allopt[i].addr == &iflags.menu_tab_sep && !wizard)
                 continue;
-            next_opt(datawin, boolopt[i].name);
+            next_opt(datawin, allopt[i].name);
         }
     }
     next_opt(datawin, "");
 
     /* Compound options */
     putstr(datawin, 0, "Compound options:");
-    for (i = 0; compopt[i].name; i++) {
-        Sprintf(buf2, "`%s'", compopt[i].name);
-        Sprintf(buf, "%-20s - %s%c", buf2, compopt[i].descr,
-                compopt[i + 1].name ? ',' : '.');
+    for (i = 0; allopt[i].name; i++) {
+        Sprintf(buf2, "`%s'", allopt[i].name);
+        Sprintf(buf, "%-20s - %s%c", buf2, allopt[i].descr,
+                allopt[i + 1].name ? ',' : '.');
         putstr(datawin, 0, buf);
     }
 
@@ -6353,134 +8087,6 @@ const char *str;
     return;
 }
 
-/* Returns the fid of the fruit type; if that type already exists, it
- * returns the fid of that one; if it does not exist, it adds a new fruit
- * type to the chain and returns the new one.
- * If replace_fruit is sent in, replace the fruit in the chain rather than
- * adding a new entry--for user specified fruits only.
- */
-int
-fruitadd(str, replace_fruit)
-char *str;
-struct fruit *replace_fruit;
-{
-    register int i;
-    register struct fruit *f;
-    int highest_fruit_id = 0, globpfx;
-    char buf[PL_FSIZ], altname[PL_FSIZ];
-    boolean user_specified = (str == g.pl_fruit);
-    /* if not user-specified, then it's a fruit name for a fruit on
-     * a bones level or from orctown raider's loot...
-     */
-
-    /* Note: every fruit has an id (kept in obj->spe) of at least 1;
-     * 0 is an error.
-     */
-    if (user_specified) {
-        boolean found = FALSE, numeric = FALSE;
-
-        /* force fruit to be singular; this handling is not
-           needed--or wanted--for fruits from bones because
-           they already received it in their original game;
-           str==pl_fruit but makesingular() creates a copy
-           so we need to copy that back into pl_fruit */
-        nmcpy(g.pl_fruit, makesingular(str), PL_FSIZ);
-        /* (assertion doesn't matter; we use 'g.pl_fruit' from here on out) */
-        /* assert( str == g.pl_fruit ); */
-
-        /* disallow naming after other foods (since it'd be impossible
-         * to tell the difference); globs might have a size prefix which
-         * needs to be skipped in order to match the object type name
-         */
-        globpfx = (!strncmp(g.pl_fruit, "small ", 6)
-                   || !strncmp(g.pl_fruit, "large ", 6)) ? 6
-                  : (!strncmp(g.pl_fruit, "very large ", 11)) ? 11
-                    : 0;
-        for (i = g.bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS; i++) {
-            if (!strcmp(OBJ_NAME(objects[i]), g.pl_fruit)
-                || (globpfx > 0
-                    && !strcmp(OBJ_NAME(objects[i]), &g.pl_fruit[globpfx]))) {
-                found = TRUE;
-                break;
-            }
-        }
-        if (!found) {
-            char *c;
-
-            for (c = g.pl_fruit; *c >= '0' && *c <= '9'; c++)
-                continue;
-            if (!*c || isspace((uchar) *c))
-                numeric = TRUE;
-        }
-        if (found || numeric
-            /* these checks for applying food attributes to actual items
-               are case sensitive; "glob of foo" is caught by 'found'
-               if 'foo' is a valid glob; when not valid, allow it as-is */
-            || !strncmp(g.pl_fruit, "cursed ", 7)
-            || !strncmp(g.pl_fruit, "uncursed ", 9)
-            || !strncmp(g.pl_fruit, "blessed ", 8)
-            || !strncmp(g.pl_fruit, "partly eaten ", 13)
-            || (!strncmp(g.pl_fruit, "tin of ", 7)
-                && (!strcmp(g.pl_fruit + 7, "spinach")
-                    || name_to_mon(g.pl_fruit + 7) >= LOW_PM))
-            || !strcmp(g.pl_fruit, "empty tin")
-            || (!strcmp(g.pl_fruit, "glob")
-                || (globpfx > 0 && !strcmp("glob", &g.pl_fruit[globpfx])))
-            || ((str_end_is(g.pl_fruit, " corpse")
-                 || str_end_is(g.pl_fruit, " egg"))
-                && name_to_mon(g.pl_fruit) >= LOW_PM)) {
-            Strcpy(buf, g.pl_fruit);
-            Strcpy(g.pl_fruit, "candied ");
-            nmcpy(g.pl_fruit + 8, buf, PL_FSIZ - 8);
-        }
-        *altname = '\0';
-        /* This flag indicates that a fruit has been made since the
-         * last time the user set the fruit.  If it hasn't, we can
-         * safely overwrite the current fruit, preventing the user from
-         * setting many fruits in a row and overflowing.
-         * Possible expansion: check for specific fruit IDs, not for
-         * any fruit.
-         */
-        flags.made_fruit = FALSE;
-        if (replace_fruit) {
-            /* replace_fruit is already part of the fruit chain;
-               update it in place rather than looking it up again */
-            f = replace_fruit;
-            copynchars(f->fname, g.pl_fruit, PL_FSIZ - 1);
-            goto nonew;
-        }
-    } else {
-        /* not user_supplied, so assumed to be from bones (or orc gang) */
-        copynchars(altname, str, PL_FSIZ - 1);
-        sanitize_name(altname);
-        flags.made_fruit = TRUE; /* for safety.  Any fruit name added from a
-                                  * bones level should exist anyway. */
-    }
-    f = fruit_from_name(*altname ? altname : str, FALSE, &highest_fruit_id);
-    if (f)
-        goto nonew;
-
-    /* Maximum number of named fruits is 127, even if obj->spe can
-       handle bigger values.  If adding another fruit would overflow,
-       use a random fruit instead... we've got a lot to choose from.
-       current_fruit remains as is. */
-    if (highest_fruit_id >= 127)
-        return rnd(127);
-
-    f = newfruit();
-    (void) memset((genericptr_t) f, 0, sizeof (struct fruit));
-    copynchars(f->fname, *altname ? altname : str, PL_FSIZ - 1);
-    f->fid = ++highest_fruit_id;
-    /* we used to go out of our way to add it at the end of the list,
-       but the order is arbitrary so use simpler insertion at start */
-    f->nextf = g.ffruit;
-    g.ffruit = f;
- nonew:
-    if (user_specified)
-        g.context.current_fruit = f->fid;
-    return f->fid;
-}
-
 /*
  * This is a somewhat generic menu for taking a list of NetHack style
  * class choices and presenting them via a description
@@ -6660,8 +8266,8 @@ static struct wc_Opt wc2_options[] = {
 };
 
 /*
- * If a port wants to change or ensure that the SET_IN_SYS,
- * SET_IN_FILE, DISP_IN_GAME, or SET_IN_GAME status of an option is
+ * If a port wants to change or ensure that the set_in_sysconf,
+ * set_in_config, set_gameview, or set_in_game status of an option is
  * correct (for controlling its display in the option menu) call
  * set_option_mod_status()
  * with the appropriate second argument.
@@ -6677,15 +8283,15 @@ int status;
         impossible("set_option_mod_status: status out of range %d.", status);
         return;
     }
-    for (k = 0; boolopt[k].name; k++) {
-        if (!strncmpi(boolopt[k].name, optnam, strlen(optnam))) {
-            boolopt[k].optflags = status;
+    for (k = 0; allopt[k].name; k++) {
+        if (!strncmpi(allopt[k].name, optnam, strlen(optnam))) {
+            allopt[k].setwhere = status;
             return;
         }
     }
-    for (k = 0; compopt[k].name; k++) {
-        if (!strncmpi(compopt[k].name, optnam, strlen(optnam))) {
-            compopt[k].optflags = status;
+    for (k = 0; allopt[k].name; k++) {
+        if (!strncmpi(allopt[k].name, optnam, strlen(optnam))) {
+            allopt[k].setwhere = status;
             return;
         }
     }
@@ -6698,7 +8304,7 @@ int status;
  * are setting in the optmask argument
  * prior to calling.
  *    example: set_wc_option_mod_status(WC_COLOR|WC_SCROLL_MARGIN,
- * SET_IN_GAME);
+ * set_in_game);
  */
 void
 set_wc_option_mod_status(optmask, status)
@@ -6755,7 +8361,7 @@ const char *optnam;
  * prior to calling.
  *    example:
  * set_wc2_option_mod_status(WC2_FULLSCREEN|WC2_SOFTKEYBOARD|WC2_WRAPTEXT,
- * SET_IN_FILE);
+ * set_in_config);
  */
 
 void
@@ -6960,3 +8566,4 @@ set_playmode()
 #endif /* OPTION_LISTS_ONLY */
 
 /*options.c*/
+
index 5293dd70a9b5f857a3ec0115aa0b79b9ff4cbbcf..3620043d098b89faaf13aa3539dac98570b6817a 100644 (file)
@@ -499,10 +499,10 @@ HACKINCL = align.h amiconf.h artifact.h artilist.h attrib.h beconf.h botl.h \
        func_tab.h global.h hack.h lint.h macconf.h mextra.h mfndpos.h \
        micro.h mkroom.h \
        monattk.h mondata.h monflag.h monst.h monsym.h obj.h objclass.h \
-       os2conf.h patchlevel.h pcconf.h permonst.h prop.h rect.h region.h \
-       rm.h sp_lev.h spell.h sys.h system.h tcap.h timeout.h tosconf.h \
-       tradstdc.h trampoli.h trap.h unixconf.h vision.h vmsconf.h wintty.h \
-       wincurs.h winX.h winprocs.h wintype.h you.h youprop.h
+       optlist.h os2conf.h patchlevel.h pcconf.h permonst.h prop.h rect.h \
+       region.h rm.h sp_lev.h spell.h sys.h system.h tcap.h timeout.h \
+       tosconf.h tradstdc.h trampoli.h trap.h unixconf.h vision.h vmsconf.h \
+       wintty.h wincurs.h winX.h winprocs.h wintype.h you.h youprop.h
 
 HSOURCES = $(HACKINCL) date.h onames.h pm.h vis_tab.h dgn_file.h
 
@@ -1118,7 +1118,7 @@ objects.o: objects.c $(CONFIG_H) ../include/obj.h ../include/objclass.h \
                ../include/prop.h ../include/skills.h ../include/color.h
 objnam.o: objnam.c $(HACK_H)
 options.o: options.c $(CONFIG_H) ../include/objclass.h ../include/flag.h \
-               $(HACK_H) ../include/tcap.h
+               $(HACK_H) ../include/tcap.h ../include/optlist.h
 pager.o: pager.c $(HACK_H) ../include/dlb.h
 pickup.o: pickup.c $(HACK_H)
 pline.o: pline.c $(HACK_H)
index 6955c080aac5afa5865bebd6605729ef2c806a61..2aac97efcfbb80b7fdf57654e6449d0527cede63 100644 (file)
@@ -158,7 +158,7 @@ mswin_init_nhwindows(int *argc, char **argv)
      * non-console applications
      */
     iflags.toptenwin = 1;
-    set_option_mod_status("toptenwin", SET_IN_FILE);
+    set_option_mod_status("toptenwin", set_in_config);
 
     /* initialize map tiles bitmap */
     initMapTiles();
@@ -176,14 +176,14 @@ mswin_init_nhwindows(int *argc, char **argv)
             | WC_MAP_MODE | WC_FONT_MESSAGE | WC_FONT_STATUS | WC_FONT_MENU
             | WC_FONT_TEXT | WC_FONTSIZ_MESSAGE | WC_FONTSIZ_STATUS
             | WC_FONTSIZ_MENU | WC_FONTSIZ_TEXT | WC_VARY_MSGCOUNT,
-        SET_IN_GAME);
+        set_in_game);
 
     /* WC2 options */
-    set_wc2_option_mod_status(WC2_FULLSCREEN | WC2_SOFTKEYBOARD, SET_IN_FILE);
+    set_wc2_option_mod_status(WC2_FULLSCREEN | WC2_SOFTKEYBOARD, set_in_config);
     GetNHApp()->bFullScreen = iflags.wc2_fullscreen;
     GetNHApp()->bUseSIP = iflags.wc2_softkeyboard;
 
-    set_wc2_option_mod_status(WC2_WRAPTEXT, SET_IN_GAME);
+    set_wc2_option_mod_status(WC2_WRAPTEXT, set_in_game);
     GetNHApp()->bWrapText = iflags.wc2_wraptext;
 
     /* create the main nethack window */
index 21b03934280176956963326c93a07f02c440aa22..8f0617ebb62910c57dc541f7cba1986addf5a1b0 100644 (file)
@@ -2068,8 +2068,10 @@ $(O)o_init.o: o_init.c $(HACK_H)
 $(O)objects.o: objects.c $(CONFIG_H) $(INCL)\obj.h $(INCL)\objclass.h \
                $(INCL)\prop.h $(INCL)\skills.h $(INCL)\color.h
 $(O)objnam.o: objnam.c $(HACK_H)
-$(O)options.o: options.c $(CONFIG_H) $(INCL)\objclass.h $(INCL)\flag.h \
+$(O)options.o: options.c $(INCL)\optlist.h $(CONFIG_H) $(INCL)\objclass.h $(INCL)\flag.h \
                $(HACK_H) $(INCL)\tcap.h
+       $(cc) $(cflagsBuild) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) /EP options.c >$(@B).preproc
+       $(cc) $(cflagsBuild) $(CROSSCOMPILE) $(CROSSCOMPILE_TARGET) -Fo$@ options.c
 $(O)pager.o: pager.c $(HACK_H) $(INCL)\dlb.h
 $(O)pickup.o: pickup.c $(HACK_H)
 $(O)pline.o: pline.c $(HACK_H)
index f9e223cf08fca715353a0b0c74eabbb305867c75..6b1a7ea644a75bc6a9fff0ccd9e6d1282a843284 100644 (file)
@@ -339,7 +339,7 @@ int *wid, *hgt;
 {
     *wid = console.width;
     *hgt = console.height;
-    set_option_mod_status("mouse_support", SET_IN_GAME);
+    set_option_mod_status("mouse_support", set_in_game);
 }
 
 void
index 1c547adcafa9bb39910f5a1a277f6d6a9e3fe08a..02e936131e63c25154627be173dc613d2ef04b5e 100644 (file)
@@ -209,7 +209,7 @@ test_portable_config(
         /* assure_syscf_file(); */
         config_error_init(TRUE, tmppath, FALSE);
         /* ... and _must_ parse correctly. */
-        if (read_config_file(tmppath, SET_IN_SYS)
+        if (read_config_file(tmppath, set_in_sysconf)
             && sysopt.portable_device_paths)
             retval = TRUE;
         (void) config_error_done();
index 7f8acc5824fe000bac997074da3615026e9e681b..c7afa415f1ebdf63817b33aed426eb53f6e5975c 100644 (file)
@@ -1492,8 +1492,8 @@ char **argv;
         window_list[i].type = NHW_NONE;
 
     /* add another option that can be set */
-    set_wc_option_mod_status(WC_TILED_MAP, SET_IN_GAME);
-    set_option_mod_status("mouse_support", SET_IN_GAME);
+    set_wc_option_mod_status(WC_TILED_MAP, set_in_game);
+    set_option_mod_status("mouse_support", set_in_game);
 
     load_default_resources(); /* create default_resource_data[] */
 
index d6a103f889f3c790bcbc287175a9d974853c7967..aae3863241cff5752b3e199e802a82dedb244989 100644 (file)
@@ -778,11 +778,11 @@ curses_character_dialog(const char **choices, const char *prompt)
 void
 curses_init_options()
 {
-    /* change these from DISP_IN_GAME to SET_IN_GAME */
-    set_wc_option_mod_status(WC_ALIGN_MESSAGE | WC_ALIGN_STATUS, SET_IN_GAME);
+    /* change these from set_gameview to set_in_game */
+    set_wc_option_mod_status(WC_ALIGN_MESSAGE | WC_ALIGN_STATUS, set_in_game);
 
     /* Remove a few options that are irrelevant to this windowport */
-    set_option_mod_status("eight_bit_tty", SET_IN_FILE);
+    set_option_mod_status("eight_bit_tty", set_in_config);
 
     /* If we don't have a symset defined, load the curses symset by default */
     if (!g.symset[PRIMARY].explicitly)
index 65037c0a8262b162dc4a2f4b5ee94e95edd7cf4d..c2e424028fe0e8fdcf7382e89e1fd60b3f0d1d75 100644 (file)
@@ -162,15 +162,15 @@ curses_init_nhwindows(int *argcp UNUSED,
         curses_init_nhcolors();
     } else {
         iflags.use_color = FALSE;
-        set_option_mod_status("color", SET_IN_FILE);
+        set_option_mod_status("color", set_in_config);
         iflags.wc2_guicolor = FALSE;
-        set_wc2_option_mod_status(WC2_GUICOLOR, SET_IN_FILE);
+        set_wc2_option_mod_status(WC2_GUICOLOR, set_in_config);
     }
 #else
     iflags.use_color = FALSE;
-    set_option_mod_status("color", SET_IN_FILE);
+    set_option_mod_status("color", set_in_config);
     iflags.wc2_guicolor = FALSE;
-    set_wc2_option_mod_status(WC2_GUICOLOR, SET_IN_FILE);
+    set_wc2_option_mod_status(WC2_GUICOLOR, set_in_config);
 #endif
     noecho();
     raw();
index f12012c05f5f97a381bb36918c8cd408f1d51a86..547613b69a31e172f23482a72822e21ad0ad47c7 100644 (file)
@@ -159,7 +159,7 @@ char **argv;
             | WC_FONT_STATUS | WC_FONT_MENU | WC_FONT_TEXT
             | WC_FONTSIZ_MESSAGE | WC_FONTSIZ_MAP | WC_FONTSIZ_STATUS
             | WC_FONTSIZ_MENU | WC_FONTSIZ_TEXT | WC_VARY_MSGCOUNT,
-        SET_IN_GAME);
+        set_in_game);
     if (iflags.wc_align_message == 0)
         iflags.wc_align_message = ALIGN_TOP;
     if (iflags.wc_align_status == 0)
index 77555db522eb6fc0c3d6a3e2ebe32e2383bc510d..084f82939eb53823b9a09eb8cf9362335f59dd2b 100644 (file)
@@ -440,14 +440,14 @@ char **argv UNUSED;
     tty_putstr(BASE_WINDOW, 0, "");
     tty_display_nhwindow(BASE_WINDOW, FALSE);
 
-    /* 'statuslines' defaults to SET_IN_FILE, allowed but invisible;
+    /* 'statuslines' defaults to set_in_config, allowed but invisible;
        make it dynamically settable if feasible, otherwise visible */
     if (tty_procs.wincap2 & WC2_STATUSLINES)
         set_wc2_option_mod_status(WC2_STATUSLINES,
 #ifndef CLIPPING
                                   (LI < 1 + ROWNO + 2) ? DISP_IN_GAME :
 #endif
-                                   SET_IN_GAME);
+                                   set_in_game);
 }
 
 void
index b50612f2ceaeb2ffbdeb551966a6f2a67842b26b..2296b6800d891a29e32505cc0cbe0235794f1d04 100644 (file)
@@ -213,16 +213,16 @@ mswin_init_nhwindows(int *argc, char **argv)
      * non-console applications
      */
     iflags.toptenwin = 1;
-    set_option_mod_status("toptenwin", SET_IN_FILE);
-    //set_option_mod_status("perm_invent", SET_IN_FILE);
-    set_option_mod_status("mouse_support", SET_IN_GAME);
+    set_option_mod_status("toptenwin", set_in_config);
+    //set_option_mod_status("perm_invent", set_in_config);
+    set_option_mod_status("mouse_support", set_in_game);
 
     /* initialize map tiles bitmap */
     initMapTiles();
 
     /* set tile-related options to readonly */
     set_wc_option_mod_status(WC_TILE_WIDTH | WC_TILE_HEIGHT | WC_TILE_FILE,
-                             DISP_IN_GAME);
+                             set_gameview);
 
     /* set font-related options to change in the game */
     set_wc_option_mod_status(
@@ -231,7 +231,7 @@ mswin_init_nhwindows(int *argc, char **argv)
             | WC_FONT_STATUS | WC_FONT_MENU | WC_FONT_TEXT
             | WC_FONTSIZ_MESSAGE | WC_FONTSIZ_STATUS | WC_FONTSIZ_MENU
             | WC_FONTSIZ_TEXT | WC_VARY_MSGCOUNT,
-        SET_IN_GAME);
+        set_in_game);
 
     mswin_color_from_string(iflags.wc_foregrnd_menu, &menu_fg_brush,
                             &menu_fg_color);