OSDN Git Service

import nethack-3.6.0
[jnethack/source.git] / src / options.c
1 /* NetHack 3.6  options.c       $NHDT-Date: 1448241657 2015/11/23 01:20:57 $  $NHDT-Branch: master $:$NHDT-Revision: 1.243 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 #ifdef OPTION_LISTS_ONLY /* (AMIGA) external program for opt lists */
6 #include "config.h"
7 #include "objclass.h"
8 #include "flag.h"
9 NEARDATA struct flag flags; /* provide linkage */
10 #ifdef SYSFLAGS
11 NEARDATA struct sysflag sysflags; /* provide linkage */
12 #endif
13 NEARDATA struct instance_flags iflags; /* provide linkage */
14 #define static
15 #else
16 #include "hack.h"
17 #include "tcap.h"
18 #include <ctype.h>
19 #endif
20
21 #define BACKWARD_COMPAT
22 #define WINTYPELEN 16
23
24 #ifdef DEFAULT_WC_TILED_MAP
25 #define PREFER_TILED TRUE
26 #else
27 #define PREFER_TILED FALSE
28 #endif
29
30 #define MESSAGE_OPTION 1
31 #define STATUS_OPTION 2
32 #define MAP_OPTION 3
33 #define MENU_OPTION 4
34 #define TEXT_OPTION 5
35
36 #define PILE_LIMIT_DFLT 5
37
38 /*
39  *  NOTE:  If you add (or delete) an option, please update the short
40  *  options help (option_help()), the long options help (dat/opthelp),
41  *  and the current options setting display function (doset()),
42  *  and also the Guidebooks.
43  *
44  *  The order matters.  If an option is a an initial substring of another
45  *  option (e.g. time and timed_delay) the shorter one must come first.
46  */
47
48 static struct Bool_Opt {
49     const char *name;
50     boolean *addr, initvalue;
51     int optflags;
52 } boolopt[] = {
53     { "acoustics", &flags.acoustics, TRUE, SET_IN_GAME },
54 #if defined(SYSFLAGS) && defined(AMIGA)
55     /* Amiga altmeta causes Alt+key to be converted into Meta+key by
56        low level nethack code; on by default, can be toggled off if
57        Alt+key is needed for some ASCII chars on non-ASCII keyboard */
58     { "altmeta", &sysflags.altmeta, TRUE, DISP_IN_GAME },
59 #else
60 #ifdef ALTMETA
61     /* non-Amiga altmeta causes nethack's top level command loop to treat
62        two character sequence "ESC c" as M-c, for terminals or emulators
63        which send "ESC c" when Alt+c is pressed; off by default, enabling
64        this can potentially make trouble if user types ESC when nethack
65        is honoring this conversion request (primarily after starting a
66        count prefix prior to a command and then deciding to cancel it) */
67     { "altmeta", &iflags.altmeta, FALSE, SET_IN_GAME },
68 #else
69     { "altmeta", (boolean *) 0, TRUE, DISP_IN_GAME },
70 #endif
71 #endif
72     { "ascii_map", &iflags.wc_ascii_map, !PREFER_TILED, SET_IN_GAME }, /*WC*/
73 #if defined(SYSFLAGS) && defined(MFLOPPY)
74     { "asksavedisk", &sysflags.asksavedisk, FALSE, SET_IN_GAME },
75 #else
76     { "asksavedisk", (boolean *) 0, FALSE, SET_IN_FILE },
77 #endif
78     { "autodig", &flags.autodig, FALSE, SET_IN_GAME },
79     { "autoopen", &flags.autoopen, TRUE, SET_IN_GAME },
80     { "autopickup", &flags.pickup, TRUE, SET_IN_GAME },
81     { "autoquiver", &flags.autoquiver, FALSE, SET_IN_GAME },
82 #if defined(MICRO) && !defined(AMIGA)
83     { "BIOS", &iflags.BIOS, FALSE, SET_IN_FILE },
84 #else
85     { "BIOS", (boolean *) 0, FALSE, SET_IN_FILE },
86 #endif
87     { "blind", &u.uroleplay.blind, FALSE, DISP_IN_GAME },
88     { "bones", &flags.bones, TRUE, SET_IN_FILE },
89 #ifdef INSURANCE
90     { "checkpoint", &flags.ins_chkpt, TRUE, SET_IN_GAME },
91 #else
92     { "checkpoint", (boolean *) 0, FALSE, SET_IN_FILE },
93 #endif
94 #ifdef MFLOPPY
95     { "checkspace", &iflags.checkspace, TRUE, SET_IN_GAME },
96 #else
97     { "checkspace", (boolean *) 0, FALSE, SET_IN_FILE },
98 #endif
99     { "clicklook", &iflags.clicklook, FALSE, SET_IN_GAME },
100     { "cmdassist", &iflags.cmdassist, TRUE, SET_IN_GAME },
101 #if defined(MICRO) || defined(WIN32)
102     { "color", &iflags.wc_color, TRUE, SET_IN_GAME }, /*WC*/
103 #else /* systems that support multiple terminals, many monochrome */
104     { "color", &iflags.wc_color, FALSE, SET_IN_GAME }, /*WC*/
105 #endif
106     { "confirm", &flags.confirm, TRUE, SET_IN_GAME },
107     { "dark_room", &flags.dark_room, TRUE, SET_IN_GAME },
108     { "eight_bit_tty", &iflags.wc_eight_bit_input, FALSE,
109       SET_IN_GAME }, /*WC*/
110 #ifdef TTY_GRAPHICS
111     { "extmenu", &iflags.extmenu, FALSE, SET_IN_GAME },
112 #else
113     { "extmenu", (boolean *) 0, FALSE, SET_IN_FILE },
114 #endif
115 #ifdef OPT_DISPMAP
116     { "fast_map", &flags.fast_map, TRUE, SET_IN_GAME },
117 #else
118     { "fast_map", (boolean *) 0, TRUE, SET_IN_FILE },
119 #endif
120     { "female", &flags.female, FALSE, DISP_IN_GAME },
121     { "fixinv", &flags.invlet_constant, TRUE, SET_IN_GAME },
122 #if defined(SYSFLAGS) && defined(AMIFLUSH)
123     { "flush", &sysflags.amiflush, FALSE, SET_IN_GAME },
124 #else
125     { "flush", (boolean *) 0, FALSE, SET_IN_FILE },
126 #endif
127     { "fullscreen", &iflags.wc2_fullscreen, FALSE, SET_IN_FILE },
128     { "help", &flags.help, TRUE, SET_IN_GAME },
129     { "hilite_pet", &iflags.wc_hilite_pet, FALSE, SET_IN_GAME }, /*WC*/
130     { "hilite_pile", &iflags.hilite_pile, FALSE, SET_IN_GAME },
131 #ifndef MAC
132     { "ignintr", &flags.ignintr, FALSE, SET_IN_GAME },
133 #else
134     { "ignintr", (boolean *) 0, FALSE, SET_IN_FILE },
135 #endif
136     { "implicit_uncursed", &iflags.implicit_uncursed, TRUE, SET_IN_GAME },
137     { "large_font", &iflags.obsolete, FALSE, SET_IN_FILE }, /* OBSOLETE */
138     { "legacy", &flags.legacy, TRUE, DISP_IN_GAME },
139     { "lit_corridor", &flags.lit_corridor, FALSE, SET_IN_GAME },
140     { "lootabc", &flags.lootabc, FALSE, SET_IN_GAME },
141 #ifdef MAIL
142     { "mail", &flags.biff, TRUE, SET_IN_GAME },
143 #else
144     { "mail", (boolean *) 0, TRUE, SET_IN_FILE },
145 #endif
146     { "mention_walls", &iflags.mention_walls, FALSE, SET_IN_GAME },
147     { "menucolors", &iflags.use_menu_color, FALSE, SET_IN_GAME },
148     /* for menu debugging only*/
149     { "menu_tab_sep", &iflags.menu_tab_sep, FALSE, SET_IN_GAME },
150     { "menu_objsyms", &iflags.menu_head_objsym, FALSE, SET_IN_GAME },
151     { "mouse_support", &iflags.wc_mouse_support, TRUE, DISP_IN_GAME }, /*WC*/
152 #ifdef NEWS
153     { "news", &iflags.news, TRUE, DISP_IN_GAME },
154 #else
155     { "news", (boolean *) 0, FALSE, SET_IN_FILE },
156 #endif
157     { "nudist", &u.uroleplay.nudist, FALSE, DISP_IN_GAME },
158     { "null", &flags.null, TRUE, SET_IN_GAME },
159 #if defined(SYSFLAGS) && defined(MAC)
160     { "page_wait", &sysflags.page_wait, TRUE, SET_IN_GAME },
161 #else
162     { "page_wait", (boolean *) 0, FALSE, SET_IN_FILE },
163 #endif
164     { "perm_invent", &flags.perm_invent, FALSE, SET_IN_GAME },
165     { "pickup_thrown", &flags.pickup_thrown, TRUE, SET_IN_GAME },
166     { "popup_dialog", &iflags.wc_popup_dialog, FALSE, SET_IN_GAME },   /*WC*/
167     { "preload_tiles", &iflags.wc_preload_tiles, TRUE, DISP_IN_GAME }, /*WC*/
168     { "pushweapon", &flags.pushweapon, FALSE, SET_IN_GAME },
169 #if defined(MICRO) && !defined(AMIGA)
170     { "rawio", &iflags.rawio, FALSE, DISP_IN_GAME },
171 #else
172     { "rawio", (boolean *) 0, FALSE, SET_IN_FILE },
173 #endif
174     { "rest_on_space", &flags.rest_on_space, FALSE, SET_IN_GAME },
175 #ifdef RLECOMP
176     { "rlecomp", &iflags.rlecomp,
177 #if defined(COMPRESS) || defined(ZLIB_COMP)
178       FALSE,
179 #else
180       TRUE,
181 #endif
182       DISP_IN_GAME },
183 #endif
184     { "safe_pet", &flags.safe_dog, TRUE, SET_IN_GAME },
185     { "sanity_check", &iflags.sanity_check, FALSE, SET_IN_GAME },
186     { "selectsaved", &iflags.wc2_selectsaved, TRUE, DISP_IN_GAME }, /*WC*/
187     { "showexp", &flags.showexp, FALSE, SET_IN_GAME },
188     { "showrace", &flags.showrace, FALSE, SET_IN_GAME },
189 #ifdef SCORE_ON_BOTL
190     { "showscore", &flags.showscore, FALSE, SET_IN_GAME },
191 #else
192     { "showscore", (boolean *) 0, FALSE, SET_IN_FILE },
193 #endif
194     { "silent", &flags.silent, TRUE, SET_IN_GAME },
195     { "softkeyboard", &iflags.wc2_softkeyboard, FALSE, SET_IN_FILE },
196     { "sortpack", &flags.sortpack, TRUE, SET_IN_GAME },
197     { "sparkle", &flags.sparkle, TRUE, SET_IN_GAME },
198     { "splash_screen", &iflags.wc_splash_screen, TRUE, DISP_IN_GAME }, /*WC*/
199     { "standout", &flags.standout, FALSE, SET_IN_GAME },
200 #if defined(STATUS_VIA_WINDOWPORT) && defined(STATUS_HILITES)
201     { "statushilites", &iflags.use_status_hilites, TRUE, SET_IN_GAME },
202 #else
203     { "statushilites", &iflags.use_status_hilites, FALSE, DISP_IN_GAME },
204 #endif
205     { "tiled_map", &iflags.wc_tiled_map, PREFER_TILED, DISP_IN_GAME }, /*WC*/
206     { "time", &flags.time, FALSE, SET_IN_GAME },
207 #ifdef TIMED_DELAY
208     { "timed_delay", &flags.nap, TRUE, SET_IN_GAME },
209 #else
210     { "timed_delay", (boolean *) 0, FALSE, SET_IN_GAME },
211 #endif
212     { "tombstone", &flags.tombstone, TRUE, SET_IN_GAME },
213     { "toptenwin", &iflags.toptenwin, FALSE, SET_IN_GAME },
214     { "travel", &flags.travelcmd, TRUE, SET_IN_GAME },
215     { "use_darkgray", &iflags.wc2_darkgray, TRUE, SET_IN_FILE },
216 #ifdef WIN32
217     { "use_inverse", &iflags.wc_inverse, TRUE, SET_IN_GAME }, /*WC*/
218 #else
219     { "use_inverse", &iflags.wc_inverse, FALSE, SET_IN_GAME }, /*WC*/
220 #endif
221     { "verbose", &flags.verbose, TRUE, SET_IN_GAME },
222     { "wraptext", &iflags.wc2_wraptext, FALSE, SET_IN_GAME },
223 #ifdef ZEROCOMP
224     { "zerocomp", &iflags.zerocomp,
225 #if defined(COMPRESS) || defined(ZLIB_COMP)
226       FALSE,
227 #else
228       TRUE,
229 #endif
230       DISP_IN_GAME },
231 #endif
232     { (char *) 0, (boolean *) 0, FALSE, 0 }
233 };
234
235 /* compound options, for option_help() and external programs like Amiga
236  * frontend */
237 static struct Comp_Opt {
238     const char *name, *descr;
239     int size; /* for frontends and such allocating space --
240                * usually allowed size of data in game, but
241                * occasionally maximum reasonable size for
242                * typing when game maintains information in
243                * a different format */
244     int optflags;
245 } compopt[] = {
246     { "align", "your starting alignment (lawful, neutral, or chaotic)", 8,
247       DISP_IN_GAME },
248     { "align_message", "message window alignment", 20, DISP_IN_GAME }, /*WC*/
249     { "align_status", "status window alignment", 20, DISP_IN_GAME },   /*WC*/
250     { "altkeyhandler", "alternate key handler", 20, DISP_IN_GAME },
251 #ifdef BACKWARD_COMPAT
252     { "boulder", "deprecated (use S_boulder in sym file instead)", 1,
253       SET_IN_FILE },
254 #endif
255     { "catname", "the name of your (first) cat (e.g., catname:Tabby)",
256       PL_PSIZ, DISP_IN_GAME },
257     { "disclose", "the kinds of information to disclose at end of game",
258       sizeof(flags.end_disclose) * 2, SET_IN_GAME },
259     { "dogname", "the name of your (first) dog (e.g., dogname:Fang)", PL_PSIZ,
260       DISP_IN_GAME },
261     { "dungeon", "the symbols to use in drawing the dungeon map",
262       MAXDCHARS + 1, SET_IN_FILE },
263     { "effects", "the symbols to use in drawing special effects",
264       MAXECHARS + 1, SET_IN_FILE },
265     { "font_map", "the font to use in the map window", 40,
266       DISP_IN_GAME },                                              /*WC*/
267     { "font_menu", "the font to use in menus", 40, DISP_IN_GAME }, /*WC*/
268     { "font_message", "the font to use in the message window", 40,
269       DISP_IN_GAME },                                                  /*WC*/
270     { "font_size_map", "the size of the map font", 20, DISP_IN_GAME }, /*WC*/
271     { "font_size_menu", "the size of the menu font", 20,
272       DISP_IN_GAME }, /*WC*/
273     { "font_size_message", "the size of the message font", 20,
274       DISP_IN_GAME }, /*WC*/
275     { "font_size_status", "the size of the status font", 20,
276       DISP_IN_GAME }, /*WC*/
277     { "font_size_text", "the size of the text font", 20,
278       DISP_IN_GAME }, /*WC*/
279     { "font_status", "the font to use in status window", 40,
280       DISP_IN_GAME }, /*WC*/
281     { "font_text", "the font to use in text windows", 40,
282       DISP_IN_GAME }, /*WC*/
283     { "fruit", "the name of a fruit you enjoy eating", PL_FSIZ, SET_IN_GAME },
284     { "gender", "your starting gender (male or female)", 8, DISP_IN_GAME },
285     { "horsename", "the name of your (first) horse (e.g., horsename:Silver)",
286       PL_PSIZ, DISP_IN_GAME },
287     { "map_mode", "map display mode under Windows", 20, DISP_IN_GAME }, /*WC*/
288     { "menustyle", "user interface for object selection", MENUTYPELEN,
289       SET_IN_GAME },
290     { "menu_deselect_all", "deselect all items in a menu", 4, SET_IN_FILE },
291     { "menu_deselect_page", "deselect all items on this page of a menu", 4,
292       SET_IN_FILE },
293     { "menu_first_page", "jump to the first page in a menu", 4, SET_IN_FILE },
294     { "menu_headings", "text attribute for menu headings", 9, SET_IN_GAME },
295     { "menu_invert_all", "invert all items in a menu", 4, SET_IN_FILE },
296     { "menu_invert_page", "invert all items on this page of a menu", 4,
297       SET_IN_FILE },
298     { "menu_last_page", "jump to the last page in a menu", 4, SET_IN_FILE },
299     { "menu_next_page", "goto the next menu page", 4, SET_IN_FILE },
300     { "menu_previous_page", "goto the previous menu page", 4, SET_IN_FILE },
301     { "menu_search", "search for a menu item", 4, SET_IN_FILE },
302     { "menu_select_all", "select all items in a menu", 4, SET_IN_FILE },
303     { "menu_select_page", "select all items on this page of a menu", 4,
304       SET_IN_FILE },
305     { "monsters", "the symbols to use for monsters", MAXMCLASSES,
306       SET_IN_FILE },
307     { "msghistory", "number of top line messages to save", 5, DISP_IN_GAME },
308 #ifdef TTY_GRAPHICS
309     { "msg_window", "the type of message window required", 1, SET_IN_GAME },
310 #else
311     { "msg_window", "the type of message window required", 1, SET_IN_FILE },
312 #endif
313     { "name", "your character's name (e.g., name:Merlin-W)", PL_NSIZ,
314       DISP_IN_GAME },
315     { "number_pad", "use the number pad for movement", 1, SET_IN_GAME },
316     { "objects", "the symbols to use for objects", MAXOCLASSES, SET_IN_FILE },
317     { "packorder", "the inventory order of the items in your pack",
318       MAXOCLASSES, SET_IN_GAME },
319 #ifdef CHANGE_COLOR
320     { "palette",
321 #ifndef WIN32
322       "palette (00c/880/-fff is blue/yellow/reverse white)", 15,
323       SET_IN_GAME },
324 #else
325       "palette (adjust an RGB color in palette (color-R-G-B)", 15,
326       SET_IN_FILE },
327 #endif
328 #if defined(MAC)
329     { "hicolor", "same as palette, only order is reversed", 15, SET_IN_FILE },
330 #endif
331 #endif
332     { "paranoid_confirmation", "extra prompting in certain situations", 28,
333       SET_IN_GAME },
334     { "pettype", "your preferred initial pet type", 4, DISP_IN_GAME },
335     { "pickup_burden", "maximum burden picked up before prompt", 20,
336       SET_IN_GAME },
337     { "pickup_types", "types of objects to pick up automatically",
338       MAXOCLASSES, SET_IN_GAME },
339     { "pile_limit", "threshold for \"there are many objects here\"", 24,
340       SET_IN_GAME },
341     { "playmode", "normal play, non-scoring explore mode, or debug mode", 8,
342       DISP_IN_GAME },
343     { "player_selection", "choose character via dialog or prompts", 12,
344       DISP_IN_GAME },
345     { "race", "your starting race (e.g., Human, Elf)", PL_CSIZ,
346       DISP_IN_GAME },
347     { "role", "your starting role (e.g., Barbarian, Valkyrie)", PL_CSIZ,
348       DISP_IN_GAME },
349     { "runmode", "display frequency when `running' or `travelling'",
350       sizeof "teleport", SET_IN_GAME },
351     { "scores", "the parts of the score list you wish to see", 32,
352       SET_IN_GAME },
353     { "scroll_amount", "amount to scroll map when scroll_margin is reached",
354       20, DISP_IN_GAME }, /*WC*/
355     { "scroll_margin", "scroll map when this far from the edge", 20,
356       DISP_IN_GAME }, /*WC*/
357     { "sortloot", "sort object selection lists by description", 4,
358       SET_IN_GAME },
359 #ifdef MSDOS
360     { "soundcard", "type of sound card to use", 20, SET_IN_FILE },
361 #endif
362     { "symset", "load a set of display symbols from the symbols file", 70,
363       SET_IN_GAME },
364     { "roguesymset",
365       "load a set of rogue display symbols from the symbols file", 70,
366       SET_IN_GAME },
367     { "suppress_alert", "suppress alerts about version-specific features", 8,
368       SET_IN_GAME },
369     { "tile_width", "width of tiles", 20, DISP_IN_GAME },   /*WC*/
370     { "tile_height", "height of tiles", 20, DISP_IN_GAME }, /*WC*/
371     { "tile_file", "name of tile file", 70, DISP_IN_GAME }, /*WC*/
372     { "traps", "the symbols to use in drawing traps", MAXTCHARS + 1,
373       SET_IN_FILE },
374     { "vary_msgcount", "show more old messages at a time", 20,
375       DISP_IN_GAME }, /*WC*/
376 #ifdef MSDOS
377     { "video", "method of video updating", 20, SET_IN_FILE },
378 #endif
379 #ifdef VIDEOSHADES
380     { "videocolors", "color mappings for internal screen routines", 40,
381       DISP_IN_GAME },
382     { "videoshades", "gray shades to map to black/gray/white", 32,
383       DISP_IN_GAME },
384 #endif
385 #ifdef WIN32
386     { "subkeyvalue", "override keystroke value", 7, SET_IN_FILE },
387 #endif
388     { "windowcolors", "the foreground/background colors of windows", /*WC*/
389       80, DISP_IN_GAME },
390     { "windowtype", "windowing system to use", WINTYPELEN, DISP_IN_GAME },
391 #ifdef WINCHAIN
392     { "windowchain", "window processor to use", WINTYPELEN, SET_IN_SYS },
393 #endif
394 #ifdef BACKWARD_COMPAT
395     { "DECgraphics", "load DECGraphics display symbols", 70, SET_IN_FILE },
396     { "IBMgraphics", "load IBMGraphics display symbols", 70, SET_IN_FILE },
397 #ifdef MAC_GRAPHICS_ENV
398     { "Macgraphics", "load MACGraphics display symbols", 70, SET_IN_FILE },
399 #endif
400 #endif
401     { (char *) 0, (char *) 0, 0, 0 }
402 };
403
404 #ifdef OPTION_LISTS_ONLY
405 #undef static
406
407 #else /* use rest of file */
408
409 extern struct symparse loadsyms[];
410 static boolean need_redraw; /* for doset() */
411
412 #if defined(TOS) && defined(TEXTCOLOR)
413 extern boolean colors_changed;  /* in tos.c */
414 #endif
415
416 #ifdef VIDEOSHADES
417 extern char *shade[3];          /* in sys/msdos/video.c */
418 extern char ttycolors[CLR_MAX]; /* in sys/msdos/video.c */
419 #endif
420
421 static char def_inv_order[MAXOCLASSES] = {
422     COIN_CLASS, AMULET_CLASS, WEAPON_CLASS, ARMOR_CLASS, FOOD_CLASS,
423     SCROLL_CLASS, SPBOOK_CLASS, POTION_CLASS, RING_CLASS, WAND_CLASS,
424     TOOL_CLASS, GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, 0,
425 };
426
427 /*
428  * Default menu manipulation command accelerators.  These may _not_ be:
429  *
430  *      + a number - reserved for counts
431  *      + an upper or lower case US ASCII letter - used for accelerators
432  *      + ESC - reserved for escaping the menu
433  *      + NULL, CR or LF - reserved for commiting the selection(s).  NULL
434  *        is kind of odd, but the tty's xwaitforspace() will return it if
435  *        someone hits a <ret>.
436  *      + a default object class symbol - used for object class accelerators
437  *
438  * Standard letters (for now) are:
439  *
440  *              <  back 1 page
441  *              >  forward 1 page
442  *              ^  first page
443  *              |  last page
444  *              :  search
445  *
446  *              page            all
447  *               ,    select     .
448  *               \    deselect   -
449  *               ~    invert     @
450  *
451  * The command name list is duplicated in the compopt array.
452  */
453 typedef struct {
454     const char *name;
455     char cmd;
456 } menu_cmd_t;
457
458 #define NUM_MENU_CMDS 11
459 static const menu_cmd_t default_menu_cmd_info[NUM_MENU_CMDS] = {
460 /* 0*/  { "menu_first_page", MENU_FIRST_PAGE },
461         { "menu_last_page", MENU_LAST_PAGE },
462         { "menu_next_page", MENU_NEXT_PAGE },
463         { "menu_previous_page", MENU_PREVIOUS_PAGE },
464         { "menu_select_all", MENU_SELECT_ALL },
465 /* 5*/  { "menu_deselect_all", MENU_UNSELECT_ALL },
466         { "menu_invert_all", MENU_INVERT_ALL },
467         { "menu_select_page", MENU_SELECT_PAGE },
468         { "menu_deselect_page", MENU_UNSELECT_PAGE },
469         { "menu_invert_page", MENU_INVERT_PAGE },
470 /*10*/  { "menu_search", MENU_SEARCH },
471 };
472
473 /*
474  * Allow the user to map incoming characters to various menu commands.
475  * The accelerator list must be a valid C string.
476  */
477 #define MAX_MENU_MAPPED_CMDS 32 /* some number */
478 char mapped_menu_cmds[MAX_MENU_MAPPED_CMDS + 1]; /* exported */
479 static char mapped_menu_op[MAX_MENU_MAPPED_CMDS + 1];
480 static short n_menu_mapped = 0;
481
482 static boolean initial, from_file;
483
484 STATIC_DCL void FDECL(doset_add_menu, (winid, const char *, int));
485 STATIC_DCL void FDECL(nmcpy, (char *, const char *, int));
486 STATIC_DCL void FDECL(escapes, (const char *, char *));
487 STATIC_DCL void FDECL(rejectoption, (const char *));
488 STATIC_DCL void FDECL(badoption, (const char *));
489 STATIC_DCL char *FDECL(string_for_opt, (char *, BOOLEAN_P));
490 STATIC_DCL char *FDECL(string_for_env_opt, (const char *, char *, BOOLEAN_P));
491 STATIC_DCL void FDECL(bad_negation, (const char *, BOOLEAN_P));
492 STATIC_DCL int FDECL(change_inv_order, (char *));
493 STATIC_DCL void FDECL(oc_to_str, (char *, char *));
494 STATIC_DCL int FDECL(feature_alert_opts, (char *, const char *));
495 STATIC_DCL const char *FDECL(get_compopt_value, (const char *, char *));
496 STATIC_DCL boolean FDECL(special_handling, (const char *,
497                                             BOOLEAN_P, BOOLEAN_P));
498 STATIC_DCL void FDECL(warning_opts, (char *, const char *));
499 STATIC_DCL boolean FDECL(duplicate_opt_detection, (const char *, int));
500 STATIC_DCL void FDECL(complain_about_duplicate, (const char *, int));
501
502 STATIC_OVL void FDECL(wc_set_font_name, (int, char *));
503 STATIC_OVL int FDECL(wc_set_window_colors, (char *));
504 STATIC_OVL boolean FDECL(is_wc_option, (const char *));
505 STATIC_OVL boolean FDECL(wc_supported, (const char *));
506 STATIC_OVL boolean FDECL(is_wc2_option, (const char *));
507 STATIC_OVL boolean FDECL(wc2_supported, (const char *));
508 STATIC_DCL void FDECL(remove_autopickup_exception,
509                       (struct autopickup_exception *));
510 STATIC_OVL int FDECL(count_ape_maps, (int *, int *));
511 STATIC_DCL const char *FDECL(attr2attrname, (int));
512 STATIC_DCL int NDECL(query_color);
513 STATIC_DCL int NDECL(query_msgtype);
514 STATIC_DCL int FDECL(query_attr, (const char *));
515 STATIC_DCL const char * FDECL(msgtype2name, (int));
516 STATIC_DCL boolean FDECL(msgtype_add, (int, char *));
517 STATIC_DCL void FDECL(free_one_msgtype, (int));
518 STATIC_DCL int NDECL(msgtype_count);
519 STATIC_DCL boolean FDECL(add_menu_coloring_parsed, (char *, int, int));
520 STATIC_DCL void FDECL(free_one_menu_coloring, (int));
521 STATIC_DCL int NDECL(count_menucolors);
522 STATIC_DCL int FDECL(handle_add_list_remove, (const char *, int));
523
524 void
525 reglyph_darkroom()
526 {
527     xchar x, y;
528
529     for (x = 0; x < COLNO; x++)
530         for (y = 0; y < ROWNO; y++) {
531             struct rm *lev = &levl[x][y];
532
533             if (!flags.dark_room || !iflags.use_color
534                 || Is_rogue_level(&u.uz)) {
535                 if (lev->glyph == cmap_to_glyph(S_darkroom))
536                     lev->glyph = lev->waslit ? cmap_to_glyph(S_room)
537                                              : cmap_to_glyph(S_stone);
538             } else {
539                 if (lev->glyph == cmap_to_glyph(S_room) && lev->seenv
540                     && lev->waslit && !cansee(x, y))
541                     lev->glyph = cmap_to_glyph(S_darkroom);
542                 else if (lev->glyph == cmap_to_glyph(S_stone)
543                          && lev->typ == ROOM && lev->seenv && !cansee(x, y))
544                     lev->glyph = cmap_to_glyph(S_darkroom);
545             }
546         }
547     if (flags.dark_room && iflags.use_color)
548         showsyms[S_darkroom] = showsyms[S_room];
549     else
550         showsyms[S_darkroom] = showsyms[S_stone];
551 }
552
553 /* check whether a user-supplied option string is a proper leading
554    substring of a particular option name; option string might have
555    a colon or equals sign and arbitrary value appended to it */
556 boolean
557 match_optname(user_string, opt_name, min_length, val_allowed)
558 const char *user_string, *opt_name;
559 int min_length;
560 boolean val_allowed;
561 {
562     int len = (int) strlen(user_string);
563
564     if (val_allowed) {
565         const char *p = index(user_string, ':'),
566                    *q = index(user_string, '=');
567
568         if (!p || (q && q < p))
569             p = q;
570         while (p && p > user_string && isspace((uchar) * (p - 1)))
571             p--;
572         if (p)
573             len = (int) (p - user_string);
574     }
575
576     return (boolean) (len >= min_length
577                       && !strncmpi(opt_name, user_string, len));
578 }
579
580 /* most environment variables will eventually be printed in an error
581  * message if they don't work, and most error message paths go through
582  * BUFSZ buffers, which could be overflowed by a maliciously long
583  * environment variable.  If a variable can legitimately be long, or
584  * if it's put in a smaller buffer, the responsible code will have to
585  * bounds-check itself.
586  */
587 char *
588 nh_getenv(ev)
589 const char *ev;
590 {
591     char *getev = getenv(ev);
592
593     if (getev && strlen(getev) <= (BUFSZ / 2))
594         return getev;
595     else
596         return (char *) 0;
597 }
598
599 /* process options, possibly including SYSCF */
600 void
601 initoptions()
602 {
603     initoptions_init();
604 #ifdef SYSCF
605 /* someday there may be other SYSCF alternatives besides text file */
606 #ifdef SYSCF_FILE
607     /* If SYSCF_FILE is specified, it _must_ exist... */
608     assure_syscf_file();
609     /* ... and _must_ parse correctly. */
610     if (!read_config_file(SYSCF_FILE, SET_IN_SYS)) {
611         raw_printf("Error(s) found in SYSCF_FILE, quitting.");
612         terminate(EXIT_FAILURE);
613     }
614     /*
615      * TODO [maybe]: parse the sysopt entries which are space-separated
616      * lists of usernames into arrays with one name per element.
617      */
618 #endif
619 #endif
620     initoptions_finish();
621 }
622
623 void
624 initoptions_init()
625 {
626 #if defined(UNIX) || defined(VMS)
627     char *opts;
628 #endif
629     int i;
630
631     /* set up the command parsing */
632     reset_commands(TRUE); /* init */
633
634     /* initialize the random number generator */
635     setrandom();
636
637     /* for detection of configfile options specified multiple times */
638     iflags.opt_booldup = iflags.opt_compdup = (int *) 0;
639
640     for (i = 0; boolopt[i].name; i++) {
641         if (boolopt[i].addr)
642             *(boolopt[i].addr) = boolopt[i].initvalue;
643     }
644 #if defined(COMPRESS) || defined(ZLIB_COMP)
645     set_savepref("externalcomp");
646     set_restpref("externalcomp");
647 #ifdef RLECOMP
648     set_savepref("!rlecomp");
649     set_restpref("!rlecomp");
650 #endif
651 #else
652 #ifdef ZEROCOMP
653     set_savepref("zerocomp");
654     set_restpref("zerocomp");
655 #endif
656 #ifdef RLECOMP
657     set_savepref("rlecomp");
658     set_restpref("rlecomp");
659 #endif
660 #endif
661 #ifdef SYSFLAGS
662     Strcpy(sysflags.sysflagsid, "sysflags");
663     sysflags.sysflagsid[9] = (char) sizeof(struct sysflag);
664 #endif
665     flags.end_own = FALSE;
666     flags.end_top = 3;
667     flags.end_around = 2;
668     flags.paranoia_bits = PARANOID_PRAY; /* old prayconfirm=TRUE */
669     flags.pile_limit = PILE_LIMIT_DFLT;  /* 5 */
670     flags.runmode = RUN_LEAP;
671     iflags.msg_history = 20;
672 #ifdef TTY_GRAPHICS
673     iflags.prevmsg_window = 's';
674 #endif
675     iflags.menu_headings = ATR_INVERSE;
676
677     /* hero's role, race, &c haven't been chosen yet */
678     flags.initrole = flags.initrace = flags.initgend = flags.initalign =
679         ROLE_NONE;
680
681     /* Set the default monster and object class symbols. */
682     init_symbols();
683     for (i = 0; i < WARNCOUNT; i++)
684         warnsyms[i] = def_warnsyms[i].sym;
685     iflags.bouldersym = 0;
686
687     iflags.travelcc.x = iflags.travelcc.y = -1;
688
689     /* assert( sizeof flags.inv_order == sizeof def_inv_order ); */
690     (void) memcpy((genericptr_t) flags.inv_order,
691                   (genericptr_t) def_inv_order, sizeof flags.inv_order);
692     flags.pickup_types[0] = '\0';
693     flags.pickup_burden = MOD_ENCUMBER;
694     flags.sortloot = 'l'; /* sort only loot by default */
695
696     for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++)
697         flags.end_disclose[i] = DISCLOSE_PROMPT_DEFAULT_NO;
698     switch_symbols(FALSE); /* set default characters */
699 #if defined(UNIX) && defined(TTY_GRAPHICS)
700     /*
701      * Set defaults for some options depending on what we can
702      * detect about the environment's capabilities.
703      * This has to be done after the global initialization above
704      * and before reading user-specific initialization via
705      * config file/environment variable below.
706      */
707     /* this detects the IBM-compatible console on most 386 boxes */
708     if ((opts = nh_getenv("TERM")) && !strncmp(opts, "AT", 2)) {
709         if (!symset[PRIMARY].name)
710             load_symset("IBMGraphics", PRIMARY);
711         if (!symset[ROGUESET].name)
712             load_symset("RogueIBM", ROGUESET);
713         switch_symbols(TRUE);
714 #ifdef TEXTCOLOR
715         iflags.use_color = TRUE;
716 #endif
717     }
718 #endif /* UNIX && TTY_GRAPHICS */
719 #if defined(UNIX) || defined(VMS)
720 #ifdef TTY_GRAPHICS
721     /* detect whether a "vt" terminal can handle alternate charsets */
722     if ((opts = nh_getenv("TERM"))
723         /* [could also check "xterm" which emulates vtXXX by default] */
724         && !strncmpi(opts, "vt", 2)
725         && AS && AE && index(AS, '\016') && index(AE, '\017')) {
726         if (!symset[PRIMARY].name)
727             load_symset("DECGraphics", PRIMARY);
728         switch_symbols(TRUE);
729     }
730 #endif
731 #endif /* UNIX || VMS */
732
733 #ifdef MAC_GRAPHICS_ENV
734     if (!symset[PRIMARY].name)
735         load_symset("MACGraphics", PRIMARY);
736     switch_symbols(TRUE);
737 #endif /* MAC_GRAPHICS_ENV */
738     flags.menu_style = MENU_FULL;
739
740     /* since this is done before init_objects(), do partial init here */
741     objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD;
742     nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
743 }
744
745 void
746 initoptions_finish()
747 {
748 #ifndef MAC
749     char *opts = getenv("NETHACKOPTIONS");
750
751     if (!opts)
752         opts = getenv("HACKOPTIONS");
753     if (opts) {
754         if (*opts == '/' || *opts == '\\' || *opts == '@') {
755             if (*opts == '@')
756                 opts++; /* @filename */
757             /* looks like a filename */
758             if (strlen(opts) < BUFSZ / 2)
759                 read_config_file(opts, SET_IN_FILE);
760         } else {
761             read_config_file((char *) 0, SET_IN_FILE);
762             /* let the total length of options be long;
763              * parseoptions() will check each individually
764              */
765             parseoptions(opts, TRUE, FALSE);
766         }
767     } else
768 #endif
769         read_config_file((char *) 0, SET_IN_FILE);
770
771     (void) fruitadd(pl_fruit, (struct fruit *) 0);
772     /*
773      * Remove "slime mold" from list of object names.  This will
774      * prevent it from being wished unless it's actually present
775      * as a named (or default) fruit.  Wishing for "fruit" will
776      * result in the player's preferred fruit [better than "\033"].
777      */
778     obj_descr[SLIME_MOLD].oc_name = "fruit";
779
780     if (iflags.bouldersym)
781         update_bouldersym();
782     reglyph_darkroom();
783     return;
784 }
785
786 STATIC_OVL void
787 nmcpy(dest, src, maxlen)
788 char *dest;
789 const char *src;
790 int maxlen;
791 {
792     int count;
793
794     for (count = 1; count < maxlen; count++) {
795         if (*src == ',' || *src == '\0')
796             break; /*exit on \0 terminator*/
797         *dest++ = *src++;
798     }
799     *dest = 0;
800 }
801
802 /*
803  * escapes(): escape expansion for showsyms.  C-style escapes understood
804  * include \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal).
805  * The ^-prefix for control characters is also understood, and \[mM]
806  * has the effect of 'meta'-ing the value which follows (so that the
807  * alternate character set will be enabled).
808  *
809  * For 3.4.3 and earlier, input ending with "\M", backslash, or caret
810  * prior to terminating '\0' would pull that '\0' into the output and then
811  * keep processing past it, potentially overflowing the output buffer.
812  * Now, trailing \ or ^ will act like \\ or \^ and add '\\' or '^' to the
813  * output and stop there; trailing \M will fall through to \<other> and
814  * yield 'M', then stop.  Any \X or \O followed by something other than
815  * an appropriate digit will also fall through to \<other> and yield 'X'
816  * or 'O', plus stop if the non-digit is end-of-string.
817  */
818 STATIC_OVL void
819 escapes(cp, tp)
820 const char *cp;
821 char *tp;
822 {
823     static NEARDATA const char oct[] = "01234567", dec[] = "0123456789",
824                                hex[] = "00112233445566778899aAbBcCdDeEfF";
825     const char *dp;
826     int cval, meta, dcount;
827
828     while (*cp) {
829         /* \M has to be followed by something to do meta conversion,
830            otherwise it will just be \M which ultimately yields 'M' */
831         meta = (*cp == '\\' && (cp[1] == 'm' || cp[1] == 'M') && cp[2]);
832         if (meta)
833             cp += 2;
834
835         cval = dcount = 0; /* for decimal, octal, hexadecimal cases */
836         if ((*cp != '\\' && *cp != '^') || !cp[1]) {
837             /* simple character, or nothing left for \ or ^ to escape */
838             cval = *cp++;
839         } else if (*cp == '^') { /* expand control-character syntax */
840             cval = (*++cp & 0x1f);
841             ++cp;
842             /* remaining cases are all for backslash and we know cp[1] is not
843              * \0 */
844         } else if (index(dec, cp[1])) {
845             ++cp; /* move past backslash to first digit */
846             do {
847                 cval = (cval * 10) + (*cp - '0');
848             } while (*++cp && index(dec, *cp) && ++dcount < 3);
849         } else if ((cp[1] == 'o' || cp[1] == 'O') && cp[2]
850                    && index(oct, cp[2])) {
851             cp += 2; /* move past backslash and 'O' */
852             do {
853                 cval = (cval * 8) + (*cp - '0');
854             } while (*++cp && index(oct, *cp) && ++dcount < 3);
855         } else if ((cp[1] == 'x' || cp[1] == 'X') && cp[2]
856                    && (dp = index(hex, cp[2])) != 0) {
857             cp += 2; /* move past backslash and 'X' */
858             do {
859                 cval = (cval * 16) + ((int) (dp - hex) / 2);
860             } while (*++cp && (dp = index(hex, *cp)) != 0 && ++dcount < 2);
861         } else { /* C-style character escapes */
862             switch (*++cp) {
863             case '\\':
864                 cval = '\\';
865                 break;
866             case 'n':
867                 cval = '\n';
868                 break;
869             case 't':
870                 cval = '\t';
871                 break;
872             case 'b':
873                 cval = '\b';
874                 break;
875             case 'r':
876                 cval = '\r';
877                 break;
878             default:
879                 cval = *cp;
880             }
881             ++cp;
882         }
883
884         if (meta)
885             cval |= 0x80;
886         *tp++ = (char) cval;
887     }
888     *tp = '\0';
889 }
890
891 STATIC_OVL void
892 rejectoption(optname)
893 const char *optname;
894 {
895 #ifdef MICRO
896     pline("\"%s\" settable only from %s.", optname, lastconfigfile);
897 #else
898     pline("%s can be set only from NETHACKOPTIONS or %s.", optname,
899           lastconfigfile);
900 #endif
901 }
902
903 STATIC_OVL void
904 badoption(opts)
905 const char *opts;
906 {
907     if (!initial) {
908         if (!strncmp(opts, "h", 1) || !strncmp(opts, "?", 1))
909             option_help();
910         else
911             pline("Bad syntax: %s.  Enter \"?g\" for help.", opts);
912         return;
913     }
914 #ifdef MAC
915     else
916         return;
917 #endif
918
919     if (from_file)
920         raw_printf("Bad syntax in OPTIONS in %s: %s%s.\n", lastconfigfile,
921 #ifdef WIN32
922                     "\n",
923 #else
924                     "",
925 #endif
926                     opts);
927     else
928         raw_printf("Bad syntax in NETHACKOPTIONS: %s%s.\n",
929 #ifdef WIN32
930                     "\n",
931 #else
932                     "",
933 #endif
934                     opts);
935     wait_synch();
936 }
937
938 STATIC_OVL char *
939 string_for_opt(opts, val_optional)
940 char *opts;
941 boolean val_optional;
942 {
943     char *colon, *equals;
944
945     colon = index(opts, ':');
946     equals = index(opts, '=');
947     if (!colon || (equals && equals < colon))
948         colon = equals;
949
950     if (!colon || !*++colon) {
951         if (!val_optional)
952             badoption(opts);
953         return (char *) 0;
954     }
955     return colon;
956 }
957
958 STATIC_OVL char *
959 string_for_env_opt(optname, opts, val_optional)
960 const char *optname;
961 char *opts;
962 boolean val_optional;
963 {
964     if (!initial) {
965         rejectoption(optname);
966         return (char *) 0;
967     }
968     return string_for_opt(opts, val_optional);
969 }
970
971 STATIC_OVL void
972 bad_negation(optname, with_parameter)
973 const char *optname;
974 boolean with_parameter;
975 {
976     pline_The("%s option may not %sbe negated.", optname,
977               with_parameter ? "both have a value and " : "");
978 }
979
980 /*
981  * Change the inventory order, using the given string as the new order.
982  * Missing characters in the new order are filled in at the end from
983  * the current inv_order, except for gold, which is forced to be first
984  * if not explicitly present.
985  *
986  * This routine returns 1 unless there is a duplicate or bad char in
987  * the string.
988  */
989 STATIC_OVL int
990 change_inv_order(op)
991 char *op;
992 {
993     int oc_sym, num;
994     char *sp, buf[BUFSZ];
995
996     num = 0;
997     /*  !!!! probably unnecessary with gold as normal inventory */
998
999     for (sp = op; *sp; sp++) {
1000         oc_sym = def_char_to_objclass(*sp);
1001         /* reject bad or duplicate entries */
1002         if (oc_sym == MAXOCLASSES || oc_sym == RANDOM_CLASS
1003             || oc_sym == ILLOBJ_CLASS || !index(flags.inv_order, oc_sym)
1004             || index(sp + 1, *sp))
1005             return 0;
1006         /* retain good ones */
1007         buf[num++] = (char) oc_sym;
1008     }
1009     buf[num] = '\0';
1010
1011     /* fill in any omitted classes, using previous ordering */
1012     for (sp = flags.inv_order; *sp; sp++)
1013         if (!index(buf, *sp)) {
1014             buf[num++] = *sp;
1015             buf[num] = '\0'; /* explicitly terminate for next index() */
1016         }
1017
1018     Strcpy(flags.inv_order, buf);
1019     return 1;
1020 }
1021
1022 STATIC_OVL void
1023 warning_opts(opts, optype)
1024 register char *opts;
1025 const char *optype;
1026 {
1027     uchar translate[WARNCOUNT];
1028     int length, i;
1029
1030     if (!(opts = string_for_env_opt(optype, opts, FALSE)))
1031         return;
1032     escapes(opts, opts);
1033
1034     length = (int) strlen(opts);
1035     /* match the form obtained from PC configuration files */
1036     for (i = 0; i < WARNCOUNT; i++)
1037         translate[i] = (i >= length) ? 0
1038                                      : opts[i] ? (uchar) opts[i]
1039                                                : def_warnsyms[i].sym;
1040     assign_warnings(translate);
1041 }
1042
1043 void
1044 assign_warnings(graph_chars)
1045 register uchar *graph_chars;
1046 {
1047     int i;
1048
1049     for (i = 0; i < WARNCOUNT; i++)
1050         if (graph_chars[i])
1051             warnsyms[i] = graph_chars[i];
1052 }
1053
1054 STATIC_OVL int
1055 feature_alert_opts(op, optn)
1056 char *op;
1057 const char *optn;
1058 {
1059     char buf[BUFSZ];
1060     boolean rejectver = FALSE;
1061     unsigned long fnv = get_feature_notice_ver(op); /* version.c */
1062
1063     if (fnv == 0L)
1064         return 0;
1065     if (fnv > get_current_feature_ver())
1066         rejectver = TRUE;
1067     else
1068         flags.suppress_alert = fnv;
1069     if (rejectver) {
1070         if (!initial) {
1071             You_cant("disable new feature alerts for future versions.");
1072         } else {
1073             Sprintf(buf,
1074                     "\n%s=%s Invalid reference to a future version ignored",
1075                     optn, op);
1076             badoption(buf);
1077         }
1078         return 0;
1079     }
1080     if (!initial) {
1081         Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
1082                 FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
1083         pline(
1084           "Feature change alerts disabled for NetHack %s features and prior.",
1085               buf);
1086     }
1087     return 1;
1088 }
1089
1090 void
1091 set_duplicate_opt_detection(on_or_off)
1092 int on_or_off;
1093 {
1094     int k, *optptr;
1095
1096     if (on_or_off != 0) {
1097         /*-- ON --*/
1098         if (iflags.opt_booldup)
1099             impossible("iflags.opt_booldup already on (memory leak)");
1100         iflags.opt_booldup = (int *) alloc(SIZE(boolopt) * sizeof(int));
1101         optptr = iflags.opt_booldup;
1102         for (k = 0; k < SIZE(boolopt); ++k)
1103             *optptr++ = 0;
1104
1105         if (iflags.opt_compdup)
1106             impossible("iflags.opt_compdup already on (memory leak)");
1107         iflags.opt_compdup = (int *) alloc(SIZE(compopt) * sizeof(int));
1108         optptr = iflags.opt_compdup;
1109         for (k = 0; k < SIZE(compopt); ++k)
1110             *optptr++ = 0;
1111     } else {
1112         /*-- OFF --*/
1113         if (iflags.opt_booldup)
1114             free((genericptr_t) iflags.opt_booldup);
1115         iflags.opt_booldup = (int *) 0;
1116         if (iflags.opt_compdup)
1117             free((genericptr_t) iflags.opt_compdup);
1118         iflags.opt_compdup = (int *) 0;
1119     }
1120 }
1121
1122 STATIC_OVL boolean
1123 duplicate_opt_detection(opts, iscompound)
1124 const char *opts;
1125 int iscompound; /* 0 == boolean option, 1 == compound */
1126 {
1127     int i, *optptr;
1128
1129     if (!iscompound && iflags.opt_booldup && initial && from_file) {
1130         for (i = 0; boolopt[i].name; i++) {
1131             if (match_optname(opts, boolopt[i].name, 3, FALSE)) {
1132                 optptr = iflags.opt_booldup + i;
1133                 *optptr += 1;
1134                 if (*optptr > 1)
1135                     return TRUE;
1136                 else
1137                     return FALSE;
1138             }
1139         }
1140     } else if (iscompound && iflags.opt_compdup && initial && from_file) {
1141         for (i = 0; compopt[i].name; i++) {
1142             if (match_optname(opts, compopt[i].name, strlen(compopt[i].name),
1143                               TRUE)) {
1144                 optptr = iflags.opt_compdup + i;
1145                 *optptr += 1;
1146                 if (*optptr > 1)
1147                     return TRUE;
1148                 else
1149                     return FALSE;
1150             }
1151         }
1152     }
1153     return FALSE;
1154 }
1155
1156 STATIC_OVL void
1157 complain_about_duplicate(opts, iscompound)
1158 const char *opts;
1159 int iscompound; /* 0 == boolean option, 1 == compound */
1160 {
1161 #ifdef MAC
1162     /* the Mac has trouble dealing with the output of messages while
1163      * processing the config file.  That should get fixed one day.
1164      * For now just return.
1165      */
1166 #else /* !MAC */
1167     raw_printf("\nWarning - %s option specified multiple times: %s.\n",
1168                iscompound ? "compound" : "boolean", opts);
1169     wait_synch();
1170 #endif /* ?MAC */
1171     return;
1172 }
1173
1174 /* paranoia[] - used by parseoptions() and special_handling() */
1175 STATIC_VAR const struct paranoia_opts {
1176     int flagmask;        /* which paranoid option */
1177     const char *argname; /* primary name */
1178     int argMinLen;       /* minimum number of letters to match */
1179     const char *synonym; /* alternate name (optional) */
1180     int synMinLen;
1181     const char *explain; /* for interactive menu */
1182 } paranoia[] = {
1183     /* there are some initial-letter conflicts: "a"ttack vs "a"ll, "attack"
1184        takes precedence and "all" isn't present in the interactive menu,
1185        and "d"ie vs "d"eath, synonyms for each other so doesn't matter;
1186        (also "p"ray vs "P"aranoia, "pray" takes precedence since "Paranoia"
1187        is just a synonym for "Confirm") */
1188     { PARANOID_CONFIRM, "Confirm", 1, "Paranoia", 2,
1189       "for \"yes\" confirmations, require \"no\" to reject" },
1190     { PARANOID_QUIT, "quit", 1, "explore", 1,
1191       "yes vs y to quit or to enter explore mode" },
1192     { PARANOID_DIE, "die", 1, "death", 2,
1193       "yes vs y to die (explore mode or debug mode)" },
1194     { PARANOID_BONES, "bones", 1, 0, 0,
1195       "yes vs y to save bones data when dying in debug mode" },
1196     { PARANOID_HIT, "attack", 1, "hit", 1,
1197       "yes vs y to attack a peaceful monster" },
1198     { PARANOID_PRAY, "pray", 1, 0, 0,
1199       "y to pray (supersedes old \"prayconfirm\" option)" },
1200     { PARANOID_REMOVE, "Remove", 1, "Takeoff", 1,
1201       "always pick from inventory for Remove and Takeoff" },
1202     { PARANOID_BREAKWAND, "wand", 1, "breakwand", 2,
1203       "yes vs y to break a wand" },
1204     /* for config file parsing; interactive menu skips these */
1205     { 0, "none", 4, 0, 0, 0 }, /* require full word match */
1206     { ~0, "all", 3, 0, 0, 0 }, /* ditto */
1207 };
1208
1209 extern struct menucoloring *menu_colorings;
1210
1211 static const struct {
1212     const char *name;
1213     const int color;
1214 } colornames[] = {
1215     { "black", CLR_BLACK },
1216     { "red", CLR_RED },
1217     { "green", CLR_GREEN },
1218     { "brown", CLR_BROWN },
1219     { "blue", CLR_BLUE },
1220     { "magenta", CLR_MAGENTA },
1221     { "cyan", CLR_CYAN },
1222     { "gray", CLR_GRAY },
1223     { "grey", CLR_GRAY },
1224     { "orange", CLR_ORANGE },
1225     { "light green", CLR_BRIGHT_GREEN },
1226     { "yellow", CLR_YELLOW },
1227     { "light blue", CLR_BRIGHT_BLUE },
1228     { "light magenta", CLR_BRIGHT_MAGENTA },
1229     { "light cyan", CLR_BRIGHT_CYAN },
1230     { "white", CLR_WHITE }
1231 };
1232
1233 static const struct {
1234     const char *name;
1235     const int attr;
1236 } attrnames[] = {
1237     { "none", ATR_NONE },
1238     { "bold", ATR_BOLD },
1239     { "dim", ATR_DIM },
1240     { "underline", ATR_ULINE },
1241     { "blink", ATR_BLINK },
1242     { "inverse", ATR_INVERSE }
1243 };
1244
1245 const char *
1246 clr2colorname(clr)
1247 int clr;
1248 {
1249     int i;
1250
1251     for (i = 0; i < SIZE(colornames); i++)
1252         if (colornames[i].color == clr)
1253             return colornames[i].name;
1254     return (char *) 0;
1255 }
1256
1257 const char *
1258 attr2attrname(attr)
1259 int attr;
1260 {
1261     int i;
1262
1263     for (i = 0; i < SIZE(attrnames); i++)
1264         if (attrnames[i].attr == attr)
1265             return attrnames[i].name;
1266     return (char *) 0;
1267 }
1268
1269 int
1270 query_color()
1271 {
1272     winid tmpwin;
1273     anything any;
1274     int i, pick_cnt;
1275     menu_item *picks = (menu_item *) 0;
1276
1277     tmpwin = create_nhwindow(NHW_MENU);
1278     start_menu(tmpwin);
1279     any = zeroany;
1280     for (i = 0; i < SIZE(colornames); i++) {
1281         if (!strcmp(colornames[i].name, "grey"))
1282             continue;
1283         any.a_int = i + 1;
1284         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, colornames[i].name,
1285                  MENU_UNSELECTED);
1286     }
1287     end_menu(tmpwin, "Pick a color");
1288     pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
1289     destroy_nhwindow(tmpwin);
1290     if (pick_cnt > 0) {
1291         i = colornames[picks->item.a_int - 1].color;
1292         free((genericptr_t) picks);
1293         return i;
1294     }
1295     return -1;
1296 }
1297
1298 int
1299 query_attr(prompt)
1300 const char *prompt;
1301 {
1302     winid tmpwin;
1303     anything any;
1304     int i, pick_cnt;
1305     menu_item *picks = (menu_item *) 0;
1306
1307     tmpwin = create_nhwindow(NHW_MENU);
1308     start_menu(tmpwin);
1309     any = zeroany;
1310     for (i = 0; i < SIZE(attrnames); i++) {
1311         any.a_int = i + 1;
1312         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, attrnames[i].attr,
1313                  attrnames[i].name, MENU_UNSELECTED);
1314     }
1315     end_menu(tmpwin, prompt ? prompt : "Pick an attribute");
1316     pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
1317     destroy_nhwindow(tmpwin);
1318     if (pick_cnt > 0) {
1319         i = attrnames[picks->item.a_int - 1].attr;
1320         free((genericptr_t) picks);
1321         return i;
1322     }
1323     return -1;
1324 }
1325
1326 static const struct {
1327     const char *name;
1328     const xchar msgtyp;
1329     const char *descr;
1330 } msgtype_names[] = {
1331     { "show", MSGTYP_NORMAL, "Show message normally" },
1332     { "hide", MSGTYP_NOSHOW, "Hide message" },
1333     { "noshow", MSGTYP_NOSHOW, NULL },
1334     { "stop", MSGTYP_STOP, "Prompt for more after the message" },
1335     { "more", MSGTYP_STOP, NULL },
1336     { "norep", MSGTYP_NOREP, "Do not repeat the message" }
1337 };
1338
1339 const char *
1340 msgtype2name(typ)
1341 int typ;
1342 {
1343     int i;
1344
1345     for (i = 0; i < SIZE(msgtype_names); i++)
1346         if (msgtype_names[i].descr && msgtype_names[i].msgtyp == typ)
1347             return msgtype_names[i].name;
1348     return (char *) 0;
1349 }
1350
1351 int
1352 query_msgtype()
1353 {
1354     winid tmpwin;
1355     anything any;
1356     int i, pick_cnt;
1357     menu_item *picks = (menu_item *) 0;
1358
1359     tmpwin = create_nhwindow(NHW_MENU);
1360     start_menu(tmpwin);
1361     any = zeroany;
1362     for (i = 0; i < SIZE(msgtype_names); i++)
1363         if (msgtype_names[i].descr) {
1364             any.a_int = msgtype_names[i].msgtyp + 1;
1365             add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1366                  msgtype_names[i].descr, MENU_UNSELECTED);
1367         }
1368     end_menu(tmpwin, "How to show the message");
1369     pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
1370     destroy_nhwindow(tmpwin);
1371     if (pick_cnt > 0) {
1372         i = picks->item.a_int - 1;
1373         free((genericptr_t) picks);
1374         return i;
1375     }
1376     return -1;
1377 }
1378
1379 boolean
1380 msgtype_add(typ, pattern)
1381 int typ;
1382 char *pattern;
1383 {
1384     struct plinemsg_type *tmp
1385               = (struct plinemsg_type *) alloc(sizeof (struct plinemsg_type));
1386
1387     if (!tmp)
1388         return FALSE;
1389     tmp->msgtype = typ;
1390     tmp->regex = regex_init();
1391     if (!regex_compile(pattern, tmp->regex)) {
1392         static const char *re_error = "MSGTYPE regex error";
1393
1394         if (!iflags.window_inited)
1395             raw_printf("\n%s: %s\n", re_error, regex_error_desc(tmp->regex));
1396         else
1397             pline("%s: %s", re_error, regex_error_desc(tmp->regex));
1398         wait_synch();
1399         regex_free(tmp->regex);
1400         free((genericptr_t) tmp);
1401         return FALSE;
1402     }
1403     tmp->pattern = dupstr(pattern);
1404     tmp->next = plinemsg_types;
1405     plinemsg_types = tmp;
1406     return TRUE;
1407 }
1408
1409 void
1410 msgtype_free()
1411 {
1412     struct plinemsg_type *tmp, *tmp2 = 0;
1413
1414     for (tmp = plinemsg_types; tmp; tmp = tmp2) {
1415         tmp2 = tmp->next;
1416         free((genericptr_t) tmp->pattern);
1417         regex_free(tmp->regex);
1418         free((genericptr_t) tmp);
1419     }
1420     plinemsg_types = (struct plinemsg_type *) 0;
1421 }
1422
1423 void
1424 free_one_msgtype(idx)
1425 int idx; /* 0 .. */
1426 {
1427     struct plinemsg_type *tmp = plinemsg_types;
1428     struct plinemsg_type *prev = NULL;
1429
1430     while (tmp) {
1431         if (idx == 0) {
1432             struct plinemsg_type *next = tmp->next;
1433
1434             regex_free(tmp->regex);
1435             free((genericptr_t) tmp->pattern);
1436             free((genericptr_t) tmp);
1437             if (prev)
1438                 prev->next = next;
1439             else
1440                 plinemsg_types = next;
1441             return;
1442         }
1443         idx--;
1444         prev = tmp;
1445         tmp = tmp->next;
1446     }
1447 }
1448
1449 int
1450 msgtype_type(msg)
1451 const char *msg;
1452 {
1453     struct plinemsg_type *tmp = plinemsg_types;
1454
1455     while (tmp) {
1456         if (regex_match(msg, tmp->regex))
1457             return tmp->msgtype;
1458         tmp = tmp->next;
1459     }
1460     return MSGTYP_NORMAL;
1461 }
1462
1463 int
1464 msgtype_count()
1465 {
1466     int c = 0;
1467     struct plinemsg_type *tmp = plinemsg_types;
1468
1469     while (tmp) {
1470         c++;
1471         tmp = tmp->next;
1472     }
1473     return c;
1474 }
1475
1476 boolean
1477 msgtype_parse_add(str)
1478 char *str;
1479 {
1480     char pattern[256];
1481     char msgtype[11];
1482
1483     if (sscanf(str, "%10s \"%255[^\"]\"", msgtype, pattern) == 2) {
1484         int typ = -1;
1485         int i;
1486
1487         for (i = 0; i < SIZE(msgtype_names); i++)
1488             if (!strncmpi(msgtype_names[i].name, msgtype, strlen(msgtype))) {
1489                 typ = msgtype_names[i].msgtyp;
1490                 break;
1491             }
1492         if (typ != -1)
1493             return msgtype_add(typ, pattern);
1494     }
1495     return FALSE;
1496 }
1497
1498 boolean
1499 add_menu_coloring_parsed(str, c, a)
1500 char *str;
1501 int c, a;
1502 {
1503     static const char re_error[] = "Menucolor regex error";
1504     struct menucoloring *tmp;
1505
1506     if (!str)
1507         return FALSE;
1508     tmp = (struct menucoloring *) alloc(sizeof (struct menucoloring));
1509     tmp->match = regex_init();
1510     if (!regex_compile(str, tmp->match)) {
1511         if (!iflags.window_inited)
1512             raw_printf("\n%s: %s\n", re_error, regex_error_desc(tmp->match));
1513         else
1514             pline("%s: %s", re_error, regex_error_desc(tmp->match));
1515         wait_synch();
1516         regex_free(tmp->match);
1517         free(tmp);
1518         return FALSE;
1519     } else {
1520         tmp->next = menu_colorings;
1521         tmp->origstr = dupstr(str);
1522         tmp->color = c;
1523         tmp->attr = a;
1524         menu_colorings = tmp;
1525         return TRUE;
1526     }
1527 }
1528
1529 /* parse '"regex_string"=color&attr' and add it to menucoloring */
1530 boolean
1531 add_menu_coloring(str)
1532 char *str;
1533 {
1534     int i, c = NO_COLOR, a = ATR_NONE;
1535     char *tmps, *cs, *amp;
1536
1537     if (!str || (cs = index(str, '=')) == 0)
1538         return FALSE;
1539
1540     tmps = cs + 1; /* advance past '=' */
1541     mungspaces(tmps);
1542     if ((amp = index(tmps, '&')) != 0)
1543         *amp = '\0';
1544
1545     /* allow "lightblue", "light blue", and "light-blue" to match "light blue"
1546        (also junk like "_l i-gh_t---b l u e" but we won't worry about that);
1547        also copes with trailing space; mungspaces removed any leading space */
1548     for (i = 0; i < SIZE(colornames); i++)
1549         if (fuzzymatch(tmps, colornames[i].name, " -_", TRUE)) {
1550             c = colornames[i].color;
1551             break;
1552         }
1553     if (i == SIZE(colornames) && (*tmps >= '0' && *tmps <= '9'))
1554         c = atoi(tmps);
1555
1556     if (c > 15)
1557         return FALSE;
1558
1559     if (amp) {
1560         tmps = amp + 1; /* advance past '&' */
1561         /* unlike colors, none of he attribute names has any embedded spaces,
1562            but use of fuzzymatch() allows us ignore the presence of leading
1563            and/or trailing (and also embedded) spaces in the user's string;
1564            dash and underscore skipping could be omitted but does no harm */
1565         for (i = 0; i < SIZE(attrnames); i++)
1566             if (fuzzymatch(tmps, attrnames[i].name, " -_", TRUE)) {
1567                 a = attrnames[i].attr;
1568                 break;
1569             }
1570         if (i == SIZE(attrnames) && (*tmps >= '0' && *tmps <= '9'))
1571             a = atoi(tmps);
1572     }
1573
1574     /* the regexp portion here has not been condensed by mungspaces() */
1575     *cs = '\0';
1576     tmps = str;
1577     if (*tmps == '"' || *tmps == '\'') {
1578         cs--;
1579         while (isspace((uchar) *cs))
1580             cs--;
1581         if (*cs == *tmps) {
1582             *cs = '\0';
1583             tmps++;
1584         }
1585     }
1586
1587     return add_menu_coloring_parsed(tmps, c, a);
1588 }
1589
1590 boolean
1591 get_menu_coloring(str, color, attr)
1592 char *str;
1593 int *color, *attr;
1594 {
1595     struct menucoloring *tmpmc;
1596
1597     if (iflags.use_menu_color)
1598         for (tmpmc = menu_colorings; tmpmc; tmpmc = tmpmc->next)
1599             if (regex_match(str, tmpmc->match)) {
1600                 *color = tmpmc->color;
1601                 *attr = tmpmc->attr;
1602                 return TRUE;
1603             }
1604     return FALSE;
1605 }
1606
1607 void
1608 free_menu_coloring()
1609 {
1610     struct menucoloring *tmp = menu_colorings;
1611
1612     while (tmp) {
1613         struct menucoloring *tmp2 = tmp->next;
1614
1615         regex_free(tmp->match);
1616         free((genericptr_t) tmp->origstr);
1617         free((genericptr_t) tmp);
1618         tmp = tmp2;
1619     }
1620 }
1621
1622 void
1623 free_one_menu_coloring(idx)
1624 int idx; /* 0 .. */
1625 {
1626     struct menucoloring *tmp = menu_colorings;
1627     struct menucoloring *prev = NULL;
1628
1629     while (tmp) {
1630         if (idx == 0) {
1631             struct menucoloring *next = tmp->next;
1632
1633             regex_free(tmp->match);
1634             free((genericptr_t) tmp->origstr);
1635             free((genericptr_t) tmp);
1636             if (prev)
1637                 prev->next = next;
1638             else
1639                 menu_colorings = next;
1640             return;
1641         }
1642         idx--;
1643         prev = tmp;
1644         tmp = tmp->next;
1645     }
1646 }
1647
1648 int
1649 count_menucolors()
1650 {
1651     int count = 0;
1652     struct menucoloring *tmp = menu_colorings;
1653
1654     while (tmp) {
1655         count++;
1656         tmp = tmp->next;
1657     }
1658     return count;
1659 }
1660
1661 void
1662 parseoptions(opts, tinitial, tfrom_file)
1663 register char *opts;
1664 boolean tinitial, tfrom_file;
1665 {
1666     register char *op;
1667     unsigned num;
1668     boolean negated, val_negated, duplicate;
1669     int i;
1670     const char *fullname;
1671
1672     initial = tinitial;
1673     from_file = tfrom_file;
1674     if ((op = index(opts, ',')) != 0) {
1675         *op++ = 0;
1676         parseoptions(op, initial, from_file);
1677     }
1678     if (strlen(opts) > BUFSZ / 2) {
1679         badoption("option too long");
1680         return;
1681     }
1682
1683     /* strip leading and trailing white space */
1684     while (isspace((uchar) *opts))
1685         opts++;
1686     op = eos(opts);
1687     while (--op >= opts && isspace((uchar) *op))
1688         *op = '\0';
1689
1690     if (!*opts)
1691         return;
1692     negated = FALSE;
1693     while ((*opts == '!') || !strncmpi(opts, "no", 2)) {
1694         if (*opts == '!')
1695             opts++;
1696         else
1697             opts += 2;
1698         negated = !negated;
1699     }
1700
1701     /* variant spelling */
1702
1703     if (match_optname(opts, "colour", 5, FALSE))
1704         Strcpy(opts, "color"); /* fortunately this isn't longer */
1705
1706     /* special boolean options */
1707
1708     if (match_optname(opts, "female", 3, FALSE)) {
1709         if (duplicate_opt_detection(opts, 0))
1710             complain_about_duplicate(opts, 0);
1711         if (!initial && flags.female == negated)
1712             pline("That is not anatomically possible.");
1713         else
1714             flags.initgend = flags.female = !negated;
1715         return;
1716     }
1717
1718     if (match_optname(opts, "male", 4, FALSE)) {
1719         if (duplicate_opt_detection(opts, 0))
1720             complain_about_duplicate(opts, 0);
1721         if (!initial && flags.female != negated)
1722             pline("That is not anatomically possible.");
1723         else
1724             flags.initgend = flags.female = negated;
1725         return;
1726     }
1727
1728 #if defined(MICRO) && !defined(AMIGA)
1729     /* included for compatibility with old NetHack.cnf files */
1730     if (match_optname(opts, "IBM_", 4, FALSE)) {
1731         iflags.BIOS = !negated;
1732         return;
1733     }
1734 #endif /* MICRO */
1735
1736     /* compound options */
1737
1738     /* This first batch can be duplicated if their values are negated */
1739
1740     /* align:string */
1741     fullname = "align";
1742     if (match_optname(opts, fullname, sizeof("align") - 1, TRUE)) {
1743         if (negated) {
1744             bad_negation(fullname, FALSE);
1745         } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1746             val_negated = FALSE;
1747             while ((*op == '!') || !strncmpi(op, "no", 2)) {
1748                 if (*op == '!')
1749                     op++;
1750                 else
1751                     op += 2;
1752                 val_negated = !val_negated;
1753             }
1754             if (val_negated) {
1755                 if (!setrolefilter(op))
1756                     badoption(opts);
1757             } else {
1758                 if (duplicate_opt_detection(opts, 1))
1759                     complain_about_duplicate(opts, 1);
1760                 if ((flags.initalign = str2align(op)) == ROLE_NONE)
1761                     badoption(opts);
1762             }
1763         }
1764         return;
1765     }
1766
1767     /* role:string or character:string */
1768     fullname = "role";
1769     if (match_optname(opts, fullname, 4, TRUE)
1770         || match_optname(opts, (fullname = "character"), 4, TRUE)) {
1771         if (negated) {
1772             bad_negation(fullname, FALSE);
1773         } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1774             val_negated = FALSE;
1775             while ((*op == '!') || !strncmpi(op, "no", 2)) {
1776                 if (*op == '!')
1777                     op++;
1778                 else
1779                     op += 2;
1780                 val_negated = !val_negated;
1781             }
1782             if (val_negated) {
1783                 if (!setrolefilter(op))
1784                     badoption(opts);
1785             } else {
1786                 if (duplicate_opt_detection(opts, 1))
1787                     complain_about_duplicate(opts, 1);
1788                 if ((flags.initrole = str2role(op)) == ROLE_NONE)
1789                     badoption(opts);
1790                 else /* Backwards compatibility */
1791                     nmcpy(pl_character, op, PL_NSIZ);
1792             }
1793         }
1794         return;
1795     }
1796
1797     /* race:string */
1798     fullname = "race";
1799     if (match_optname(opts, fullname, 4, TRUE)) {
1800         if (negated) {
1801             bad_negation(fullname, FALSE);
1802         } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1803             val_negated = FALSE;
1804             while ((*op == '!') || !strncmpi(op, "no", 2)) {
1805                 if (*op == '!')
1806                     op++;
1807                 else
1808                     op += 2;
1809                 val_negated = !val_negated;
1810             }
1811             if (val_negated) {
1812                 if (!setrolefilter(op))
1813                     badoption(opts);
1814             } else {
1815                 if (duplicate_opt_detection(opts, 1))
1816                     complain_about_duplicate(opts, 1);
1817                 if ((flags.initrace = str2race(op)) == ROLE_NONE)
1818                     badoption(opts);
1819                 else /* Backwards compatibility */
1820                     pl_race = *op;
1821             }
1822         }
1823         return;
1824     }
1825
1826     /* gender:string */
1827     fullname = "gender";
1828     if (match_optname(opts, fullname, 4, TRUE)) {
1829         if (negated) {
1830             bad_negation(fullname, FALSE);
1831         } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1832             val_negated = FALSE;
1833             while ((*op == '!') || !strncmpi(op, "no", 2)) {
1834                 if (*op == '!')
1835                     op++;
1836                 else
1837                     op += 2;
1838                 val_negated = !val_negated;
1839             }
1840             if (val_negated) {
1841                 if (!setrolefilter(op))
1842                     badoption(opts);
1843             } else {
1844                 if (duplicate_opt_detection(opts, 1))
1845                     complain_about_duplicate(opts, 1);
1846                 if ((flags.initgend = str2gend(op)) == ROLE_NONE)
1847                     badoption(opts);
1848                 else
1849                     flags.female = flags.initgend;
1850             }
1851         }
1852         return;
1853     }
1854
1855     /* We always check for duplicates on the remaining compound options,
1856        although individual option processing can choose to complain or not */
1857
1858     duplicate =
1859         duplicate_opt_detection(opts, 1); /* 1 means check compounds */
1860
1861     fullname = "pettype";
1862     if (match_optname(opts, fullname, 3, TRUE)) {
1863         if (duplicate)
1864             complain_about_duplicate(opts, 1);
1865         if ((op = string_for_env_opt(fullname, opts, negated)) != 0) {
1866             if (negated)
1867                 bad_negation(fullname, TRUE);
1868             else
1869                 switch (lowc(*op)) {
1870                 case 'd': /* dog */
1871                     preferred_pet = 'd';
1872                     break;
1873                 case 'c': /* cat */
1874                 case 'f': /* feline */
1875                     preferred_pet = 'c';
1876                     break;
1877                 case 'h': /* horse */
1878                 case 'q': /* quadruped */
1879                     /* avoids giving "unrecognized type of pet" but
1880                        pet_type(dog.c) won't actually honor this */
1881                     preferred_pet = 'h';
1882                     break;
1883                 case 'n': /* no pet */
1884                     preferred_pet = 'n';
1885                     break;
1886                 case '*': /* random */
1887                     preferred_pet = '\0';
1888                     break;
1889                 default:
1890                     pline("Unrecognized pet type '%s'.", op);
1891                     break;
1892                 }
1893         } else if (negated)
1894             preferred_pet = 'n';
1895         return;
1896     }
1897
1898     fullname = "catname";
1899     if (match_optname(opts, fullname, 3, TRUE)) {
1900         if (duplicate)
1901             complain_about_duplicate(opts, 1);
1902         if (negated)
1903             bad_negation(fullname, FALSE);
1904         else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1905             nmcpy(catname, op, PL_PSIZ);
1906         sanitize_name(catname);
1907         return;
1908     }
1909
1910     fullname = "dogname";
1911     if (match_optname(opts, fullname, 3, TRUE)) {
1912         if (duplicate)
1913             complain_about_duplicate(opts, 1);
1914         if (negated)
1915             bad_negation(fullname, FALSE);
1916         else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1917             nmcpy(dogname, op, PL_PSIZ);
1918         sanitize_name(dogname);
1919         return;
1920     }
1921
1922     fullname = "horsename";
1923     if (match_optname(opts, fullname, 5, TRUE)) {
1924         if (duplicate)
1925             complain_about_duplicate(opts, 1);
1926         if (negated)
1927             bad_negation(fullname, FALSE);
1928         else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1929             nmcpy(horsename, op, PL_PSIZ);
1930         sanitize_name(horsename);
1931         return;
1932     }
1933
1934     fullname = "number_pad";
1935     if (match_optname(opts, fullname, 10, TRUE)) {
1936         boolean compat = (strlen(opts) <= 10);
1937
1938         if (duplicate)
1939             complain_about_duplicate(opts, 1);
1940         op = string_for_opt(opts, (compat || !initial));
1941         if (!op) {
1942             if (compat || negated || initial) {
1943                 /* for backwards compatibility, "number_pad" without a
1944                    value is a synonym for number_pad:1 */
1945                 iflags.num_pad = !negated;
1946                 iflags.num_pad_mode = 0;
1947             }
1948         } else if (negated) {
1949             bad_negation("number_pad", TRUE);
1950             return;
1951         } else {
1952             int mode = atoi(op);
1953
1954             if (mode < -1 || mode > 4 || (mode == 0 && *op != '0')) {
1955                 badoption(opts);
1956                 return;
1957             } else if (mode <= 0) {
1958                 iflags.num_pad = FALSE;
1959                 /* German keyboard; y and z keys swapped */
1960                 iflags.num_pad_mode = (mode < 0); /* 0 or 1 */
1961             } else {                              /* mode > 0 */
1962                 iflags.num_pad = TRUE;
1963                 iflags.num_pad_mode = 0;
1964                 /* PC Hack / MSDOS compatibility */
1965                 if (mode == 2 || mode == 4)
1966                     iflags.num_pad_mode |= 1;
1967                 /* phone keypad layout */
1968                 if (mode == 3 || mode == 4)
1969                     iflags.num_pad_mode |= 2;
1970             }
1971         }
1972         reset_commands(FALSE);
1973         number_pad(iflags.num_pad ? 1 : 0);
1974         return;
1975     }
1976
1977     fullname = "roguesymset";
1978     if (match_optname(opts, fullname, 7, TRUE)) {
1979         if (duplicate)
1980             complain_about_duplicate(opts, 1);
1981         if (negated) {
1982             bad_negation(fullname, FALSE);
1983         } else if ((op = string_for_opt(opts, FALSE)) != 0) {
1984             symset[ROGUESET].name = dupstr(op);
1985             if (!read_sym_file(ROGUESET)) {
1986                 clear_symsetentry(ROGUESET, TRUE);
1987                 raw_printf("Unable to load symbol set \"%s\" from \"%s\".",
1988                            op, SYMBOLS);
1989                 wait_synch();
1990             } else {
1991                 if (!initial && Is_rogue_level(&u.uz))
1992                     assign_graphics(ROGUESET);
1993                 need_redraw = TRUE;
1994             }
1995         }
1996         return;
1997     }
1998
1999     fullname = "symset";
2000     if (match_optname(opts, fullname, 6, TRUE)) {
2001         if (duplicate)
2002             complain_about_duplicate(opts, 1);
2003         if (negated) {
2004             bad_negation(fullname, FALSE);
2005         } else if ((op = string_for_opt(opts, FALSE)) != 0) {
2006             symset[PRIMARY].name = dupstr(op);
2007             if (!read_sym_file(PRIMARY)) {
2008                 clear_symsetentry(PRIMARY, TRUE);
2009                 raw_printf("Unable to load symbol set \"%s\" from \"%s\".",
2010                            op, SYMBOLS);
2011                 wait_synch();
2012             } else {
2013                 switch_symbols(TRUE);
2014                 need_redraw = TRUE;
2015             }
2016         }
2017         return;
2018     }
2019
2020     fullname = "runmode";
2021     if (match_optname(opts, fullname, 4, TRUE)) {
2022         if (duplicate)
2023             complain_about_duplicate(opts, 1);
2024         if (negated) {
2025             flags.runmode = RUN_TPORT;
2026         } else if ((op = string_for_opt(opts, FALSE)) != 0) {
2027             if (!strncmpi(op, "teleport", strlen(op)))
2028                 flags.runmode = RUN_TPORT;
2029             else if (!strncmpi(op, "run", strlen(op)))
2030                 flags.runmode = RUN_LEAP;
2031             else if (!strncmpi(op, "walk", strlen(op)))
2032                 flags.runmode = RUN_STEP;
2033             else if (!strncmpi(op, "crawl", strlen(op)))
2034                 flags.runmode = RUN_CRAWL;
2035             else
2036                 badoption(opts);
2037         }
2038         return;
2039     }
2040
2041     /* menucolor:"regex_string"=color */
2042     fullname = "menucolor";
2043     if (match_optname(opts, fullname, 9, TRUE)) {
2044         if (negated)
2045             bad_negation(fullname, FALSE);
2046         else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
2047             if (!add_menu_coloring(op))
2048                 badoption(opts);
2049         return;
2050     }
2051
2052     fullname = "msghistory";
2053     if (match_optname(opts, fullname, 3, TRUE)) {
2054         if (duplicate)
2055             complain_about_duplicate(opts, 1);
2056         op = string_for_env_opt(fullname, opts, negated);
2057         if ((negated && !op) || (!negated && op)) {
2058             iflags.msg_history = negated ? 0 : atoi(op);
2059         } else if (negated)
2060             bad_negation(fullname, TRUE);
2061         return;
2062     }
2063
2064     fullname = "msg_window";
2065     /* msg_window:single, combo, full or reversed */
2066     if (match_optname(opts, fullname, 4, TRUE)) {
2067 /* allow option to be silently ignored by non-tty ports */
2068 #ifdef TTY_GRAPHICS
2069         int tmp;
2070
2071         if (duplicate)
2072             complain_about_duplicate(opts, 1);
2073         if (!(op = string_for_opt(opts, TRUE))) {
2074             tmp = negated ? 's' : 'f';
2075         } else {
2076             if (negated) {
2077                 bad_negation(fullname, TRUE);
2078                 return;
2079             }
2080             tmp = tolower(*op);
2081         }
2082         switch (tmp) {
2083         case 's': /* single message history cycle (default if negated) */
2084             iflags.prevmsg_window = 's';
2085             break;
2086         case 'c': /* combination: two singles, then full page reversed */
2087             iflags.prevmsg_window = 'c';
2088             break;
2089         case 'f': /* full page (default if no opts) */
2090             iflags.prevmsg_window = 'f';
2091             break;
2092         case 'r': /* full page (reversed) */
2093             iflags.prevmsg_window = 'r';
2094             break;
2095         default:
2096             badoption(opts);
2097         }
2098 #endif
2099         return;
2100     }
2101
2102     /* WINCAP
2103      * setting font options  */
2104     fullname = "font";
2105     if (!strncmpi(opts, fullname, 4)) {
2106         int opttype = -1;
2107         char *fontopts = opts + 4;
2108
2109         if (!strncmpi(fontopts, "map", 3) || !strncmpi(fontopts, "_map", 4))
2110             opttype = MAP_OPTION;
2111         else if (!strncmpi(fontopts, "message", 7)
2112                  || !strncmpi(fontopts, "_message", 8))
2113             opttype = MESSAGE_OPTION;
2114         else if (!strncmpi(fontopts, "text", 4)
2115                  || !strncmpi(fontopts, "_text", 5))
2116             opttype = TEXT_OPTION;
2117         else if (!strncmpi(fontopts, "menu", 4)
2118                  || !strncmpi(fontopts, "_menu", 5))
2119             opttype = MENU_OPTION;
2120         else if (!strncmpi(fontopts, "status", 6)
2121                  || !strncmpi(fontopts, "_status", 7))
2122             opttype = STATUS_OPTION;
2123         else if (!strncmpi(fontopts, "_size", 5)) {
2124             if (!strncmpi(fontopts, "_size_map", 8))
2125                 opttype = MAP_OPTION;
2126             else if (!strncmpi(fontopts, "_size_message", 12))
2127                 opttype = MESSAGE_OPTION;
2128             else if (!strncmpi(fontopts, "_size_text", 9))
2129                 opttype = TEXT_OPTION;
2130             else if (!strncmpi(fontopts, "_size_menu", 9))
2131                 opttype = MENU_OPTION;
2132             else if (!strncmpi(fontopts, "_size_status", 11))
2133                 opttype = STATUS_OPTION;
2134             else {
2135                 badoption(opts);
2136                 return;
2137             }
2138             if (duplicate)
2139                 complain_about_duplicate(opts, 1);
2140             if (opttype > 0 && !negated
2141                 && (op = string_for_opt(opts, FALSE)) != 0) {
2142                 switch (opttype) {
2143                 case MAP_OPTION:
2144                     iflags.wc_fontsiz_map = atoi(op);
2145                     break;
2146                 case MESSAGE_OPTION:
2147                     iflags.wc_fontsiz_message = atoi(op);
2148                     break;
2149                 case TEXT_OPTION:
2150                     iflags.wc_fontsiz_text = atoi(op);
2151                     break;
2152                 case MENU_OPTION:
2153                     iflags.wc_fontsiz_menu = atoi(op);
2154                     break;
2155                 case STATUS_OPTION:
2156                     iflags.wc_fontsiz_status = atoi(op);
2157                     break;
2158                 }
2159             }
2160             return;
2161         } else {
2162             badoption(opts);
2163         }
2164         if (opttype > 0 && (op = string_for_opt(opts, FALSE)) != 0) {
2165             wc_set_font_name(opttype, op);
2166 #ifdef MAC
2167             set_font_name(opttype, op);
2168 #endif
2169             return;
2170         } else if (negated)
2171             bad_negation(fullname, TRUE);
2172         return;
2173     }
2174 #ifdef CHANGE_COLOR
2175     if (match_optname(opts, "palette", 3, TRUE)
2176 #ifdef MAC
2177         || match_optname(opts, "hicolor", 3, TRUE)
2178 #endif
2179             ) {
2180         int color_number, color_incr;
2181
2182 #ifndef WIN32
2183         if (duplicate)
2184             complain_about_duplicate(opts, 1);
2185 #endif
2186 #ifdef MAC
2187         if (match_optname(opts, "hicolor", 3, TRUE)) {
2188             if (negated) {
2189                 bad_negation("hicolor", FALSE);
2190                 return;
2191             }
2192             color_number = CLR_MAX + 4; /* HARDCODED inverse number */
2193             color_incr = -1;
2194         } else {
2195 #endif
2196             if (negated) {
2197                 bad_negation("palette", FALSE);
2198                 return;
2199             }
2200             color_number = 0;
2201             color_incr = 1;
2202 #ifdef MAC
2203         }
2204 #endif
2205 #ifdef WIN32
2206         op = string_for_opt(opts, TRUE);
2207         if (!alternative_palette(op))
2208             badoption(opts);
2209 #else
2210         if ((op = string_for_opt(opts, FALSE)) != (char *) 0) {
2211             char *pt = op;
2212             int cnt, tmp, reverse;
2213             long rgb;
2214
2215             while (*pt && color_number >= 0) {
2216                 cnt = 3;
2217                 rgb = 0L;
2218                 if (*pt == '-') {
2219                     reverse = 1;
2220                     pt++;
2221                 } else {
2222                     reverse = 0;
2223                 }
2224                 while (cnt-- > 0) {
2225                     if (*pt && *pt != '/') {
2226 #ifdef AMIGA
2227                         rgb <<= 4;
2228 #else
2229                         rgb <<= 8;
2230 #endif
2231                         tmp = *(pt++);
2232                         if (isalpha(tmp)) {
2233                             tmp = (tmp + 9) & 0xf; /* Assumes ASCII... */
2234                         } else {
2235                             tmp &= 0xf; /* Digits in ASCII too... */
2236                         }
2237 #ifndef AMIGA
2238                         /* Add an extra so we fill f -> ff and 0 -> 00 */
2239                         rgb += tmp << 4;
2240 #endif
2241                         rgb += tmp;
2242                     }
2243                 }
2244                 if (*pt == '/') {
2245                     pt++;
2246                 }
2247                 change_color(color_number, rgb, reverse);
2248                 color_number += color_incr;
2249             }
2250         }
2251 #endif /* !WIN32 */
2252         if (!initial) {
2253             need_redraw = TRUE;
2254         }
2255         return;
2256     }
2257 #endif /* CHANGE_COLOR */
2258
2259     if (match_optname(opts, "fruit", 2, TRUE)) {
2260         struct fruit *forig = 0;
2261         char empty_str = '\0';
2262
2263         if (duplicate)
2264             complain_about_duplicate(opts, 1);
2265         op = string_for_opt(opts, negated);
2266         if (negated) {
2267             if (op) {
2268                 bad_negation("fruit", TRUE);
2269                 return;
2270             }
2271             op = &empty_str;
2272             goto goodfruit;
2273         }
2274         if (!op)
2275             return;
2276         if (!initial) {
2277             struct fruit *f;
2278
2279             num = 0;
2280             for (f = ffruit; f; f = f->nextf) {
2281                 if (!strcmp(op, f->fname))
2282                     break;
2283                 num++;
2284             }
2285             if (!flags.made_fruit) {
2286                 for (forig = ffruit; forig; forig = forig->nextf) {
2287                     if (!strcmp(pl_fruit, forig->fname)) {
2288                         break;
2289                     }
2290                 }
2291             }
2292             if (!forig && num >= 100) {
2293                 pline("Doing that so many times isn't very fruitful.");
2294                 return;
2295             }
2296         }
2297     goodfruit:
2298         nmcpy(pl_fruit, op, PL_FSIZ);
2299         sanitize_name(pl_fruit);
2300         /* OBJ_NAME(objects[SLIME_MOLD]) won't work after initialization */
2301         if (!*pl_fruit)
2302             nmcpy(pl_fruit, "slime mold", PL_FSIZ);
2303         if (!initial) {
2304             (void) fruitadd(pl_fruit, forig);
2305             pline("Fruit is now \"%s\".", pl_fruit);
2306         }
2307         /* If initial, then initoptions is allowed to do it instead
2308          * of here (initoptions always has to do it even if there's
2309          * no fruit option at all.  Also, we don't want people
2310          * setting multiple fruits in their options.)
2311          */
2312         return;
2313     }
2314
2315     fullname = "warnings";
2316     if (match_optname(opts, fullname, 5, TRUE)) {
2317         if (duplicate)
2318             complain_about_duplicate(opts, 1);
2319         if (negated)
2320             bad_negation(fullname, FALSE);
2321         else
2322             warning_opts(opts, fullname);
2323         return;
2324     }
2325
2326 #ifdef BACKWARD_COMPAT
2327     /* boulder:symbol */
2328     fullname = "boulder";
2329     if (match_optname(opts, fullname, 7, TRUE)) {
2330         int clash = 0;
2331         if (duplicate)
2332             complain_about_duplicate(opts, 1);
2333         if (negated) {
2334             bad_negation(fullname, FALSE);
2335             return;
2336         }
2337         /* if (!(opts = string_for_env_opt(fullname, opts, FALSE)))
2338          */
2339         if (!(opts = string_for_opt(opts, FALSE)))
2340             return;
2341         escapes(opts, opts);
2342         if (def_char_to_monclass(opts[0]) != MAXMCLASSES)
2343             clash = 1;
2344         else if (opts[0] >= '1' && opts[0] <= '5')
2345             clash = 2;
2346         if (clash) {
2347             /* symbol chosen matches a used monster or warning
2348                symbol which is not good - reject it*/
2349             pline(
2350                 "Badoption - boulder symbol '%c' conflicts with a %s symbol.",
2351                 opts[0], (clash == 1) ? "monster" : "warning");
2352         } else {
2353             /*
2354              * Override the default boulder symbol.
2355              */
2356             iflags.bouldersym = (uchar) opts[0];
2357         }
2358         if (!initial)
2359             need_redraw = TRUE;
2360         return;
2361     }
2362 #endif
2363
2364     /* name:string */
2365     fullname = "name";
2366     if (match_optname(opts, fullname, 4, TRUE)) {
2367         if (duplicate)
2368             complain_about_duplicate(opts, 1);
2369         if (negated)
2370             bad_negation(fullname, FALSE);
2371         else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
2372             nmcpy(plname, op, PL_NSIZ);
2373         return;
2374     }
2375
2376     /* altkeyhandler:string */
2377     fullname = "altkeyhandler";
2378     if (match_optname(opts, fullname, 4, TRUE)) {
2379         if (duplicate)
2380             complain_about_duplicate(opts, 1);
2381         if (negated) {
2382             bad_negation(fullname, FALSE);
2383         } else if ((op = string_for_opt(opts, negated)) != 0) {
2384 #ifdef WIN32
2385             (void) strncpy(iflags.altkeyhandler, op, MAX_ALTKEYHANDLER - 5);
2386             load_keyboard_handler();
2387 #endif
2388         }
2389         return;
2390     }
2391
2392     /* WINCAP
2393      * align_status:[left|top|right|bottom] */
2394     fullname = "align_status";
2395     if (match_optname(opts, fullname, sizeof("align_status") - 1, TRUE)) {
2396         op = string_for_opt(opts, negated);
2397         if (op && !negated) {
2398             if (!strncmpi(op, "left", sizeof("left") - 1))
2399                 iflags.wc_align_status = ALIGN_LEFT;
2400             else if (!strncmpi(op, "top", sizeof("top") - 1))
2401                 iflags.wc_align_status = ALIGN_TOP;
2402             else if (!strncmpi(op, "right", sizeof("right") - 1))
2403                 iflags.wc_align_status = ALIGN_RIGHT;
2404             else if (!strncmpi(op, "bottom", sizeof("bottom") - 1))
2405                 iflags.wc_align_status = ALIGN_BOTTOM;
2406             else
2407                 badoption(opts);
2408         } else if (negated)
2409             bad_negation(fullname, TRUE);
2410         return;
2411     }
2412     /* WINCAP
2413      * align_message:[left|top|right|bottom] */
2414     fullname = "align_message";
2415     if (match_optname(opts, fullname, sizeof("align_message") - 1, TRUE)) {
2416         if (duplicate)
2417             complain_about_duplicate(opts, 1);
2418         op = string_for_opt(opts, negated);
2419         if (op && !negated) {
2420             if (!strncmpi(op, "left", sizeof("left") - 1))
2421                 iflags.wc_align_message = ALIGN_LEFT;
2422             else if (!strncmpi(op, "top", sizeof("top") - 1))
2423                 iflags.wc_align_message = ALIGN_TOP;
2424             else if (!strncmpi(op, "right", sizeof("right") - 1))
2425                 iflags.wc_align_message = ALIGN_RIGHT;
2426             else if (!strncmpi(op, "bottom", sizeof("bottom") - 1))
2427                 iflags.wc_align_message = ALIGN_BOTTOM;
2428             else
2429                 badoption(opts);
2430         } else if (negated)
2431             bad_negation(fullname, TRUE);
2432         return;
2433     }
2434     /* the order to list the pack */
2435     fullname = "packorder";
2436     if (match_optname(opts, fullname, 4, TRUE)) {
2437         if (duplicate)
2438             complain_about_duplicate(opts, 1);
2439         if (negated) {
2440             bad_negation(fullname, FALSE);
2441             return;
2442         } else if (!(op = string_for_opt(opts, FALSE)))
2443             return;
2444
2445         if (!change_inv_order(op))
2446             badoption(opts);
2447         return;
2448     }
2449
2450     /* user can change required response for some prompts (quit, die, hit),
2451        or add an extra prompt (pray, Remove) that isn't ordinarily there */
2452     fullname = "paranoid_confirmation";
2453     if (match_optname(opts, fullname, 8, TRUE)) {
2454         /* at present we don't complain about duplicates for this
2455            option, but we do throw away the old settings whenever
2456            we process a new one [clearing old flags is essential
2457            for handling default paranoid_confirm:pray sanely] */
2458         flags.paranoia_bits = 0; /* clear all */
2459         if (negated) {
2460             flags.paranoia_bits = 0; /* [now redundant...] */
2461         } else if ((op = string_for_opt(opts, TRUE)) != 0) {
2462             char *pp, buf[BUFSZ];
2463
2464             op = mungspaces(strcpy(buf, op));
2465             for (;;) {
2466                 /* We're looking to parse
2467                    "paranoid_confirm:whichone wheretwo whothree"
2468                    and "paranoid_confirm:" prefix has already
2469                    been stripped off by the time we get here */
2470                 pp = index(op, ' ');
2471                 if (pp)
2472                     *pp = '\0';
2473                 /* we aren't matching option names but match_optname
2474                    does what we want once we've broken the space
2475                    delimited aggregate into separate tokens */
2476                 for (i = 0; i < SIZE(paranoia); ++i) {
2477                     if (match_optname(op, paranoia[i].argname,
2478                                       paranoia[i].argMinLen, FALSE)
2479                         || (paranoia[i].synonym
2480                             && match_optname(op, paranoia[i].synonym,
2481                                              paranoia[i].synMinLen, FALSE))) {
2482                         if (paranoia[i].flagmask)
2483                             flags.paranoia_bits |= paranoia[i].flagmask;
2484                         else /* 0 == "none", so clear all */
2485                             flags.paranoia_bits = 0;
2486                         break;
2487                     }
2488                 }
2489                 if (i == SIZE(paranoia)) {
2490                     /* didn't match anything, so arg is bad;
2491                        any flags already set will stay set */
2492                     badoption(opts);
2493                     break;
2494                 }
2495                 /* move on to next token */
2496                 if (pp)
2497                     op = pp + 1;
2498                 else
2499                     break; /* no next token */
2500             }              /* for(;;) */
2501         }
2502         return;
2503     }
2504
2505     /* accept deprecated boolean; superseded by paranoid_confirm:pray */
2506     fullname = "prayconfirm";
2507     if (match_optname(opts, fullname, 4, FALSE)) {
2508         if (negated)
2509             flags.paranoia_bits &= ~PARANOID_PRAY;
2510         else
2511             flags.paranoia_bits |= PARANOID_PRAY;
2512         return;
2513     }
2514
2515     /* maximum burden picked up before prompt (Warren Cheung) */
2516     fullname = "pickup_burden";
2517     if (match_optname(opts, fullname, 8, TRUE)) {
2518         if (duplicate)
2519             complain_about_duplicate(opts, 1);
2520         if (negated) {
2521             bad_negation(fullname, FALSE);
2522             return;
2523         } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
2524             switch (tolower(*op)) {
2525             /* Unencumbered */
2526             case 'u':
2527                 flags.pickup_burden = UNENCUMBERED;
2528                 break;
2529             /* Burdened (slight encumbrance) */
2530             case 'b':
2531                 flags.pickup_burden = SLT_ENCUMBER;
2532                 break;
2533             /* streSsed (moderate encumbrance) */
2534             case 's':
2535                 flags.pickup_burden = MOD_ENCUMBER;
2536                 break;
2537             /* straiNed (heavy encumbrance) */
2538             case 'n':
2539                 flags.pickup_burden = HVY_ENCUMBER;
2540                 break;
2541             /* OverTaxed (extreme encumbrance) */
2542             case 'o':
2543             case 't':
2544                 flags.pickup_burden = EXT_ENCUMBER;
2545                 break;
2546             /* overLoaded */
2547             case 'l':
2548                 flags.pickup_burden = OVERLOADED;
2549                 break;
2550             default:
2551                 badoption(opts);
2552             }
2553         }
2554         return;
2555     }
2556
2557     /* types of objects to pick up automatically */
2558     if (match_optname(opts, "pickup_types", 8, TRUE)) {
2559         char ocl[MAXOCLASSES + 1], tbuf[MAXOCLASSES + 1], qbuf[QBUFSZ],
2560             abuf[BUFSZ];
2561         int oc_sym;
2562         boolean badopt = FALSE, compat = (strlen(opts) <= 6), use_menu;
2563
2564         if (duplicate)
2565             complain_about_duplicate(opts, 1);
2566         oc_to_str(flags.pickup_types, tbuf);
2567         flags.pickup_types[0] = '\0'; /* all */
2568         op = string_for_opt(opts, (compat || !initial));
2569         if (!op) {
2570             if (compat || negated || initial) {
2571                 /* for backwards compatibility, "pickup" without a
2572                    value is a synonym for autopickup of all types
2573                    (and during initialization, we can't prompt yet) */
2574                 flags.pickup = !negated;
2575                 return;
2576             }
2577             oc_to_str(flags.inv_order, ocl);
2578             use_menu = TRUE;
2579             if (flags.menu_style == MENU_TRADITIONAL
2580                 || flags.menu_style == MENU_COMBINATION) {
2581                 use_menu = FALSE;
2582                 Sprintf(qbuf, "New pickup_types: [%s am] (%s)", ocl,
2583                         *tbuf ? tbuf : "all");
2584                 getlin(qbuf, abuf);
2585                 op = mungspaces(abuf);
2586                 if (abuf[0] == '\0' || abuf[0] == '\033')
2587                     op = tbuf; /* restore */
2588                 else if (abuf[0] == 'm')
2589                     use_menu = TRUE;
2590             }
2591             if (use_menu) {
2592                 (void) choose_classes_menu("Auto-Pickup what?", 1, TRUE, ocl,
2593                                            tbuf);
2594                 op = tbuf;
2595             }
2596         }
2597         if (negated) {
2598             bad_negation("pickup_types", TRUE);
2599             return;
2600         }
2601         while (*op == ' ')
2602             op++;
2603         if (*op != 'a' && *op != 'A') {
2604             num = 0;
2605             while (*op) {
2606                 oc_sym = def_char_to_objclass(*op);
2607                 /* make sure all are valid obj symbols occurring once */
2608                 if (oc_sym != MAXOCLASSES
2609                     && !index(flags.pickup_types, oc_sym)) {
2610                     flags.pickup_types[num] = (char) oc_sym;
2611                     flags.pickup_types[++num] = '\0';
2612                 } else
2613                     badopt = TRUE;
2614                 op++;
2615             }
2616             if (badopt)
2617                 badoption(opts);
2618         }
2619         return;
2620     }
2621
2622     /* pile limit: when walking over objects, number which triggers
2623        "there are several/many objects here" instead of listing them */
2624     fullname = "pile_limit";
2625     if (match_optname(opts, fullname, 4, TRUE)) {
2626         if (duplicate)
2627             complain_about_duplicate(opts, 1);
2628         op = string_for_opt(opts, negated);
2629         if ((negated && !op) || (!negated && op))
2630             flags.pile_limit = negated ? 0 : atoi(op);
2631         else if (negated)
2632             bad_negation(fullname, TRUE);
2633         else /* !op */
2634             flags.pile_limit = PILE_LIMIT_DFLT;
2635         /* sanity check */
2636         if (flags.pile_limit < 0)
2637             flags.pile_limit = PILE_LIMIT_DFLT;
2638         return;
2639     }
2640
2641     /* play mode: normal, explore/discovery, or debug/wizard */
2642     fullname = "playmode";
2643     if (match_optname(opts, fullname, 4, TRUE)) {
2644         if (duplicate)
2645             complain_about_duplicate(opts, 1);
2646         if (negated)
2647             bad_negation(fullname, FALSE);
2648         if (duplicate || negated)
2649             return;
2650         op = string_for_opt(opts, TRUE);
2651         if (!strncmpi(op, "normal", 6) || !strcmpi(op, "play")) {
2652             wizard = discover = FALSE;
2653         } else if (!strncmpi(op, "explore", 6)
2654                    || !strncmpi(op, "discovery", 6)) {
2655             wizard = FALSE, discover = TRUE;
2656         } else if (!strncmpi(op, "debug", 5) || !strncmpi(op, "wizard", 6)) {
2657             wizard = TRUE, discover = FALSE;
2658         } else {
2659             raw_printf("Invalid value for \"%s\":%s.", fullname, op);
2660         }
2661         return;
2662     }
2663
2664     /* WINCAP
2665      * player_selection: dialog | prompts */
2666     fullname = "player_selection";
2667     if (match_optname(opts, fullname, sizeof("player_selection") - 1, TRUE)) {
2668         if (duplicate)
2669             complain_about_duplicate(opts, 1);
2670         op = string_for_opt(opts, negated);
2671         if (op && !negated) {
2672             if (!strncmpi(op, "dialog", sizeof("dialog") - 1))
2673                 iflags.wc_player_selection = VIA_DIALOG;
2674             else if (!strncmpi(op, "prompt", sizeof("prompt") - 1))
2675                 iflags.wc_player_selection = VIA_PROMPTS;
2676             else
2677                 badoption(opts);
2678         } else if (negated)
2679             bad_negation(fullname, TRUE);
2680         return;
2681     }
2682
2683     /* things to disclose at end of game */
2684     if (match_optname(opts, "disclose", 7, TRUE)) {
2685         /*
2686          * The order that the end_disclose options are stored:
2687          *      inventory, attribs, vanquished, genocided,
2688          *      conduct, overview.
2689          * There is an array in flags:
2690          *      end_disclose[NUM_DISCLOSURE_OPT];
2691          * with option settings for the each of the following:
2692          * iagvc [see disclosure_options in decl.c]:
2693          * Legal setting values in that array are:
2694          *      DISCLOSE_PROMPT_DEFAULT_YES  ask with default answer yes
2695          *      DISCLOSE_PROMPT_DEFAULT_NO   ask with default answer no
2696          *      DISCLOSE_YES_WITHOUT_PROMPT  always disclose and don't ask
2697          *      DISCLOSE_NO_WITHOUT_PROMPT   never disclose and don't ask
2698          *
2699          * Those setting values can be used in the option
2700          * string as a prefix to get the desired behaviour.
2701          *
2702          * For backward compatibility, no prefix is required,
2703          * and the presence of a i,a,g,v, or c without a prefix
2704          * sets the corresponding value to DISCLOSE_YES_WITHOUT_PROMPT.
2705          */
2706         boolean badopt = FALSE;
2707         int idx, prefix_val;
2708
2709         if (duplicate)
2710             complain_about_duplicate(opts, 1);
2711         op = string_for_opt(opts, TRUE);
2712         if (op && negated) {
2713             bad_negation("disclose", TRUE);
2714             return;
2715         }
2716         /* "disclose" without a value means "all with prompting"
2717            and negated means "none without prompting" */
2718         if (!op || !strcmpi(op, "all") || !strcmpi(op, "none")) {
2719             if (op && !strcmpi(op, "none"))
2720                 negated = TRUE;
2721             for (num = 0; num < NUM_DISCLOSURE_OPTIONS; num++)
2722                 flags.end_disclose[num] = negated
2723                                               ? DISCLOSE_NO_WITHOUT_PROMPT
2724                                               : DISCLOSE_PROMPT_DEFAULT_YES;
2725             return;
2726         }
2727
2728         num = 0;
2729         prefix_val = -1;
2730         while (*op && num < sizeof flags.end_disclose - 1) {
2731             static char valid_settings[] = {
2732                 DISCLOSE_PROMPT_DEFAULT_YES, DISCLOSE_PROMPT_DEFAULT_NO,
2733                 DISCLOSE_YES_WITHOUT_PROMPT, DISCLOSE_NO_WITHOUT_PROMPT, '\0'
2734             };
2735             register char c, *dop;
2736
2737             c = lowc(*op);
2738             if (c == 'k')
2739                 c = 'v'; /* killed -> vanquished */
2740             if (c == 'd')
2741                 c = 'o'; /* dungeon -> overview */
2742             dop = index(disclosure_options, c);
2743             if (dop) {
2744                 idx = (int) (dop - disclosure_options);
2745                 if (idx < 0 || idx > NUM_DISCLOSURE_OPTIONS - 1) {
2746                     impossible("bad disclosure index %d %c", idx, c);
2747                     continue;
2748                 }
2749                 if (prefix_val != -1) {
2750                     flags.end_disclose[idx] = prefix_val;
2751                     prefix_val = -1;
2752                 } else
2753                     flags.end_disclose[idx] = DISCLOSE_YES_WITHOUT_PROMPT;
2754             } else if (index(valid_settings, c)) {
2755                 prefix_val = c;
2756             } else if (c == ' ') {
2757                 ; /* do nothing */
2758             } else
2759                 badopt = TRUE;
2760             op++;
2761         }
2762         if (badopt)
2763             badoption(opts);
2764         return;
2765     }
2766
2767     /* scores:5t[op] 5a[round] o[wn] */
2768     if (match_optname(opts, "scores", 4, TRUE)) {
2769         if (duplicate)
2770             complain_about_duplicate(opts, 1);
2771         if (negated) {
2772             bad_negation("scores", FALSE);
2773             return;
2774         }
2775         if (!(op = string_for_opt(opts, FALSE)))
2776             return;
2777
2778         while (*op) {
2779             int inum = 1;
2780
2781             if (digit(*op)) {
2782                 inum = atoi(op);
2783                 while (digit(*op))
2784                     op++;
2785             } else if (*op == '!') {
2786                 negated = !negated;
2787                 op++;
2788             }
2789             while (*op == ' ')
2790                 op++;
2791
2792             switch (*op) {
2793             case 't':
2794             case 'T':
2795                 flags.end_top = inum;
2796                 break;
2797             case 'a':
2798             case 'A':
2799                 flags.end_around = inum;
2800                 break;
2801             case 'o':
2802             case 'O':
2803                 flags.end_own = !negated;
2804                 break;
2805             default:
2806                 badoption(opts);
2807                 return;
2808             }
2809             while (letter(*++op) || *op == ' ')
2810                 continue;
2811             if (*op == '/')
2812                 op++;
2813         }
2814         return;
2815     }
2816
2817     fullname = "sortloot";
2818     if (match_optname(opts, fullname, 4, TRUE)) {
2819         op = string_for_env_opt(fullname, opts, FALSE);
2820         if (op) {
2821             switch (tolower(*op)) {
2822             case 'n':
2823             case 'l':
2824             case 'f':
2825                 flags.sortloot = tolower(*op);
2826                 break;
2827             default:
2828                 badoption(opts);
2829                 return;
2830             }
2831         }
2832         return;
2833     }
2834
2835     fullname = "suppress_alert";
2836     if (match_optname(opts, fullname, 4, TRUE)) {
2837         if (duplicate)
2838             complain_about_duplicate(opts, 1);
2839         op = string_for_opt(opts, negated);
2840         if (negated)
2841             bad_negation(fullname, FALSE);
2842         else if (op)
2843             (void) feature_alert_opts(op, fullname);
2844         return;
2845     }
2846
2847 #ifdef VIDEOSHADES
2848     /* videocolors:string */
2849     fullname = "videocolors";
2850     if (match_optname(opts, fullname, 6, TRUE)
2851         || match_optname(opts, "videocolours", 10, TRUE)) {
2852         if (duplicate)
2853             complain_about_duplicate(opts, 1);
2854         if (negated) {
2855             bad_negation(fullname, FALSE);
2856             return;
2857         } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
2858             return;
2859         }
2860         if (!assign_videocolors(opts))
2861             badoption(opts);
2862         return;
2863     }
2864     /* videoshades:string */
2865     fullname = "videoshades";
2866     if (match_optname(opts, fullname, 6, TRUE)) {
2867         if (duplicate)
2868             complain_about_duplicate(opts, 1);
2869         if (negated) {
2870             bad_negation(fullname, FALSE);
2871             return;
2872         } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
2873             return;
2874         }
2875         if (!assign_videoshades(opts))
2876             badoption(opts);
2877         return;
2878     }
2879 #endif /* VIDEOSHADES */
2880 #ifdef MSDOS
2881 #ifdef NO_TERMS
2882     /* video:string -- must be after longer tests */
2883     fullname = "video";
2884     if (match_optname(opts, fullname, 5, TRUE)) {
2885         if (duplicate)
2886             complain_about_duplicate(opts, 1);
2887         if (negated) {
2888             bad_negation(fullname, FALSE);
2889             return;
2890         } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
2891             return;
2892         }
2893         if (!assign_video(opts))
2894             badoption(opts);
2895         return;
2896     }
2897 #endif /* NO_TERMS */
2898     /* soundcard:string -- careful not to match boolean 'sound' */
2899     fullname = "soundcard";
2900     if (match_optname(opts, fullname, 6, TRUE)) {
2901         if (duplicate)
2902             complain_about_duplicate(opts, 1);
2903         if (negated) {
2904             bad_negation(fullname, FALSE);
2905             return;
2906         } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
2907             return;
2908         }
2909         if (!assign_soundcard(opts))
2910             badoption(opts);
2911         return;
2912     }
2913 #endif /* MSDOS */
2914
2915     /* WINCAP
2916      *
2917      *  map_mode:[tiles|ascii4x6|ascii6x8|ascii8x8|ascii16x8|ascii7x12|
2918      *            ascii8x12|ascii16x12|ascii12x16|ascii10x18|fit_to_screen]
2919      */
2920     fullname = "map_mode";
2921     if (match_optname(opts, fullname, sizeof("map_mode") - 1, TRUE)) {
2922         if (duplicate)
2923             complain_about_duplicate(opts, 1);
2924         op = string_for_opt(opts, negated);
2925         if (op && !negated) {
2926             if (!strncmpi(op, "tiles", sizeof("tiles") - 1))
2927                 iflags.wc_map_mode = MAP_MODE_TILES;
2928             else if (!strncmpi(op, "ascii4x6", sizeof("ascii4x6") - 1))
2929                 iflags.wc_map_mode = MAP_MODE_ASCII4x6;
2930             else if (!strncmpi(op, "ascii6x8", sizeof("ascii6x8") - 1))
2931                 iflags.wc_map_mode = MAP_MODE_ASCII6x8;
2932             else if (!strncmpi(op, "ascii8x8", sizeof("ascii8x8") - 1))
2933                 iflags.wc_map_mode = MAP_MODE_ASCII8x8;
2934             else if (!strncmpi(op, "ascii16x8", sizeof("ascii16x8") - 1))
2935                 iflags.wc_map_mode = MAP_MODE_ASCII16x8;
2936             else if (!strncmpi(op, "ascii7x12", sizeof("ascii7x12") - 1))
2937                 iflags.wc_map_mode = MAP_MODE_ASCII7x12;
2938             else if (!strncmpi(op, "ascii8x12", sizeof("ascii8x12") - 1))
2939                 iflags.wc_map_mode = MAP_MODE_ASCII8x12;
2940             else if (!strncmpi(op, "ascii16x12", sizeof("ascii16x12") - 1))
2941                 iflags.wc_map_mode = MAP_MODE_ASCII16x12;
2942             else if (!strncmpi(op, "ascii12x16", sizeof("ascii12x16") - 1))
2943                 iflags.wc_map_mode = MAP_MODE_ASCII12x16;
2944             else if (!strncmpi(op, "ascii10x18", sizeof("ascii10x18") - 1))
2945                 iflags.wc_map_mode = MAP_MODE_ASCII10x18;
2946             else if (!strncmpi(op, "fit_to_screen",
2947                                sizeof("fit_to_screen") - 1))
2948                 iflags.wc_map_mode = MAP_MODE_ASCII_FIT_TO_SCREEN;
2949             else
2950                 badoption(opts);
2951         } else if (negated)
2952             bad_negation(fullname, TRUE);
2953         return;
2954     }
2955     /* WINCAP
2956      * scroll_amount:nn */
2957     fullname = "scroll_amount";
2958     if (match_optname(opts, fullname, sizeof("scroll_amount") - 1, TRUE)) {
2959         if (duplicate)
2960             complain_about_duplicate(opts, 1);
2961         op = string_for_opt(opts, negated);
2962         if ((negated && !op) || (!negated && op)) {
2963             iflags.wc_scroll_amount = negated ? 1 : atoi(op);
2964         } else if (negated)
2965             bad_negation(fullname, TRUE);
2966         return;
2967     }
2968     /* WINCAP
2969      * scroll_margin:nn */
2970     fullname = "scroll_margin";
2971     if (match_optname(opts, fullname, sizeof("scroll_margin") - 1, TRUE)) {
2972         if (duplicate)
2973             complain_about_duplicate(opts, 1);
2974         op = string_for_opt(opts, negated);
2975         if ((negated && !op) || (!negated && op)) {
2976             iflags.wc_scroll_margin = negated ? 5 : atoi(op);
2977         } else if (negated)
2978             bad_negation(fullname, TRUE);
2979         return;
2980     }
2981     fullname = "subkeyvalue";
2982     if (match_optname(opts, fullname, 5, TRUE)) {
2983         /* no duplicate complaint here */
2984         if (negated) {
2985             bad_negation(fullname, FALSE);
2986         } else {
2987 #if defined(WIN32)
2988             op = string_for_opt(opts, 0);
2989             map_subkeyvalue(op);
2990 #endif
2991         }
2992         return;
2993     }
2994     /* WINCAP
2995      * tile_width:nn */
2996     fullname = "tile_width";
2997     if (match_optname(opts, fullname, sizeof("tile_width") - 1, TRUE)) {
2998         if (duplicate)
2999             complain_about_duplicate(opts, 1);
3000         op = string_for_opt(opts, negated);
3001         if ((negated && !op) || (!negated && op)) {
3002             iflags.wc_tile_width = negated ? 0 : atoi(op);
3003         } else if (negated)
3004             bad_negation(fullname, TRUE);
3005         return;
3006     }
3007     /* WINCAP
3008      * tile_file:name */
3009     fullname = "tile_file";
3010     if (match_optname(opts, fullname, sizeof("tile_file") - 1, TRUE)) {
3011         if (duplicate)
3012             complain_about_duplicate(opts, 1);
3013         if ((op = string_for_opt(opts, FALSE)) != 0) {
3014             if (iflags.wc_tile_file)
3015                 free(iflags.wc_tile_file);
3016             iflags.wc_tile_file = (char *) alloc(strlen(op) + 1);
3017             Strcpy(iflags.wc_tile_file, op);
3018         }
3019         return;
3020     }
3021     /* WINCAP
3022      * tile_height:nn */
3023     fullname = "tile_height";
3024     if (match_optname(opts, fullname, sizeof("tile_height") - 1, TRUE)) {
3025         if (duplicate)
3026             complain_about_duplicate(opts, 1);
3027         op = string_for_opt(opts, negated);
3028         if ((negated && !op) || (!negated && op)) {
3029             iflags.wc_tile_height = negated ? 0 : atoi(op);
3030         } else if (negated)
3031             bad_negation(fullname, TRUE);
3032         return;
3033     }
3034     /* WINCAP
3035      * vary_msgcount:nn */
3036     fullname = "vary_msgcount";
3037     if (match_optname(opts, fullname, sizeof("vary_msgcount") - 1, TRUE)) {
3038         if (duplicate)
3039             complain_about_duplicate(opts, 1);
3040         op = string_for_opt(opts, negated);
3041         if ((negated && !op) || (!negated && op)) {
3042             iflags.wc_vary_msgcount = negated ? 0 : atoi(op);
3043         } else if (negated)
3044             bad_negation(fullname, TRUE);
3045         return;
3046     }
3047     fullname = "windowtype";
3048     if (match_optname(opts, fullname, 3, TRUE)) {
3049         if (duplicate)
3050             complain_about_duplicate(opts, 1);
3051         if (negated) {
3052             bad_negation(fullname, FALSE);
3053             return;
3054         } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
3055             char buf[WINTYPELEN];
3056             nmcpy(buf, op, WINTYPELEN);
3057             choose_windows(buf);
3058         }
3059         return;
3060     }
3061 #ifdef WINCHAIN
3062     fullname = "windowchain";
3063     if (match_optname(opts, fullname, 3, TRUE)) {
3064         if (negated) {
3065             bad_negation(fullname, FALSE);
3066             return;
3067         } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
3068             char buf[WINTYPELEN];
3069             nmcpy(buf, op, WINTYPELEN);
3070             addto_windowchain(buf);
3071         }
3072         return;
3073     }
3074 #endif
3075
3076     /* WINCAP
3077      * setting window colors
3078      * syntax: windowcolors=menu foregrnd/backgrnd text foregrnd/backgrnd
3079      */
3080     fullname = "windowcolors";
3081     if (match_optname(opts, fullname, 7, TRUE)) {
3082         if (duplicate)
3083             complain_about_duplicate(opts, 1);
3084         if ((op = string_for_opt(opts, FALSE)) != 0) {
3085             if (!wc_set_window_colors(op))
3086                 badoption(opts);
3087         } else if (negated)
3088             bad_negation(fullname, TRUE);
3089         return;
3090     }
3091
3092     /* menustyle:traditional or combination or full or partial */
3093     if (match_optname(opts, "menustyle", 4, TRUE)) {
3094         int tmp;
3095         boolean val_required = (strlen(opts) > 5 && !negated);
3096
3097         if (duplicate)
3098             complain_about_duplicate(opts, 1);
3099         if (!(op = string_for_opt(opts, !val_required))) {
3100             if (val_required)
3101                 return; /* string_for_opt gave feedback */
3102             tmp = negated ? 'n' : 'f';
3103         } else {
3104             tmp = tolower(*op);
3105         }
3106         switch (tmp) {
3107         case 'n': /* none */
3108         case 't': /* traditional */
3109             flags.menu_style = MENU_TRADITIONAL;
3110             break;
3111         case 'c': /* combo: trad.class sel+menu */
3112             flags.menu_style = MENU_COMBINATION;
3113             break;
3114         case 'p': /* partial: no class menu */
3115             flags.menu_style = MENU_PARTIAL;
3116             break;
3117         case 'f': /* full: class menu + menu */
3118             flags.menu_style = MENU_FULL;
3119             break;
3120         default:
3121             badoption(opts);
3122         }
3123         return;
3124     }
3125
3126     fullname = "menu_headings";
3127     if (match_optname(opts, fullname, 12, TRUE)) {
3128         if (duplicate)
3129             complain_about_duplicate(opts, 1);
3130         if (negated) {
3131             bad_negation(fullname, FALSE);
3132             return;
3133         } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
3134             return;
3135         }
3136         for (i = 0; i < SIZE(attrnames); i++)
3137             if (!strcmpi(opts, attrnames[i].name)) {
3138                 iflags.menu_headings = attrnames[i].attr;
3139                 return;
3140             }
3141         badoption(opts);
3142         return;
3143     }
3144
3145     /* check for menu command mapping */
3146     for (i = 0; i < NUM_MENU_CMDS; i++) {
3147         fullname = default_menu_cmd_info[i].name;
3148         if (duplicate)
3149             complain_about_duplicate(opts, 1);
3150         if (match_optname(opts, fullname, (int) strlen(fullname), TRUE)) {
3151             if (negated) {
3152                 bad_negation(fullname, FALSE);
3153             } else if ((op = string_for_opt(opts, FALSE)) != 0) {
3154                 int j;
3155                 char c, op_buf[BUFSZ];
3156                 boolean isbad = FALSE;
3157
3158                 escapes(op, op_buf);
3159                 c = *op_buf;
3160
3161                 if (c == 0 || c == '\r' || c == '\n' || c == '\033'
3162                     || c == ' ' || digit(c) || (letter(c) && c != '@'))
3163                     isbad = TRUE;
3164                 else /* reject default object class symbols */
3165                     for (j = 1; j < MAXOCLASSES; j++)
3166                         if (c == def_oc_syms[i].sym) {
3167                             isbad = TRUE;
3168                             break;
3169                         }
3170
3171                 if (isbad)
3172                     badoption(opts);
3173                 else
3174                     add_menu_cmd_alias(c, default_menu_cmd_info[i].cmd);
3175             }
3176             return;
3177         }
3178     }
3179 #if defined(STATUS_VIA_WINDOWPORT) && defined(STATUS_HILITES)
3180     /* hilite fields in status prompt */
3181     if (match_optname(opts, "hilite_status", 13, TRUE)) {
3182         if (duplicate)
3183             complain_about_duplicate(opts, 1);
3184         op = string_for_opt(opts, TRUE);
3185         if (op && negated) {
3186             clear_status_hilites(tfrom_file);
3187             return;
3188         } else if (!op) {
3189             /* a value is mandatory */
3190             badoption(opts);
3191             return;
3192         }
3193         if (!set_status_hilites(op, tfrom_file))
3194             badoption(opts);
3195         return;
3196     }
3197 #endif
3198
3199 #if defined(BACKWARD_COMPAT)
3200     fullname = "DECgraphics";
3201     if (match_optname(opts, fullname, 3, TRUE)) {
3202         boolean badflag = FALSE;
3203
3204         if (duplicate)
3205             complain_about_duplicate(opts, 1);
3206         if (!negated) {
3207             /* There is no rogue level DECgraphics-specific set */
3208             if (symset[PRIMARY].name) {
3209                 badflag = TRUE;
3210             } else {
3211                 symset[PRIMARY].name = dupstr(fullname);
3212                 if (!read_sym_file(PRIMARY)) {
3213                     badflag = TRUE;
3214                     clear_symsetentry(PRIMARY, TRUE);
3215                 } else
3216                     switch_symbols(TRUE);
3217             }
3218             if (badflag) {
3219                 pline("Failure to load symbol set %s.", fullname);
3220                 wait_synch();
3221             }
3222         }
3223         return;
3224     }
3225     fullname = "IBMgraphics";
3226     if (match_optname(opts, fullname, 3, TRUE)) {
3227         const char *sym_name = fullname;
3228         boolean badflag = FALSE;
3229
3230         if (duplicate)
3231             complain_about_duplicate(opts, 1);
3232         if (!negated) {
3233             for (i = 0; i < NUM_GRAPHICS; ++i) {
3234                 if (symset[i].name) {
3235                     badflag = TRUE;
3236                 } else {
3237                     if (i == ROGUESET)
3238                         sym_name = "RogueIBM";
3239                     symset[i].name = dupstr(sym_name);
3240                     if (!read_sym_file(i)) {
3241                         badflag = TRUE;
3242                         clear_symsetentry(i, TRUE);
3243                         break;
3244                     }
3245                 }
3246             }
3247             if (badflag) {
3248                 pline("Failure to load symbol set %s.", sym_name);
3249                 wait_synch();
3250             } else {
3251                 switch_symbols(TRUE);
3252                 if (!initial && Is_rogue_level(&u.uz))
3253                     assign_graphics(ROGUESET);
3254             }
3255         }
3256         return;
3257     }
3258 #endif
3259 #ifdef MAC_GRAPHICS_ENV
3260     fullname = "MACgraphics";
3261     if (match_optname(opts, fullname, 3, TRUE)) {
3262         boolean badflag = FALSE;
3263
3264         if (duplicate)
3265             complain_about_duplicate(opts, 1);
3266         if (!negated) {
3267             if (symset[PRIMARY].name) {
3268                 badflag = TRUE;
3269             } else {
3270                 symset[PRIMARY].name = dupstr(fullname);
3271                 if (!read_sym_file(PRIMARY)) {
3272                     badflag = TRUE;
3273                     clear_symsetentry(PRIMARY, TRUE);
3274                 }
3275             }
3276             if (badflag) {
3277                 pline("Failure to load symbol set %s.", fullname);
3278                 wait_synch();
3279             } else {
3280                 switch_symbols(TRUE);
3281                 if (!initial && Is_rogue_level(&u.uz))
3282                     assign_graphics(ROGUESET);
3283             }
3284         }
3285         return;
3286     }
3287 #endif
3288
3289     /* OK, if we still haven't recognized the option, check the boolean
3290      * options list
3291      */
3292     for (i = 0; boolopt[i].name; i++) {
3293         if (match_optname(opts, boolopt[i].name, 3, FALSE)) {
3294             /* options that don't exist */
3295             if (!boolopt[i].addr) {
3296                 if (!initial && !negated)
3297                     pline_The("\"%s\" option is not available.",
3298                               boolopt[i].name);
3299                 return;
3300             }
3301             /* options that must come from config file */
3302             if (!initial && (boolopt[i].optflags == SET_IN_FILE)) {
3303                 rejectoption(boolopt[i].name);
3304                 return;
3305             }
3306
3307             *(boolopt[i].addr) = !negated;
3308
3309             /* 0 means boolean opts */
3310             if (duplicate_opt_detection(boolopt[i].name, 0))
3311                 complain_about_duplicate(boolopt[i].name, 0);
3312
3313 #ifdef RLECOMP
3314             if ((boolopt[i].addr) == &iflags.rlecomp) {
3315                 if (*boolopt[i].addr)
3316                     set_savepref("rlecomp");
3317                 else
3318                     set_savepref("!rlecomp");
3319             }
3320 #endif
3321 #ifdef ZEROCOMP
3322             if ((boolopt[i].addr) == &iflags.zerocomp) {
3323                 if (*boolopt[i].addr)
3324                     set_savepref("zerocomp");
3325                 else
3326                     set_savepref("externalcomp");
3327             }
3328 #endif
3329             /* only do processing below if setting with doset() */
3330             if (initial)
3331                 return;
3332
3333             if ((boolopt[i].addr) == &flags.time
3334                 || (boolopt[i].addr) == &flags.showexp
3335 #ifdef SCORE_ON_BOTL
3336                 || (boolopt[i].addr) == &flags.showscore
3337 #endif
3338                 ) {
3339 #ifdef STATUS_VIA_WINDOWPORT
3340                 status_initialize(REASSESS_ONLY);
3341 #endif
3342                 context.botl = TRUE;
3343             } else if ((boolopt[i].addr) == &flags.invlet_constant) {
3344                 if (flags.invlet_constant)
3345                     reassign();
3346             } else if (((boolopt[i].addr) == &flags.lit_corridor)
3347                        || ((boolopt[i].addr) == &flags.dark_room)) {
3348                 /*
3349                  * All corridor squares seen via night vision or
3350                  * candles & lamps change.  Update them by calling
3351                  * newsym() on them.  Don't do this if we are
3352                  * initializing the options --- the vision system
3353                  * isn't set up yet.
3354                  */
3355                 vision_recalc(2);       /* shut down vision */
3356                 vision_full_recalc = 1; /* delayed recalc */
3357                 if (iflags.use_color)
3358                     need_redraw = TRUE; /* darkroom refresh */
3359             } else if ((boolopt[i].addr) == &iflags.use_inverse
3360                        || (boolopt[i].addr) == &flags.showrace
3361                        || (boolopt[i].addr) == &iflags.hilite_pet) {
3362                 need_redraw = TRUE;
3363 #ifdef TEXTCOLOR
3364             } else if ((boolopt[i].addr) == &iflags.use_color) {
3365                 need_redraw = TRUE;
3366 #ifdef TOS
3367                 if ((boolopt[i].addr) == &iflags.use_color && iflags.BIOS) {
3368                     if (colors_changed)
3369                         restore_colors();
3370                     else
3371                         set_colors();
3372                 }
3373 #endif
3374 #endif /* TEXTCOLOR */
3375             }
3376             return;
3377         }
3378     }
3379
3380     /* out of valid options */
3381     badoption(opts);
3382 }
3383
3384 static NEARDATA const char *menutype[] = { "traditional", "combination",
3385                                            "full", "partial" };
3386
3387 static NEARDATA const char *burdentype[] = { "unencumbered", "burdened",
3388                                              "stressed",     "strained",
3389                                              "overtaxed",    "overloaded" };
3390
3391 static NEARDATA const char *runmodes[] = { "teleport", "run", "walk",
3392                                            "crawl" };
3393
3394 static NEARDATA const char *sortltype[] = { "none", "loot", "full" };
3395
3396 /*
3397  * Convert the given string of object classes to a string of default object
3398  * symbols.
3399  */
3400 STATIC_OVL void
3401 oc_to_str(src, dest)
3402 char *src, *dest;
3403 {
3404     int i;
3405
3406     while ((i = (int) *src++) != 0) {
3407         if (i < 0 || i >= MAXOCLASSES)
3408             impossible("oc_to_str:  illegal object class %d", i);
3409         else
3410             *dest++ = def_oc_syms[i].sym;
3411     }
3412     *dest = '\0';
3413 }
3414
3415 /*
3416  * Add the given mapping to the menu command map list.  Always keep the
3417  * maps valid C strings.
3418  */
3419 void
3420 add_menu_cmd_alias(from_ch, to_ch)
3421 char from_ch, to_ch;
3422 {
3423     if (n_menu_mapped >= MAX_MENU_MAPPED_CMDS) {
3424         pline("out of menu map space.");
3425     } else {
3426         mapped_menu_cmds[n_menu_mapped] = from_ch;
3427         mapped_menu_op[n_menu_mapped] = to_ch;
3428         n_menu_mapped++;
3429         mapped_menu_cmds[n_menu_mapped] = 0;
3430         mapped_menu_op[n_menu_mapped] = 0;
3431     }
3432 }
3433
3434 /*
3435  * Map the given character to its corresponding menu command.  If it
3436  * doesn't match anything, just return the original.
3437  */
3438 char
3439 map_menu_cmd(ch)
3440 char ch;
3441 {
3442     char *found = index(mapped_menu_cmds, ch);
3443     if (found) {
3444         int idx = (int) (found - mapped_menu_cmds);
3445         ch = mapped_menu_op[idx];
3446     }
3447     return ch;
3448 }
3449
3450 #if defined(MICRO) || defined(MAC) || defined(WIN32)
3451 #define OPTIONS_HEADING "OPTIONS"
3452 #else
3453 #define OPTIONS_HEADING "NETHACKOPTIONS"
3454 #endif
3455
3456 static char fmtstr_doset_add_menu[] = "%s%-15s [%s]   ";
3457 static char fmtstr_doset_add_menu_tab[] = "%s\t[%s]";
3458
3459 STATIC_OVL void
3460 doset_add_menu(win, option, indexoffset)
3461 winid win;          /* window to add to */
3462 const char *option; /* option name */
3463 int indexoffset;    /* value to add to index in compopt[], or zero
3464                        if option cannot be changed */
3465 {
3466     const char *value = "unknown"; /* current value */
3467     char buf[BUFSZ], buf2[BUFSZ];
3468     anything any;
3469     int i;
3470
3471     any = zeroany;
3472     if (indexoffset == 0) {
3473         any.a_int = 0;
3474         value = get_compopt_value(option, buf2);
3475     } else {
3476         for (i = 0; compopt[i].name; i++)
3477             if (strcmp(option, compopt[i].name) == 0)
3478                 break;
3479
3480         if (compopt[i].name) {
3481             any.a_int = i + 1 + indexoffset;
3482             value = get_compopt_value(option, buf2);
3483         } else {
3484             /* We are trying to add an option not found in compopt[].
3485                This is almost certainly bad, but we'll let it through anyway
3486                (with a zero value, so it can't be selected). */
3487             any.a_int = 0;
3488         }
3489     }
3490     /* "    " replaces "a - " -- assumes menus follow that style */
3491     if (!iflags.menu_tab_sep)
3492         Sprintf(buf, fmtstr_doset_add_menu, any.a_int ? "" : "    ", option,
3493                 value);
3494     else
3495         Sprintf(buf, fmtstr_doset_add_menu_tab, option, value);
3496     add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
3497 }
3498
3499 /* Changing options via menu by Per Liboriussen */
3500 int
3501 doset()
3502 {
3503     char buf[BUFSZ], buf2[BUFSZ];
3504     int i = 0, pass, boolcount, pick_cnt, pick_idx, opt_indx;
3505     boolean *bool_p;
3506     winid tmpwin;
3507     anything any;
3508     menu_item *pick_list;
3509     int indexoffset, startpass, endpass;
3510     boolean setinitial = FALSE, fromfile = FALSE;
3511     int biggest_name = 0;
3512     const char *n_currently_set = "(%d currently set)";
3513
3514     tmpwin = create_nhwindow(NHW_MENU);
3515     start_menu(tmpwin);
3516
3517     any = zeroany;
3518     add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
3519              "Booleans (selecting will toggle value):", MENU_UNSELECTED);
3520     any.a_int = 0;
3521     /* first list any other non-modifiable booleans, then modifiable ones */
3522     for (pass = 0; pass <= 1; pass++)
3523         for (i = 0; boolopt[i].name; i++)
3524             if ((bool_p = boolopt[i].addr) != 0
3525                 && ((boolopt[i].optflags == DISP_IN_GAME && pass == 0)
3526                     || (boolopt[i].optflags == SET_IN_GAME && pass == 1))) {
3527                 if (bool_p == &flags.female)
3528                     continue; /* obsolete */
3529                 if (bool_p == &iflags.sanity_check && !wizard)
3530                     continue;
3531                 if (bool_p == &iflags.menu_tab_sep && !wizard)
3532                     continue;
3533                 if (is_wc_option(boolopt[i].name)
3534                     && !wc_supported(boolopt[i].name))
3535                     continue;
3536                 if (is_wc2_option(boolopt[i].name)
3537                     && !wc2_supported(boolopt[i].name))
3538                     continue;
3539                 any.a_int = (pass == 0) ? 0 : i + 1;
3540                 if (!iflags.menu_tab_sep)
3541                     Sprintf(buf, "%s%-17s [%s]", pass == 0 ? "    " : "",
3542                             boolopt[i].name, *bool_p ? "true" : "false");
3543                 else
3544                     Sprintf(buf, "%s\t[%s]", boolopt[i].name,
3545                             *bool_p ? "true" : "false");
3546                 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf,
3547                          MENU_UNSELECTED);
3548             }
3549
3550     boolcount = i;
3551     indexoffset = boolcount;
3552     any = zeroany;
3553     add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
3554     add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
3555              "Compounds (selecting will prompt for new value):",
3556              MENU_UNSELECTED);
3557
3558 #ifdef notyet /* SYSCF */
3559     /* XXX I think this is still fragile.  Fixing initial/from_file and/or
3560      changing
3561      the SET_* etc to bitmaps will let me make this better. */
3562     if (wizard)
3563         startpass = SET_IN_SYS;
3564     else
3565 #endif
3566         startpass = DISP_IN_GAME;
3567     endpass = SET_IN_GAME;
3568
3569     /* spin through the options to find the biggest name
3570        and adjust the format string accordingly if needed */
3571     biggest_name = 0;
3572     for (i = 0; compopt[i].name; i++)
3573         if (compopt[i].optflags >= startpass && compopt[i].optflags <= endpass
3574             && strlen(compopt[i].name) > (unsigned) biggest_name)
3575             biggest_name = (int) strlen(compopt[i].name);
3576     if (biggest_name > 30)
3577         biggest_name = 30;
3578     if (!iflags.menu_tab_sep)
3579         Sprintf(fmtstr_doset_add_menu, "%%s%%-%ds [%%s]", biggest_name);
3580
3581     /* deliberately put `playmode', `name', `role', `race', `gender' first
3582        (also alignment if anything ever comes before it in compopt[]) */
3583     doset_add_menu(tmpwin, "playmode", 0);
3584     doset_add_menu(tmpwin, "name", 0);
3585     doset_add_menu(tmpwin, "role", 0);
3586     doset_add_menu(tmpwin, "race", 0);
3587     doset_add_menu(tmpwin, "gender", 0);
3588
3589     for (pass = startpass; pass <= endpass; pass++)
3590         for (i = 0; compopt[i].name; i++)
3591             if (compopt[i].optflags == pass) {
3592                 if (!strcmp(compopt[i].name, "playmode")
3593                     || !strcmp(compopt[i].name, "name")
3594                     || !strcmp(compopt[i].name, "role")
3595                     || !strcmp(compopt[i].name, "race")
3596                     || !strcmp(compopt[i].name, "gender"))
3597                     continue;
3598                 else if (is_wc_option(compopt[i].name)
3599                          && !wc_supported(compopt[i].name))
3600                     continue;
3601                 else if (is_wc2_option(compopt[i].name)
3602                          && !wc2_supported(compopt[i].name))
3603                     continue;
3604                 else
3605                     doset_add_menu(tmpwin, compopt[i].name,
3606                                    (pass == DISP_IN_GAME) ? 0 : indexoffset);
3607             }
3608     any.a_int = -4;
3609     Sprintf(buf2, n_currently_set, msgtype_count());
3610     Sprintf(buf, fmtstr_doset_add_menu, any.a_int ? "" : "    ",
3611             "message types", buf2);
3612     add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
3613     any.a_int = -3;
3614     Sprintf(buf2, n_currently_set, count_menucolors());
3615     Sprintf(buf, fmtstr_doset_add_menu, any.a_int ? "" : "    ",
3616             "menucolors", buf2);
3617     add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
3618 #ifdef STATUS_VIA_WINDOWPORT
3619 #ifdef STATUS_HILITES
3620     any.a_int = -2;
3621     get_status_hilites(buf2, 60);
3622     if (!*buf2)
3623         Sprintf(buf2, "%s", "(none)");
3624     if (!iflags.menu_tab_sep)
3625         Sprintf(buf, fmtstr_doset_add_menu, any.a_int ? "" : "    ",
3626                 "status_hilites", buf2);
3627     else
3628         Sprintf(buf, fmtstr_doset_add_menu_tab, "status_hilites", buf2);
3629     add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
3630 #endif
3631 #endif
3632     any.a_int = -1;
3633     Sprintf(buf2, n_currently_set, count_ape_maps((int *) 0, (int *) 0));
3634     Sprintf(buf, fmtstr_doset_add_menu, any.a_int ? "" : "    ",
3635             "autopickup exceptions", buf2);
3636     add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
3637 #ifdef PREFIXES_IN_USE
3638     any = zeroany;
3639     add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
3640     add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
3641              "Variable playground locations:", MENU_UNSELECTED);
3642     for (i = 0; i < PREFIX_COUNT; i++)
3643         doset_add_menu(tmpwin, fqn_prefix_names[i], 0);
3644 #endif
3645     end_menu(tmpwin, "Set what options?");
3646     need_redraw = FALSE;
3647     if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &pick_list)) > 0) {
3648         /*
3649          * Walk down the selection list and either invert the booleans
3650          * or prompt for new values. In most cases, call parseoptions()
3651          * to take care of options that require special attention, like
3652          * redraws.
3653          */
3654         for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
3655             opt_indx = pick_list[pick_idx].item.a_int - 1;
3656             if (opt_indx == -2) {
3657                 /* -2 due to -1 offset for select_menu() */
3658                 (void) special_handling("autopickup_exception", setinitial,
3659                                         fromfile);
3660 #ifdef STATUS_VIA_WINDOWPORT
3661 #ifdef STATUS_HILITES
3662             } else if (opt_indx == -3) {
3663                 /* -3 due to -1 offset for select_menu() */
3664                 if (!status_hilite_menu()) {
3665                     pline("Bad status hilite(s) specified.");
3666                 } else {
3667                     if (wc2_supported("status_hilites"))
3668                         preference_update("status_hilites");
3669                 }
3670 #endif
3671 #endif
3672             } else if (opt_indx == -4) {
3673                     (void) special_handling("menucolors", setinitial,
3674                                             fromfile);
3675             } else if (opt_indx == -5) {
3676                     (void) special_handling("msgtype", setinitial, fromfile);
3677             } else if (opt_indx < boolcount) {
3678                 /* boolean option */
3679                 Sprintf(buf, "%s%s", *boolopt[opt_indx].addr ? "!" : "",
3680                         boolopt[opt_indx].name);
3681                 parseoptions(buf, setinitial, fromfile);
3682                 if (wc_supported(boolopt[opt_indx].name)
3683                     || wc2_supported(boolopt[opt_indx].name))
3684                     preference_update(boolopt[opt_indx].name);
3685             } else {
3686                 /* compound option */
3687                 opt_indx -= boolcount;
3688
3689                 if (!special_handling(compopt[opt_indx].name, setinitial,
3690                                       fromfile)) {
3691                     Sprintf(buf, "Set %s to what?", compopt[opt_indx].name);
3692                     getlin(buf, buf2);
3693                     if (buf2[0] == '\033')
3694                         continue;
3695                     Sprintf(buf, "%s:%s", compopt[opt_indx].name, buf2);
3696                     /* pass the buck */
3697                     parseoptions(buf, setinitial, fromfile);
3698                 }
3699                 if (wc_supported(compopt[opt_indx].name)
3700                     || wc2_supported(compopt[opt_indx].name))
3701                     preference_update(compopt[opt_indx].name);
3702             }
3703         }
3704         free((genericptr_t) pick_list);
3705         pick_list = (menu_item *) 0;
3706     }
3707
3708     destroy_nhwindow(tmpwin);
3709     if (need_redraw) {
3710         reglyph_darkroom();
3711         (void) doredraw();
3712     }
3713     return 0;
3714 }
3715
3716 int
3717 handle_add_list_remove(optname, numtotal)
3718 const char *optname;
3719 int numtotal;
3720 {
3721     winid tmpwin;
3722     anything any;
3723     int i, pick_cnt, pick_idx, opt_idx;
3724     menu_item *pick_list = (menu_item *) 0;
3725     static const struct action {
3726         char letr;
3727         const char *desc;
3728     } action_titles[] = {
3729         { 'a', "add new %s" },         /* [0] */
3730         { 'l', "list %s" },            /* [1] */
3731         { 'r', "remove existing %s" }, /* [2] */
3732         { 'x', "exit this menu" },     /* [3] */
3733     };
3734
3735     opt_idx = 0;
3736     tmpwin = create_nhwindow(NHW_MENU);
3737     start_menu(tmpwin);
3738     any = zeroany;
3739     for (i = 0; i < SIZE(action_titles); i++) {
3740         char tmpbuf[BUFSZ];
3741         any.a_int++;
3742         /* omit list and remove if there aren't any yet */
3743         if (!numtotal && (i == 1 || i == 2))
3744             continue;
3745         Sprintf(tmpbuf, action_titles[i].desc,
3746                 (i == 1) ? makeplural(optname) : optname);
3747         add_menu(tmpwin, NO_GLYPH, &any, action_titles[i].letr, 0, ATR_NONE,
3748                  tmpbuf,
3749 #if 0 /* this ought to work but doesn't... */
3750                  (action_titles[i].letr == 'x') ? MENU_SELECTED :
3751 #endif
3752                  MENU_UNSELECTED);
3753     }
3754     end_menu(tmpwin, "Do what?");
3755     if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &pick_list)) > 0) {
3756         for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
3757             opt_idx = pick_list[pick_idx].item.a_int - 1;
3758         }
3759         free((genericptr_t) pick_list);
3760         pick_list = (menu_item *) 0;
3761     }
3762     destroy_nhwindow(tmpwin);
3763
3764     if (pick_cnt < 1)
3765         opt_idx = 3; /* none selected, exit menu */
3766     return opt_idx;
3767 }
3768
3769 struct symsetentry *symset_list = 0; /* files.c will populate this with
3770                                               list of available sets */
3771
3772 STATIC_OVL boolean
3773 special_handling(optname, setinitial, setfromfile)
3774 const char *optname;
3775 boolean setinitial, setfromfile;
3776 {
3777     winid tmpwin;
3778     anything any;
3779     int i;
3780     char buf[BUFSZ];
3781
3782     /* Special handling of menustyle, pickup_burden, pickup_types,
3783      * disclose, runmode, msg_window, menu_headings, sortloot,
3784      * and number_pad options.
3785      * Also takes care of interactive autopickup_exception_handling changes.
3786      */
3787     if (!strcmp("menustyle", optname)) {
3788         const char *style_name;
3789         menu_item *style_pick = (menu_item *) 0;
3790         tmpwin = create_nhwindow(NHW_MENU);
3791         start_menu(tmpwin);
3792         any = zeroany;
3793         for (i = 0; i < SIZE(menutype); i++) {
3794             style_name = menutype[i];
3795             /* note: separate `style_name' variable used
3796                to avoid an optimizer bug in VAX C V2.3 */
3797             any.a_int = i + 1;
3798             add_menu(tmpwin, NO_GLYPH, &any, *style_name, 0, ATR_NONE,
3799                      style_name, MENU_UNSELECTED);
3800         }
3801         end_menu(tmpwin, "Select menustyle:");
3802         if (select_menu(tmpwin, PICK_ONE, &style_pick) > 0) {
3803             flags.menu_style = style_pick->item.a_int - 1;
3804             free((genericptr_t) style_pick);
3805         }
3806         destroy_nhwindow(tmpwin);
3807     } else if (!strcmp("paranoid_confirmation", optname)) {
3808         menu_item *paranoia_picks = (menu_item *) 0;
3809
3810         tmpwin = create_nhwindow(NHW_MENU);
3811         start_menu(tmpwin);
3812         any = zeroany;
3813         for (i = 0; paranoia[i].flagmask != 0; ++i) {
3814             if (paranoia[i].flagmask == PARANOID_BONES && !wizard)
3815                 continue;
3816             any.a_int = paranoia[i].flagmask;
3817             add_menu(tmpwin, NO_GLYPH, &any, *paranoia[i].argname, 0,
3818                      ATR_NONE, paranoia[i].explain,
3819                      (flags.paranoia_bits & paranoia[i].flagmask)
3820                          ? MENU_SELECTED
3821                          : MENU_UNSELECTED);
3822         }
3823         end_menu(tmpwin, "Actions requiring extra confirmation:");
3824         i = select_menu(tmpwin, PICK_ANY, &paranoia_picks);
3825         if (i >= 0) {
3826             /* player didn't cancel; we reset all the paranoia options
3827                here even if there were no items picked, since user
3828                could have toggled off preselected ones to end up with 0 */
3829             flags.paranoia_bits = 0;
3830             if (i > 0) {
3831                 /* at least 1 item set, either preselected or newly picked */
3832                 while (--i >= 0)
3833                     flags.paranoia_bits |= paranoia_picks[i].item.a_int;
3834                 free((genericptr_t) paranoia_picks);
3835             }
3836         }
3837         destroy_nhwindow(tmpwin);
3838     } else if (!strcmp("pickup_burden", optname)) {
3839         const char *burden_name, *burden_letters = "ubsntl";
3840         menu_item *burden_pick = (menu_item *) 0;
3841
3842         tmpwin = create_nhwindow(NHW_MENU);
3843         start_menu(tmpwin);
3844         any = zeroany;
3845         for (i = 0; i < SIZE(burdentype); i++) {
3846             burden_name = burdentype[i];
3847             any.a_int = i + 1;
3848             add_menu(tmpwin, NO_GLYPH, &any, burden_letters[i], 0, ATR_NONE,
3849                      burden_name, MENU_UNSELECTED);
3850         }
3851         end_menu(tmpwin, "Select encumbrance level:");
3852         if (select_menu(tmpwin, PICK_ONE, &burden_pick) > 0) {
3853             flags.pickup_burden = burden_pick->item.a_int - 1;
3854             free((genericptr_t) burden_pick);
3855         }
3856         destroy_nhwindow(tmpwin);
3857     } else if (!strcmp("pickup_types", optname)) {
3858         /* parseoptions will prompt for the list of types */
3859         parseoptions(strcpy(buf, "pickup_types"), setinitial, setfromfile);
3860     } else if (!strcmp("disclose", optname)) {
3861         /* order of disclose_names[] must correspond to
3862            disclosure_options in decl.c */
3863         static const char *disclosure_names[] = {
3864             "inventory", "attributes", "vanquished",
3865             "genocides", "conduct",    "overview",
3866         };
3867         int disc_cat[NUM_DISCLOSURE_OPTIONS];
3868         int pick_cnt, pick_idx, opt_idx;
3869         menu_item *disclosure_pick = (menu_item *) 0;
3870
3871         tmpwin = create_nhwindow(NHW_MENU);
3872         start_menu(tmpwin);
3873         any = zeroany;
3874         for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
3875             Sprintf(buf, "%-12s[%c%c]", disclosure_names[i],
3876                     flags.end_disclose[i], disclosure_options[i]);
3877             any.a_int = i + 1;
3878             add_menu(tmpwin, NO_GLYPH, &any, disclosure_options[i], 0,
3879                      ATR_NONE, buf, MENU_UNSELECTED);
3880             disc_cat[i] = 0;
3881         }
3882         end_menu(tmpwin, "Change which disclosure options categories:");
3883         pick_cnt = select_menu(tmpwin, PICK_ANY, &disclosure_pick);
3884         if (pick_cnt > 0) {
3885             for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
3886                 opt_idx = disclosure_pick[pick_idx].item.a_int - 1;
3887                 disc_cat[opt_idx] = 1;
3888             }
3889             free((genericptr_t) disclosure_pick);
3890             disclosure_pick = (menu_item *) 0;
3891         }
3892         destroy_nhwindow(tmpwin);
3893
3894         for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
3895             if (disc_cat[i]) {
3896                 Sprintf(buf, "Disclosure options for %s:",
3897                         disclosure_names[i]);
3898                 tmpwin = create_nhwindow(NHW_MENU);
3899                 start_menu(tmpwin);
3900                 any = zeroany;
3901                 /* 'y','n',and '+' work as alternate selectors; '-' doesn't */
3902                 any.a_char = DISCLOSE_NO_WITHOUT_PROMPT;
3903                 add_menu(tmpwin, NO_GLYPH, &any, 'a', any.a_char, ATR_NONE,
3904                          "Never disclose, without prompting",
3905                          MENU_UNSELECTED);
3906                 any.a_char = DISCLOSE_YES_WITHOUT_PROMPT;
3907                 add_menu(tmpwin, NO_GLYPH, &any, 'b', any.a_char, ATR_NONE,
3908                          "Always disclose, without prompting",
3909                          MENU_UNSELECTED);
3910                 any.a_char = DISCLOSE_PROMPT_DEFAULT_NO;
3911                 add_menu(tmpwin, NO_GLYPH, &any, 'c', any.a_char, ATR_NONE,
3912                          "Prompt, with default answer of \"No\"",
3913                          MENU_UNSELECTED);
3914                 any.a_char = DISCLOSE_PROMPT_DEFAULT_YES;
3915                 add_menu(tmpwin, NO_GLYPH, &any, 'd', any.a_char, ATR_NONE,
3916                          "Prompt, with default answer of \"Yes\"",
3917                          MENU_UNSELECTED);
3918                 end_menu(tmpwin, buf);
3919                 if (select_menu(tmpwin, PICK_ONE, &disclosure_pick) > 0) {
3920                     flags.end_disclose[i] = disclosure_pick->item.a_char;
3921                     free((genericptr_t) disclosure_pick);
3922                 }
3923                 destroy_nhwindow(tmpwin);
3924             }
3925         }
3926     } else if (!strcmp("runmode", optname)) {
3927         const char *mode_name;
3928         menu_item *mode_pick = (menu_item *) 0;
3929
3930         tmpwin = create_nhwindow(NHW_MENU);
3931         start_menu(tmpwin);
3932         any = zeroany;
3933         for (i = 0; i < SIZE(runmodes); i++) {
3934             mode_name = runmodes[i];
3935             any.a_int = i + 1;
3936             add_menu(tmpwin, NO_GLYPH, &any, *mode_name, 0, ATR_NONE,
3937                      mode_name, MENU_UNSELECTED);
3938         }
3939         end_menu(tmpwin, "Select run/travel display mode:");
3940         if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
3941             flags.runmode = mode_pick->item.a_int - 1;
3942             free((genericptr_t) mode_pick);
3943         }
3944         destroy_nhwindow(tmpwin);
3945     } else if (!strcmp("msg_window", optname)) {
3946 #ifdef TTY_GRAPHICS
3947         /* by Christian W. Cooper */
3948         menu_item *window_pick = (menu_item *) 0;
3949
3950         tmpwin = create_nhwindow(NHW_MENU);
3951         start_menu(tmpwin);
3952         any = zeroany;
3953         any.a_char = 's';
3954         add_menu(tmpwin, NO_GLYPH, &any, 's', 0, ATR_NONE, "single",
3955                  MENU_UNSELECTED);
3956         any.a_char = 'c';
3957         add_menu(tmpwin, NO_GLYPH, &any, 'c', 0, ATR_NONE, "combination",
3958                  MENU_UNSELECTED);
3959         any.a_char = 'f';
3960         add_menu(tmpwin, NO_GLYPH, &any, 'f', 0, ATR_NONE, "full",
3961                  MENU_UNSELECTED);
3962         any.a_char = 'r';
3963         add_menu(tmpwin, NO_GLYPH, &any, 'r', 0, ATR_NONE, "reversed",
3964                  MENU_UNSELECTED);
3965         end_menu(tmpwin, "Select message history display type:");
3966         if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) {
3967             iflags.prevmsg_window = window_pick->item.a_char;
3968             free((genericptr_t) window_pick);
3969         }
3970         destroy_nhwindow(tmpwin);
3971 #endif
3972     } else if (!strcmp("sortloot", optname)) {
3973         const char *sortl_name;
3974         menu_item *sortl_pick = (menu_item *) 0;
3975
3976         tmpwin = create_nhwindow(NHW_MENU);
3977         start_menu(tmpwin);
3978         any = zeroany;
3979         for (i = 0; i < SIZE(sortltype); i++) {
3980             sortl_name = sortltype[i];
3981             any.a_char = *sortl_name;
3982             add_menu(tmpwin, NO_GLYPH, &any, *sortl_name, 0, ATR_NONE,
3983                      sortl_name, MENU_UNSELECTED);
3984         }
3985         end_menu(tmpwin, "Select loot sorting type:");
3986         if (select_menu(tmpwin, PICK_ONE, &sortl_pick) > 0) {
3987             flags.sortloot = sortl_pick->item.a_char;
3988             free((genericptr_t) sortl_pick);
3989         }
3990         destroy_nhwindow(tmpwin);
3991     } else if (!strcmp("align_message", optname)
3992                || !strcmp("align_status", optname)) {
3993         menu_item *window_pick = (menu_item *) 0;
3994         char abuf[BUFSZ];
3995         boolean msg = (*(optname + 6) == 'm');
3996
3997         tmpwin = create_nhwindow(NHW_MENU);
3998         start_menu(tmpwin);
3999         any = zeroany;
4000         any.a_int = ALIGN_TOP;
4001         add_menu(tmpwin, NO_GLYPH, &any, 't', 0, ATR_NONE, "top",
4002                  MENU_UNSELECTED);
4003         any.a_int = ALIGN_BOTTOM;
4004         add_menu(tmpwin, NO_GLYPH, &any, 'b', 0, ATR_NONE, "bottom",
4005                  MENU_UNSELECTED);
4006         any.a_int = ALIGN_LEFT;
4007         add_menu(tmpwin, NO_GLYPH, &any, 'l', 0, ATR_NONE, "left",
4008                  MENU_UNSELECTED);
4009         any.a_int = ALIGN_RIGHT;
4010         add_menu(tmpwin, NO_GLYPH, &any, 'r', 0, ATR_NONE, "right",
4011                  MENU_UNSELECTED);
4012         Sprintf(abuf, "Select %s window placement relative to the map:",
4013                 msg ? "message" : "status");
4014         end_menu(tmpwin, abuf);
4015         if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) {
4016             if (msg)
4017                 iflags.wc_align_message = window_pick->item.a_int;
4018             else
4019                 iflags.wc_align_status = window_pick->item.a_int;
4020             free((genericptr_t) window_pick);
4021         }
4022         destroy_nhwindow(tmpwin);
4023     } else if (!strcmp("number_pad", optname)) {
4024         static const char *npchoices[] = {
4025             " 0 (off)", " 1 (on)", " 2 (on, MSDOS compatible)",
4026             " 3 (on, phone-style digit layout)",
4027             " 4 (on, phone-style layout, MSDOS compatible)",
4028             "-1 (off, 'z' to move upper-left, 'y' to zap wands)"
4029         };
4030         menu_item *mode_pick = (menu_item *) 0;
4031
4032         tmpwin = create_nhwindow(NHW_MENU);
4033         start_menu(tmpwin);
4034         any = zeroany;
4035         for (i = 0; i < SIZE(npchoices); i++) {
4036             any.a_int = i + 1;
4037             add_menu(tmpwin, NO_GLYPH, &any, 'a' + i, 0, ATR_NONE,
4038                      npchoices[i], MENU_UNSELECTED);
4039         }
4040         end_menu(tmpwin, "Select number_pad mode:");
4041         if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
4042             switch (mode_pick->item.a_int - 1) {
4043             case 0:
4044                 iflags.num_pad = FALSE;
4045                 iflags.num_pad_mode = 0;
4046                 break;
4047             case 1:
4048                 iflags.num_pad = TRUE;
4049                 iflags.num_pad_mode = 0;
4050                 break;
4051             case 2:
4052                 iflags.num_pad = TRUE;
4053                 iflags.num_pad_mode = 1;
4054                 break;
4055             case 3:
4056                 iflags.num_pad = TRUE;
4057                 iflags.num_pad_mode = 2;
4058                 break;
4059             case 4:
4060                 iflags.num_pad = TRUE;
4061                 iflags.num_pad_mode = 3;
4062                 break;
4063             /* last menu choice: number_pad == -1 */
4064             case 5:
4065                 iflags.num_pad = FALSE;
4066                 iflags.num_pad_mode = 1;
4067                 break;
4068             }
4069             reset_commands(FALSE);
4070             number_pad(iflags.num_pad ? 1 : 0);
4071             free((genericptr_t) mode_pick);
4072         }
4073         destroy_nhwindow(tmpwin);
4074     } else if (!strcmp("menu_headings", optname)) {
4075         int mhattr = query_attr("How to highlight menu headings:");
4076
4077         if (mhattr != -1)
4078             iflags.menu_headings = mhattr;
4079     } else if (!strcmp("msgtype", optname)) {
4080         int opt_idx, nmt, mttyp;
4081         char mtbuf[BUFSZ];
4082
4083     msgtypes_again:
4084         nmt = msgtype_count();
4085         opt_idx = handle_add_list_remove("message type", nmt);
4086         if (opt_idx == 3) {
4087             ; /* done--fall through to function exit */
4088         } else if (opt_idx == 0) { /* add new */
4089             getlin("What new message pattern?", mtbuf);
4090             if (*mtbuf == '\033' || !*mtbuf)
4091                 goto msgtypes_again;
4092             mttyp = query_msgtype();
4093             if (mttyp == -1)
4094                 goto msgtypes_again;
4095             if (!msgtype_add(mttyp, mtbuf)) {
4096                 pline("Error adding the message type.");
4097                 wait_synch();
4098                 goto msgtypes_again;
4099             }
4100         } else { /* list or remove */
4101             int pick_idx, pick_cnt;
4102             int mt_idx;
4103             menu_item *pick_list = (menu_item *) 0;
4104             struct plinemsg_type *tmp = plinemsg_types;
4105
4106             tmpwin = create_nhwindow(NHW_MENU);
4107             start_menu(tmpwin);
4108             any = zeroany;
4109             mt_idx = 0;
4110             while (tmp) {
4111                 const char *mtype = msgtype2name(tmp->msgtype);
4112
4113                 any.a_int = ++mt_idx;
4114                 Sprintf(mtbuf, "%-5s \"%s\"", mtype, tmp->pattern);
4115                 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, mtbuf,
4116                          MENU_UNSELECTED);
4117                 tmp = tmp->next;
4118             }
4119             Sprintf(mtbuf, "%s message types",
4120                     (opt_idx == 1) ? "List of" : "Remove which");
4121             end_menu(tmpwin, mtbuf);
4122             pick_cnt = select_menu(tmpwin,
4123                                    (opt_idx == 1) ? PICK_NONE : PICK_ANY,
4124                                    &pick_list);
4125             if (pick_cnt > 0) {
4126                 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
4127                     free_one_msgtype(pick_list[pick_idx].item.a_int - 1
4128                                            - pick_idx);
4129                 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
4130             }
4131             destroy_nhwindow(tmpwin);
4132             if (pick_cnt >= 0)
4133                 goto msgtypes_again;
4134         }
4135     } else if (!strcmp("menucolors", optname)) {
4136         int opt_idx, nmc, mcclr, mcattr;
4137         char mcbuf[BUFSZ];
4138
4139     menucolors_again:
4140         nmc = count_menucolors();
4141         opt_idx = handle_add_list_remove("menucolor", nmc);
4142         if (opt_idx == 3) {
4143             ;                      /* done--fall through to function exit */
4144         } else if (opt_idx == 0) { /* add new */
4145             getlin("What new menucolor pattern?", mcbuf);
4146             if (*mcbuf == '\033' || !*mcbuf)
4147                 goto menucolors_again;
4148             mcclr = query_color();
4149             if (mcclr == -1)
4150                 goto menucolors_again;
4151             mcattr = query_attr(NULL);
4152             if (mcattr == -1)
4153                 goto menucolors_again;
4154             if (!add_menu_coloring_parsed(mcbuf, mcclr, mcattr)) {
4155                 pline("Error adding the menu color.");
4156                 wait_synch();
4157                 goto menucolors_again;
4158             }
4159         } else { /* list or remove */
4160             int pick_idx, pick_cnt;
4161             int mc_idx;
4162             menu_item *pick_list = (menu_item *) 0;
4163             struct menucoloring *tmp = menu_colorings;
4164
4165             tmpwin = create_nhwindow(NHW_MENU);
4166             start_menu(tmpwin);
4167             any = zeroany;
4168             mc_idx = 0;
4169             while (tmp) {
4170                 const char *sattr = attr2attrname(tmp->attr);
4171                 const char *sclr = clr2colorname(tmp->color);
4172
4173                 any.a_int = (++mc_idx);
4174                 Sprintf(mcbuf, "\"%s\"=%s%s%s", tmp->origstr, sclr,
4175                         (tmp->attr != ATR_NONE) ? " & " : "",
4176                         (tmp->attr != ATR_NONE) ? sattr : "");
4177                 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, mcbuf,
4178                          MENU_UNSELECTED);
4179                 tmp = tmp->next;
4180             }
4181             Sprintf(mcbuf, "%s menu colors",
4182                     (opt_idx == 1) ? "List of" : "Remove which");
4183             end_menu(tmpwin, mcbuf);
4184             pick_cnt = select_menu(tmpwin,
4185                                    (opt_idx == 1) ? PICK_NONE : PICK_ANY,
4186                                    &pick_list);
4187             if (pick_cnt > 0) {
4188                 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
4189                     free_one_menu_coloring(pick_list[pick_idx].item.a_int - 1
4190                                            - pick_idx);
4191                 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
4192             }
4193             destroy_nhwindow(tmpwin);
4194             if (pick_cnt >= 0)
4195                 goto menucolors_again;
4196         }
4197     } else if (!strcmp("autopickup_exception", optname)) {
4198         int opt_idx, pass, totalapes = 0, numapes[2] = { 0, 0 };
4199         char apebuf[1 + BUFSZ]; /* so &apebuf[1] is BUFSZ long for getlin() */
4200         struct autopickup_exception *ape;
4201
4202     ape_again:
4203         totalapes = count_ape_maps(&numapes[AP_LEAVE], &numapes[AP_GRAB]);
4204         opt_idx = handle_add_list_remove("autopickup exception", totalapes);
4205         if (opt_idx == 3) {
4206             ;                      /* done--fall through to function exit */
4207         } else if (opt_idx == 0) { /* add new */
4208             getlin("What new autopickup exception pattern?", &apebuf[1]);
4209             mungspaces(&apebuf[1]); /* regularize whitespace */
4210             if (apebuf[1] == '\033') {
4211                 ; /* fall through to function exit */
4212             } else {
4213                 if (apebuf[1]) {
4214                     apebuf[0] = '\"';
4215                     /* guarantee room for \" prefix and \"\0 suffix;
4216                        -2 is good enough for apebuf[] but -3 makes
4217                        sure the whole thing fits within normal BUFSZ */
4218                     apebuf[sizeof apebuf - 3] = '\0';
4219                     Strcat(apebuf, "\"");
4220                     add_autopickup_exception(apebuf);
4221                 }
4222                 goto ape_again;
4223             }
4224         } else { /* list or remove */
4225             int pick_idx, pick_cnt;
4226             menu_item *pick_list = (menu_item *) 0;
4227
4228             tmpwin = create_nhwindow(NHW_MENU);
4229             start_menu(tmpwin);
4230             for (pass = AP_LEAVE; pass <= AP_GRAB; ++pass) {
4231                 if (numapes[pass] == 0)
4232                     continue;
4233                 ape = iflags.autopickup_exceptions[pass];
4234                 any = zeroany;
4235                 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
4236                          (pass == 0) ? "Never pickup" : "Always pickup",
4237                          MENU_UNSELECTED);
4238                 for (i = 0; i < numapes[pass] && ape; i++) {
4239                     any.a_void = (opt_idx == 1) ? 0 : ape;
4240                     Sprintf(apebuf, "\"%s\"", ape->pattern);
4241                     add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, apebuf,
4242                              MENU_UNSELECTED);
4243                     ape = ape->next;
4244                 }
4245             }
4246             Sprintf(apebuf, "%s autopickup exceptions",
4247                     (opt_idx == 1) ? "List of" : "Remove which");
4248             end_menu(tmpwin, apebuf);
4249             pick_cnt = select_menu(tmpwin,
4250                                    (opt_idx == 1) ? PICK_NONE : PICK_ANY,
4251                                    &pick_list);
4252             if (pick_cnt > 0) {
4253                 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
4254                     remove_autopickup_exception(
4255                                          (struct autopickup_exception *)
4256                                              pick_list[pick_idx].item.a_void);
4257                 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
4258             }
4259             destroy_nhwindow(tmpwin);
4260             if (pick_cnt >= 0)
4261                 goto ape_again;
4262         }
4263     } else if (!strcmp("symset", optname)
4264                || !strcmp("roguesymset", optname)) {
4265         menu_item *symset_pick = (menu_item *) 0;
4266         boolean primaryflag = (*optname == 's'),
4267                 rogueflag = (*optname == 'r'),
4268                 ready_to_switch = FALSE,
4269                 nothing_to_do = FALSE;
4270         char *symset_name, fmtstr[20];
4271         struct symsetentry *sl;
4272         int res, which_set, setcount = 0, chosen = -2;
4273
4274         if (rogueflag)
4275             which_set = ROGUESET;
4276         else
4277             which_set = PRIMARY;
4278
4279         /* clear symset[].name as a flag to read_sym_file() to build list */
4280         symset_name = symset[which_set].name;
4281         symset[which_set].name = (char *) 0;
4282         symset_list = (struct symsetentry *) 0;
4283
4284         res = read_sym_file(which_set);
4285         if (res && symset_list) {
4286             char symsetchoice[BUFSZ];
4287             int let = 'a', biggest = 0, thissize = 0;
4288
4289             sl = symset_list;
4290             while (sl) {
4291                 /* check restrictions */
4292                 if ((!rogueflag && sl->rogue)
4293                     || (!primaryflag && sl->primary)) {
4294                     sl = sl->next;
4295                     continue;
4296                 }
4297                 setcount++;
4298                 /* find biggest name */
4299                 if (sl->name)
4300                     thissize = strlen(sl->name);
4301                 if (thissize > biggest)
4302                     biggest = thissize;
4303                 sl = sl->next;
4304             }
4305             if (!setcount) {
4306                 pline("There are no appropriate %ssymbol sets available.",
4307                       (rogueflag) ? "rogue level "
4308                                   : (primaryflag) ? "primary " : "");
4309                 return TRUE;
4310             }
4311
4312             Sprintf(fmtstr, "%%-%ds %%s", biggest + 5);
4313             tmpwin = create_nhwindow(NHW_MENU);
4314             start_menu(tmpwin);
4315             any = zeroany;
4316             any.a_int = 1;
4317             add_menu(tmpwin, NO_GLYPH, &any, let++, 0, ATR_NONE,
4318                      "Default Symbols", MENU_UNSELECTED);
4319
4320             sl = symset_list;
4321             while (sl) {
4322                 /* check restrictions */
4323                 if ((!rogueflag && sl->rogue)
4324                     || (!primaryflag && sl->primary)) {
4325                     sl = sl->next;
4326                     continue;
4327                 }
4328                 if (sl->name) {
4329                     any.a_int = sl->idx + 2;
4330                     Sprintf(symsetchoice, fmtstr, sl->name,
4331                             sl->desc ? sl->desc : "");
4332                     add_menu(tmpwin, NO_GLYPH, &any, let, 0, ATR_NONE,
4333                              symsetchoice, MENU_UNSELECTED);
4334                     if (let == 'z')
4335                         let = 'A';
4336                     else
4337                         let++;
4338                 }
4339                 sl = sl->next;
4340             }
4341             end_menu(tmpwin, "Select symbol set:");
4342             if (select_menu(tmpwin, PICK_ONE, &symset_pick) > 0) {
4343                 chosen = symset_pick->item.a_int - 2;
4344                 free((genericptr_t) symset_pick);
4345             }
4346             destroy_nhwindow(tmpwin);
4347
4348             if (chosen > -1) {
4349                 /* chose an actual symset name from file */
4350                 sl = symset_list;
4351                 while (sl) {
4352                     if (sl->idx == chosen) {
4353                         if (symset_name) {
4354                             free((genericptr_t) symset_name);
4355                             symset_name = (char *) 0;
4356                         }
4357                         /* free the now stale attributes */
4358                         clear_symsetentry(which_set, TRUE);
4359
4360                         /* transfer only the name of the symbol set */
4361                         symset[which_set].name = dupstr(sl->name);
4362                         ready_to_switch = TRUE;
4363                         break;
4364                     }
4365                     sl = sl->next;
4366                 }
4367             } else if (chosen == -1) {
4368                 /* explicit selection of defaults */
4369                 /* free the now stale symset attributes */
4370                 if (symset_name) {
4371                     free((genericptr_t) symset_name);
4372                     symset_name = (char *) 0;
4373                 }
4374                 clear_symsetentry(which_set, TRUE);
4375             } else
4376                 nothing_to_do = TRUE;
4377         } else if (!res) {
4378             /* The symbols file could not be accessed */
4379             pline("Unable to access \"%s\" file.", SYMBOLS);
4380             return TRUE;
4381         } else if (!symset_list) {
4382             /* The symbols file was empty */
4383             pline("There were no symbol sets found in \"%s\".", SYMBOLS);
4384             return TRUE;
4385         }
4386
4387         /* clean up */
4388         while (symset_list) {
4389             sl = symset_list;
4390             if (sl->name)
4391                 free((genericptr_t) sl->name);
4392             sl->name = (char *) 0;
4393
4394             if (sl->desc)
4395                 free((genericptr_t) sl->desc);
4396             sl->desc = (char *) 0;
4397
4398             symset_list = sl->next;
4399             free((genericptr_t) sl);
4400         }
4401
4402         if (nothing_to_do)
4403             return TRUE;
4404
4405         if (!symset[which_set].name && symset_name)
4406             symset[which_set].name = symset_name; /* not dupstr() here */
4407
4408         /* Set default symbols and clear the handling value */
4409         if (rogueflag)
4410             init_r_symbols();
4411         else
4412             init_l_symbols();
4413
4414         if (symset[which_set].name) {
4415             if (read_sym_file(which_set)) {
4416                 ready_to_switch = TRUE;
4417             } else {
4418                 clear_symsetentry(which_set, TRUE);
4419                 return TRUE;
4420             }
4421         }
4422
4423         if (ready_to_switch)
4424             switch_symbols(TRUE);
4425
4426         if (Is_rogue_level(&u.uz)) {
4427             if (rogueflag)
4428                 assign_graphics(ROGUESET);
4429         } else if (!rogueflag)
4430             assign_graphics(PRIMARY);
4431         need_redraw = TRUE;
4432         return TRUE;
4433
4434     } else {
4435         /* didn't match any of the special options */
4436         return FALSE;
4437     }
4438     return TRUE;
4439 }
4440
4441 #define rolestring(val, array, field) \
4442     ((val >= 0) ? array[val].field : (val == ROLE_RANDOM) ? randomrole : none)
4443
4444 /* This is ugly. We have all the option names in the compopt[] array,
4445    but we need to look at each option individually to get the value. */
4446 STATIC_OVL const char *
4447 get_compopt_value(optname, buf)
4448 const char *optname;
4449 char *buf;
4450 {
4451     char ocl[MAXOCLASSES + 1];
4452     static const char none[] = "(none)", randomrole[] = "random",
4453                       to_be_done[] = "(to be done)", defopt[] = "default",
4454                       defbrief[] = "def";
4455     int i;
4456
4457     buf[0] = '\0';
4458     if (!strcmp(optname, "align_message"))
4459         Sprintf(buf, "%s",
4460                 iflags.wc_align_message == ALIGN_TOP
4461                     ? "top"
4462                     : iflags.wc_align_message == ALIGN_LEFT
4463                           ? "left"
4464                           : iflags.wc_align_message == ALIGN_BOTTOM
4465                                 ? "bottom"
4466                                 : iflags.wc_align_message == ALIGN_RIGHT
4467                                       ? "right"
4468                                       : defopt);
4469     else if (!strcmp(optname, "align_status"))
4470         Sprintf(buf, "%s",
4471                 iflags.wc_align_status == ALIGN_TOP
4472                     ? "top"
4473                     : iflags.wc_align_status == ALIGN_LEFT
4474                           ? "left"
4475                           : iflags.wc_align_status == ALIGN_BOTTOM
4476                                 ? "bottom"
4477                                 : iflags.wc_align_status == ALIGN_RIGHT
4478                                       ? "right"
4479                                       : defopt);
4480     else if (!strcmp(optname, "align"))
4481         Sprintf(buf, "%s", rolestring(flags.initalign, aligns, adj));
4482 #ifdef WIN32
4483     else if (!strcmp(optname, "altkeyhandler"))
4484         Sprintf(buf, "%s",
4485                 iflags.altkeyhandler[0] ? iflags.altkeyhandler : "default");
4486 #endif
4487 #ifdef BACKWARD_COMPAT
4488     else if (!strcmp(optname, "boulder"))
4489         Sprintf(buf, "%c",
4490                 iflags.bouldersym
4491                     ? iflags.bouldersym
4492                     : showsyms[(int) objects[BOULDER].oc_class + SYM_OFF_O]);
4493 #endif
4494     else if (!strcmp(optname, "catname"))
4495         Sprintf(buf, "%s", catname[0] ? catname : none);
4496     else if (!strcmp(optname, "disclose"))
4497         for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
4498             if (i)
4499                 (void) strkitten(buf, ' ');
4500             (void) strkitten(buf, flags.end_disclose[i]);
4501             (void) strkitten(buf, disclosure_options[i]);
4502         }
4503     else if (!strcmp(optname, "dogname"))
4504         Sprintf(buf, "%s", dogname[0] ? dogname : none);
4505     else if (!strcmp(optname, "dungeon"))
4506         Sprintf(buf, "%s", to_be_done);
4507     else if (!strcmp(optname, "effects"))
4508         Sprintf(buf, "%s", to_be_done);
4509     else if (!strcmp(optname, "font_map"))
4510         Sprintf(buf, "%s", iflags.wc_font_map ? iflags.wc_font_map : defopt);
4511     else if (!strcmp(optname, "font_message"))
4512         Sprintf(buf, "%s",
4513                 iflags.wc_font_message ? iflags.wc_font_message : defopt);
4514     else if (!strcmp(optname, "font_status"))
4515         Sprintf(buf, "%s",
4516                 iflags.wc_font_status ? iflags.wc_font_status : defopt);
4517     else if (!strcmp(optname, "font_menu"))
4518         Sprintf(buf, "%s",
4519                 iflags.wc_font_menu ? iflags.wc_font_menu : defopt);
4520     else if (!strcmp(optname, "font_text"))
4521         Sprintf(buf, "%s",
4522                 iflags.wc_font_text ? iflags.wc_font_text : defopt);
4523     else if (!strcmp(optname, "font_size_map")) {
4524         if (iflags.wc_fontsiz_map)
4525             Sprintf(buf, "%d", iflags.wc_fontsiz_map);
4526         else
4527             Strcpy(buf, defopt);
4528     } else if (!strcmp(optname, "font_size_message")) {
4529         if (iflags.wc_fontsiz_message)
4530             Sprintf(buf, "%d", iflags.wc_fontsiz_message);
4531         else
4532             Strcpy(buf, defopt);
4533     } else if (!strcmp(optname, "font_size_status")) {
4534         if (iflags.wc_fontsiz_status)
4535             Sprintf(buf, "%d", iflags.wc_fontsiz_status);
4536         else
4537             Strcpy(buf, defopt);
4538     } else if (!strcmp(optname, "font_size_menu")) {
4539         if (iflags.wc_fontsiz_menu)
4540             Sprintf(buf, "%d", iflags.wc_fontsiz_menu);
4541         else
4542             Strcpy(buf, defopt);
4543     } else if (!strcmp(optname, "font_size_text")) {
4544         if (iflags.wc_fontsiz_text)
4545             Sprintf(buf, "%d", iflags.wc_fontsiz_text);
4546         else
4547             Strcpy(buf, defopt);
4548     } else if (!strcmp(optname, "fruit"))
4549         Sprintf(buf, "%s", pl_fruit);
4550     else if (!strcmp(optname, "gender"))
4551         Sprintf(buf, "%s", rolestring(flags.initgend, genders, adj));
4552     else if (!strcmp(optname, "horsename"))
4553         Sprintf(buf, "%s", horsename[0] ? horsename : none);
4554     else if (!strcmp(optname, "map_mode"))
4555         Sprintf(buf, "%s",
4556                 iflags.wc_map_mode == MAP_MODE_TILES
4557                   ? "tiles"
4558                   : iflags.wc_map_mode == MAP_MODE_ASCII4x6
4559                      ? "ascii4x6"
4560                      : iflags.wc_map_mode == MAP_MODE_ASCII6x8
4561                         ? "ascii6x8"
4562                         : iflags.wc_map_mode == MAP_MODE_ASCII8x8
4563                            ? "ascii8x8"
4564                            : iflags.wc_map_mode == MAP_MODE_ASCII16x8
4565                               ? "ascii16x8"
4566                               : iflags.wc_map_mode == MAP_MODE_ASCII7x12
4567                                  ? "ascii7x12"
4568                                  : iflags.wc_map_mode == MAP_MODE_ASCII8x12
4569                                     ? "ascii8x12"
4570                                     : iflags.wc_map_mode
4571                                       == MAP_MODE_ASCII16x12
4572                                        ? "ascii16x12"
4573                                        : iflags.wc_map_mode
4574                                          == MAP_MODE_ASCII12x16
4575                                           ? "ascii12x16"
4576                                           : iflags.wc_map_mode
4577                                             == MAP_MODE_ASCII10x18
4578                                              ? "ascii10x18"
4579                                              : iflags.wc_map_mode
4580                                                == MAP_MODE_ASCII_FIT_TO_SCREEN
4581                                                 ? "fit_to_screen"
4582                                                 : defopt);
4583     else if (!strcmp(optname, "menustyle"))
4584         Sprintf(buf, "%s", menutype[(int) flags.menu_style]);
4585     else if (!strcmp(optname, "menu_deselect_all"))
4586         Sprintf(buf, "%s", to_be_done);
4587     else if (!strcmp(optname, "menu_deselect_page"))
4588         Sprintf(buf, "%s", to_be_done);
4589     else if (!strcmp(optname, "menu_first_page"))
4590         Sprintf(buf, "%s", to_be_done);
4591     else if (!strcmp(optname, "menu_invert_all"))
4592         Sprintf(buf, "%s", to_be_done);
4593     else if (!strcmp(optname, "menu_headings"))
4594         Sprintf(buf, "%s", attr2attrname(iflags.menu_headings));
4595     else if (!strcmp(optname, "menu_invert_page"))
4596         Sprintf(buf, "%s", to_be_done);
4597     else if (!strcmp(optname, "menu_last_page"))
4598         Sprintf(buf, "%s", to_be_done);
4599     else if (!strcmp(optname, "menu_next_page"))
4600         Sprintf(buf, "%s", to_be_done);
4601     else if (!strcmp(optname, "menu_previous_page"))
4602         Sprintf(buf, "%s", to_be_done);
4603     else if (!strcmp(optname, "menu_search"))
4604         Sprintf(buf, "%s", to_be_done);
4605     else if (!strcmp(optname, "menu_select_all"))
4606         Sprintf(buf, "%s", to_be_done);
4607     else if (!strcmp(optname, "menu_select_page"))
4608         Sprintf(buf, "%s", to_be_done);
4609     else if (!strcmp(optname, "monsters")) {
4610         Sprintf(buf, "%s", to_be_done);
4611     } else if (!strcmp(optname, "msghistory")) {
4612         Sprintf(buf, "%u", iflags.msg_history);
4613 #ifdef TTY_GRAPHICS
4614     } else if (!strcmp(optname, "msg_window")) {
4615         Sprintf(buf, "%s", (iflags.prevmsg_window == 's')
4616                                ? "single"
4617                                : (iflags.prevmsg_window == 'c')
4618                                      ? "combination"
4619                                      : (iflags.prevmsg_window == 'f')
4620                                            ? "full"
4621                                            : "reversed");
4622 #endif
4623     } else if (!strcmp(optname, "name")) {
4624         Sprintf(buf, "%s", plname);
4625     } else if (!strcmp(optname, "number_pad")) {
4626         static const char *numpadmodes[] = {
4627             "0=off", "1=on", "2=on, MSDOS compatible",
4628             "3=on, phone-style layout",
4629             "4=on, phone layout, MSDOS compatible",
4630             "-1=off, y & z swapped", /*[5]*/
4631         };
4632         int indx = Cmd.num_pad
4633                        ? (Cmd.phone_layout ? (Cmd.pcHack_compat ? 4 : 3)
4634                                            : (Cmd.pcHack_compat ? 2 : 1))
4635                        : Cmd.swap_yz ? 5 : 0;
4636
4637         Strcpy(buf, numpadmodes[indx]);
4638     } else if (!strcmp(optname, "objects")) {
4639         Sprintf(buf, "%s", to_be_done);
4640     } else if (!strcmp(optname, "packorder")) {
4641         oc_to_str(flags.inv_order, ocl);
4642         Sprintf(buf, "%s", ocl);
4643 #ifdef CHANGE_COLOR
4644     } else if (!strcmp(optname, "palette")) {
4645         Sprintf(buf, "%s", get_color_string());
4646 #endif
4647     } else if (!strcmp(optname, "paranoid_confirmation")) {
4648         char tmpbuf[QBUFSZ];
4649
4650         tmpbuf[0] = '\0';
4651         if (ParanoidConfirm)
4652             Strcat(tmpbuf, " Confirm");
4653         if (ParanoidQuit)
4654             Strcat(tmpbuf, " quit");
4655         if (ParanoidDie)
4656             Strcat(tmpbuf, " die");
4657         if (ParanoidBones)
4658             Strcat(tmpbuf, " bones");
4659         if (ParanoidHit)
4660             Strcat(tmpbuf, " attack");
4661         if (ParanoidPray)
4662             Strcat(tmpbuf, " pray");
4663         if (ParanoidRemove)
4664             Strcat(tmpbuf, " Remove");
4665         Strcpy(buf, tmpbuf[0] ? &tmpbuf[1] : "none");
4666     } else if (!strcmp(optname, "pettype")) {
4667         Sprintf(buf, "%s", (preferred_pet == 'c') ? "cat"
4668                            : (preferred_pet == 'd') ? "dog"
4669                              : (preferred_pet == 'h') ? "horse"
4670                                : (preferred_pet == 'n') ? "none"
4671                                  : "random");
4672     } else if (!strcmp(optname, "pickup_burden")) {
4673         Sprintf(buf, "%s", burdentype[flags.pickup_burden]);
4674     } else if (!strcmp(optname, "pickup_types")) {
4675         oc_to_str(flags.pickup_types, ocl);
4676         Sprintf(buf, "%s", ocl[0] ? ocl : "all");
4677     } else if (!strcmp(optname, "pile_limit")) {
4678         Sprintf(buf, "%d", flags.pile_limit);
4679     } else if (!strcmp(optname, "playmode")) {
4680         Strcpy(buf, wizard ? "debug" : discover ? "explore" : "normal");
4681     } else if (!strcmp(optname, "race")) {
4682         Sprintf(buf, "%s", rolestring(flags.initrace, races, noun));
4683     } else if (!strcmp(optname, "roguesymset")) {
4684         Sprintf(buf, "%s",
4685                 symset[ROGUESET].name ? symset[ROGUESET].name : "default");
4686         if (currentgraphics == ROGUESET && symset[ROGUESET].name)
4687             Strcat(buf, ", active");
4688     } else if (!strcmp(optname, "role")) {
4689         Sprintf(buf, "%s", rolestring(flags.initrole, roles, name.m));
4690     } else if (!strcmp(optname, "runmode")) {
4691         Sprintf(buf, "%s", runmodes[flags.runmode]);
4692     } else if (!strcmp(optname, "scores")) {
4693         Sprintf(buf, "%d top/%d around%s", flags.end_top, flags.end_around,
4694                 flags.end_own ? "/own" : "");
4695     } else if (!strcmp(optname, "scroll_amount")) {
4696         if (iflags.wc_scroll_amount)
4697             Sprintf(buf, "%d", iflags.wc_scroll_amount);
4698         else
4699             Strcpy(buf, defopt);
4700     } else if (!strcmp(optname, "scroll_margin")) {
4701         if (iflags.wc_scroll_margin)
4702             Sprintf(buf, "%d", iflags.wc_scroll_margin);
4703         else
4704             Strcpy(buf, defopt);
4705     } else if (!strcmp(optname, "sortloot")) {
4706         for (i = 0; i < SIZE(sortltype); i++)
4707             if (flags.sortloot == sortltype[i][0]) {
4708                 Strcpy(buf, sortltype[i]);
4709                 break;
4710             }
4711     } else if (!strcmp(optname, "player_selection")) {
4712         Sprintf(buf, "%s", iflags.wc_player_selection ? "prompts" : "dialog");
4713 #ifdef MSDOS
4714     } else if (!strcmp(optname, "soundcard")) {
4715         Sprintf(buf, "%s", to_be_done);
4716 #endif
4717     } else if (!strcmp(optname, "suppress_alert")) {
4718         if (flags.suppress_alert == 0L)
4719             Strcpy(buf, none);
4720         else
4721             Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
4722                     FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
4723     } else if (!strcmp(optname, "symset")) {
4724         Sprintf(buf, "%s",
4725                 symset[PRIMARY].name ? symset[PRIMARY].name : "default");
4726         if (currentgraphics == PRIMARY && symset[PRIMARY].name)
4727             Strcat(buf, ", active");
4728     } else if (!strcmp(optname, "tile_file")) {
4729         Sprintf(buf, "%s",
4730                 iflags.wc_tile_file ? iflags.wc_tile_file : defopt);
4731     } else if (!strcmp(optname, "tile_height")) {
4732         if (iflags.wc_tile_height)
4733             Sprintf(buf, "%d", iflags.wc_tile_height);
4734         else
4735             Strcpy(buf, defopt);
4736     } else if (!strcmp(optname, "tile_width")) {
4737         if (iflags.wc_tile_width)
4738             Sprintf(buf, "%d", iflags.wc_tile_width);
4739         else
4740             Strcpy(buf, defopt);
4741     } else if (!strcmp(optname, "traps")) {
4742         Sprintf(buf, "%s", to_be_done);
4743     } else if (!strcmp(optname, "vary_msgcount")) {
4744         if (iflags.wc_vary_msgcount)
4745             Sprintf(buf, "%d", iflags.wc_vary_msgcount);
4746         else
4747             Strcpy(buf, defopt);
4748 #ifdef MSDOS
4749     } else if (!strcmp(optname, "video")) {
4750         Sprintf(buf, "%s", to_be_done);
4751 #endif
4752 #ifdef VIDEOSHADES
4753     } else if (!strcmp(optname, "videoshades")) {
4754         Sprintf(buf, "%s-%s-%s", shade[0], shade[1], shade[2]);
4755     } else if (!strcmp(optname, "videocolors")) {
4756         Sprintf(buf, "%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d",
4757                 ttycolors[CLR_RED], ttycolors[CLR_GREEN],
4758                 ttycolors[CLR_BROWN], ttycolors[CLR_BLUE],
4759                 ttycolors[CLR_MAGENTA], ttycolors[CLR_CYAN],
4760                 ttycolors[CLR_ORANGE], ttycolors[CLR_BRIGHT_GREEN],
4761                 ttycolors[CLR_YELLOW], ttycolors[CLR_BRIGHT_BLUE],
4762                 ttycolors[CLR_BRIGHT_MAGENTA], ttycolors[CLR_BRIGHT_CYAN]);
4763 #endif /* VIDEOSHADES */
4764     } else if (!strcmp(optname, "windowtype")) {
4765         Sprintf(buf, "%s", windowprocs.name);
4766     } else if (!strcmp(optname, "windowcolors")) {
4767         Sprintf(
4768             buf, "%s/%s %s/%s %s/%s %s/%s",
4769             iflags.wc_foregrnd_menu ? iflags.wc_foregrnd_menu : defbrief,
4770             iflags.wc_backgrnd_menu ? iflags.wc_backgrnd_menu : defbrief,
4771             iflags.wc_foregrnd_message ? iflags.wc_foregrnd_message
4772                                        : defbrief,
4773             iflags.wc_backgrnd_message ? iflags.wc_backgrnd_message
4774                                        : defbrief,
4775             iflags.wc_foregrnd_status ? iflags.wc_foregrnd_status : defbrief,
4776             iflags.wc_backgrnd_status ? iflags.wc_backgrnd_status : defbrief,
4777             iflags.wc_foregrnd_text ? iflags.wc_foregrnd_text : defbrief,
4778             iflags.wc_backgrnd_text ? iflags.wc_backgrnd_text : defbrief);
4779 #ifdef PREFIXES_IN_USE
4780     } else {
4781         for (i = 0; i < PREFIX_COUNT; ++i)
4782             if (!strcmp(optname, fqn_prefix_names[i]) && fqn_prefix[i])
4783                 Sprintf(buf, "%s", fqn_prefix[i]);
4784 #endif
4785     }
4786
4787     if (buf[0])
4788         return buf;
4789     else
4790         return "unknown";
4791 }
4792
4793 int
4794 dotogglepickup()
4795 {
4796     char buf[BUFSZ], ocl[MAXOCLASSES + 1];
4797
4798     flags.pickup = !flags.pickup;
4799     if (flags.pickup) {
4800         oc_to_str(flags.pickup_types, ocl);
4801         Sprintf(buf, "ON, for %s objects%s", ocl[0] ? ocl : "all",
4802                 (iflags.autopickup_exceptions[AP_LEAVE]
4803                  || iflags.autopickup_exceptions[AP_GRAB])
4804                     ? ((count_ape_maps((int *) 0, (int *) 0) == 1)
4805                            ? ", with one exception"
4806                            : ", with some exceptions")
4807                     : "");
4808     } else {
4809         Strcpy(buf, "OFF");
4810     }
4811     pline("Autopickup: %s.", buf);
4812     return 0;
4813 }
4814
4815 int
4816 add_autopickup_exception(mapping)
4817 const char *mapping;
4818 {
4819     struct autopickup_exception *ape, **apehead;
4820     char text[256], *text2;
4821     boolean grab = FALSE;
4822
4823     if (sscanf(mapping, "\"%255[^\"]\"", text) == 1) {
4824         text2 = &text[0];
4825         if (*text2 == '<') { /* force autopickup */
4826             grab = TRUE;
4827             ++text2;
4828         } else if (*text2 == '>') { /* default - Do not pickup */
4829             grab = FALSE;
4830             ++text2;
4831         }
4832         apehead = (grab) ? &iflags.autopickup_exceptions[AP_GRAB]
4833                          : &iflags.autopickup_exceptions[AP_LEAVE];
4834         ape = (struct autopickup_exception *) alloc(
4835                                         sizeof (struct autopickup_exception));
4836         ape->regex = regex_init();
4837         if (!regex_compile(text2, ape->regex)) {
4838             raw_print("regex error in AUTOPICKUP_EXCEPTION");
4839             regex_free(ape->regex);
4840             free((genericptr_t) ape);
4841             return 0;
4842         }
4843         ape->pattern = (char *) alloc(strlen(text2) + 1);
4844         strcpy(ape->pattern, text2);
4845         ape->grab = grab;
4846         ape->next = *apehead;
4847         *apehead = ape;
4848     } else {
4849         raw_print("syntax error in AUTOPICKUP_EXCEPTION");
4850         return 0;
4851     }
4852     return 1;
4853 }
4854
4855 STATIC_OVL void
4856 remove_autopickup_exception(whichape)
4857 struct autopickup_exception *whichape;
4858 {
4859     struct autopickup_exception *ape, *prev = 0;
4860     int chain = whichape->grab ? AP_GRAB : AP_LEAVE;
4861
4862     for (ape = iflags.autopickup_exceptions[chain]; ape;) {
4863         if (ape == whichape) {
4864             struct autopickup_exception *freeape = ape;
4865
4866             ape = ape->next;
4867             if (prev)
4868                 prev->next = ape;
4869             else
4870                 iflags.autopickup_exceptions[chain] = ape;
4871             regex_free(freeape->regex);
4872             free((genericptr_t) freeape->pattern);
4873             free((genericptr_t) freeape);
4874         } else {
4875             prev = ape;
4876             ape = ape->next;
4877         }
4878     }
4879 }
4880
4881 STATIC_OVL int
4882 count_ape_maps(leave, grab)
4883 int *leave, *grab;
4884 {
4885     struct autopickup_exception *ape;
4886     int pass, totalapes, numapes[2] = { 0, 0 };
4887
4888     for (pass = AP_LEAVE; pass <= AP_GRAB; ++pass) {
4889         ape = iflags.autopickup_exceptions[pass];
4890         while (ape) {
4891             ape = ape->next;
4892             numapes[pass]++;
4893         }
4894     }
4895     totalapes = numapes[AP_LEAVE] + numapes[AP_GRAB];
4896     if (leave)
4897         *leave = numapes[AP_LEAVE];
4898     if (grab)
4899         *grab = numapes[AP_GRAB];
4900     return totalapes;
4901 }
4902
4903 void
4904 free_autopickup_exceptions()
4905 {
4906     struct autopickup_exception *ape;
4907     int pass;
4908
4909     for (pass = AP_LEAVE; pass <= AP_GRAB; ++pass) {
4910         while ((ape = iflags.autopickup_exceptions[pass]) != 0) {
4911             regex_free(ape->regex);
4912             free((genericptr_t) ape->pattern);
4913             iflags.autopickup_exceptions[pass] = ape->next;
4914             free((genericptr_t) ape);
4915         }
4916     }
4917 }
4918
4919 /* bundle some common usage into one easy-to-use routine */
4920 int
4921 load_symset(s, which_set)
4922 const char *s;
4923 int which_set;
4924 {
4925     clear_symsetentry(which_set, TRUE);
4926
4927     if (symset[which_set].name)
4928         free((genericptr_t) symset[which_set].name);
4929     symset[which_set].name = dupstr(s);
4930
4931     if (read_sym_file(which_set)) {
4932         switch_symbols(TRUE);
4933     } else {
4934         clear_symsetentry(which_set, TRUE);
4935         return 0;
4936     }
4937     return 1;
4938 }
4939
4940 void
4941 free_symsets()
4942 {
4943     clear_symsetentry(PRIMARY, TRUE);
4944     clear_symsetentry(ROGUESET, TRUE);
4945
4946     /* symset_list is cleaned up as soon as it's used, so we shouldn't
4947        have to anything about it here */
4948     /* assert( symset_list == NULL ); */
4949 }
4950
4951 /* Parse the value of a SYMBOLS line from a config file */
4952 void
4953 parsesymbols(opts)
4954 register char *opts;
4955 {
4956     int val;
4957     char *op, *symname, *strval;
4958     struct symparse *symp;
4959
4960     if ((op = index(opts, ',')) != 0) {
4961         *op++ = 0;
4962         parsesymbols(op);
4963     }
4964
4965     /* S_sample:string */
4966     symname = opts;
4967     strval = index(opts, ':');
4968     if (!strval)
4969         strval = index(opts, '=');
4970     if (!strval)
4971         return;
4972     *strval++ = '\0';
4973
4974     /* strip leading and trailing white space from symname and strval */
4975     mungspaces(symname);
4976     mungspaces(strval);
4977
4978     symp = match_sym(symname);
4979     if (!symp)
4980         return;
4981
4982     if (symp->range && symp->range != SYM_CONTROL) {
4983         val = sym_val(strval);
4984         update_l_symset(symp, val);
4985     }
4986 }
4987
4988 struct symparse *
4989 match_sym(buf)
4990 char *buf;
4991 {
4992     size_t len = strlen(buf);
4993     const char *p = index(buf, ':'), *q = index(buf, '=');
4994     struct symparse *sp = loadsyms;
4995
4996     if (!p || (q && q < p))
4997         p = q;
4998     if (p) {
4999         /* note: there will be at most one space before the '='
5000            because caller has condensed buf[] with mungspaces() */
5001         if (p > buf && p[-1] == ' ')
5002             p--;
5003         len = (int) (p - buf);
5004     }
5005     while (sp->range) {
5006         if ((len >= strlen(sp->name)) && !strncmpi(buf, sp->name, len))
5007             return sp;
5008         sp++;
5009     }
5010     return (struct symparse *) 0;
5011 }
5012
5013 int
5014 sym_val(strval)
5015 char *strval;
5016 {
5017     char buf[QBUFSZ];
5018     buf[0] = '\0';
5019     escapes(strval, buf);
5020     return (int) *buf;
5021 }
5022
5023 /* data for option_help() */
5024 static const char *opt_intro[] = {
5025     "", "                 NetHack Options Help:", "",
5026 #define CONFIG_SLOT 3 /* fill in next value at run-time */
5027     (char *) 0,
5028 #if !defined(MICRO) && !defined(MAC)
5029     "or use `NETHACKOPTIONS=\"<options>\"' in your environment",
5030 #endif
5031     "(<options> is a list of options separated by commas)",
5032 #ifdef VMS
5033     "-- for example, $ DEFINE NETHACKOPTIONS \"noautopickup,fruit:kumquat\"",
5034 #endif
5035     "or press \"O\" while playing and use the menu.", "",
5036  "Boolean options (which can be negated by prefixing them with '!' or \"no\"):",
5037     (char *) 0
5038 };
5039
5040 static const char *opt_epilog[] = {
5041     "",
5042     "Some of the options can be set only before the game is started; those",
5043     "items will not be selectable in the 'O' command's menu.", (char *) 0
5044 };
5045
5046 void
5047 option_help()
5048 {
5049     char buf[BUFSZ], buf2[BUFSZ];
5050     register int i;
5051     winid datawin;
5052
5053     datawin = create_nhwindow(NHW_TEXT);
5054     Sprintf(buf, "Set options as OPTIONS=<options> in %s", lastconfigfile);
5055     opt_intro[CONFIG_SLOT] = (const char *) buf;
5056     for (i = 0; opt_intro[i]; i++)
5057         putstr(datawin, 0, opt_intro[i]);
5058
5059     /* Boolean options */
5060     for (i = 0; boolopt[i].name; i++) {
5061         if (boolopt[i].addr) {
5062             if (boolopt[i].addr == &iflags.sanity_check && !wizard)
5063                 continue;
5064             if (boolopt[i].addr == &iflags.menu_tab_sep && !wizard)
5065                 continue;
5066             next_opt(datawin, boolopt[i].name);
5067         }
5068     }
5069     next_opt(datawin, "");
5070
5071     /* Compound options */
5072     putstr(datawin, 0, "Compound options:");
5073     for (i = 0; compopt[i].name; i++) {
5074         Sprintf(buf2, "`%s'", compopt[i].name);
5075         Sprintf(buf, "%-20s - %s%c", buf2, compopt[i].descr,
5076                 compopt[i + 1].name ? ',' : '.');
5077         putstr(datawin, 0, buf);
5078     }
5079
5080     for (i = 0; opt_epilog[i]; i++)
5081         putstr(datawin, 0, opt_epilog[i]);
5082
5083     display_nhwindow(datawin, FALSE);
5084     destroy_nhwindow(datawin);
5085     return;
5086 }
5087
5088 /*
5089  * prints the next boolean option, on the same line if possible, on a new
5090  * line if not. End with next_opt("").
5091  */
5092 void
5093 next_opt(datawin, str)
5094 winid datawin;
5095 const char *str;
5096 {
5097     static char *buf = 0;
5098     int i;
5099     char *s;
5100
5101     if (!buf)
5102         *(buf = (char *) alloc(BUFSZ)) = '\0';
5103
5104     if (!*str) {
5105         s = eos(buf);
5106         if (s > &buf[1] && s[-2] == ',')
5107             Strcpy(s - 2, "."); /* replace last ", " */
5108         i = COLNO;              /* (greater than COLNO - 2) */
5109     } else {
5110         i = strlen(buf) + strlen(str) + 2;
5111     }
5112
5113     if (i > COLNO - 2) { /* rule of thumb */
5114         putstr(datawin, 0, buf);
5115         buf[0] = 0;
5116     }
5117     if (*str) {
5118         Strcat(buf, str);
5119         Strcat(buf, ", ");
5120     } else {
5121         putstr(datawin, 0, str);
5122         free((genericptr_t) buf), buf = 0;
5123     }
5124     return;
5125 }
5126
5127 /* Returns the fid of the fruit type; if that type already exists, it
5128  * returns the fid of that one; if it does not exist, it adds a new fruit
5129  * type to the chain and returns the new one.
5130  * If replace_fruit is sent in, replace the fruit in the chain rather than
5131  * adding a new entry--for user specified fruits only.
5132  */
5133 int
5134 fruitadd(str, replace_fruit)
5135 char *str;
5136 struct fruit *replace_fruit;
5137 {
5138     register int i;
5139     register struct fruit *f;
5140     int highest_fruit_id = 0;
5141     char buf[PL_FSIZ], altname[PL_FSIZ];
5142     boolean user_specified = (str == pl_fruit);
5143     /* if not user-specified, then it's a fruit name for a fruit on
5144      * a bones level...
5145      */
5146
5147     /* Note: every fruit has an id (kept in obj->spe) of at least 1;
5148      * 0 is an error.
5149      */
5150     if (user_specified) {
5151         boolean found = FALSE, numeric = FALSE;
5152
5153         /* force fruit to be singular; this handling is not
5154            needed--or wanted--for fruits from bones because
5155            they already received it in their original game */
5156         nmcpy(pl_fruit, makesingular(str), PL_FSIZ);
5157         /* assert( str == pl_fruit ); */
5158
5159         /* disallow naming after other foods (since it'd be impossible
5160          * to tell the difference)
5161          */
5162
5163         for (i = bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS; i++) {
5164             if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) {
5165                 found = TRUE;
5166                 break;
5167             }
5168         }
5169         {
5170             char *c;
5171
5172             c = pl_fruit;
5173
5174             for (c = pl_fruit; *c >= '0' && *c <= '9'; c++)
5175                 ;
5176             if (isspace((uchar) *c) || *c == 0)
5177                 numeric = TRUE;
5178         }
5179         if (found || numeric || !strncmp(str, "cursed ", 7)
5180             || !strncmp(str, "uncursed ", 9) || !strncmp(str, "blessed ", 8)
5181             || !strncmp(str, "partly eaten ", 13)
5182             || (!strncmp(str, "tin of ", 7)
5183                 && (!strcmp(str + 7, "spinach")
5184                     || name_to_mon(str + 7) >= LOW_PM))
5185             || !strcmp(str, "empty tin")
5186             || ((str_end_is(str, " corpse")
5187                  || str_end_is(str, " egg"))
5188                 && name_to_mon(str) >= LOW_PM)) {
5189             Strcpy(buf, pl_fruit);
5190             Strcpy(pl_fruit, "candied ");
5191             nmcpy(pl_fruit + 8, buf, PL_FSIZ - 8);
5192         }
5193         *altname = '\0';
5194         /* This flag indicates that a fruit has been made since the
5195          * last time the user set the fruit.  If it hasn't, we can
5196          * safely overwrite the current fruit, preventing the user from
5197          * setting many fruits in a row and overflowing.
5198          * Possible expansion: check for specific fruit IDs, not for
5199          * any fruit.
5200          */
5201         flags.made_fruit = FALSE;
5202         if (replace_fruit) {
5203             for (f = ffruit; f; f = f->nextf) {
5204                 if (f == replace_fruit) {
5205                     copynchars(f->fname, str, PL_FSIZ - 1);
5206                     goto nonew;
5207                 }
5208             }
5209         }
5210     } else {
5211         /* not user_supplied, so assumed to be from bones */
5212         copynchars(altname, str, PL_FSIZ - 1);
5213         sanitize_name(altname);
5214         flags.made_fruit = TRUE; /* for safety.  Any fruit name added from a
5215                                     bones level should exist anyway. */
5216     }
5217     for (f = ffruit; f; f = f->nextf) {
5218         if (f->fid > highest_fruit_id)
5219             highest_fruit_id = f->fid;
5220         if (!strncmp(str, f->fname, PL_FSIZ - 1)
5221             || (*altname && !strcmp(altname, f->fname)))
5222             goto nonew;
5223     }
5224     /* if adding another fruit would overflow spe, use a random
5225        fruit instead... we've got a lot to choose from.
5226        current_fruit remains as is. */
5227     if (highest_fruit_id >= 127)
5228         return rnd(127);
5229
5230     f = newfruit();
5231     copynchars(f->fname, *altname ? altname : str, PL_FSIZ - 1);
5232     f->fid = ++highest_fruit_id;
5233     /* we used to go out of our way to add it at the end of the list,
5234        but the order is arbitrary so use simpler insertion at start */
5235     f->nextf = ffruit;
5236     ffruit = f;
5237 nonew:
5238     if (user_specified)
5239         context.current_fruit = f->fid;
5240     return f->fid;
5241 }
5242
5243 /*
5244  * This is a somewhat generic menu for taking a list of NetHack style
5245  * class choices and presenting them via a description
5246  * rather than the traditional NetHack characters.
5247  * (Benefits users whose first exposure to NetHack is via tiles).
5248  *
5249  * prompt
5250  *           The title at the top of the menu.
5251  *
5252  * category: 0 = monster class
5253  *           1 = object  class
5254  *
5255  * way
5256  *           FALSE = PICK_ONE, TRUE = PICK_ANY
5257  *
5258  * class_list
5259  *           a null terminated string containing the list of choices.
5260  *
5261  * class_selection
5262  *           a null terminated string containing the selected characters.
5263  *
5264  * Returns number selected.
5265  */
5266 int
5267 choose_classes_menu(prompt, category, way, class_list, class_select)
5268 const char *prompt;
5269 int category;
5270 boolean way;
5271 char *class_list;
5272 char *class_select;
5273 {
5274     menu_item *pick_list = (menu_item *) 0;
5275     winid win;
5276     anything any;
5277     char buf[BUFSZ];
5278     int i, n;
5279     int ret;
5280     int next_accelerator, accelerator;
5281
5282     if (class_list == (char *) 0 || class_select == (char *) 0)
5283         return 0;
5284     accelerator = 0;
5285     next_accelerator = 'a';
5286     any = zeroany;
5287     win = create_nhwindow(NHW_MENU);
5288     start_menu(win);
5289     while (*class_list) {
5290         const char *text;
5291         boolean selected;
5292
5293         text = (char *) 0;
5294         selected = FALSE;
5295         switch (category) {
5296         case 0:
5297             text = def_monsyms[def_char_to_monclass(*class_list)].explain;
5298             accelerator = *class_list;
5299             Sprintf(buf, "%s", text);
5300             break;
5301         case 1:
5302             text = def_oc_syms[def_char_to_objclass(*class_list)].explain;
5303             accelerator = next_accelerator;
5304             Sprintf(buf, "%c  %s", *class_list, text);
5305             break;
5306         default:
5307             impossible("choose_classes_menu: invalid category %d", category);
5308         }
5309         if (way && *class_select) { /* Selections there already */
5310             if (index(class_select, *class_list)) {
5311                 selected = TRUE;
5312             }
5313         }
5314         any.a_int = *class_list;
5315         add_menu(win, NO_GLYPH, &any, accelerator, category ? *class_list : 0,
5316                  ATR_NONE, buf, selected);
5317         ++class_list;
5318         if (category > 0) {
5319             ++next_accelerator;
5320             if (next_accelerator == ('z' + 1))
5321                 next_accelerator = 'A';
5322             if (next_accelerator == ('Z' + 1))
5323                 break;
5324         }
5325     }
5326     end_menu(win, prompt);
5327     n = select_menu(win, way ? PICK_ANY : PICK_ONE, &pick_list);
5328     destroy_nhwindow(win);
5329     if (n > 0) {
5330         for (i = 0; i < n; ++i)
5331             *class_select++ = (char) pick_list[i].item.a_int;
5332         free((genericptr_t) pick_list);
5333         ret = n;
5334     } else if (n == -1) {
5335         class_select = eos(class_select);
5336         ret = -1;
5337     } else
5338         ret = 0;
5339     *class_select = '\0';
5340     return ret;
5341 }
5342
5343 struct wc_Opt wc_options[] = { { "ascii_map", WC_ASCII_MAP },
5344                                { "color", WC_COLOR },
5345                                { "eight_bit_tty", WC_EIGHT_BIT_IN },
5346                                { "hilite_pet", WC_HILITE_PET },
5347                                { "popup_dialog", WC_POPUP_DIALOG },
5348                                { "player_selection", WC_PLAYER_SELECTION },
5349                                { "preload_tiles", WC_PRELOAD_TILES },
5350                                { "tiled_map", WC_TILED_MAP },
5351                                { "tile_file", WC_TILE_FILE },
5352                                { "tile_width", WC_TILE_WIDTH },
5353                                { "tile_height", WC_TILE_HEIGHT },
5354                                { "use_inverse", WC_INVERSE },
5355                                { "align_message", WC_ALIGN_MESSAGE },
5356                                { "align_status", WC_ALIGN_STATUS },
5357                                { "font_map", WC_FONT_MAP },
5358                                { "font_menu", WC_FONT_MENU },
5359                                { "font_message", WC_FONT_MESSAGE },
5360 #if 0
5361                                {"perm_invent", WC_PERM_INVENT},
5362 #endif
5363                                { "font_size_map", WC_FONTSIZ_MAP },
5364                                { "font_size_menu", WC_FONTSIZ_MENU },
5365                                { "font_size_message", WC_FONTSIZ_MESSAGE },
5366                                { "font_size_status", WC_FONTSIZ_STATUS },
5367                                { "font_size_text", WC_FONTSIZ_TEXT },
5368                                { "font_status", WC_FONT_STATUS },
5369                                { "font_text", WC_FONT_TEXT },
5370                                { "map_mode", WC_MAP_MODE },
5371                                { "scroll_amount", WC_SCROLL_AMOUNT },
5372                                { "scroll_margin", WC_SCROLL_MARGIN },
5373                                { "splash_screen", WC_SPLASH_SCREEN },
5374                                { "vary_msgcount", WC_VARY_MSGCOUNT },
5375                                { "windowcolors", WC_WINDOWCOLORS },
5376                                { "mouse_support", WC_MOUSE_SUPPORT },
5377                                { (char *) 0, 0L } };
5378
5379 struct wc_Opt wc2_options[] = { { "fullscreen", WC2_FULLSCREEN },
5380                                 { "softkeyboard", WC2_SOFTKEYBOARD },
5381                                 { "wraptext", WC2_WRAPTEXT },
5382                                 { "use_darkgray", WC2_DARKGRAY },
5383 #ifdef STATUS_VIA_WINDOWPORT
5384                                 { "hilite_status", WC2_HILITE_STATUS },
5385 #endif
5386                                 { (char *) 0, 0L } };
5387
5388 /*
5389  * If a port wants to change or ensure that the SET_IN_SYS,
5390  * SET_IN_FILE, DISP_IN_GAME, or SET_IN_GAME status of an option is
5391  * correct (for controlling its display in the option menu) call
5392  * set_option_mod_status()
5393  * with the appropriate second argument.
5394  */
5395 void
5396 set_option_mod_status(optnam, status)
5397 const char *optnam;
5398 int status;
5399 {
5400     int k;
5401
5402     if (SET__IS_VALUE_VALID(status)) {
5403         impossible("set_option_mod_status: status out of range %d.", status);
5404         return;
5405     }
5406     for (k = 0; boolopt[k].name; k++) {
5407         if (!strncmpi(boolopt[k].name, optnam, strlen(optnam))) {
5408             boolopt[k].optflags = status;
5409             return;
5410         }
5411     }
5412     for (k = 0; compopt[k].name; k++) {
5413         if (!strncmpi(compopt[k].name, optnam, strlen(optnam))) {
5414             compopt[k].optflags = status;
5415             return;
5416         }
5417     }
5418 }
5419
5420 /*
5421  * You can set several wc_options in one call to
5422  * set_wc_option_mod_status() by setting
5423  * the appropriate bits for each option that you
5424  * are setting in the optmask argument
5425  * prior to calling.
5426  *    example: set_wc_option_mod_status(WC_COLOR|WC_SCROLL_MARGIN,
5427  * SET_IN_GAME);
5428  */
5429 void
5430 set_wc_option_mod_status(optmask, status)
5431 unsigned long optmask;
5432 int status;
5433 {
5434     int k = 0;
5435
5436     if (SET__IS_VALUE_VALID(status)) {
5437         impossible("set_wc_option_mod_status: status out of range %d.",
5438                    status);
5439         return;
5440     }
5441     while (wc_options[k].wc_name) {
5442         if (optmask & wc_options[k].wc_bit) {
5443             set_option_mod_status(wc_options[k].wc_name, status);
5444         }
5445         k++;
5446     }
5447 }
5448
5449 STATIC_OVL boolean
5450 is_wc_option(optnam)
5451 const char *optnam;
5452 {
5453     int k = 0;
5454
5455     while (wc_options[k].wc_name) {
5456         if (strcmp(wc_options[k].wc_name, optnam) == 0)
5457             return TRUE;
5458         k++;
5459     }
5460     return FALSE;
5461 }
5462
5463 STATIC_OVL boolean
5464 wc_supported(optnam)
5465 const char *optnam;
5466 {
5467     int k = 0;
5468
5469     while (wc_options[k].wc_name) {
5470         if (!strcmp(wc_options[k].wc_name, optnam)
5471             && (windowprocs.wincap & wc_options[k].wc_bit))
5472             return TRUE;
5473         k++;
5474     }
5475     return FALSE;
5476 }
5477
5478 /*
5479  * You can set several wc2_options in one call to
5480  * set_wc2_option_mod_status() by setting
5481  * the appropriate bits for each option that you
5482  * are setting in the optmask argument
5483  * prior to calling.
5484  *    example:
5485  * set_wc2_option_mod_status(WC2_FULLSCREEN|WC2_SOFTKEYBOARD|WC2_WRAPTEXT,
5486  * SET_IN_FILE);
5487  */
5488
5489 void
5490 set_wc2_option_mod_status(optmask, status)
5491 unsigned long optmask;
5492 int status;
5493 {
5494     int k = 0;
5495
5496     if (SET__IS_VALUE_VALID(status)) {
5497         impossible("set_wc2_option_mod_status: status out of range %d.",
5498                    status);
5499         return;
5500     }
5501     while (wc2_options[k].wc_name) {
5502         if (optmask & wc2_options[k].wc_bit) {
5503             set_option_mod_status(wc2_options[k].wc_name, status);
5504         }
5505         k++;
5506     }
5507 }
5508
5509 STATIC_OVL boolean
5510 is_wc2_option(optnam)
5511 const char *optnam;
5512 {
5513     int k = 0;
5514
5515     while (wc2_options[k].wc_name) {
5516         if (strcmp(wc2_options[k].wc_name, optnam) == 0)
5517             return TRUE;
5518         k++;
5519     }
5520     return FALSE;
5521 }
5522
5523 STATIC_OVL boolean
5524 wc2_supported(optnam)
5525 const char *optnam;
5526 {
5527     int k = 0;
5528
5529     while (wc2_options[k].wc_name) {
5530         if (!strcmp(wc2_options[k].wc_name, optnam)
5531             && (windowprocs.wincap2 & wc2_options[k].wc_bit))
5532             return TRUE;
5533         k++;
5534     }
5535     return FALSE;
5536 }
5537
5538 STATIC_OVL void
5539 wc_set_font_name(opttype, fontname)
5540 int opttype;
5541 char *fontname;
5542 {
5543     char **fn = (char **) 0;
5544
5545     if (!fontname)
5546         return;
5547     switch (opttype) {
5548     case MAP_OPTION:
5549         fn = &iflags.wc_font_map;
5550         break;
5551     case MESSAGE_OPTION:
5552         fn = &iflags.wc_font_message;
5553         break;
5554     case TEXT_OPTION:
5555         fn = &iflags.wc_font_text;
5556         break;
5557     case MENU_OPTION:
5558         fn = &iflags.wc_font_menu;
5559         break;
5560     case STATUS_OPTION:
5561         fn = &iflags.wc_font_status;
5562         break;
5563     default:
5564         return;
5565     }
5566     if (fn) {
5567         if (*fn)
5568             free((genericptr_t) *fn);
5569         *fn = dupstr(fontname);
5570     }
5571     return;
5572 }
5573
5574 STATIC_OVL int
5575 wc_set_window_colors(op)
5576 char *op;
5577 {
5578     /* syntax:
5579      *  menu white/black message green/yellow status white/blue text
5580      * white/black
5581      */
5582     int j;
5583     char buf[BUFSZ];
5584     char *wn, *tfg, *tbg, *newop;
5585     static const char *wnames[] = { "menu", "message", "status", "text" };
5586     static const char *shortnames[] = { "mnu", "msg", "sts", "txt" };
5587     static char **fgp[] = { &iflags.wc_foregrnd_menu,
5588                             &iflags.wc_foregrnd_message,
5589                             &iflags.wc_foregrnd_status,
5590                             &iflags.wc_foregrnd_text };
5591     static char **bgp[] = { &iflags.wc_backgrnd_menu,
5592                             &iflags.wc_backgrnd_message,
5593                             &iflags.wc_backgrnd_status,
5594                             &iflags.wc_backgrnd_text };
5595
5596     Strcpy(buf, op);
5597     newop = mungspaces(buf);
5598     while (newop && *newop) {
5599         wn = tfg = tbg = (char *) 0;
5600
5601         /* until first non-space in case there's leading spaces - before
5602          * colorname*/
5603         if (*newop == ' ')
5604             newop++;
5605         if (*newop)
5606             wn = newop;
5607         else
5608             return 0;
5609
5610         /* until first space - colorname*/
5611         while (*newop && *newop != ' ')
5612             newop++;
5613         if (*newop)
5614             *newop = '\0';
5615         else
5616             return 0;
5617         newop++;
5618
5619         /* until first non-space - before foreground*/
5620         if (*newop == ' ')
5621             newop++;
5622         if (*newop)
5623             tfg = newop;
5624         else
5625             return 0;
5626
5627         /* until slash - foreground */
5628         while (*newop && *newop != '/')
5629             newop++;
5630         if (*newop)
5631             *newop = '\0';
5632         else
5633             return 0;
5634         newop++;
5635
5636         /* until first non-space (in case there's leading space after slash) -
5637          * before background */
5638         if (*newop == ' ')
5639             newop++;
5640         if (*newop)
5641             tbg = newop;
5642         else
5643             return 0;
5644
5645         /* until first space - background */
5646         while (*newop && *newop != ' ')
5647             newop++;
5648         if (*newop)
5649             *newop++ = '\0';
5650
5651         for (j = 0; j < 4; ++j) {
5652             if (!strcmpi(wn, wnames[j]) || !strcmpi(wn, shortnames[j])) {
5653                 if (tfg && !strstri(tfg, " ")) {
5654                     if (*fgp[j])
5655                         free((genericptr_t) *fgp[j]);
5656                     *fgp[j] = dupstr(tfg);
5657                 }
5658                 if (tbg && !strstri(tbg, " ")) {
5659                     if (*bgp[j])
5660                         free((genericptr_t) *bgp[j]);
5661                     *bgp[j] = dupstr(tbg);
5662                 }
5663                 break;
5664             }
5665         }
5666     }
5667     return 1;
5668 }
5669
5670 /* set up for wizard mode if player or save file has requested to it;
5671    called from port-specific startup code to handle `nethack -D' or
5672    OPTIONS=playmode:debug, or from dorecover()'s restgamestate() if
5673    restoring a game which was saved in wizard mode */
5674 void
5675 set_playmode()
5676 {
5677     if (wizard) {
5678         if (authorize_wizard_mode())
5679             Strcpy(plname, "wizard");
5680         else
5681             wizard = FALSE; /* not allowed or not available */
5682         /* force explore mode if we didn't make it into wizard mode */
5683         discover = !wizard;
5684         iflags.deferred_X = FALSE;
5685     }
5686     /* don't need to do anything special for explore mode or normal play */
5687 }
5688
5689 #endif /* OPTION_LISTS_ONLY */
5690
5691 /*options.c*/