OSDN Git Service

add gitignore
[nethackexpress/trunk.git] / src / options.c
1 /*      SCCS Id: @(#)options.c  3.4     2003/11/14      */
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 NEARDATA struct instance_flags iflags;  /* provide linkage */
11 #define static
12 #else
13 #include "hack.h"
14 #include "tcap.h"
15 #include <ctype.h>
16 #endif
17
18 #define WINTYPELEN 16
19
20 #ifdef DEFAULT_WC_TILED_MAP
21 #define PREFER_TILED TRUE
22 #else
23 #define PREFER_TILED FALSE
24 #endif
25
26 /*
27  *  NOTE:  If you add (or delete) an option, please update the short
28  *  options help (option_help()), the long options help (dat/opthelp),
29  *  and the current options setting display function (doset()),
30  *  and also the Guidebooks.
31  *
32  *  The order matters.  If an option is a an initial substring of another
33  *  option (e.g. time and timed_delay) the shorter one must come first.
34  */
35
36 static struct Bool_Opt
37 {
38         const char *name;
39         boolean *addr, initvalue;
40         int optflags;
41 } boolopt[] = {
42 #ifdef AMIGA
43         {"altmeta", &flags.altmeta, TRUE, DISP_IN_GAME},
44 #else
45         {"altmeta", (boolean *)0, TRUE, DISP_IN_GAME},
46 #endif
47         {"ascii_map",     &iflags.wc_ascii_map, !PREFER_TILED, SET_IN_GAME},    /*WC*/
48 #ifdef MFLOPPY
49         {"asksavedisk", &flags.asksavedisk, FALSE, SET_IN_GAME},
50 #else
51         {"asksavedisk", (boolean *)0, FALSE, SET_IN_FILE},
52 #endif
53         {"autodig", &flags.autodig, FALSE, SET_IN_GAME},
54         {"autopickup", &flags.pickup, TRUE, SET_IN_GAME},
55         {"autoquiver", &flags.autoquiver, FALSE, SET_IN_GAME},
56 #if defined(MICRO) && !defined(AMIGA)
57         {"BIOS", &iflags.BIOS, FALSE, SET_IN_FILE},
58 #else
59         {"BIOS", (boolean *)0, FALSE, SET_IN_FILE},
60 #endif
61 #ifdef INSURANCE
62         {"checkpoint", &flags.ins_chkpt, TRUE, SET_IN_GAME},
63 #else
64         {"checkpoint", (boolean *)0, FALSE, SET_IN_FILE},
65 #endif
66 #ifdef MFLOPPY
67         {"checkspace", &iflags.checkspace, TRUE, SET_IN_GAME},
68 #else
69         {"checkspace", (boolean *)0, FALSE, SET_IN_FILE},
70 #endif
71         {"cmdassist", &iflags.cmdassist, TRUE, SET_IN_GAME},
72 # if defined(MICRO) || defined(WIN32)
73         {"color",         &iflags.wc_color,TRUE, SET_IN_GAME},          /*WC*/
74 # else  /* systems that support multiple terminals, many monochrome */
75         {"color",         &iflags.wc_color, FALSE, SET_IN_GAME},        /*WC*/
76 # endif
77         {"confirm",&flags.confirm, TRUE, SET_IN_GAME},
78 #if defined(TERMLIB) && !defined(MAC_GRAPHICS_ENV)
79         {"DECgraphics", &iflags.DECgraphics, FALSE, SET_IN_GAME},
80 #else
81         {"DECgraphics", (boolean *)0, FALSE, SET_IN_FILE},
82 #endif
83         {"eight_bit_tty", &iflags.wc_eight_bit_input, FALSE, SET_IN_GAME},      /*WC*/
84 #ifdef TTY_GRAPHICS
85         {"extmenu", &iflags.extmenu, FALSE, SET_IN_GAME},
86 #else
87         {"extmenu", (boolean *)0, FALSE, SET_IN_FILE},
88 #endif
89 #ifdef OPT_DISPMAP
90         {"fast_map", &flags.fast_map, TRUE, SET_IN_GAME},
91 #else
92         {"fast_map", (boolean *)0, TRUE, SET_IN_FILE},
93 #endif
94         {"female", &flags.female, FALSE, DISP_IN_GAME},
95         {"fixinv", &flags.invlet_constant, TRUE, SET_IN_GAME},
96 #ifdef AMIFLUSH
97         {"flush", &flags.amiflush, FALSE, SET_IN_GAME},
98 #else
99         {"flush", (boolean *)0, FALSE, SET_IN_FILE},
100 #endif
101         {"fullscreen", &iflags.wc2_fullscreen, FALSE, SET_IN_FILE},
102         {"help", &flags.help, TRUE, SET_IN_GAME},
103         {"hilite_pet",    &iflags.wc_hilite_pet, FALSE, SET_IN_GAME},   /*WC*/
104 #ifdef ASCIIGRAPH
105         {"IBMgraphics", &iflags.IBMgraphics, FALSE, SET_IN_GAME},
106 #else
107         {"IBMgraphics", (boolean *)0, FALSE, SET_IN_FILE},
108 #endif
109 #ifndef MAC
110         {"ignintr", &flags.ignintr, FALSE, SET_IN_GAME},
111 #else
112         {"ignintr", (boolean *)0, FALSE, SET_IN_FILE},
113 #endif
114         {"large_font", &iflags.obsolete, FALSE, SET_IN_FILE},   /* OBSOLETE */
115         {"legacy", &flags.legacy, TRUE, DISP_IN_GAME},
116         {"lit_corridor", &flags.lit_corridor, FALSE, SET_IN_GAME},
117         {"lootabc", &iflags.lootabc, FALSE, SET_IN_GAME},
118 #ifdef MAC_GRAPHICS_ENV
119         {"Macgraphics", &iflags.MACgraphics, TRUE, SET_IN_GAME},
120 #else
121         {"Macgraphics", (boolean *)0, FALSE, SET_IN_FILE},
122 #endif
123 #ifdef MAIL
124         {"mail", &flags.biff, TRUE, SET_IN_GAME},
125 #else
126         {"mail", (boolean *)0, TRUE, SET_IN_FILE},
127 #endif
128 #ifdef WIZARD
129         /* for menu debugging only*/
130         {"menu_tab_sep", &iflags.menu_tab_sep, FALSE, SET_IN_GAME},
131 #else
132         {"menu_tab_sep", (boolean *)0, FALSE, SET_IN_FILE},
133 #endif
134         {"mouse_support", &iflags.wc_mouse_support, TRUE, DISP_IN_GAME},        /*WC*/
135 #ifdef NEWS
136         {"news", &iflags.news, TRUE, DISP_IN_GAME},
137 #else
138         {"news", (boolean *)0, FALSE, SET_IN_FILE},
139 #endif
140         {"null", &flags.null, TRUE, SET_IN_GAME},
141 #ifdef MAC
142         {"page_wait", &flags.page_wait, TRUE, SET_IN_GAME},
143 #else
144         {"page_wait", (boolean *)0, FALSE, SET_IN_FILE},
145 #endif
146         {"perm_invent", &flags.perm_invent, FALSE, SET_IN_GAME},
147         {"popup_dialog",  &iflags.wc_popup_dialog, FALSE, SET_IN_GAME}, /*WC*/
148         {"prayconfirm", &flags.prayconfirm, TRUE, SET_IN_GAME},
149         {"preload_tiles", &iflags.wc_preload_tiles, TRUE, DISP_IN_GAME},        /*WC*/
150         {"pushweapon", &flags.pushweapon, FALSE, SET_IN_GAME},
151 #if defined(MICRO) && !defined(AMIGA)
152         {"rawio", &iflags.rawio, FALSE, DISP_IN_GAME},
153 #else
154         {"rawio", (boolean *)0, FALSE, SET_IN_FILE},
155 #endif
156         {"rest_on_space", &flags.rest_on_space, FALSE, SET_IN_GAME},
157         {"safe_pet", &flags.safe_dog, TRUE, SET_IN_GAME},
158 #ifdef WIZARD
159         {"sanity_check", &iflags.sanity_check, FALSE, SET_IN_GAME},
160 #else
161         {"sanity_check", (boolean *)0, FALSE, SET_IN_FILE},
162 #endif
163 #ifdef EXP_ON_BOTL
164         {"showexp", &flags.showexp, FALSE, SET_IN_GAME},
165 #else
166         {"showexp", (boolean *)0, FALSE, SET_IN_FILE},
167 #endif
168         {"showrace", &iflags.showrace, FALSE, SET_IN_GAME},
169 #ifdef SCORE_ON_BOTL
170         {"showscore", &flags.showscore, FALSE, SET_IN_GAME},
171 #else
172         {"showscore", (boolean *)0, FALSE, SET_IN_FILE},
173 #endif
174         {"silent", &flags.silent, TRUE, SET_IN_GAME},
175         {"softkeyboard", &iflags.wc2_softkeyboard, FALSE, SET_IN_FILE},
176         {"sortpack", &flags.sortpack, TRUE, SET_IN_GAME},
177         {"sound", &flags.soundok, TRUE, SET_IN_GAME},
178         {"sparkle", &flags.sparkle, TRUE, SET_IN_GAME},
179         {"standout", &flags.standout, FALSE, SET_IN_GAME},
180         {"splash_screen",     &iflags.wc_splash_screen, TRUE, DISP_IN_GAME},    /*WC*/
181         {"tiled_map",     &iflags.wc_tiled_map, PREFER_TILED, DISP_IN_GAME},    /*WC*/
182         {"time", &flags.time, FALSE, SET_IN_GAME},
183 #ifdef TIMED_DELAY
184         {"timed_delay", &flags.nap, TRUE, SET_IN_GAME},
185 #else
186         {"timed_delay", (boolean *)0, FALSE, SET_IN_GAME},
187 #endif
188         {"tombstone",&flags.tombstone, TRUE, SET_IN_GAME},
189         {"toptenwin",&flags.toptenwin, FALSE, SET_IN_GAME},
190         {"travel", &iflags.travelcmd, TRUE, SET_IN_GAME},
191 #ifdef WIN32CON
192         {"use_inverse",   &iflags.wc_inverse, TRUE, SET_IN_GAME},               /*WC*/
193 #else
194         {"use_inverse",   &iflags.wc_inverse, FALSE, SET_IN_GAME},              /*WC*/
195 #endif
196         {"verbose", &flags.verbose, TRUE, SET_IN_GAME},
197         {"wraptext", &iflags.wc2_wraptext, FALSE, SET_IN_GAME},
198         {(char *)0, (boolean *)0, FALSE, 0}
199 };
200
201 /* compound options, for option_help() and external programs like Amiga
202  * frontend */
203 static struct Comp_Opt
204 {
205         const char *name, *descr;
206         int size;       /* for frontends and such allocating space --
207                          * usually allowed size of data in game, but
208                          * occasionally maximum reasonable size for
209                          * typing when game maintains information in
210                          * a different format */
211         int optflags;
212 } compopt[] = {
213         { "align",    "your starting alignment (lawful, neutral, or chaotic)",
214                                                 8, DISP_IN_GAME },
215         { "align_message", "message window alignment", 20, DISP_IN_GAME },      /*WC*/
216         { "align_status", "status window alignment", 20, DISP_IN_GAME },        /*WC*/
217         { "altkeyhandler", "alternate key handler", 20, DISP_IN_GAME },
218         { "boulder",  "the symbol to use for displaying boulders",
219                                                 1, SET_IN_GAME },
220         { "catname",  "the name of your (first) cat (e.g., catname:Tabby)",
221                                                 PL_PSIZ, DISP_IN_GAME },
222         { "disclose", "the kinds of information to disclose at end of game",
223                                                 sizeof(flags.end_disclose) * 2,
224                                                 SET_IN_GAME },
225         { "dogname",  "the name of your (first) dog (e.g., dogname:Fang)",
226                                                 PL_PSIZ, DISP_IN_GAME },
227         { "dungeon",  "the symbols to use in drawing the dungeon map",
228                                                 MAXDCHARS+1, SET_IN_FILE },
229         { "effects",  "the symbols to use in drawing special effects",
230                                                 MAXECHARS+1, SET_IN_FILE },
231         { "font_map", "the font to use in the map window", 40, DISP_IN_GAME },  /*WC*/
232         { "font_menu", "the font to use in menus", 40, DISP_IN_GAME },          /*WC*/
233         { "font_message", "the font to use in the message window",
234                                                 40, DISP_IN_GAME },             /*WC*/
235         { "font_size_map", "the size of the map font", 20, DISP_IN_GAME },      /*WC*/
236         { "font_size_menu", "the size of the menu font", 20, DISP_IN_GAME },    /*WC*/
237         { "font_size_message", "the size of the message font", 20, DISP_IN_GAME },      /*WC*/
238         { "font_size_status", "the size of the status font", 20, DISP_IN_GAME },        /*WC*/
239         { "font_size_text", "the size of the text font", 20, DISP_IN_GAME },    /*WC*/
240         { "font_status", "the font to use in status window", 40, DISP_IN_GAME }, /*WC*/
241         { "font_text", "the font to use in text windows", 40, DISP_IN_GAME },   /*WC*/
242         { "fruit",    "the name of a fruit you enjoy eating",
243                                                 PL_FSIZ, SET_IN_GAME },
244         { "gender",   "your starting gender (male or female)",
245                                                 8, DISP_IN_GAME },
246         { "horsename", "the name of your (first) horse (e.g., horsename:Silver)",
247                                                 PL_PSIZ, DISP_IN_GAME },
248         { "map_mode", "map display mode under Windows", 20, DISP_IN_GAME },     /*WC*/
249         { "menustyle", "user interface for object selection",
250                                                 MENUTYPELEN, SET_IN_GAME },
251         { "menu_deselect_all", "deselect all items in a menu", 4, SET_IN_FILE },
252         { "menu_deselect_page", "deselect all items on this page of a menu",
253                                                 4, SET_IN_FILE },
254         { "menu_first_page", "jump to the first page in a menu",
255                                                 4, SET_IN_FILE },
256         { "menu_headings", "bold, inverse, or underline headings", 9, SET_IN_GAME },
257         { "menu_invert_all", "invert all items in a menu", 4, SET_IN_FILE },
258         { "menu_invert_page", "invert all items on this page of a menu",
259                                                 4, SET_IN_FILE },
260         { "menu_last_page", "jump to the last page in a menu", 4, SET_IN_FILE },
261         { "menu_next_page", "goto the next menu page", 4, SET_IN_FILE },
262         { "menu_previous_page", "goto the previous menu page", 4, SET_IN_FILE },
263         { "menu_search", "search for a menu item", 4, SET_IN_FILE },
264         { "menu_select_all", "select all items in a menu", 4, SET_IN_FILE },
265         { "menu_select_page", "select all items on this page of a menu",
266                                                 4, SET_IN_FILE },
267         { "monsters", "the symbols to use for monsters",
268                                                 MAXMCLASSES, SET_IN_FILE },
269         { "msghistory", "number of top line messages to save",
270                                                 5, DISP_IN_GAME },
271 # ifdef TTY_GRAPHICS
272         {"msg_window", "the type of message window required",1, SET_IN_GAME},
273 # else
274         {"msg_window", "the type of message window required", 1, SET_IN_FILE},
275 # endif
276         { "name",     "your character's name (e.g., name:Merlin-W)",
277                                                 PL_NSIZ, DISP_IN_GAME },
278         { "number_pad", "use the number pad", 1, SET_IN_GAME},
279         { "objects",  "the symbols to use for objects",
280                                                 MAXOCLASSES, SET_IN_FILE },
281         { "packorder", "the inventory order of the items in your pack",
282                                                 MAXOCLASSES, SET_IN_GAME },
283 #ifdef CHANGE_COLOR
284         { "palette",  "palette (00c/880/-fff is blue/yellow/reverse white)",
285                                                 15 , SET_IN_GAME },
286 # if defined(MAC)
287         { "hicolor",  "same as palette, only order is reversed",
288                                                 15, SET_IN_FILE },
289 # endif
290 #endif
291         { "pettype",  "your preferred initial pet type", 4, DISP_IN_GAME },
292         { "pickup_burden",  "maximum burden picked up before prompt",
293                                                 20, SET_IN_GAME },
294         { "pickup_types", "types of objects to pick up automatically",
295                                                 MAXOCLASSES, SET_IN_GAME },
296         { "player_selection", "choose character via dialog or prompts",
297                                                 12, DISP_IN_GAME },
298         { "race",     "your starting race (e.g., Human, Elf)",
299                                                 PL_CSIZ, DISP_IN_GAME },
300         { "role",     "your starting role (e.g., Barbarian, Valkyrie)",
301                                                 PL_CSIZ, DISP_IN_GAME },
302         { "runmode", "display frequency when `running' or `travelling'",
303                                                 sizeof "teleport", SET_IN_GAME },
304         { "scores",   "the parts of the score list you wish to see",
305                                                 32, SET_IN_GAME },
306         { "scroll_amount", "amount to scroll map when scroll_margin is reached",
307                                                 20, DISP_IN_GAME }, /*WC*/
308         { "scroll_margin", "scroll map when this far from the edge", 20, DISP_IN_GAME }, /*WC*/
309 #ifdef MSDOS
310         { "soundcard", "type of sound card to use", 20, SET_IN_FILE },
311 #endif
312         { "suppress_alert", "suppress alerts about version-specific features",
313                                                 8, SET_IN_GAME },
314         { "tile_width", "width of tiles", 20, DISP_IN_GAME},    /*WC*/
315         { "tile_height", "height of tiles", 20, DISP_IN_GAME},  /*WC*/
316         { "tile_file", "name of tile file", 70, DISP_IN_GAME},  /*WC*/
317         { "traps",    "the symbols to use in drawing traps",
318                                                 MAXTCHARS+1, SET_IN_FILE },
319         { "vary_msgcount", "show more old messages at a time", 20, DISP_IN_GAME }, /*WC*/
320 #ifdef MSDOS
321         { "video",    "method of video updating", 20, SET_IN_FILE },
322 #endif
323 #ifdef VIDEOSHADES
324         { "videocolors", "color mappings for internal screen routines",
325                                                 40, DISP_IN_GAME },
326         { "videoshades", "gray shades to map to black/gray/white",
327                                                 32, DISP_IN_GAME },
328 #endif
329 #ifdef WIN32CON
330         {"subkeyvalue", "override keystroke value", 7, SET_IN_FILE},
331 #endif
332         { "windowcolors",  "the foreground/background colors of windows",       /*WC*/
333                                                 80, DISP_IN_GAME },
334         { "windowtype", "windowing system to use", WINTYPELEN, DISP_IN_GAME },
335         { (char *)0, (char *)0, 0, 0 }
336 };
337
338 #ifdef OPTION_LISTS_ONLY
339 #undef static
340
341 #else   /* use rest of file */
342
343 static boolean need_redraw; /* for doset() */
344
345 #if defined(TOS) && defined(TEXTCOLOR)
346 extern boolean colors_changed;  /* in tos.c */
347 #endif
348
349 #ifdef VIDEOSHADES
350 extern char *shade[3];            /* in sys/msdos/video.c */
351 extern char ttycolors[CLR_MAX];   /* in sys/msdos/video.c */
352 #endif
353
354 static char def_inv_order[MAXOCLASSES] = {
355         COIN_CLASS, AMULET_CLASS, WEAPON_CLASS, ARMOR_CLASS, FOOD_CLASS,
356         SCROLL_CLASS, SPBOOK_CLASS, POTION_CLASS, RING_CLASS, WAND_CLASS,
357         TOOL_CLASS, GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, 0,
358 };
359
360 /*
361  * Default menu manipulation command accelerators.  These may _not_ be:
362  *
363  *      + a number - reserved for counts
364  *      + an upper or lower case US ASCII letter - used for accelerators
365  *      + ESC - reserved for escaping the menu
366  *      + NULL, CR or LF - reserved for commiting the selection(s).  NULL
367  *        is kind of odd, but the tty's xwaitforspace() will return it if
368  *        someone hits a <ret>.
369  *      + a default object class symbol - used for object class accelerators
370  *
371  * Standard letters (for now) are:
372  *
373  *              <  back 1 page
374  *              >  forward 1 page
375  *              ^  first page
376  *              |  last page
377  *              :  search
378  *
379  *              page            all
380  *               ,    select     .
381  *               \    deselect   -
382  *               ~    invert     @
383  *
384  * The command name list is duplicated in the compopt array.
385  */
386 typedef struct {
387     const char *name;
388     char cmd;
389 } menu_cmd_t;
390
391 #define NUM_MENU_CMDS 11
392 static const menu_cmd_t default_menu_cmd_info[NUM_MENU_CMDS] = {
393 /* 0*/  { "menu_first_page",    MENU_FIRST_PAGE },
394         { "menu_last_page",     MENU_LAST_PAGE },
395         { "menu_next_page",     MENU_NEXT_PAGE },
396         { "menu_previous_page", MENU_PREVIOUS_PAGE },
397         { "menu_select_all",    MENU_SELECT_ALL },
398 /* 5*/  { "menu_deselect_all",  MENU_UNSELECT_ALL },
399         { "menu_invert_all",    MENU_INVERT_ALL },
400         { "menu_select_page",   MENU_SELECT_PAGE },
401         { "menu_deselect_page", MENU_UNSELECT_PAGE },
402         { "menu_invert_page",   MENU_INVERT_PAGE },
403 /*10*/  { "menu_search",                MENU_SEARCH },
404 };
405
406 /*
407  * Allow the user to map incoming characters to various menu commands.
408  * The accelerator list must be a valid C string.
409  */
410 #define MAX_MENU_MAPPED_CMDS 32 /* some number */
411        char mapped_menu_cmds[MAX_MENU_MAPPED_CMDS+1];   /* exported */
412 static char mapped_menu_op[MAX_MENU_MAPPED_CMDS+1];
413 static short n_menu_mapped = 0;
414
415
416 static boolean initial, from_file;
417
418 STATIC_DCL void FDECL(doset_add_menu, (winid,const char *,int));
419 STATIC_DCL void FDECL(nmcpy, (char *, const char *, int));
420 STATIC_DCL void FDECL(escapes, (const char *, char *));
421 STATIC_DCL void FDECL(rejectoption, (const char *));
422 STATIC_DCL void FDECL(badoption, (const char *));
423 STATIC_DCL char *FDECL(string_for_opt, (char *,BOOLEAN_P));
424 STATIC_DCL char *FDECL(string_for_env_opt, (const char *, char *,BOOLEAN_P));
425 STATIC_DCL void FDECL(bad_negation, (const char *,BOOLEAN_P));
426 STATIC_DCL int FDECL(change_inv_order, (char *));
427 STATIC_DCL void FDECL(oc_to_str, (char *, char *));
428 STATIC_DCL void FDECL(graphics_opts, (char *,const char *,int,int));
429 STATIC_DCL int FDECL(feature_alert_opts, (char *, const char *));
430 STATIC_DCL const char *FDECL(get_compopt_value, (const char *, char *));
431 STATIC_DCL boolean FDECL(special_handling, (const char *, BOOLEAN_P, BOOLEAN_P));
432 STATIC_DCL void FDECL(warning_opts, (char *,const char *));
433 STATIC_DCL void FDECL(duplicate_opt_detection, (const char *, int));
434
435 STATIC_OVL void FDECL(wc_set_font_name, (int, char *));
436 STATIC_OVL int FDECL(wc_set_window_colors, (char *));
437 STATIC_OVL boolean FDECL(is_wc_option, (const char *));
438 STATIC_OVL boolean FDECL(wc_supported, (const char *));
439 STATIC_OVL boolean FDECL(is_wc2_option, (const char *));
440 STATIC_OVL boolean FDECL(wc2_supported, (const char *));
441 #ifdef AUTOPICKUP_EXCEPTIONS
442 STATIC_DCL void FDECL(remove_autopickup_exception, (struct autopickup_exception *));
443 STATIC_OVL int FDECL(count_ape_maps, (int *, int *));
444 #endif
445
446 /* check whether a user-supplied option string is a proper leading
447    substring of a particular option name; option string might have
448    a colon or equals sign and arbitrary value appended to it */
449 boolean
450 match_optname(user_string, opt_name, min_length, val_allowed)
451 const char *user_string, *opt_name;
452 int min_length;
453 boolean val_allowed;
454 {
455         int len = (int)strlen(user_string);
456
457         if (val_allowed) {
458             const char *p = index(user_string, ':'),
459                        *q = index(user_string, '=');
460
461             if (!p || (q && q < p)) p = q;
462             while(p && p > user_string && isspace(*(p-1))) p--;
463             if (p) len = (int)(p - user_string);
464         }
465
466         return (len >= min_length) && !strncmpi(opt_name, user_string, len);
467 }
468
469 /* most environment variables will eventually be printed in an error
470  * message if they don't work, and most error message paths go through
471  * BUFSZ buffers, which could be overflowed by a maliciously long
472  * environment variable.  if a variable can legitimately be long, or
473  * if it's put in a smaller buffer, the responsible code will have to
474  * bounds-check itself.
475  */
476 char *
477 nh_getenv(ev)
478 const char *ev;
479 {
480         char *getev = getenv(ev);
481
482         if (getev && strlen(getev) <= (BUFSZ / 2))
483                 return getev;
484         else
485                 return (char *)0;
486 }
487
488 void
489 initoptions()
490 {
491 #ifndef MAC
492         char *opts;
493 #endif
494         int i;
495
496         /* initialize the random number generator */
497         setrandom();
498
499         /* for detection of configfile options specified multiple times */
500         iflags.opt_booldup = iflags.opt_compdup = (int *)0;
501         
502         for (i = 0; boolopt[i].name; i++) {
503                 if (boolopt[i].addr)
504                         *(boolopt[i].addr) = boolopt[i].initvalue;
505         }
506         flags.end_own = FALSE;
507         flags.end_top = 3;
508         flags.end_around = 2;
509         iflags.runmode = RUN_LEAP;
510         iflags.msg_history = 20;
511 #ifdef TTY_GRAPHICS
512         iflags.prevmsg_window = 's';
513 #endif
514         iflags.menu_headings = ATR_INVERSE;
515
516         /* Use negative indices to indicate not yet selected */
517         flags.initrole = -1;
518         flags.initrace = -1;
519         flags.initgend = -1;
520         flags.initalign = -1;
521
522         /* Set the default monster and object class symbols.  Don't use */
523         /* memcpy() --- sizeof char != sizeof uchar on some machines.   */
524         for (i = 0; i < MAXOCLASSES; i++)
525                 oc_syms[i] = (uchar) def_oc_syms[i];
526         for (i = 0; i < MAXMCLASSES; i++)
527                 monsyms[i] = (uchar) def_monsyms[i];
528         for (i = 0; i < WARNCOUNT; i++)
529                 warnsyms[i] = def_warnsyms[i].sym;
530         iflags.bouldersym = 0;
531         iflags.travelcc.x = iflags.travelcc.y = -1;
532         flags.warnlevel = 1;
533         flags.warntype = 0L;
534
535      /* assert( sizeof flags.inv_order == sizeof def_inv_order ); */
536         (void)memcpy((genericptr_t)flags.inv_order,
537                      (genericptr_t)def_inv_order, sizeof flags.inv_order);
538         flags.pickup_types[0] = '\0';
539         flags.pickup_burden = MOD_ENCUMBER;
540
541         for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++)
542                 flags.end_disclose[i] = DISCLOSE_PROMPT_DEFAULT_NO;
543         switch_graphics(ASCII_GRAPHICS);        /* set default characters */
544 #if defined(UNIX) && defined(TTY_GRAPHICS)
545         /*
546          * Set defaults for some options depending on what we can
547          * detect about the environment's capabilities.
548          * This has to be done after the global initialization above
549          * and before reading user-specific initialization via
550          * config file/environment variable below.
551          */
552         /* this detects the IBM-compatible console on most 386 boxes */
553         if ((opts = nh_getenv("TERM")) && !strncmp(opts, "AT", 2)) {
554                 switch_graphics(IBM_GRAPHICS);
555 # ifdef TEXTCOLOR
556                 iflags.use_color = TRUE;
557 # endif
558         }
559 #endif /* UNIX && TTY_GRAPHICS */
560 #if defined(UNIX) || defined(VMS)
561 # ifdef TTY_GRAPHICS
562         /* detect whether a "vt" terminal can handle alternate charsets */
563         if ((opts = nh_getenv("TERM")) &&
564             !strncmpi(opts, "vt", 2) && AS && AE &&
565             index(AS, '\016') && index(AE, '\017')) {
566                 switch_graphics(DEC_GRAPHICS);
567         }
568 # endif
569 #endif /* UNIX || VMS */
570
571 #ifdef MAC_GRAPHICS_ENV
572         switch_graphics(MAC_GRAPHICS);
573 #endif /* MAC_GRAPHICS_ENV */
574         flags.menu_style = MENU_FULL;
575
576         /* since this is done before init_objects(), do partial init here */
577         objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD;
578         nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
579 #ifndef MAC
580         opts = getenv("NETHACKOPTIONS");
581         if (!opts) opts = getenv("HACKOPTIONS");
582         if (opts) {
583                 if (*opts == '/' || *opts == '\\' || *opts == '@') {
584                         if (*opts == '@') opts++;       /* @filename */
585                         /* looks like a filename */
586                         if (strlen(opts) < BUFSZ/2)
587                             read_config_file(opts);
588                 } else {
589                         read_config_file((char *)0);
590                         /* let the total length of options be long;
591                          * parseoptions() will check each individually
592                          */
593                         parseoptions(opts, TRUE, FALSE);
594                 }
595         } else
596 #endif
597                 read_config_file((char *)0);
598
599         (void)fruitadd(pl_fruit);
600         /* Remove "slime mold" from list of object names; this will     */
601         /* prevent it from being wished unless it's actually present    */
602         /* as a named (or default) fruit.  Wishing for "fruit" will     */
603         /* result in the player's preferred fruit [better than "\033"]. */
604         obj_descr[SLIME_MOLD].oc_name = "fruit";
605
606         return;
607 }
608
609 STATIC_OVL void
610 nmcpy(dest, src, maxlen)
611         char    *dest;
612         const char *src;
613         int     maxlen;
614 {
615         int     count;
616
617         for(count = 1; count < maxlen; count++) {
618                 if(*src == ',' || *src == '\0') break; /*exit on \0 terminator*/
619                 *dest++ = *src++;
620         }
621         *dest = 0;
622 }
623
624 /*
625  * escapes: escape expansion for showsyms. C-style escapes understood include
626  * \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal). The ^-prefix
627  * for control characters is also understood, and \[mM] followed by any of the
628  * previous forms or by a character has the effect of 'meta'-ing the value (so
629  * that the alternate character set will be enabled).
630  */
631 STATIC_OVL void
632 escapes(cp, tp)
633 const char      *cp;
634 char *tp;
635 {
636     while (*cp)
637     {
638         int     cval = 0, meta = 0;
639
640         if (*cp == '\\' && index("mM", cp[1])) {
641                 meta = 1;
642                 cp += 2;
643         }
644         if (*cp == '\\' && index("0123456789xXoO", cp[1]))
645         {
646             const char *dp, *hex = "00112233445566778899aAbBcCdDeEfF";
647             int dcount = 0;
648
649             cp++;
650             if (*cp == 'x' || *cp == 'X')
651                 for (++cp; (dp = index(hex, *cp)) && (dcount++ < 2); cp++)
652                     cval = (cval * 16) + (dp - hex) / 2;
653             else if (*cp == 'o' || *cp == 'O')
654                 for (++cp; (index("01234567",*cp)) && (dcount++ < 3); cp++)
655                     cval = (cval * 8) + (*cp - '0');
656             else
657                 for (; (index("0123456789",*cp)) && (dcount++ < 3); cp++)
658                     cval = (cval * 10) + (*cp - '0');
659         }
660         else if (*cp == '\\')           /* C-style character escapes */
661         {
662             switch (*++cp)
663             {
664             case '\\': cval = '\\'; break;
665             case 'n': cval = '\n'; break;
666             case 't': cval = '\t'; break;
667             case 'b': cval = '\b'; break;
668             case 'r': cval = '\r'; break;
669             default: cval = *cp;
670             }
671             cp++;
672         }
673         else if (*cp == '^')            /* expand control-character syntax */
674         {
675             cval = (*++cp & 0x1f);
676             cp++;
677         }
678         else
679             cval = *cp++;
680         if (meta)
681             cval |= 0x80;
682         *tp++ = cval;
683     }
684     *tp = '\0';
685 }
686
687 STATIC_OVL void
688 rejectoption(optname)
689 const char *optname;
690 {
691 #ifdef MICRO
692         pline("\"%s\" settable only from %s.", optname, configfile);
693 #else
694         pline("%s can be set only from NETHACKOPTIONS or %s.", optname,
695                         configfile);
696 #endif
697 }
698
699 STATIC_OVL void
700 badoption(opts)
701 const char *opts;
702 {
703         if (!initial) {
704             if (!strncmp(opts, "h", 1) || !strncmp(opts, "?", 1))
705                 option_help();
706             else
707                 pline("Bad syntax: %s.  Enter \"?g\" for help.", opts);
708             return;
709         }
710 #ifdef MAC
711         else return;
712 #endif
713
714         if(from_file)
715             raw_printf("Bad syntax in OPTIONS in %s: %s.", configfile, opts);
716         else
717             raw_printf("Bad syntax in NETHACKOPTIONS: %s.", opts);
718
719         wait_synch();
720 }
721
722 STATIC_OVL char *
723 string_for_opt(opts, val_optional)
724 char *opts;
725 boolean val_optional;
726 {
727         char *colon, *equals;
728
729         colon = index(opts, ':');
730         equals = index(opts, '=');
731         if (!colon || (equals && equals < colon)) colon = equals;
732
733         if (!colon || !*++colon) {
734                 if (!val_optional) badoption(opts);
735                 return (char *)0;
736         }
737         return colon;
738 }
739
740 STATIC_OVL char *
741 string_for_env_opt(optname, opts, val_optional)
742 const char *optname;
743 char *opts;
744 boolean val_optional;
745 {
746         if(!initial) {
747                 rejectoption(optname);
748                 return (char *)0;
749         }
750         return string_for_opt(opts, val_optional);
751 }
752
753 STATIC_OVL void
754 bad_negation(optname, with_parameter)
755 const char *optname;
756 boolean with_parameter;
757 {
758         pline_The("%s option may not %sbe negated.",
759                 optname,
760                 with_parameter ? "both have a value and " : "");
761 }
762
763 /*
764  * Change the inventory order, using the given string as the new order.
765  * Missing characters in the new order are filled in at the end from
766  * the current inv_order, except for gold, which is forced to be first
767  * if not explicitly present.
768  *
769  * This routine returns 1 unless there is a duplicate or bad char in
770  * the string.
771  */
772 STATIC_OVL int
773 change_inv_order(op)
774 char *op;
775 {
776     int oc_sym, num;
777     char *sp, buf[BUFSZ];
778
779     num = 0;
780 #ifndef GOLDOBJ
781     if (!index(op, GOLD_SYM))
782         buf[num++] = COIN_CLASS;
783 #else
784     /*  !!!! probably unnecessary with gold as normal inventory */
785 #endif
786
787     for (sp = op; *sp; sp++) {
788         oc_sym = def_char_to_objclass(*sp);
789         /* reject bad or duplicate entries */
790         if (oc_sym == MAXOCLASSES ||
791                 oc_sym == RANDOM_CLASS || oc_sym == ILLOBJ_CLASS ||
792                 !index(flags.inv_order, oc_sym) || index(sp+1, *sp))
793             return 0;
794         /* retain good ones */
795         buf[num++] = (char) oc_sym;
796     }
797     buf[num] = '\0';
798
799     /* fill in any omitted classes, using previous ordering */
800     for (sp = flags.inv_order; *sp; sp++)
801         if (!index(buf, *sp)) {
802             buf[num++] = *sp;
803             buf[num] = '\0';    /* explicitly terminate for next index() */
804         }
805
806     Strcpy(flags.inv_order, buf);
807     return 1;
808 }
809
810 STATIC_OVL void
811 graphics_opts(opts, optype, maxlen, offset)
812 register char *opts;
813 const char *optype;
814 int maxlen, offset;
815 {
816         uchar translate[MAXPCHARS+1];
817         int length, i;
818
819         if (!(opts = string_for_env_opt(optype, opts, FALSE)))
820                 return;
821         escapes(opts, opts);
822
823         length = strlen(opts);
824         if (length > maxlen) length = maxlen;
825         /* match the form obtained from PC configuration files */
826         for (i = 0; i < length; i++)
827                 translate[i] = (uchar) opts[i];
828         assign_graphics(translate, length, maxlen, offset);
829 }
830
831 STATIC_OVL void
832 warning_opts(opts, optype)
833 register char *opts;
834 const char *optype;
835 {
836         uchar translate[MAXPCHARS+1];
837         int length, i;
838
839         if (!(opts = string_for_env_opt(optype, opts, FALSE)))
840                 return;
841         escapes(opts, opts);
842
843         length = strlen(opts);
844         if (length > WARNCOUNT) length = WARNCOUNT;
845         /* match the form obtained from PC configuration files */
846         for (i = 0; i < length; i++)
847              translate[i] = (((i < WARNCOUNT) && opts[i]) ?
848                            (uchar) opts[i] : def_warnsyms[i].sym);
849         assign_warnings(translate);
850 }
851
852 void
853 assign_warnings(graph_chars)
854 register uchar *graph_chars;
855 {
856         int i;
857         for (i = 0; i < WARNCOUNT; i++)
858             if (graph_chars[i]) warnsyms[i] = graph_chars[i];
859 }
860
861 STATIC_OVL int
862 feature_alert_opts(op, optn)
863 char *op;
864 const char *optn;
865 {
866         char buf[BUFSZ];
867         boolean rejectver = FALSE;
868         unsigned long fnv = get_feature_notice_ver(op);         /* version.c */
869         if (fnv == 0L) return 0;
870         if (fnv > get_current_feature_ver())
871                 rejectver = TRUE;
872         else
873                 flags.suppress_alert = fnv;
874         if (rejectver) {
875                 if (!initial)
876                         You_cant("disable new feature alerts for future versions.");
877                 else {
878                         Sprintf(buf,
879                                 "\n%s=%s Invalid reference to a future version ignored",
880                                 optn, op);
881                         badoption(buf);
882                 }
883                 return 0;
884         }
885         if (!initial) {
886                 Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
887                         FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
888                 pline("Feature change alerts disabled for NetHack %s features and prior.",
889                         buf);
890         }
891         return 1;
892 }
893
894 void
895 set_duplicate_opt_detection(on_or_off)
896 int on_or_off;
897 {
898         int k, *optptr;
899         if (on_or_off != 0) {
900                 /*-- ON --*/
901                 if (iflags.opt_booldup)
902                         impossible("iflags.opt_booldup already on (memory leak)");
903                 iflags.opt_booldup = (int *)alloc(SIZE(boolopt) * sizeof(int));
904                 optptr = iflags.opt_booldup;
905                 for (k = 0; k < SIZE(boolopt); ++k)
906                         *optptr++ = 0;
907                         
908                 if (iflags.opt_compdup)
909                         impossible("iflags.opt_compdup already on (memory leak)");
910                 iflags.opt_compdup = (int *)alloc(SIZE(compopt) * sizeof(int));
911                 optptr = iflags.opt_compdup;
912                 for (k = 0; k < SIZE(compopt); ++k)
913                         *optptr++ = 0;
914         } else {
915                 /*-- OFF --*/
916                 if (iflags.opt_booldup) free((genericptr_t) iflags.opt_booldup);
917                 iflags.opt_booldup = (int *)0;
918                 if (iflags.opt_compdup) free((genericptr_t) iflags.opt_compdup);
919                 iflags.opt_compdup = (int *)0;
920         } 
921 }
922
923 STATIC_OVL void
924 duplicate_opt_detection(opts, bool_or_comp)
925 const char *opts;
926 int bool_or_comp;       /* 0 == boolean option, 1 == compound */
927 {
928         int i, *optptr;
929 #if defined(MAC)
930         /* the Mac has trouble dealing with the output of messages while
931          * processing the config file.  That should get fixed one day.
932          * For now just return.
933          */
934         return;
935 #endif
936         if ((bool_or_comp == 0) && iflags.opt_booldup && initial && from_file) {
937             for (i = 0; boolopt[i].name; i++) {
938                 if (match_optname(opts, boolopt[i].name, 3, FALSE)) {
939                         optptr = iflags.opt_booldup + i;
940                         if (*optptr == 1) {
941                             raw_printf(
942                                 "\nWarning - Boolean option specified multiple times: %s.\n",
943                                         opts);
944                                 wait_synch();
945                         }
946                         *optptr += 1;
947                         break; /* don't match multiple options */
948                 }
949             }
950         } else if ((bool_or_comp == 1) && iflags.opt_compdup && initial && from_file) {
951             for (i = 0; compopt[i].name; i++) {
952                 if (match_optname(opts, compopt[i].name, strlen(compopt[i].name), TRUE)) {
953                         optptr = iflags.opt_compdup + i;
954                         if (*optptr == 1) {
955                             raw_printf(
956                                 "\nWarning - compound option specified multiple times: %s.\n",
957                                         compopt[i].name);
958                                 wait_synch();
959                         }
960                         *optptr += 1;
961                         break; /* don't match multiple options */
962                 }
963             }
964         }
965 }
966
967 void
968 parseoptions(opts, tinitial, tfrom_file)
969 register char *opts;
970 boolean tinitial, tfrom_file;
971 {
972         register char *op;
973         unsigned num;
974         boolean negated;
975         int i;
976         const char *fullname;
977
978         initial = tinitial;
979         from_file = tfrom_file;
980         if ((op = index(opts, ',')) != 0) {
981                 *op++ = 0;
982                 parseoptions(op, initial, from_file);
983         }
984         if (strlen(opts) > BUFSZ/2) {
985                 badoption("option too long");
986                 return;
987         }
988
989         /* strip leading and trailing white space */
990         while (isspace(*opts)) opts++;
991         op = eos(opts);
992         while (--op >= opts && isspace(*op)) *op = '\0';
993
994         if (!*opts) return;
995         negated = FALSE;
996         while ((*opts == '!') || !strncmpi(opts, "no", 2)) {
997                 if (*opts == '!') opts++; else opts += 2;
998                 negated = !negated;
999         }
1000
1001         /* variant spelling */
1002
1003         if (match_optname(opts, "colour", 5, FALSE))
1004                 Strcpy(opts, "color");  /* fortunately this isn't longer */
1005
1006         if (!match_optname(opts, "subkeyvalue", 11, TRUE)) /* allow multiple */
1007         duplicate_opt_detection(opts, 1);       /* 1 means compound opts */
1008
1009         /* special boolean options */
1010
1011         if (match_optname(opts, "female", 3, FALSE)) {
1012                 if(!initial && flags.female == negated)
1013                         pline("That is not anatomically possible.");
1014                 else
1015                         flags.initgend = flags.female = !negated;
1016                 return;
1017         }
1018
1019         if (match_optname(opts, "male", 4, FALSE)) {
1020                 if(!initial && flags.female != negated)
1021                         pline("That is not anatomically possible.");
1022                 else
1023                         flags.initgend = flags.female = negated;
1024                 return;
1025         }
1026
1027 #if defined(MICRO) && !defined(AMIGA)
1028         /* included for compatibility with old NetHack.cnf files */
1029         if (match_optname(opts, "IBM_", 4, FALSE)) {
1030                 iflags.BIOS = !negated;
1031                 return;
1032         }
1033 #endif /* MICRO */
1034
1035         /* compound options */
1036
1037         fullname = "pettype";
1038         if (match_optname(opts, fullname, 3, TRUE)) {
1039                 if ((op = string_for_env_opt(fullname, opts, negated)) != 0) {
1040                     if (negated) bad_negation(fullname, TRUE);
1041                     else switch (*op) {
1042                         case 'd':       /* dog */
1043                         case 'D':
1044                             preferred_pet = 'd';
1045                             break;
1046                         case 'c':       /* cat */
1047                         case 'C':
1048                         case 'f':       /* feline */
1049                         case 'F':
1050                             preferred_pet = 'c';
1051                             break;
1052                         case 'n':       /* no pet */
1053                         case 'N':
1054                             preferred_pet = 'n';
1055                             break;
1056                         default:
1057                             pline("Unrecognized pet type '%s'.", op);
1058                             break;
1059                     }
1060                 } else if (negated) preferred_pet = 'n';
1061                 return;
1062         }
1063
1064         fullname = "catname";
1065         if (match_optname(opts, fullname, 3, TRUE)) {
1066                 if (negated) bad_negation(fullname, FALSE);
1067                 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1068                         nmcpy(catname, op, PL_PSIZ);
1069                 return;
1070         }
1071
1072         fullname = "dogname";
1073         if (match_optname(opts, fullname, 3, TRUE)) {
1074                 if (negated) bad_negation(fullname, FALSE);
1075                 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1076                         nmcpy(dogname, op, PL_PSIZ);
1077                 return;
1078         }
1079
1080         fullname = "horsename";
1081         if (match_optname(opts, fullname, 5, TRUE)) {
1082                 if (negated) bad_negation(fullname, FALSE);
1083                 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1084                         nmcpy(horsename, op, PL_PSIZ);
1085                 return;
1086         }
1087
1088         fullname = "number_pad";
1089         if (match_optname(opts, fullname, 10, TRUE)) {
1090                 boolean compat = (strlen(opts) <= 10);
1091                 number_pad(iflags.num_pad ? 1 : 0);
1092                 op = string_for_opt(opts, (compat || !initial));
1093                 if (!op) {
1094                     if (compat || negated || initial) {
1095                         /* for backwards compatibility, "number_pad" without a
1096                            value is a synonym for number_pad:1 */
1097                         iflags.num_pad = !negated;
1098                         if (iflags.num_pad) iflags.num_pad_mode = 0;
1099                     }
1100                     return;
1101                 }
1102                 if (negated) {
1103                     bad_negation("number_pad", TRUE);
1104                     return;
1105                 }
1106                 if (*op == '1' || *op == '2') {
1107                         iflags.num_pad = 1;
1108                         if (*op == '2') iflags.num_pad_mode = 1;
1109                         else iflags.num_pad_mode = 0;
1110                 } else if (*op == '0') {
1111                         iflags.num_pad = 0;
1112                         iflags.num_pad_mode = 0;
1113                 } else badoption(opts);
1114                 return;
1115         }
1116
1117         fullname = "runmode";
1118         if (match_optname(opts, fullname, 4, TRUE)) {
1119                 if (negated) {
1120                         iflags.runmode = RUN_TPORT;
1121                 } else if ((op = string_for_opt(opts, FALSE)) != 0) {
1122                     if (!strncmpi(op, "teleport", strlen(op)))
1123                         iflags.runmode = RUN_TPORT;
1124                     else if (!strncmpi(op, "run", strlen(op)))
1125                         iflags.runmode = RUN_LEAP;
1126                     else if (!strncmpi(op, "walk", strlen(op)))
1127                         iflags.runmode = RUN_STEP;
1128                     else if (!strncmpi(op, "crawl", strlen(op)))
1129                         iflags.runmode = RUN_CRAWL;
1130                     else
1131                         badoption(opts);
1132                 }
1133                 return;
1134         }
1135
1136         fullname = "msghistory";
1137         if (match_optname(opts, fullname, 3, TRUE)) {
1138                 op = string_for_env_opt(fullname, opts, negated);
1139                 if ((negated && !op) || (!negated && op)) {
1140                         iflags.msg_history = negated ? 0 : atoi(op);
1141                 } else if (negated) bad_negation(fullname, TRUE);
1142                 return;
1143         }
1144
1145         fullname="msg_window";
1146         /* msg_window:single, combo, full or reversed */
1147         if (match_optname(opts, fullname, 4, TRUE)) {
1148         /* allow option to be silently ignored by non-tty ports */
1149 #ifdef TTY_GRAPHICS
1150                 int tmp;
1151                 if (!(op = string_for_opt(opts, TRUE))) {
1152                     tmp = negated ? 's' : 'f';
1153                 } else {
1154                           if (negated) {
1155                                 bad_negation(fullname, TRUE);
1156                                 return;
1157                                   }
1158                     tmp = tolower(*op);
1159                 }
1160                 switch (tmp) {
1161                         case 's':       /* single message history cycle (default if negated) */
1162                                 iflags.prevmsg_window = 's';
1163                                 break;
1164                         case 'c':       /* combination: two singles, then full page reversed */
1165                                 iflags.prevmsg_window = 'c';
1166                                 break;
1167                         case 'f':       /* full page (default if no opts) */
1168                                 iflags.prevmsg_window = 'f';
1169                                 break;
1170                         case 'r':       /* full page (reversed) */
1171                                 iflags.prevmsg_window = 'r';
1172                                 break;
1173                         default:
1174                                 badoption(opts);
1175                 }
1176 #endif
1177                 return;
1178         }
1179
1180         /* WINCAP
1181          * setting font options  */
1182         fullname = "font";
1183         if (!strncmpi(opts, fullname, 4))
1184         {
1185                 int wintype = -1;
1186                 char *fontopts = opts + 4;
1187
1188                 if (!strncmpi(fontopts, "map", 3) ||
1189                     !strncmpi(fontopts, "_map", 4))
1190                         wintype = NHW_MAP;
1191                 else if (!strncmpi(fontopts, "message", 7) ||
1192                          !strncmpi(fontopts, "_message", 8))
1193                         wintype = NHW_MESSAGE;
1194                 else if (!strncmpi(fontopts, "text", 4) ||
1195                          !strncmpi(fontopts, "_text", 5))
1196                         wintype = NHW_TEXT;                     
1197                 else if (!strncmpi(fontopts, "menu", 4) ||
1198                          !strncmpi(fontopts, "_menu", 5))
1199                         wintype = NHW_MENU;
1200                 else if (!strncmpi(fontopts, "status", 6) ||
1201                          !strncmpi(fontopts, "_status", 7))
1202                         wintype = NHW_STATUS;
1203                 else if (!strncmpi(fontopts, "_size", 5)) {
1204                         if (!strncmpi(fontopts, "_size_map", 8))
1205                                 wintype = NHW_MAP;
1206                         else if (!strncmpi(fontopts, "_size_message", 12))
1207                                 wintype = NHW_MESSAGE;
1208                         else if (!strncmpi(fontopts, "_size_text", 9))
1209                                 wintype = NHW_TEXT;
1210                         else if (!strncmpi(fontopts, "_size_menu", 9))
1211                                 wintype = NHW_MENU;
1212                         else if (!strncmpi(fontopts, "_size_status", 11))
1213                                 wintype = NHW_STATUS;
1214                         else {
1215                                 badoption(opts);
1216                                 return;
1217                         }
1218                         if (wintype > 0 && !negated &&
1219                             (op = string_for_opt(opts, FALSE)) != 0) {
1220                             switch(wintype)  {
1221                                 case NHW_MAP:
1222                                         iflags.wc_fontsiz_map = atoi(op);
1223                                         break;
1224                                 case NHW_MESSAGE:
1225                                         iflags.wc_fontsiz_message = atoi(op);
1226                                         break;
1227                                 case NHW_TEXT:
1228                                         iflags.wc_fontsiz_text = atoi(op);
1229                                         break;
1230                                 case NHW_MENU:
1231                                         iflags.wc_fontsiz_menu = atoi(op);
1232                                         break;
1233                                 case NHW_STATUS:
1234                                         iflags.wc_fontsiz_status = atoi(op);
1235                                         break;
1236                             }
1237                         }
1238                         return;
1239                 } else {
1240                         badoption(opts);
1241                 }
1242                 if (wintype > 0 &&
1243                     (op = string_for_opt(opts, FALSE)) != 0) {
1244                         wc_set_font_name(wintype, op);
1245 #ifdef MAC
1246                         set_font_name (wintype, op);
1247 #endif
1248                         return;
1249                 } else if (negated) bad_negation(fullname, TRUE);
1250                 return;
1251         }
1252 #ifdef CHANGE_COLOR
1253         if (match_optname(opts, "palette", 3, TRUE)
1254 # ifdef MAC
1255             || match_optname(opts, "hicolor", 3, TRUE)
1256 # endif
1257                                                         ) {
1258             int color_number, color_incr;
1259
1260 # ifdef MAC
1261             if (match_optname(opts, "hicolor", 3, TRUE)) {
1262                 if (negated) {
1263                     bad_negation("hicolor", FALSE);
1264                     return;
1265                 }
1266                 color_number = CLR_MAX + 4;     /* HARDCODED inverse number */
1267                 color_incr = -1;
1268             } else {
1269 # endif
1270                 if (negated) {
1271                     bad_negation("palette", FALSE);
1272                     return;
1273                 }
1274                 color_number = 0;
1275                 color_incr = 1;
1276 # ifdef MAC
1277             }
1278 # endif
1279             if ((op = string_for_opt(opts, FALSE)) != (char *)0) {
1280                 char *pt = op;
1281                 int cnt, tmp, reverse;
1282                 long rgb;
1283
1284                 while (*pt && color_number >= 0) {
1285                     cnt = 3;
1286                     rgb = 0L;
1287                     if (*pt == '-') {
1288                         reverse = 1;
1289                         pt++;
1290                     } else {
1291                         reverse = 0;
1292                     }
1293                     while (cnt-- > 0) {
1294                         if (*pt && *pt != '/') {
1295 # ifdef AMIGA
1296                             rgb <<= 4;
1297 # else
1298                             rgb <<= 8;
1299 # endif
1300                             tmp = *(pt++);
1301                             if (isalpha(tmp)) {
1302                                 tmp = (tmp + 9) & 0xf;  /* Assumes ASCII... */
1303                             } else {
1304                                 tmp &= 0xf;     /* Digits in ASCII too... */
1305                             }
1306 # ifndef AMIGA
1307                             /* Add an extra so we fill f -> ff and 0 -> 00 */
1308                             rgb += tmp << 4;
1309 # endif
1310                             rgb += tmp;
1311                         }
1312                     }
1313                     if (*pt == '/') {
1314                         pt++;
1315                     }
1316                     change_color(color_number, rgb, reverse);
1317                     color_number += color_incr;
1318                 }
1319             }
1320             if (!initial) {
1321                 need_redraw = TRUE;
1322             }
1323             return;
1324         }
1325 #endif /* CHANGE_COLOR */
1326
1327         if (match_optname(opts, "fruit", 2, TRUE)) {
1328                 char empty_str = '\0';
1329                 op = string_for_opt(opts, negated);
1330                 if (negated) {
1331                     if (op) {
1332                         bad_negation("fruit", TRUE);
1333                         return;
1334                     }
1335                     op = &empty_str;
1336                     goto goodfruit;
1337                 }
1338                 if (!op) return;
1339                 if (!initial) {
1340                     struct fruit *f;
1341
1342                     num = 0;
1343                     for(f=ffruit; f; f=f->nextf) {
1344                         if (!strcmp(op, f->fname)) goto goodfruit;
1345                         num++;
1346                     }
1347                     if (num >= 100) {
1348                         pline("Doing that so many times isn't very fruitful.");
1349                         return;
1350                     }
1351                 }
1352 goodfruit:
1353                 nmcpy(pl_fruit, op, PL_FSIZ);
1354         /* OBJ_NAME(objects[SLIME_MOLD]) won't work after initialization */
1355                 if (!*pl_fruit)
1356                     nmcpy(pl_fruit, "slime mold", PL_FSIZ);
1357                 if (!initial)
1358                     (void)fruitadd(pl_fruit);
1359                 /* If initial, then initoptions is allowed to do it instead
1360                  * of here (initoptions always has to do it even if there's
1361                  * no fruit option at all.  Also, we don't want people
1362                  * setting multiple fruits in their options.)
1363                  */
1364                 return;
1365         }
1366
1367         /* graphics:string */
1368         fullname = "graphics";
1369         if (match_optname(opts, fullname, 2, TRUE)) {
1370                 if (negated) bad_negation(fullname, FALSE);
1371                 else graphics_opts(opts, fullname, MAXPCHARS, 0);
1372                 return;
1373         }
1374         fullname = "dungeon";
1375         if (match_optname(opts, fullname, 2, TRUE)) {
1376                 if (negated) bad_negation(fullname, FALSE);
1377                 else graphics_opts(opts, fullname, MAXDCHARS, 0);
1378                 return;
1379         }
1380         fullname = "traps";
1381         if (match_optname(opts, fullname, 2, TRUE)) {
1382                 if (negated) bad_negation(fullname, FALSE);
1383                 else graphics_opts(opts, fullname, MAXTCHARS, MAXDCHARS);
1384                 return;
1385         }
1386         fullname = "effects";
1387         if (match_optname(opts, fullname, 2, TRUE)) {
1388                 if (negated) bad_negation(fullname, FALSE);
1389                 else
1390                  graphics_opts(opts, fullname, MAXECHARS, MAXDCHARS+MAXTCHARS);
1391                 return;
1392         }
1393
1394         /* objects:string */
1395         fullname = "objects";
1396         if (match_optname(opts, fullname, 7, TRUE)) {
1397                 int length;
1398
1399                 if (negated) {
1400                     bad_negation(fullname, FALSE);
1401                     return;
1402                 }
1403                 if (!(opts = string_for_env_opt(fullname, opts, FALSE)))
1404                         return;
1405                 escapes(opts, opts);
1406
1407                 /*
1408                  * Override the default object class symbols.  The first
1409                  * object in the object class is the "random object".  I
1410                  * don't want to use 0 as an object class, so the "random
1411                  * object" is basically a place holder.
1412                  *
1413                  * The object class symbols have already been initialized in
1414                  * initoptions().
1415                  */
1416                 length = strlen(opts);
1417                 if (length >= MAXOCLASSES)
1418                     length = MAXOCLASSES-1;     /* don't count RANDOM_OBJECT */
1419
1420                 for (i = 0; i < length; i++)
1421                     oc_syms[i+1] = (uchar) opts[i];
1422                 return;
1423         }
1424
1425         /* monsters:string */
1426         fullname = "monsters";
1427         if (match_optname(opts, fullname, 8, TRUE)) {
1428                 int length;
1429
1430                 if (negated) {
1431                     bad_negation(fullname, FALSE);
1432                     return;
1433                 }
1434                 if (!(opts = string_for_env_opt(fullname, opts, FALSE)))
1435                         return;
1436                 escapes(opts, opts);
1437
1438                 /* Override default mon class symbols set in initoptions(). */
1439                 length = strlen(opts);
1440                 if (length >= MAXMCLASSES)
1441                     length = MAXMCLASSES-1;     /* mon class 0 unused */
1442
1443                 for (i = 0; i < length; i++)
1444                     monsyms[i+1] = (uchar) opts[i];
1445                 return;
1446         }
1447         fullname = "warnings";
1448         if (match_optname(opts, fullname, 5, TRUE)) {
1449                 if (negated) bad_negation(fullname, FALSE);
1450                 else warning_opts(opts, fullname);
1451                 return;
1452         }
1453         /* boulder:symbol */
1454         fullname = "boulder";
1455         if (match_optname(opts, fullname, 7, TRUE)) {
1456                 int clash = 0;
1457                 if (negated) {
1458                     bad_negation(fullname, FALSE);
1459                     return;
1460                 }
1461 /*              if (!(opts = string_for_env_opt(fullname, opts, FALSE))) */
1462                 if (!(opts = string_for_opt(opts, FALSE)))
1463                         return;
1464                 escapes(opts, opts);
1465                 if (def_char_to_monclass(opts[0]) != MAXMCLASSES)
1466                         clash = 1;
1467                 else if (opts[0] >= '1' && opts[0] <= '5')
1468                         clash = 2;
1469                 if (clash) {
1470                         /* symbol chosen matches a used monster or warning
1471                            symbol which is not good - reject it*/
1472                         pline(
1473                   "Badoption - boulder symbol '%c' conflicts with a %s symbol.",
1474                                 opts[0], (clash == 1) ? "monster" : "warning");
1475                 } else {
1476                         /*
1477                          * Override the default boulder symbol.
1478                          */
1479                         iflags.bouldersym = (uchar) opts[0];
1480                 }
1481                 if (!initial) need_redraw = TRUE;
1482                 return;
1483         }
1484
1485         /* name:string */
1486         fullname = "name";
1487         if (match_optname(opts, fullname, 4, TRUE)) {
1488                 if (negated) bad_negation(fullname, FALSE);
1489                 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1490                         nmcpy(plname, op, PL_NSIZ);
1491                 return;
1492         }
1493
1494         /* role:string or character:string */
1495         fullname = "role";
1496         if (match_optname(opts, fullname, 4, TRUE) ||
1497             match_optname(opts, (fullname = "character"), 4, TRUE)) {
1498                 if (negated) bad_negation(fullname, FALSE);
1499                 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1500                         if ((flags.initrole = str2role(op)) == ROLE_NONE)
1501                                 badoption(opts);
1502                         else  /* Backwards compatibility */
1503                                 nmcpy(pl_character, op, PL_NSIZ);
1504                 }
1505                 return;
1506         }
1507
1508         /* race:string */
1509         fullname = "race";
1510         if (match_optname(opts, fullname, 4, TRUE)) {
1511                 if (negated) bad_negation(fullname, FALSE);
1512                 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1513                         if ((flags.initrace = str2race(op)) == ROLE_NONE)
1514                                 badoption(opts);
1515                         else /* Backwards compatibility */
1516                                 pl_race = *op;
1517                 }
1518                 return;
1519         }
1520
1521         /* gender:string */
1522         fullname = "gender";
1523         if (match_optname(opts, fullname, 4, TRUE)) {
1524                 if (negated) bad_negation(fullname, FALSE);
1525                 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1526                         if ((flags.initgend = str2gend(op)) == ROLE_NONE)
1527                                 badoption(opts);
1528                         else
1529                                 flags.female = flags.initgend;
1530                 }
1531                 return;
1532         }
1533
1534         /* altkeyhandler:string */
1535         fullname = "altkeyhandler";
1536         if (match_optname(opts, fullname, 4, TRUE)) {
1537                 if (negated) bad_negation(fullname, FALSE);
1538                 else if ((op = string_for_opt(opts, negated))) {
1539 #ifdef WIN32CON
1540                     (void)strncpy(iflags.altkeyhandler, op, MAX_ALTKEYHANDLER - 5);
1541                     load_keyboard_handler();
1542 #endif
1543                 }
1544                 return;
1545         }
1546
1547         /* WINCAP
1548          * align_status:[left|top|right|bottom] */
1549         fullname = "align_status";
1550         if (match_optname(opts, fullname, sizeof("align_status")-1, TRUE)) {
1551                 op = string_for_opt(opts, negated);
1552                 if (op && !negated) {
1553                     if (!strncmpi (op, "left", sizeof("left")-1))
1554                         iflags.wc_align_status = ALIGN_LEFT;
1555                     else if (!strncmpi (op, "top", sizeof("top")-1))
1556                         iflags.wc_align_status = ALIGN_TOP;
1557                     else if (!strncmpi (op, "right", sizeof("right")-1))
1558                         iflags.wc_align_status = ALIGN_RIGHT;
1559                     else if (!strncmpi (op, "bottom", sizeof("bottom")-1))
1560                         iflags.wc_align_status = ALIGN_BOTTOM;
1561                     else
1562                         badoption(opts);
1563                 } else if (negated) bad_negation(fullname, TRUE);
1564                 return;
1565         }
1566         /* WINCAP
1567          * align_message:[left|top|right|bottom] */
1568         fullname = "align_message";
1569         if (match_optname(opts, fullname, sizeof("align_message")-1, TRUE)) {
1570                 op = string_for_opt(opts, negated);
1571                 if (op && !negated) {
1572                     if (!strncmpi (op, "left", sizeof("left")-1))
1573                         iflags.wc_align_message = ALIGN_LEFT;
1574                     else if (!strncmpi (op, "top", sizeof("top")-1))
1575                         iflags.wc_align_message = ALIGN_TOP;
1576                     else if (!strncmpi (op, "right", sizeof("right")-1))
1577                         iflags.wc_align_message = ALIGN_RIGHT;
1578                     else if (!strncmpi (op, "bottom", sizeof("bottom")-1))
1579                         iflags.wc_align_message = ALIGN_BOTTOM;
1580                     else
1581                         badoption(opts);
1582                 } else if (negated) bad_negation(fullname, TRUE);
1583                 return;
1584         }
1585         /* align:string */
1586         fullname = "align";
1587         if (match_optname(opts, fullname, sizeof("align")-1, TRUE)) {
1588                 if (negated) bad_negation(fullname, FALSE);
1589                 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1590                         if ((flags.initalign = str2align(op)) == ROLE_NONE)
1591                                 badoption(opts);
1592                 return;
1593         }
1594
1595         /* the order to list the pack */
1596         fullname = "packorder";
1597         if (match_optname(opts, fullname, 4, TRUE)) {
1598                 if (negated) {
1599                     bad_negation(fullname, FALSE);
1600                     return;
1601                 } else if (!(op = string_for_opt(opts, FALSE))) return;
1602
1603                 if (!change_inv_order(op))
1604                         badoption(opts);
1605                 return;
1606         }
1607
1608         /* maximum burden picked up before prompt (Warren Cheung) */
1609         fullname = "pickup_burden";
1610         if (match_optname(opts, fullname, 8, TRUE)) {
1611                 if (negated) {
1612                         bad_negation(fullname, FALSE);
1613                         return;
1614                 } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1615                     switch (tolower(*op)) {
1616                                 /* Unencumbered */
1617                                 case 'u':
1618                                         flags.pickup_burden = UNENCUMBERED;
1619                                         break;
1620                                 /* Burdened (slight encumbrance) */
1621                                 case 'b':
1622                                         flags.pickup_burden = SLT_ENCUMBER;
1623                                         break;
1624                                 /* streSsed (moderate encumbrance) */
1625                                 case 's':
1626                                         flags.pickup_burden = MOD_ENCUMBER;
1627                                         break;
1628                                 /* straiNed (heavy encumbrance) */
1629                                 case 'n':
1630                                         flags.pickup_burden = HVY_ENCUMBER;
1631                                         break;
1632                                 /* OverTaxed (extreme encumbrance) */
1633                                 case 'o':
1634                                 case 't':
1635                                         flags.pickup_burden = EXT_ENCUMBER;
1636                                         break;
1637                                 /* overLoaded */
1638                                 case 'l':
1639                                         flags.pickup_burden = OVERLOADED;
1640                                         break;
1641                                 default:
1642                                 badoption(opts);
1643                     }
1644                 }
1645                 return;
1646         }
1647
1648         /* types of objects to pick up automatically */
1649         if (match_optname(opts, "pickup_types", 8, TRUE)) {
1650                 char ocl[MAXOCLASSES + 1], tbuf[MAXOCLASSES + 1],
1651                      qbuf[QBUFSZ], abuf[BUFSZ];
1652                 int oc_sym;
1653                 boolean badopt = FALSE, compat = (strlen(opts) <= 6), use_menu;
1654
1655                 oc_to_str(flags.pickup_types, tbuf);
1656                 flags.pickup_types[0] = '\0';   /* all */
1657                 op = string_for_opt(opts, (compat || !initial));
1658                 if (!op) {
1659                     if (compat || negated || initial) {
1660                         /* for backwards compatibility, "pickup" without a
1661                            value is a synonym for autopickup of all types
1662                            (and during initialization, we can't prompt yet) */
1663                         flags.pickup = !negated;
1664                         return;
1665                     }
1666                     oc_to_str(flags.inv_order, ocl);
1667                     use_menu = TRUE;
1668                     if (flags.menu_style == MENU_TRADITIONAL ||
1669                             flags.menu_style == MENU_COMBINATION) {
1670                         use_menu = FALSE;
1671                         Sprintf(qbuf, "New pickup_types: [%s am] (%s)",
1672                                 ocl, *tbuf ? tbuf : "all");
1673                         getlin(qbuf, abuf);
1674                         op = mungspaces(abuf);
1675                         if (abuf[0] == '\0' || abuf[0] == '\033')
1676                             op = tbuf;          /* restore */
1677                         else if (abuf[0] == 'm')
1678                             use_menu = TRUE;
1679                     }
1680                     if (use_menu) {
1681                         (void) choose_classes_menu("Auto-Pickup what?", 1,
1682                                                    TRUE, ocl, tbuf);
1683                         op = tbuf;
1684                     }
1685                 }
1686                 if (negated) {
1687                     bad_negation("pickup_types", TRUE);
1688                     return;
1689                 }
1690                 while (*op == ' ') op++;
1691                 if (*op != 'a' && *op != 'A') {
1692                     num = 0;
1693                     while (*op) {
1694                         oc_sym = def_char_to_objclass(*op);
1695                         /* make sure all are valid obj symbols occuring once */
1696                         if (oc_sym != MAXOCLASSES &&
1697                             !index(flags.pickup_types, oc_sym)) {
1698                             flags.pickup_types[num] = (char)oc_sym;
1699                             flags.pickup_types[++num] = '\0';
1700                         } else
1701                             badopt = TRUE;
1702                         op++;
1703                     }
1704                     if (badopt) badoption(opts);
1705                 }
1706                 return;
1707         }
1708         /* WINCAP
1709          * player_selection: dialog | prompts */
1710         fullname = "player_selection";
1711         if (match_optname(opts, fullname, sizeof("player_selection")-1, TRUE)) {
1712                 op = string_for_opt(opts, negated);
1713                 if (op && !negated) {
1714                     if (!strncmpi (op, "dialog", sizeof("dialog")-1))
1715                         iflags.wc_player_selection = VIA_DIALOG;
1716                     else if (!strncmpi (op, "prompt", sizeof("prompt")-1))
1717                         iflags.wc_player_selection = VIA_PROMPTS;
1718                     else
1719                         badoption(opts);
1720                 } else if (negated) bad_negation(fullname, TRUE);
1721                 return;
1722         }
1723
1724         /* things to disclose at end of game */
1725         if (match_optname(opts, "disclose", 7, TRUE)) {
1726                 /*
1727                  * The order that the end_disclore options are stored:
1728                  * inventory, attribs, vanquished, genocided, conduct
1729                  * There is an array in flags:
1730                  *      end_disclose[NUM_DISCLOSURE_OPT];
1731                  * with option settings for the each of the following:
1732                  * iagvc [see disclosure_options in decl.c]:
1733                  * Legal setting values in that array are:
1734                  *      DISCLOSE_PROMPT_DEFAULT_YES  ask with default answer yes
1735                  *      DISCLOSE_PROMPT_DEFAULT_NO   ask with default answer no
1736                  *      DISCLOSE_YES_WITHOUT_PROMPT  always disclose and don't ask
1737                  *      DISCLOSE_NO_WITHOUT_PROMPT   never disclose and don't ask
1738                  *
1739                  * Those setting values can be used in the option
1740                  * string as a prefix to get the desired behaviour.
1741                  *
1742                  * For backward compatibility, no prefix is required,
1743                  * and the presence of a i,a,g,v, or c without a prefix
1744                  * sets the corresponding value to DISCLOSE_YES_WITHOUT_PROMPT.
1745                  */
1746                 boolean badopt = FALSE;
1747                 int idx, prefix_val;
1748
1749                 op = string_for_opt(opts, TRUE);
1750                 if (op && negated) {
1751                         bad_negation("disclose", TRUE);
1752                         return;
1753                 }
1754                 /* "disclose" without a value means "all with prompting"
1755                    and negated means "none without prompting" */
1756                 if (!op || !strcmpi(op, "all") || !strcmpi(op, "none")) {
1757                         if (op && !strcmpi(op, "none")) negated = TRUE;
1758                         for (num = 0; num < NUM_DISCLOSURE_OPTIONS; num++)
1759                             flags.end_disclose[num] = negated ?
1760                                                 DISCLOSE_NO_WITHOUT_PROMPT :
1761                                                 DISCLOSE_PROMPT_DEFAULT_YES;
1762                         return;
1763                 }
1764
1765                 num = 0;
1766                 prefix_val = -1;
1767                 while (*op && num < sizeof flags.end_disclose - 1) {
1768                         register char c, *dop;
1769                         static char valid_settings[] = {
1770                                 DISCLOSE_PROMPT_DEFAULT_YES,
1771                                 DISCLOSE_PROMPT_DEFAULT_NO,
1772                                 DISCLOSE_YES_WITHOUT_PROMPT,
1773                                 DISCLOSE_NO_WITHOUT_PROMPT,
1774                                 '\0'
1775                         };
1776                         c = lowc(*op);
1777                         if (c == 'k') c = 'v';  /* killed -> vanquished */
1778                         dop = index(disclosure_options, c);
1779                         if (dop) {
1780                                 idx = dop - disclosure_options;
1781                                 if (idx < 0 || idx > NUM_DISCLOSURE_OPTIONS - 1) {
1782                                     impossible("bad disclosure index %d %c",
1783                                                         idx, c);
1784                                     continue;
1785                                 }
1786                                 if (prefix_val != -1) {
1787                                     flags.end_disclose[idx] = prefix_val;
1788                                     prefix_val = -1;
1789                                 } else
1790                                     flags.end_disclose[idx] = DISCLOSE_YES_WITHOUT_PROMPT;
1791                         } else if (index(valid_settings, c)) {
1792                                 prefix_val = c;
1793                         } else if (c == ' ') {
1794                                 /* do nothing */
1795                         } else
1796                                 badopt = TRUE;                          
1797                         op++;
1798                 }
1799                 if (badopt) badoption(opts);
1800                 return;
1801         }
1802
1803         /* scores:5t[op] 5a[round] o[wn] */
1804         if (match_optname(opts, "scores", 4, TRUE)) {
1805             if (negated) {
1806                 bad_negation("scores", FALSE);
1807                 return;
1808             }
1809             if (!(op = string_for_opt(opts, FALSE))) return;
1810
1811             while (*op) {
1812                 int inum = 1;
1813
1814                 if (digit(*op)) {
1815                     inum = atoi(op);
1816                     while (digit(*op)) op++;
1817                 } else if (*op == '!') {
1818                     negated = !negated;
1819                     op++;
1820                 }
1821                 while (*op == ' ') op++;
1822
1823                 switch (*op) {
1824                  case 't':
1825                  case 'T':  flags.end_top = inum;
1826                             break;
1827                  case 'a':
1828                  case 'A':  flags.end_around = inum;
1829                             break;
1830                  case 'o':
1831                  case 'O':  flags.end_own = !negated;
1832                             break;
1833                  default:   badoption(opts);
1834                             return;
1835                 }
1836                 while (letter(*++op) || *op == ' ') continue;
1837                 if (*op == '/') op++;
1838             }
1839             return;
1840         }
1841
1842         fullname = "suppress_alert";
1843         if (match_optname(opts, fullname, 4, TRUE)) {
1844                 op = string_for_opt(opts, negated);
1845                 if (negated) bad_negation(fullname, FALSE);
1846                 else if (op) (void) feature_alert_opts(op,fullname);
1847                 return;
1848         }
1849         
1850 #ifdef VIDEOSHADES
1851         /* videocolors:string */
1852         fullname = "videocolors";
1853         if (match_optname(opts, fullname, 6, TRUE) ||
1854             match_optname(opts, "videocolours", 10, TRUE)) {
1855                 if (negated) {
1856                         bad_negation(fullname, FALSE);
1857                         return;
1858                 }
1859                 else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
1860                         return;
1861                 }
1862                 if (!assign_videocolors(opts))
1863                         badoption(opts);
1864                 return;
1865         }
1866         /* videoshades:string */
1867         fullname = "videoshades";
1868         if (match_optname(opts, fullname, 6, TRUE)) {
1869                 if (negated) {
1870                         bad_negation(fullname, FALSE);
1871                         return;
1872                 }
1873                 else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
1874                         return;
1875                 }
1876                 if (!assign_videoshades(opts))
1877                         badoption(opts);
1878                 return;
1879         }
1880 #endif /* VIDEOSHADES */
1881 #ifdef MSDOS
1882 # ifdef NO_TERMS
1883         /* video:string -- must be after longer tests */
1884         fullname = "video";
1885         if (match_optname(opts, fullname, 5, TRUE)) {
1886                 if (negated) {
1887                         bad_negation(fullname, FALSE);
1888                         return;
1889                 }
1890                 else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
1891                         return;
1892                 }
1893                 if (!assign_video(opts))
1894                         badoption(opts);
1895                 return;
1896         }
1897 # endif /* NO_TERMS */
1898         /* soundcard:string -- careful not to match boolean 'sound' */
1899         fullname = "soundcard";
1900         if (match_optname(opts, fullname, 6, TRUE)) {
1901                 if (negated) {
1902                         bad_negation(fullname, FALSE);
1903                         return;
1904                 }
1905                 else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
1906                         return;
1907                 }
1908                 if (!assign_soundcard(opts))
1909                         badoption(opts);
1910                 return;
1911         }
1912 #endif /* MSDOS */
1913
1914         /* WINCAP
1915          * map_mode:[tiles|ascii4x6|ascii6x8|ascii8x8|ascii16x8|ascii7x12|ascii8x12|
1916                         ascii16x12|ascii12x16|ascii10x18|fit_to_screen] */
1917         fullname = "map_mode";
1918         if (match_optname(opts, fullname, sizeof("map_mode")-1, TRUE)) {
1919                 op = string_for_opt(opts, negated);
1920                 if (op && !negated) {
1921                     if (!strncmpi (op, "tiles", sizeof("tiles")-1))
1922                         iflags.wc_map_mode = MAP_MODE_TILES;
1923                     else if (!strncmpi (op, "ascii4x6", sizeof("ascii4x6")-1))
1924                         iflags.wc_map_mode = MAP_MODE_ASCII4x6;
1925                     else if (!strncmpi (op, "ascii6x8", sizeof("ascii6x8")-1))
1926                         iflags.wc_map_mode = MAP_MODE_ASCII6x8;
1927                     else if (!strncmpi (op, "ascii8x8", sizeof("ascii8x8")-1))
1928                         iflags.wc_map_mode = MAP_MODE_ASCII8x8;
1929                     else if (!strncmpi (op, "ascii16x8", sizeof("ascii16x8")-1))
1930                         iflags.wc_map_mode = MAP_MODE_ASCII16x8;
1931                     else if (!strncmpi (op, "ascii7x12", sizeof("ascii7x12")-1))
1932                         iflags.wc_map_mode = MAP_MODE_ASCII7x12;
1933                     else if (!strncmpi (op, "ascii8x12", sizeof("ascii8x12")-1))
1934                         iflags.wc_map_mode = MAP_MODE_ASCII8x12;
1935                     else if (!strncmpi (op, "ascii16x12", sizeof("ascii16x12")-1))
1936                         iflags.wc_map_mode = MAP_MODE_ASCII16x12;
1937                     else if (!strncmpi (op, "ascii12x16", sizeof("ascii12x16")-1))
1938                         iflags.wc_map_mode = MAP_MODE_ASCII12x16;
1939                     else if (!strncmpi (op, "ascii10x18", sizeof("ascii10x18")-1))
1940                         iflags.wc_map_mode = MAP_MODE_ASCII10x18;
1941                     else if (!strncmpi (op, "fit_to_screen", sizeof("fit_to_screen")-1))
1942                         iflags.wc_map_mode = MAP_MODE_ASCII_FIT_TO_SCREEN;
1943                     else
1944                         badoption(opts);
1945                 } else if (negated) bad_negation(fullname, TRUE);
1946                 return;
1947         }
1948         /* WINCAP
1949          * scroll_amount:nn */
1950         fullname = "scroll_amount";
1951         if (match_optname(opts, fullname, sizeof("scroll_amount")-1, TRUE)) {
1952                 op = string_for_opt(opts, negated);
1953                 if ((negated && !op) || (!negated && op)) {
1954                         iflags.wc_scroll_amount = negated ? 1 : atoi(op);
1955                 } else if (negated) bad_negation(fullname, TRUE);
1956                 return;
1957         }
1958         /* WINCAP
1959          * scroll_margin:nn */
1960         fullname = "scroll_margin";
1961         if (match_optname(opts, fullname, sizeof("scroll_margin")-1, TRUE)) {
1962                 op = string_for_opt(opts, negated);
1963                 if ((negated && !op) || (!negated && op)) {
1964                         iflags.wc_scroll_margin = negated ? 5 : atoi(op);
1965                 } else if (negated) bad_negation(fullname, TRUE);
1966                 return;
1967         }
1968         fullname = "subkeyvalue";
1969         if (match_optname(opts, fullname, 5, TRUE)) {
1970                 if (negated) bad_negation(fullname, FALSE);
1971                 else {
1972 #if defined(WIN32CON)
1973                         op = string_for_opt(opts, 0);
1974                         map_subkeyvalue(op);
1975 #endif
1976                 }
1977                 return;
1978         }
1979         /* WINCAP
1980          * tile_width:nn */
1981         fullname = "tile_width";
1982         if (match_optname(opts, fullname, sizeof("tile_width")-1, TRUE)) {
1983                 op = string_for_opt(opts, negated);
1984                 if ((negated && !op) || (!negated && op)) {
1985                         iflags.wc_tile_width = negated ? 0 : atoi(op);
1986                 } else if (negated) bad_negation(fullname, TRUE);
1987                 return;
1988         }
1989         /* WINCAP
1990          * tile_file:name */
1991         fullname = "tile_file";
1992         if (match_optname(opts, fullname, sizeof("tile_file")-1, TRUE)) {
1993                 if ((op = string_for_opt(opts, FALSE)) != 0) {
1994                         if (iflags.wc_tile_file) free(iflags.wc_tile_file);
1995                         iflags.wc_tile_file = (char *)alloc(strlen(op) + 1);
1996                         Strcpy(iflags.wc_tile_file, op);
1997                 }
1998                 return;
1999         }
2000         /* WINCAP
2001          * tile_height:nn */
2002         fullname = "tile_height";
2003         if (match_optname(opts, fullname, sizeof("tile_height")-1, TRUE)) {
2004                 op = string_for_opt(opts, negated);
2005                 if ((negated && !op) || (!negated && op)) {
2006                         iflags.wc_tile_height = negated ? 0 : atoi(op);
2007                 } else if (negated) bad_negation(fullname, TRUE);
2008                 return;
2009         }
2010         /* WINCAP
2011          * vary_msgcount:nn */
2012         fullname = "vary_msgcount";
2013         if (match_optname(opts, fullname, sizeof("vary_msgcount")-1, TRUE)) {
2014                 op = string_for_opt(opts, negated);
2015                 if ((negated && !op) || (!negated && op)) {
2016                         iflags.wc_vary_msgcount = negated ? 0 : atoi(op);
2017                 } else if (negated) bad_negation(fullname, TRUE);
2018                 return;
2019         }
2020         fullname = "windowtype";
2021         if (match_optname(opts, fullname, 3, TRUE)) {
2022             if (negated) {
2023                 bad_negation(fullname, FALSE);
2024                 return;
2025             } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
2026                 char buf[WINTYPELEN];
2027                 nmcpy(buf, op, WINTYPELEN);
2028                 choose_windows(buf);
2029             }
2030             return;
2031         }
2032
2033         /* WINCAP
2034          * setting window colors
2035          * syntax: windowcolors=menu foregrnd/backgrnd text foregrnd/backgrnd
2036          */
2037         fullname = "windowcolors";
2038         if (match_optname(opts, fullname, 7, TRUE)) {
2039                 if ((op = string_for_opt(opts, FALSE)) != 0) {
2040                         if (!wc_set_window_colors(op))
2041                                 badoption(opts);
2042                 } else if (negated) bad_negation(fullname, TRUE);
2043                 return;
2044         }
2045
2046         /* menustyle:traditional or combo or full or partial */
2047         if (match_optname(opts, "menustyle", 4, TRUE)) {
2048                 int tmp;
2049                 boolean val_required = (strlen(opts) > 5 && !negated);
2050
2051                 if (!(op = string_for_opt(opts, !val_required))) {
2052                     if (val_required) return; /* string_for_opt gave feedback */
2053                     tmp = negated ? 'n' : 'f';
2054                 } else {
2055                     tmp = tolower(*op);
2056                 }
2057                 switch (tmp) {
2058                         case 'n':       /* none */
2059                         case 't':       /* traditional */
2060                                 flags.menu_style = MENU_TRADITIONAL;
2061                                 break;
2062                         case 'c':       /* combo: trad.class sel+menu */
2063                                 flags.menu_style = MENU_COMBINATION;
2064                                 break;
2065                         case 'p':       /* partial: no class menu */
2066                                 flags.menu_style = MENU_PARTIAL;
2067                                 break;
2068                         case 'f':       /* full: class menu + menu */
2069                                 flags.menu_style = MENU_FULL;
2070                                 break;
2071                         default:
2072                                 badoption(opts);
2073                 }
2074                 return;
2075         }
2076
2077         fullname = "menu_headings";
2078         if (match_optname(opts, fullname, 12, TRUE)) {
2079                 if (negated) {
2080                         bad_negation(fullname, FALSE);
2081                         return;
2082                 }
2083                 else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
2084                         return;
2085                 }
2086                 if (!strcmpi(opts,"bold"))
2087                         iflags.menu_headings = ATR_BOLD;
2088                 else if (!strcmpi(opts,"inverse"))
2089                         iflags.menu_headings = ATR_INVERSE;
2090                 else if (!strcmpi(opts,"underline"))
2091                         iflags.menu_headings = ATR_ULINE;
2092                 else
2093                         badoption(opts);
2094                 return;
2095         }
2096
2097         /* check for menu command mapping */
2098         for (i = 0; i < NUM_MENU_CMDS; i++) {
2099             fullname = default_menu_cmd_info[i].name;
2100             if (match_optname(opts, fullname, (int)strlen(fullname), TRUE)) {
2101                 if (negated)
2102                     bad_negation(fullname, FALSE);
2103                 else if ((op = string_for_opt(opts, FALSE)) != 0) {
2104                     int j;
2105                     char c, op_buf[BUFSZ];
2106                     boolean isbad = FALSE;
2107
2108                     escapes(op, op_buf);
2109                     c = *op_buf;
2110
2111                     if (c == 0 || c == '\r' || c == '\n' || c == '\033' ||
2112                             c == ' ' || digit(c) || (letter(c) && c != '@'))
2113                         isbad = TRUE;
2114                     else        /* reject default object class symbols */
2115                         for (j = 1; j < MAXOCLASSES; j++)
2116                             if (c == def_oc_syms[i]) {
2117                                 isbad = TRUE;
2118                                 break;
2119                             }
2120
2121                     if (isbad)
2122                         badoption(opts);
2123                     else
2124                         add_menu_cmd_alias(c, default_menu_cmd_info[i].cmd);
2125                 }
2126                 return;
2127             }
2128         }
2129
2130         /* OK, if we still haven't recognized the option, check the boolean
2131          * options list
2132          */
2133         for (i = 0; boolopt[i].name; i++) {
2134                 if (match_optname(opts, boolopt[i].name, 3, FALSE)) {
2135                         /* options that don't exist */
2136                         if (!boolopt[i].addr) {
2137                             if (!initial && !negated)
2138                                 pline_The("\"%s\" option is not available.",
2139                                         boolopt[i].name);
2140                             return;
2141                         }
2142                         /* options that must come from config file */
2143                         if (!initial && (boolopt[i].optflags == SET_IN_FILE)) {
2144                             rejectoption(boolopt[i].name);
2145                             return;
2146                         }
2147
2148                         *(boolopt[i].addr) = !negated;
2149
2150                         duplicate_opt_detection(boolopt[i].name, 0);
2151
2152 #if defined(TERMLIB) || defined(ASCIIGRAPH) || defined(MAC_GRAPHICS_ENV)
2153                         if (FALSE
2154 # ifdef TERMLIB
2155                                  || (boolopt[i].addr) == &iflags.DECgraphics
2156 # endif
2157 # ifdef ASCIIGRAPH
2158                                  || (boolopt[i].addr) == &iflags.IBMgraphics
2159 # endif
2160 # ifdef MAC_GRAPHICS_ENV
2161                                  || (boolopt[i].addr) == &iflags.MACgraphics
2162 # endif
2163                                 ) {
2164 # ifdef REINCARNATION
2165                             if (!initial && Is_rogue_level(&u.uz))
2166                                 assign_rogue_graphics(FALSE);
2167 # endif
2168                             need_redraw = TRUE;
2169 # ifdef TERMLIB
2170                             if ((boolopt[i].addr) == &iflags.DECgraphics)
2171                                 switch_graphics(iflags.DECgraphics ?
2172                                                 DEC_GRAPHICS : ASCII_GRAPHICS);
2173 # endif
2174 # ifdef ASCIIGRAPH
2175                             if ((boolopt[i].addr) == &iflags.IBMgraphics)
2176                                 switch_graphics(iflags.IBMgraphics ?
2177                                                 IBM_GRAPHICS : ASCII_GRAPHICS);
2178 # endif
2179 # ifdef MAC_GRAPHICS_ENV
2180                             if ((boolopt[i].addr) == &iflags.MACgraphics)
2181                                 switch_graphics(iflags.MACgraphics ?
2182                                                 MAC_GRAPHICS : ASCII_GRAPHICS);
2183 # endif
2184 # ifdef REINCARNATION
2185                             if (!initial && Is_rogue_level(&u.uz))
2186                                 assign_rogue_graphics(TRUE);
2187 # endif
2188                         }
2189 #endif /* TERMLIB || ASCIIGRAPH || MAC_GRAPHICS_ENV */
2190
2191                         /* only do processing below if setting with doset() */
2192                         if (initial) return;
2193
2194                         if ((boolopt[i].addr) == &flags.time
2195 #ifdef EXP_ON_BOTL
2196                          || (boolopt[i].addr) == &flags.showexp
2197 #endif
2198 #ifdef SCORE_ON_BOTL
2199                          || (boolopt[i].addr) == &flags.showscore
2200 #endif
2201                             )
2202                             flags.botl = TRUE;
2203
2204                         else if ((boolopt[i].addr) == &flags.invlet_constant) {
2205                             if (flags.invlet_constant) reassign();
2206                         }
2207 #ifdef LAN_MAIL
2208                         else if ((boolopt[i].addr) == &flags.biff) {
2209                             if (flags.biff) lan_mail_init();
2210                             else lan_mail_finish();
2211                         }
2212 #endif
2213                         else if ((boolopt[i].addr) == &flags.lit_corridor) {
2214                             /*
2215                              * All corridor squares seen via night vision or
2216                              * candles & lamps change.  Update them by calling
2217                              * newsym() on them.  Don't do this if we are
2218                              * initializing the options --- the vision system
2219                              * isn't set up yet.
2220                              */
2221                             vision_recalc(2);           /* shut down vision */
2222                             vision_full_recalc = 1;     /* delayed recalc */
2223                         }
2224                         else if ((boolopt[i].addr) == &iflags.use_inverse ||
2225                                         (boolopt[i].addr) == &iflags.showrace ||
2226                                         (boolopt[i].addr) == &iflags.hilite_pet) {
2227                             need_redraw = TRUE;
2228                         }
2229 #ifdef TEXTCOLOR
2230                         else if ((boolopt[i].addr) == &iflags.use_color) {
2231                             need_redraw = TRUE;
2232 # ifdef TOS
2233                             if ((boolopt[i].addr) == &iflags.use_color
2234                                 && iflags.BIOS) {
2235                                 if (colors_changed)
2236                                     restore_colors();
2237                                 else
2238                                     set_colors();
2239                             }
2240 # endif
2241                         }
2242 #endif
2243
2244                         return;
2245                 }
2246         }
2247
2248         /* out of valid options */
2249         badoption(opts);
2250 }
2251
2252
2253 static NEARDATA const char *menutype[] = {
2254         "traditional", "combination", "partial", "full"
2255 };
2256
2257 static NEARDATA const char *burdentype[] = {
2258         "unencumbered", "burdened", "stressed",
2259         "strained", "overtaxed", "overloaded"
2260 };
2261
2262 static NEARDATA const char *runmodes[] = {
2263         "teleport", "run", "walk", "crawl"
2264 };
2265
2266 /*
2267  * Convert the given string of object classes to a string of default object
2268  * symbols.
2269  */
2270 STATIC_OVL void
2271 oc_to_str(src,dest)
2272     char *src, *dest;
2273 {
2274     int i;
2275
2276     while ((i = (int) *src++) != 0) {
2277         if (i < 0 || i >= MAXOCLASSES)
2278             impossible("oc_to_str:  illegal object class %d", i);
2279         else
2280             *dest++ = def_oc_syms[i];
2281     }
2282     *dest = '\0';
2283 }
2284
2285 /*
2286  * Add the given mapping to the menu command map list.  Always keep the
2287  * maps valid C strings.
2288  */
2289 void
2290 add_menu_cmd_alias(from_ch, to_ch)
2291     char from_ch, to_ch;
2292 {
2293     if (n_menu_mapped >= MAX_MENU_MAPPED_CMDS)
2294         pline("out of menu map space.");
2295     else {
2296         mapped_menu_cmds[n_menu_mapped] = from_ch;
2297         mapped_menu_op[n_menu_mapped] = to_ch;
2298         n_menu_mapped++;
2299         mapped_menu_cmds[n_menu_mapped] = 0;
2300         mapped_menu_op[n_menu_mapped] = 0;
2301     }
2302 }
2303
2304 /*
2305  * Map the given character to its corresponding menu command.  If it
2306  * doesn't match anything, just return the original.
2307  */
2308 char
2309 map_menu_cmd(ch)
2310     char ch;
2311 {
2312     char *found = index(mapped_menu_cmds, ch);
2313     if (found) {
2314         int idx = found - mapped_menu_cmds;
2315         ch = mapped_menu_op[idx];
2316     }
2317     return ch;
2318 }
2319
2320
2321 #if defined(MICRO) || defined(MAC) || defined(WIN32)
2322 # define OPTIONS_HEADING "OPTIONS"
2323 #else
2324 # define OPTIONS_HEADING "NETHACKOPTIONS"
2325 #endif
2326
2327 static char fmtstr_doset_add_menu[] = "%s%-15s [%s]   "; 
2328 static char fmtstr_doset_add_menu_tab[] = "%s\t[%s]";
2329
2330 STATIC_OVL void
2331 doset_add_menu(win, option, indexoffset)
2332     winid win;                  /* window to add to */
2333     const char *option;         /* option name */
2334     int indexoffset;            /* value to add to index in compopt[], or zero
2335                                    if option cannot be changed */
2336 {
2337     const char *value = "unknown";              /* current value */
2338     char buf[BUFSZ], buf2[BUFSZ];
2339     anything any;
2340     int i;
2341
2342     any.a_void = 0;
2343     if (indexoffset == 0) {
2344         any.a_int = 0;
2345         value = get_compopt_value(option, buf2);
2346     } else {
2347         for (i=0; compopt[i].name; i++)
2348             if (strcmp(option, compopt[i].name) == 0) break;
2349
2350         if (compopt[i].name) {
2351             any.a_int = i + 1 + indexoffset;
2352             value = get_compopt_value(option, buf2);
2353         } else {
2354             /* We are trying to add an option not found in compopt[].
2355                This is almost certainly bad, but we'll let it through anyway
2356                (with a zero value, so it can't be selected). */
2357             any.a_int = 0;
2358         }
2359     }
2360     /* "    " replaces "a - " -- assumes menus follow that style */
2361     if (!iflags.menu_tab_sep)
2362         Sprintf(buf, fmtstr_doset_add_menu, any.a_int ? "" : "    ", option, value);
2363     else
2364         Sprintf(buf, fmtstr_doset_add_menu_tab, option, value);
2365     add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
2366 }
2367
2368 /* Changing options via menu by Per Liboriussen */
2369 int
2370 doset()
2371 {
2372         char buf[BUFSZ], buf2[BUFSZ];
2373         int i, pass, boolcount, pick_cnt, pick_idx, opt_indx;
2374         boolean *bool_p;
2375         winid tmpwin;
2376         anything any;
2377         menu_item *pick_list;
2378         int indexoffset, startpass, endpass;
2379         boolean setinitial = FALSE, fromfile = FALSE;
2380         int biggest_name = 0;
2381
2382         tmpwin = create_nhwindow(NHW_MENU);
2383         start_menu(tmpwin);
2384
2385         any.a_void = 0;
2386  add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2387                  "Booleans (selecting will toggle value):", MENU_UNSELECTED);
2388         any.a_int = 0;
2389         /* first list any other non-modifiable booleans, then modifiable ones */
2390         for (pass = 0; pass <= 1; pass++)
2391             for (i = 0; boolopt[i].name; i++)
2392                 if ((bool_p = boolopt[i].addr) != 0 &&
2393                         ((boolopt[i].optflags == DISP_IN_GAME && pass == 0) ||
2394                          (boolopt[i].optflags == SET_IN_GAME && pass == 1))) {
2395                     if (bool_p == &flags.female) continue;  /* obsolete */
2396 #ifdef WIZARD
2397                     if (bool_p == &iflags.sanity_check && !wizard) continue;
2398                     if (bool_p == &iflags.menu_tab_sep && !wizard) continue;
2399 #endif
2400                     if (is_wc_option(boolopt[i].name) &&
2401                         !wc_supported(boolopt[i].name)) continue;
2402                     if (is_wc2_option(boolopt[i].name) &&
2403                         !wc2_supported(boolopt[i].name)) continue;
2404                     any.a_int = (pass == 0) ? 0 : i + 1;
2405                     if (!iflags.menu_tab_sep)
2406                         Sprintf(buf, "%s%-13s [%s]",
2407                             pass == 0 ? "    " : "",
2408                             boolopt[i].name, *bool_p ? "true" : "false");
2409                     else
2410                         Sprintf(buf, "%s\t[%s]",
2411                             boolopt[i].name, *bool_p ? "true" : "false");
2412                     add_menu(tmpwin, NO_GLYPH, &any, 0, 0,
2413                              ATR_NONE, buf, MENU_UNSELECTED);
2414                 }
2415
2416         boolcount = i;
2417         indexoffset = boolcount;
2418         any.a_void = 0;
2419         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
2420  add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2421                  "Compounds (selecting will prompt for new value):",
2422                  MENU_UNSELECTED);
2423
2424         startpass = DISP_IN_GAME;
2425         endpass = SET_IN_GAME;
2426
2427         /* spin through the options to find the biggest name
2428            and adjust the format string accordingly if needed */
2429         biggest_name = 0;
2430         for (i = 0; compopt[i].name; i++)
2431                 if (compopt[i].optflags >= startpass && compopt[i].optflags <= endpass &&
2432                     strlen(compopt[i].name) > (unsigned) biggest_name)
2433                         biggest_name = (int) strlen(compopt[i].name);
2434         if (biggest_name > 30) biggest_name = 30;
2435         if (!iflags.menu_tab_sep)
2436                 Sprintf(fmtstr_doset_add_menu, "%%s%%-%ds [%%s]", biggest_name);
2437         
2438         /* deliberately put `name', `role', `race', `gender' first */
2439         doset_add_menu(tmpwin, "name", 0);
2440         doset_add_menu(tmpwin, "role", 0);
2441         doset_add_menu(tmpwin, "race", 0);
2442         doset_add_menu(tmpwin, "gender", 0);
2443
2444         for (pass = startpass; pass <= endpass; pass++) 
2445             for (i = 0; compopt[i].name; i++)
2446                 if (compopt[i].optflags == pass) {
2447                         if (!strcmp(compopt[i].name, "name") ||
2448                             !strcmp(compopt[i].name, "role") ||
2449                             !strcmp(compopt[i].name, "race") ||
2450                             !strcmp(compopt[i].name, "gender"))
2451                                 continue;
2452                         else if (is_wc_option(compopt[i].name) &&
2453                                         !wc_supported(compopt[i].name))
2454                                 continue;
2455                         else if (is_wc2_option(compopt[i].name) &&
2456                                         !wc2_supported(compopt[i].name))
2457                                 continue;
2458                         else
2459                                 doset_add_menu(tmpwin, compopt[i].name,
2460                                         (pass == DISP_IN_GAME) ? 0 : indexoffset);
2461                 }
2462 #ifdef AUTOPICKUP_EXCEPTIONS
2463         any.a_int = -1;
2464         Sprintf(buf, "autopickup exceptions (%d currently set)",
2465                 count_ape_maps((int *)0, (int *)0));
2466         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
2467
2468 #endif /* AUTOPICKUP_EXCEPTIONS */
2469 #ifdef PREFIXES_IN_USE
2470         any.a_void = 0;
2471         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
2472         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2473                  "Variable playground locations:", MENU_UNSELECTED);
2474         for (i = 0; i < PREFIX_COUNT; i++)
2475                 doset_add_menu(tmpwin, fqn_prefix_names[i], 0);
2476 #endif
2477         end_menu(tmpwin, "Set what options?");
2478         need_redraw = FALSE;
2479         if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &pick_list)) > 0) {
2480             /*
2481              * Walk down the selection list and either invert the booleans
2482              * or prompt for new values. In most cases, call parseoptions()
2483              * to take care of options that require special attention, like
2484              * redraws.
2485              */
2486             for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
2487                 opt_indx = pick_list[pick_idx].item.a_int - 1;
2488 #ifdef AUTOPICKUP_EXCEPTIONS
2489                 if (opt_indx == -2) {
2490                     special_handling("autopickup_exception",
2491                                         setinitial, fromfile);
2492                 } else
2493 #endif
2494                 if (opt_indx < boolcount) {
2495                     /* boolean option */
2496                     Sprintf(buf, "%s%s", *boolopt[opt_indx].addr ? "!" : "",
2497                             boolopt[opt_indx].name);
2498                     parseoptions(buf, setinitial, fromfile);
2499                     if (wc_supported(boolopt[opt_indx].name) ||
2500                         wc2_supported(boolopt[opt_indx].name))
2501                         preference_update(boolopt[opt_indx].name);
2502                 } else {
2503                     /* compound option */
2504                     opt_indx -= boolcount;
2505
2506                     if (!special_handling(compopt[opt_indx].name,
2507                                                         setinitial, fromfile)) {
2508                         Sprintf(buf, "Set %s to what?", compopt[opt_indx].name);
2509                         getlin(buf, buf2);
2510                         if (buf2[0] == '\033')
2511                             continue;
2512                         Sprintf(buf, "%s:%s", compopt[opt_indx].name, buf2);
2513                         /* pass the buck */
2514                         parseoptions(buf, setinitial, fromfile);
2515                     }
2516                     if (wc_supported(compopt[opt_indx].name) ||
2517                         wc2_supported(compopt[opt_indx].name))
2518                         preference_update(compopt[opt_indx].name);
2519                 }
2520             }
2521             free((genericptr_t)pick_list);
2522             pick_list = (menu_item *)0;
2523         }
2524
2525         destroy_nhwindow(tmpwin);
2526         if (need_redraw)
2527             (void) doredraw();
2528         return 0;
2529 }
2530
2531 STATIC_OVL boolean
2532 special_handling(optname, setinitial, setfromfile)
2533 const char *optname;
2534 boolean setinitial,setfromfile;
2535 {
2536     winid tmpwin;
2537     anything any;
2538     int i;
2539     char buf[BUFSZ];
2540     boolean retval = FALSE;
2541     
2542     /* Special handling of menustyle, pickup_burden, pickup_types,
2543      * disclose, runmode, msg_window, menu_headings, and number_pad options.
2544 #ifdef AUTOPICKUP_EXCEPTIONS
2545      * Also takes care of interactive autopickup_exception_handling changes.
2546 #endif
2547      */
2548     if (!strcmp("menustyle", optname)) {
2549         const char *style_name;
2550         menu_item *style_pick = (menu_item *)0;
2551         tmpwin = create_nhwindow(NHW_MENU);
2552         start_menu(tmpwin);
2553         for (i = 0; i < SIZE(menutype); i++) {
2554                 style_name = menutype[i];
2555                 /* note: separate `style_name' variable used
2556                    to avoid an optimizer bug in VAX C V2.3 */
2557                 any.a_int = i + 1;
2558                 add_menu(tmpwin, NO_GLYPH, &any, *style_name, 0,
2559                          ATR_NONE, style_name, MENU_UNSELECTED);
2560         }
2561         end_menu(tmpwin, "Select menustyle:");
2562         if (select_menu(tmpwin, PICK_ONE, &style_pick) > 0) {
2563                 flags.menu_style = style_pick->item.a_int - 1;
2564                 free((genericptr_t)style_pick);
2565         }
2566         destroy_nhwindow(tmpwin);
2567         retval = TRUE;
2568     } else if (!strcmp("pickup_burden", optname)) {
2569         const char *burden_name, *burden_letters = "ubsntl";
2570         menu_item *burden_pick = (menu_item *)0;
2571         tmpwin = create_nhwindow(NHW_MENU);
2572         start_menu(tmpwin);
2573         for (i = 0; i < SIZE(burdentype); i++) {
2574                 burden_name = burdentype[i];
2575                 any.a_int = i + 1;
2576                 add_menu(tmpwin, NO_GLYPH, &any, burden_letters[i], 0,
2577                          ATR_NONE, burden_name, MENU_UNSELECTED);
2578         }
2579         end_menu(tmpwin, "Select encumbrance level:");
2580         if (select_menu(tmpwin, PICK_ONE, &burden_pick) > 0) {
2581                 flags.pickup_burden = burden_pick->item.a_int - 1;
2582                 free((genericptr_t)burden_pick);
2583         }
2584         destroy_nhwindow(tmpwin);
2585         retval = TRUE;
2586     } else if (!strcmp("pickup_types", optname)) {
2587         /* parseoptions will prompt for the list of types */
2588         parseoptions(strcpy(buf, "pickup_types"), setinitial, setfromfile);
2589         retval = TRUE;
2590     } else if (!strcmp("disclose", optname)) {
2591         int pick_cnt, pick_idx, opt_idx;
2592         menu_item *disclosure_category_pick = (menu_item *)0;
2593         /*
2594          * The order of disclose_names[]
2595          * must correspond to disclosure_options in decl.h
2596          */
2597         static const char *disclosure_names[] = {
2598                 "inventory", "attributes", "vanquished", "genocides", "conduct"
2599         };
2600         int disc_cat[NUM_DISCLOSURE_OPTIONS];
2601         const char *disclosure_name;
2602
2603         tmpwin = create_nhwindow(NHW_MENU);
2604         start_menu(tmpwin);
2605         for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
2606                 disclosure_name = disclosure_names[i];
2607                 any.a_int = i + 1;
2608                 add_menu(tmpwin, NO_GLYPH, &any, disclosure_options[i], 0,
2609                          ATR_NONE, disclosure_name, MENU_UNSELECTED);
2610                 disc_cat[i] = 0;
2611         }
2612         end_menu(tmpwin, "Change which disclosure options categories:");
2613         if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &disclosure_category_pick)) > 0) {
2614             for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
2615                 opt_idx = disclosure_category_pick[pick_idx].item.a_int - 1;
2616                 disc_cat[opt_idx] = 1;
2617             }
2618             free((genericptr_t)disclosure_category_pick);
2619             disclosure_category_pick = (menu_item *)0;
2620         }
2621         destroy_nhwindow(tmpwin);
2622
2623         for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
2624             if (disc_cat[i]) {
2625                 char dbuf[BUFSZ];
2626                 menu_item *disclosure_option_pick = (menu_item *)0;
2627                 Sprintf(dbuf, "Disclosure options for %s:", disclosure_names[i]);
2628                 tmpwin = create_nhwindow(NHW_MENU);
2629                 start_menu(tmpwin);
2630                 any.a_char = DISCLOSE_NO_WITHOUT_PROMPT;
2631                 add_menu(tmpwin, NO_GLYPH, &any, 'a', 0,
2632                         ATR_NONE,"Never disclose and don't prompt", MENU_UNSELECTED);
2633                 any.a_void = 0;
2634                 any.a_char = DISCLOSE_YES_WITHOUT_PROMPT;
2635                 add_menu(tmpwin, NO_GLYPH, &any, 'b', 0,
2636                         ATR_NONE,"Always disclose and don't prompt", MENU_UNSELECTED);
2637                 any.a_void = 0;
2638                 any.a_char = DISCLOSE_PROMPT_DEFAULT_NO;
2639                 add_menu(tmpwin, NO_GLYPH, &any, 'c', 0,
2640                         ATR_NONE,"Prompt and default answer to \"No\"", MENU_UNSELECTED);
2641                 any.a_void = 0;
2642                 any.a_char = DISCLOSE_PROMPT_DEFAULT_YES;
2643                 add_menu(tmpwin, NO_GLYPH, &any, 'd', 0,
2644                         ATR_NONE,"Prompt and default answer to \"Yes\"", MENU_UNSELECTED);
2645                 end_menu(tmpwin, dbuf);
2646                 if (select_menu(tmpwin, PICK_ONE, &disclosure_option_pick) > 0) {
2647                         flags.end_disclose[i] = disclosure_option_pick->item.a_char;
2648                         free((genericptr_t)disclosure_option_pick);
2649                 }
2650                 destroy_nhwindow(tmpwin);
2651             }
2652         }
2653         retval = TRUE;
2654     } else if (!strcmp("runmode", optname)) {
2655         const char *mode_name;
2656         menu_item *mode_pick = (menu_item *)0;
2657         tmpwin = create_nhwindow(NHW_MENU);
2658         start_menu(tmpwin);
2659         for (i = 0; i < SIZE(runmodes); i++) {
2660                 mode_name = runmodes[i];
2661                 any.a_int = i + 1;
2662                 add_menu(tmpwin, NO_GLYPH, &any, *mode_name, 0,
2663                          ATR_NONE, mode_name, MENU_UNSELECTED);
2664         }
2665         end_menu(tmpwin, "Select run/travel display mode:");
2666         if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
2667                 iflags.runmode = mode_pick->item.a_int - 1;
2668                 free((genericptr_t)mode_pick);
2669         }
2670         destroy_nhwindow(tmpwin);
2671         retval = TRUE;
2672     } 
2673 #ifdef TTY_GRAPHICS
2674       else if (!strcmp("msg_window", optname)) {
2675         /* by Christian W. Cooper */
2676         menu_item *window_pick = (menu_item *)0;
2677         tmpwin = create_nhwindow(NHW_MENU);
2678         start_menu(tmpwin);
2679         any.a_char = 's';
2680         add_menu(tmpwin, NO_GLYPH, &any, 's', 0,
2681                 ATR_NONE, "single", MENU_UNSELECTED);
2682         any.a_char = 'c';
2683         add_menu(tmpwin, NO_GLYPH, &any, 'c', 0,
2684                 ATR_NONE, "combination", MENU_UNSELECTED);
2685         any.a_char = 'f';
2686         add_menu(tmpwin, NO_GLYPH, &any, 'f', 0,
2687                 ATR_NONE, "full", MENU_UNSELECTED);
2688         any.a_char = 'r';
2689         add_menu(tmpwin, NO_GLYPH, &any, 'r', 0,
2690                 ATR_NONE, "reversed", MENU_UNSELECTED);
2691         end_menu(tmpwin, "Select message history display type:");
2692         if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) {
2693                 iflags.prevmsg_window = window_pick->item.a_char;
2694                 free((genericptr_t)window_pick);
2695         }
2696         destroy_nhwindow(tmpwin);
2697         retval = TRUE;
2698     }
2699 #endif
2700      else if (!strcmp("align_message", optname) ||
2701                 !strcmp("align_status", optname)) {
2702         menu_item *window_pick = (menu_item *)0;
2703         char abuf[BUFSZ];
2704         boolean msg = (*(optname+6) == 'm');
2705
2706         tmpwin = create_nhwindow(NHW_MENU);
2707         start_menu(tmpwin);
2708         any.a_int = ALIGN_TOP;
2709         add_menu(tmpwin, NO_GLYPH, &any, 't', 0,
2710                 ATR_NONE, "top", MENU_UNSELECTED);
2711         any.a_int = ALIGN_BOTTOM;
2712         add_menu(tmpwin, NO_GLYPH, &any, 'b', 0,
2713                 ATR_NONE, "bottom", MENU_UNSELECTED);
2714         any.a_int = ALIGN_LEFT;
2715         add_menu(tmpwin, NO_GLYPH, &any, 'l', 0,
2716                 ATR_NONE, "left", MENU_UNSELECTED);
2717         any.a_int = ALIGN_RIGHT;
2718         add_menu(tmpwin, NO_GLYPH, &any, 'r', 0,
2719                 ATR_NONE, "right", MENU_UNSELECTED);
2720         Sprintf(abuf, "Select %s window placement relative to the map:",
2721                 msg ? "message" : "status");
2722         end_menu(tmpwin, abuf);
2723         if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) {          
2724                 if (msg) iflags.wc_align_message = window_pick->item.a_int;
2725                 else iflags.wc_align_status = window_pick->item.a_int;
2726                 free((genericptr_t)window_pick);
2727         }
2728         destroy_nhwindow(tmpwin);
2729         retval = TRUE;
2730     } else if (!strcmp("number_pad", optname)) {
2731         static const char *npchoices[3] =
2732                 {"0 (off)", "1 (on)", "2 (on, DOS compatible)"};
2733         const char *npletters = "abc";
2734         menu_item *mode_pick = (menu_item *)0;
2735
2736         tmpwin = create_nhwindow(NHW_MENU);
2737         start_menu(tmpwin);
2738         for (i = 0; i < SIZE(npchoices); i++) {
2739                 any.a_int = i + 1;
2740                 add_menu(tmpwin, NO_GLYPH, &any, npletters[i], 0,
2741                          ATR_NONE, npchoices[i], MENU_UNSELECTED);
2742         }
2743         end_menu(tmpwin, "Select number_pad mode:");
2744         if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
2745                 int mode = mode_pick->item.a_int - 1;
2746                 switch(mode) {
2747                         case 2:
2748                                 iflags.num_pad = 1;
2749                                 iflags.num_pad_mode = 1;
2750                                 break;
2751                         case 1:
2752                                 iflags.num_pad = 1;
2753                                 iflags.num_pad_mode = 0;
2754                                 break;
2755                         case 0:
2756                         default:
2757                                 iflags.num_pad = 0;
2758                                 iflags.num_pad_mode = 0;
2759                 }
2760                 free((genericptr_t)mode_pick);
2761         }
2762         destroy_nhwindow(tmpwin);
2763         retval = TRUE;
2764     } else if (!strcmp("menu_headings", optname)) {
2765         static const char *mhchoices[3] = {"bold", "inverse", "underline"};
2766         const char *npletters = "biu";
2767         menu_item *mode_pick = (menu_item *)0;
2768
2769         tmpwin = create_nhwindow(NHW_MENU);
2770         start_menu(tmpwin);
2771         for (i = 0; i < SIZE(mhchoices); i++) {
2772                 any.a_int = i + 1;
2773                 add_menu(tmpwin, NO_GLYPH, &any, npletters[i], 0,
2774                          ATR_NONE, mhchoices[i], MENU_UNSELECTED);
2775         }
2776         end_menu(tmpwin, "How to highlight menu headings:");
2777         if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
2778                 int mode = mode_pick->item.a_int - 1;
2779                 switch(mode) {
2780                         case 2:
2781                                 iflags.menu_headings = ATR_ULINE;
2782                                 break;
2783                         case 0:
2784                                 iflags.menu_headings = ATR_BOLD;
2785                                 break;
2786                         case 1:
2787                         default:
2788                                 iflags.menu_headings = ATR_INVERSE;
2789                 }
2790                 free((genericptr_t)mode_pick);
2791         }
2792         destroy_nhwindow(tmpwin);
2793         retval = TRUE;
2794 #ifdef AUTOPICKUP_EXCEPTIONS
2795     } else if (!strcmp("autopickup_exception", optname)) {
2796         boolean retval;
2797         int pick_cnt, pick_idx, opt_idx, pass;
2798         int totalapes = 0, numapes[2] = {0,0};
2799         menu_item *pick_list = (menu_item *)0;
2800         anything any;
2801         char apebuf[BUFSZ];
2802         struct autopickup_exception *ape;
2803         static const char *action_titles[] = {
2804                 "a", "add new autopickup exception",
2805                 "l", "list autopickup exceptions",
2806                 "r", "remove existing autopickup exception",
2807                 "e", "exit this menu",
2808         };
2809 ape_again:
2810         opt_idx = 0;
2811         totalapes = count_ape_maps(&numapes[AP_LEAVE], &numapes[AP_GRAB]);
2812         tmpwin = create_nhwindow(NHW_MENU);
2813         start_menu(tmpwin);
2814         any.a_int = 0;
2815         for (i = 0; i < SIZE(action_titles) ; i += 2) {
2816                 any.a_int++;
2817                 if (!totalapes && (i >= 2 && i < 6)) continue;
2818                 add_menu(tmpwin, NO_GLYPH, &any, *action_titles[i],
2819                       0, ATR_NONE, action_titles[i+1], MENU_UNSELECTED);
2820         }
2821         end_menu(tmpwin, "Do what?");
2822         if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &pick_list)) > 0) {
2823                 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
2824                         opt_idx = pick_list[pick_idx].item.a_int - 1;
2825                 }
2826                 free((genericptr_t)pick_list);
2827                 pick_list = (menu_item *)0;
2828         }
2829         destroy_nhwindow(tmpwin);
2830         if (pick_cnt < 1) return FALSE;
2831
2832         if (opt_idx == 0) {     /* add new */
2833                 getlin("What new autopickup exception pattern?", &apebuf[1]);
2834                 if (apebuf[1] == '\033') return FALSE;
2835                 apebuf[0] = '"';
2836                 Strcat(apebuf,"\"");
2837                 add_autopickup_exception(apebuf);
2838                 goto ape_again;
2839         } else if (opt_idx == 3) {
2840                 retval = TRUE;
2841         } else {        /* remove */
2842                 tmpwin = create_nhwindow(NHW_MENU);
2843                 start_menu(tmpwin);
2844                 for (pass = AP_LEAVE; pass <= AP_GRAB; ++pass) {
2845                     if (numapes[pass] == 0) continue;
2846                     ape = iflags.autopickup_exceptions[pass];
2847                     any.a_void = 0;
2848                     add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
2849                                 (pass == 0) ? "Never pickup" : "Always pickup",
2850                                 MENU_UNSELECTED);
2851                     for (i = 0; i < numapes[pass] && ape; i++) {
2852                         any.a_void = (opt_idx == 1) ? 0 : ape;
2853                         Sprintf(apebuf, "\"%s\"", ape->pattern);
2854                         add_menu(tmpwin, NO_GLYPH, &any,
2855                                 0, 0, ATR_NONE, apebuf, MENU_UNSELECTED);
2856                         ape = ape->next;
2857                     }
2858                 }
2859                 Sprintf(apebuf, "%s autopickup exceptions",
2860                         (opt_idx == 1) ? "List of" : "Remove which");
2861                 end_menu(tmpwin, apebuf);
2862                 pick_cnt = select_menu(tmpwin,
2863                                         (opt_idx == 1) ?  PICK_NONE : PICK_ANY,
2864                                         &pick_list);
2865                 if (pick_cnt > 0) {
2866                     for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
2867                         remove_autopickup_exception(
2868                          (struct autopickup_exception *)pick_list[pick_idx].item.a_void);
2869                 }
2870                 free((genericptr_t)pick_list);
2871                 pick_list = (menu_item *)0;
2872                 destroy_nhwindow(tmpwin);
2873                 goto ape_again;
2874         }
2875         retval = TRUE;
2876 #endif /* AUTOPICKUP_EXCEPTIONS */
2877     }
2878     return retval;
2879 }
2880
2881 #define rolestring(val,array,field) ((val >= 0) ? array[val].field : \
2882                                      (val == ROLE_RANDOM) ? randomrole : none)
2883
2884 /* This is ugly. We have all the option names in the compopt[] array,
2885    but we need to look at each option individually to get the value. */
2886 STATIC_OVL const char *
2887 get_compopt_value(optname, buf)
2888 const char *optname;
2889 char *buf;
2890 {
2891         char ocl[MAXOCLASSES+1];
2892         static const char none[] = "(none)", randomrole[] = "random",
2893                      to_be_done[] = "(to be done)",
2894                      defopt[] = "default",
2895                      defbrief[] = "def";
2896         int i;
2897
2898         buf[0] = '\0';
2899         if (!strcmp(optname,"align_message"))
2900                 Sprintf(buf, "%s", iflags.wc_align_message == ALIGN_TOP     ? "top" :
2901                                    iflags.wc_align_message == ALIGN_LEFT    ? "left" :
2902                                    iflags.wc_align_message == ALIGN_BOTTOM  ? "bottom" :
2903                                    iflags.wc_align_message == ALIGN_RIGHT   ? "right" :
2904                                    defopt);
2905         else if (!strcmp(optname,"align_status"))
2906                 Sprintf(buf, "%s", iflags.wc_align_status == ALIGN_TOP     ? "top" :
2907                                    iflags.wc_align_status == ALIGN_LEFT    ? "left" :
2908                                    iflags.wc_align_status == ALIGN_BOTTOM  ? "bottom" :
2909                                    iflags.wc_align_status == ALIGN_RIGHT   ? "right" :
2910                                    defopt);
2911         else if (!strcmp(optname,"align"))
2912                 Sprintf(buf, "%s", rolestring(flags.initalign, aligns, adj));
2913 #ifdef WIN32CON
2914         else if (!strcmp(optname,"altkeyhandler"))
2915                 Sprintf(buf, "%s", iflags.altkeyhandler[0] ?
2916                         iflags.altkeyhandler : "default");
2917 #endif
2918         else if (!strcmp(optname, "boulder"))
2919                 Sprintf(buf, "%c", iflags.bouldersym ?
2920                         iflags.bouldersym : oc_syms[(int)objects[BOULDER].oc_class]);
2921         else if (!strcmp(optname, "catname")) 
2922                 Sprintf(buf, "%s", catname[0] ? catname : none );
2923         else if (!strcmp(optname, "disclose")) {
2924                 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
2925                         char topt[2];
2926                         if (i) Strcat(buf," ");
2927                         topt[1] = '\0';
2928                         topt[0] = flags.end_disclose[i];
2929                         Strcat(buf, topt);
2930                         topt[0] = disclosure_options[i];
2931                         Strcat(buf, topt);
2932                 }
2933         }
2934         else if (!strcmp(optname, "dogname")) 
2935                 Sprintf(buf, "%s", dogname[0] ? dogname : none );
2936         else if (!strcmp(optname, "dungeon"))
2937                 Sprintf(buf, "%s", to_be_done);
2938         else if (!strcmp(optname, "effects"))
2939                 Sprintf(buf, "%s", to_be_done);
2940         else if (!strcmp(optname, "font_map"))
2941                 Sprintf(buf, "%s", iflags.wc_font_map ? iflags.wc_font_map : defopt);
2942         else if (!strcmp(optname, "font_message"))
2943                 Sprintf(buf, "%s", iflags.wc_font_message ? iflags.wc_font_message : defopt);
2944         else if (!strcmp(optname, "font_status"))
2945                 Sprintf(buf, "%s", iflags.wc_font_status ? iflags.wc_font_status : defopt);
2946         else if (!strcmp(optname, "font_menu"))
2947                 Sprintf(buf, "%s", iflags.wc_font_menu ? iflags.wc_font_menu : defopt);
2948         else if (!strcmp(optname, "font_text"))
2949                 Sprintf(buf, "%s", iflags.wc_font_text ? iflags.wc_font_text : defopt);
2950         else if (!strcmp(optname, "font_size_map")) {
2951                 if (iflags.wc_fontsiz_map) Sprintf(buf, "%d", iflags.wc_fontsiz_map);
2952                 else Strcpy(buf, defopt);
2953         }
2954         else if (!strcmp(optname, "font_size_message")) {
2955                 if (iflags.wc_fontsiz_message) Sprintf(buf, "%d",
2956                                                         iflags.wc_fontsiz_message);
2957                 else Strcpy(buf, defopt);
2958         }
2959         else if (!strcmp(optname, "font_size_status")) {
2960                 if (iflags.wc_fontsiz_status) Sprintf(buf, "%d", iflags.wc_fontsiz_status);
2961                 else Strcpy(buf, defopt);
2962         }
2963         else if (!strcmp(optname, "font_size_menu")) {
2964                 if (iflags.wc_fontsiz_menu) Sprintf(buf, "%d", iflags.wc_fontsiz_menu);
2965                 else Strcpy(buf, defopt);
2966         }
2967         else if (!strcmp(optname, "font_size_text")) {
2968                 if (iflags.wc_fontsiz_text) Sprintf(buf, "%d",iflags.wc_fontsiz_text);
2969                 else Strcpy(buf, defopt);
2970         }
2971         else if (!strcmp(optname, "fruit")) 
2972                 Sprintf(buf, "%s", pl_fruit);
2973         else if (!strcmp(optname, "gender"))
2974                 Sprintf(buf, "%s", rolestring(flags.initgend, genders, adj));
2975         else if (!strcmp(optname, "horsename")) 
2976                 Sprintf(buf, "%s", horsename[0] ? horsename : none);
2977         else if (!strcmp(optname, "map_mode"))
2978                 Sprintf(buf, "%s",
2979                         iflags.wc_map_mode == MAP_MODE_TILES      ? "tiles" :
2980                         iflags.wc_map_mode == MAP_MODE_ASCII4x6   ? "ascii4x6" :
2981                         iflags.wc_map_mode == MAP_MODE_ASCII6x8   ? "ascii6x8" :
2982                         iflags.wc_map_mode == MAP_MODE_ASCII8x8   ? "ascii8x8" :
2983                         iflags.wc_map_mode == MAP_MODE_ASCII16x8  ? "ascii16x8" :
2984                         iflags.wc_map_mode == MAP_MODE_ASCII7x12  ? "ascii7x12" :
2985                         iflags.wc_map_mode == MAP_MODE_ASCII8x12  ? "ascii8x12" :
2986                         iflags.wc_map_mode == MAP_MODE_ASCII16x12 ? "ascii16x12" :
2987                         iflags.wc_map_mode == MAP_MODE_ASCII12x16 ? "ascii12x16" :
2988                         iflags.wc_map_mode == MAP_MODE_ASCII10x18 ? "ascii10x18" :
2989                         iflags.wc_map_mode == MAP_MODE_ASCII_FIT_TO_SCREEN ?
2990                         "fit_to_screen" : defopt);
2991         else if (!strcmp(optname, "menustyle")) 
2992                 Sprintf(buf, "%s", menutype[(int)flags.menu_style] );
2993         else if (!strcmp(optname, "menu_deselect_all"))
2994                 Sprintf(buf, "%s", to_be_done);
2995         else if (!strcmp(optname, "menu_deselect_page"))
2996                 Sprintf(buf, "%s", to_be_done);
2997         else if (!strcmp(optname, "menu_first_page"))
2998                 Sprintf(buf, "%s", to_be_done);
2999         else if (!strcmp(optname, "menu_invert_all"))
3000                 Sprintf(buf, "%s", to_be_done);
3001         else if (!strcmp(optname, "menu_headings")) {
3002                 Sprintf(buf, "%s", (iflags.menu_headings == ATR_BOLD) ?
3003                         "bold" :   (iflags.menu_headings == ATR_INVERSE) ?
3004                         "inverse" :   (iflags.menu_headings == ATR_ULINE) ?
3005                         "underline" : "unknown");
3006         }
3007         else if (!strcmp(optname, "menu_invert_page"))
3008                 Sprintf(buf, "%s", to_be_done);
3009         else if (!strcmp(optname, "menu_last_page"))
3010                 Sprintf(buf, "%s", to_be_done);
3011         else if (!strcmp(optname, "menu_next_page"))
3012                 Sprintf(buf, "%s", to_be_done);
3013         else if (!strcmp(optname, "menu_previous_page"))
3014                 Sprintf(buf, "%s", to_be_done);
3015         else if (!strcmp(optname, "menu_search"))
3016                 Sprintf(buf, "%s", to_be_done);
3017         else if (!strcmp(optname, "menu_select_all"))
3018                 Sprintf(buf, "%s", to_be_done);
3019         else if (!strcmp(optname, "menu_select_page"))
3020                 Sprintf(buf, "%s", to_be_done);
3021         else if (!strcmp(optname, "monsters"))
3022                 Sprintf(buf, "%s", to_be_done);
3023         else if (!strcmp(optname, "msghistory"))
3024                 Sprintf(buf, "%u", iflags.msg_history);
3025 #ifdef TTY_GRAPHICS
3026         else if (!strcmp(optname, "msg_window"))
3027                 Sprintf(buf, "%s", (iflags.prevmsg_window=='s') ? "single" :
3028                                         (iflags.prevmsg_window=='c') ? "combination" :
3029                                         (iflags.prevmsg_window=='f') ? "full" : "reversed");
3030 #endif
3031         else if (!strcmp(optname, "name"))
3032                 Sprintf(buf, "%s", plname);
3033         else if (!strcmp(optname, "number_pad"))
3034                 Sprintf(buf, "%s",
3035                         (!iflags.num_pad) ? "0=off" :
3036                         (iflags.num_pad_mode) ? "2=on, DOS compatible" : "1=on");
3037         else if (!strcmp(optname, "objects"))
3038                 Sprintf(buf, "%s", to_be_done);
3039         else if (!strcmp(optname, "packorder")) {
3040                 oc_to_str(flags.inv_order, ocl);
3041                 Sprintf(buf, "%s", ocl);
3042              }
3043 #ifdef CHANGE_COLOR
3044         else if (!strcmp(optname, "palette")) 
3045                 Sprintf(buf, "%s", get_color_string());
3046 #endif
3047         else if (!strcmp(optname, "pettype")) 
3048                 Sprintf(buf, "%s", (preferred_pet == 'c') ? "cat" :
3049                                 (preferred_pet == 'd') ? "dog" :
3050                                 (preferred_pet == 'n') ? "none" : "random");
3051         else if (!strcmp(optname, "pickup_burden"))
3052                 Sprintf(buf, "%s", burdentype[flags.pickup_burden] );
3053         else if (!strcmp(optname, "pickup_types")) {
3054                 oc_to_str(flags.pickup_types, ocl);
3055                 Sprintf(buf, "%s", ocl[0] ? ocl : "all" );
3056              }
3057         else if (!strcmp(optname, "race"))
3058                 Sprintf(buf, "%s", rolestring(flags.initrace, races, noun));
3059         else if (!strcmp(optname, "role"))
3060                 Sprintf(buf, "%s", rolestring(flags.initrole, roles, name.m));
3061         else if (!strcmp(optname, "runmode"))
3062                 Sprintf(buf, "%s", runmodes[iflags.runmode]);
3063         else if (!strcmp(optname, "scores")) {
3064                 Sprintf(buf, "%d top/%d around%s", flags.end_top,
3065                                 flags.end_around, flags.end_own ? "/own" : "");
3066         }
3067         else if (!strcmp(optname, "scroll_amount")) {
3068                 if (iflags.wc_scroll_amount) Sprintf(buf, "%d",iflags.wc_scroll_amount);
3069                 else Strcpy(buf, defopt);
3070         }
3071         else if (!strcmp(optname, "scroll_margin")) {
3072                 if (iflags.wc_scroll_margin) Sprintf(buf, "%d",iflags.wc_scroll_margin);
3073                 else Strcpy(buf, defopt);
3074         }
3075         else if (!strcmp(optname, "player_selection"))
3076                 Sprintf(buf, "%s", iflags.wc_player_selection ? "prompts" : "dialog");
3077 #ifdef MSDOS
3078         else if (!strcmp(optname, "soundcard"))
3079                 Sprintf(buf, "%s", to_be_done);
3080 #endif
3081         else if (!strcmp(optname, "suppress_alert")) {
3082             if (flags.suppress_alert == 0L)
3083                 Strcpy(buf, none);
3084             else
3085                 Sprintf(buf, "%lu.%lu.%lu",
3086                         FEATURE_NOTICE_VER_MAJ,
3087                         FEATURE_NOTICE_VER_MIN,
3088                         FEATURE_NOTICE_VER_PATCH);
3089         }
3090         else if (!strcmp(optname, "tile_file"))
3091                 Sprintf(buf, "%s", iflags.wc_tile_file ? iflags.wc_tile_file : defopt);
3092         else if (!strcmp(optname, "tile_height")) {
3093                 if (iflags.wc_tile_height) Sprintf(buf, "%d",iflags.wc_tile_height);
3094                 else Strcpy(buf, defopt);
3095         }
3096         else if (!strcmp(optname, "tile_width")) {
3097                 if (iflags.wc_tile_width) Sprintf(buf, "%d",iflags.wc_tile_width);
3098                 else Strcpy(buf, defopt);
3099         }
3100         else if (!strcmp(optname, "traps"))
3101                 Sprintf(buf, "%s", to_be_done);
3102         else if (!strcmp(optname, "vary_msgcount")) {
3103                 if (iflags.wc_vary_msgcount) Sprintf(buf, "%d",iflags.wc_vary_msgcount);
3104                 else Strcpy(buf, defopt);
3105         }
3106 #ifdef MSDOS
3107         else if (!strcmp(optname, "video"))
3108                 Sprintf(buf, "%s", to_be_done);
3109 #endif
3110 #ifdef VIDEOSHADES
3111         else if (!strcmp(optname, "videoshades"))
3112                 Sprintf(buf, "%s-%s-%s", shade[0],shade[1],shade[2]);
3113         else if (!strcmp(optname, "videocolors"))
3114                 Sprintf(buf, "%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d",
3115                         ttycolors[CLR_RED], ttycolors[CLR_GREEN],
3116                         ttycolors[CLR_BROWN], ttycolors[CLR_BLUE],
3117                         ttycolors[CLR_MAGENTA], ttycolors[CLR_CYAN],
3118                         ttycolors[CLR_ORANGE], ttycolors[CLR_BRIGHT_GREEN],
3119                         ttycolors[CLR_YELLOW], ttycolors[CLR_BRIGHT_BLUE],
3120                         ttycolors[CLR_BRIGHT_MAGENTA],
3121                         ttycolors[CLR_BRIGHT_CYAN]);
3122 #endif /* VIDEOSHADES */
3123         else if (!strcmp(optname, "windowtype"))
3124                 Sprintf(buf, "%s", windowprocs.name);
3125         else if (!strcmp(optname, "windowcolors"))
3126                 Sprintf(buf, "%s/%s %s/%s %s/%s %s/%s",
3127                         iflags.wc_foregrnd_menu    ? iflags.wc_foregrnd_menu : defbrief,
3128                         iflags.wc_backgrnd_menu    ? iflags.wc_backgrnd_menu : defbrief,
3129                         iflags.wc_foregrnd_message ? iflags.wc_foregrnd_message : defbrief,
3130                         iflags.wc_backgrnd_message ? iflags.wc_backgrnd_message : defbrief,
3131                         iflags.wc_foregrnd_status  ? iflags.wc_foregrnd_status : defbrief,
3132                         iflags.wc_backgrnd_status  ? iflags.wc_backgrnd_status : defbrief,
3133                         iflags.wc_foregrnd_text    ? iflags.wc_foregrnd_text : defbrief,
3134                         iflags.wc_backgrnd_text    ? iflags.wc_backgrnd_text : defbrief);
3135 #ifdef PREFIXES_IN_USE
3136         else {
3137             for (i = 0; i < PREFIX_COUNT; ++i)
3138                 if (!strcmp(optname, fqn_prefix_names[i]) && fqn_prefix[i])
3139                         Sprintf(buf, "%s", fqn_prefix[i]);
3140         }
3141 #endif
3142
3143         if (buf[0]) return buf;
3144         else return "unknown";
3145 }
3146
3147 int
3148 dotogglepickup()
3149 {
3150         char buf[BUFSZ], ocl[MAXOCLASSES+1];
3151
3152         flags.pickup = !flags.pickup;
3153         if (flags.pickup) {
3154             oc_to_str(flags.pickup_types, ocl);
3155             Sprintf(buf, "ON, for %s objects%s", ocl[0] ? ocl : "all",
3156 #ifdef AUTOPICKUP_EXCEPTIONS
3157                         (iflags.autopickup_exceptions[AP_LEAVE] ||
3158                          iflags.autopickup_exceptions[AP_GRAB]) ?
3159                          ((count_ape_maps((int *)0, (int *)0) == 1) ?
3160                             ", with one exception" : ", with some exceptions") :
3161 #endif
3162                         "");
3163         } else {
3164             Strcpy(buf, "OFF");
3165         }
3166         pline("Autopickup: %s.", buf);
3167         return 0;
3168 }
3169
3170 #ifdef AUTOPICKUP_EXCEPTIONS
3171 int
3172 add_autopickup_exception(mapping)
3173 const char *mapping;
3174 {
3175         struct autopickup_exception *ape, **apehead;
3176         char text[256], *text2;
3177         int textsize = 0;
3178         boolean grab = FALSE;
3179
3180         if (sscanf(mapping, "\"%255[^\"]\"", text) == 1) {
3181                 text2 = &text[0];
3182                 if (*text2 == '<') {            /* force autopickup */
3183                         grab = TRUE;
3184                         ++text2;
3185                 } else if (*text2 == '>') {     /* default - Do not pickup */
3186                         grab = FALSE;
3187                         ++text2;
3188                 }
3189                 textsize = strlen(text2);
3190                 apehead = (grab) ? &iflags.autopickup_exceptions[AP_GRAB] :
3191                                    &iflags.autopickup_exceptions[AP_LEAVE];
3192                 ape = (struct autopickup_exception *)
3193                                 alloc(sizeof(struct autopickup_exception));
3194                 ape->pattern = (char *) alloc(textsize+1);
3195                 Strcpy(ape->pattern, text2);
3196                 ape->grab = grab;
3197                 if (!*apehead) ape->next = (struct autopickup_exception *)0;
3198                 else ape->next = *apehead;
3199                 *apehead = ape;
3200         } else {
3201             raw_print("syntax error in AUTOPICKUP_EXCEPTION");
3202             return 0;
3203         }
3204         return 1;
3205 }
3206
3207 STATIC_OVL void
3208 remove_autopickup_exception(whichape)
3209 struct autopickup_exception *whichape;
3210 {
3211     struct autopickup_exception *ape, *prev = 0;
3212     int chain = whichape->grab ? AP_GRAB : AP_LEAVE;
3213
3214     for (ape = iflags.autopickup_exceptions[chain]; ape;) {
3215         if (ape == whichape) {
3216             struct autopickup_exception *freeape = ape;
3217             ape = ape->next;
3218             if (prev) prev->next = ape;
3219             else iflags.autopickup_exceptions[chain] = ape;
3220             free(freeape->pattern);
3221             free(freeape);
3222         } else {
3223             prev = ape;
3224             ape = ape->next;
3225         }
3226     }
3227 }
3228
3229 STATIC_OVL int
3230 count_ape_maps(leave, grab)
3231 int *leave, *grab;
3232 {
3233         struct autopickup_exception *ape;
3234         int pass, totalapes, numapes[2] = {0,0};
3235
3236         for (pass = AP_LEAVE; pass <= AP_GRAB; ++pass) {
3237                 ape = iflags.autopickup_exceptions[pass];
3238                 while(ape) {
3239                         ape = ape->next;
3240                         numapes[pass]++;
3241                 }
3242         }
3243         totalapes = numapes[AP_LEAVE] + numapes[AP_GRAB];
3244         if (leave) *leave = numapes[AP_LEAVE];
3245         if (grab) *grab = numapes[AP_GRAB];
3246         return totalapes;
3247 }
3248
3249 void
3250 free_autopickup_exceptions()
3251 {
3252         struct autopickup_exception *ape;
3253         int pass;
3254
3255         for (pass = AP_LEAVE; pass <= AP_GRAB; ++pass) {
3256                 while((ape = iflags.autopickup_exceptions[pass]) != 0) {
3257                         free(ape->pattern);
3258                         iflags.autopickup_exceptions[pass] = ape->next;
3259                         free(ape);
3260                 }
3261         }
3262 }
3263 #endif /* AUTOPICKUP_EXCEPTIONS */
3264
3265 /* data for option_help() */
3266 static const char *opt_intro[] = {
3267         "",
3268         "                 NetHack Options Help:",
3269         "",
3270 #define CONFIG_SLOT 3   /* fill in next value at run-time */
3271         (char *)0,
3272 #if !defined(MICRO) && !defined(MAC)
3273         "or use `NETHACKOPTIONS=\"<options>\"' in your environment",
3274 #endif
3275         "(<options> is a list of options separated by commas)",
3276 #ifdef VMS
3277         "-- for example, $ DEFINE NETHACKOPTIONS \"noautopickup,fruit:kumquat\"",
3278 #endif
3279         "or press \"O\" while playing and use the menu.",
3280         "",
3281  "Boolean options (which can be negated by prefixing them with '!' or \"no\"):",
3282         (char *)0
3283 };
3284
3285 static const char *opt_epilog[] = {
3286         "",
3287  "Some of the options can be set only before the game is started; those",
3288         "items will not be selectable in the 'O' command's menu.",
3289         (char *)0
3290 };
3291
3292 void
3293 option_help()
3294 {
3295     char buf[BUFSZ], buf2[BUFSZ];
3296     register int i;
3297     winid datawin;
3298
3299     datawin = create_nhwindow(NHW_TEXT);
3300     Sprintf(buf, "Set options as OPTIONS=<options> in %s", configfile);
3301     opt_intro[CONFIG_SLOT] = (const char *) buf;
3302     for (i = 0; opt_intro[i]; i++)
3303         putstr(datawin, 0, opt_intro[i]);
3304
3305     /* Boolean options */
3306     for (i = 0; boolopt[i].name; i++) {
3307         if (boolopt[i].addr) {
3308 #ifdef WIZARD
3309             if (boolopt[i].addr == &iflags.sanity_check && !wizard) continue;
3310             if (boolopt[i].addr == &iflags.menu_tab_sep && !wizard) continue;
3311 #endif
3312             next_opt(datawin, boolopt[i].name);
3313         }
3314     }
3315     next_opt(datawin, "");
3316
3317     /* Compound options */
3318     putstr(datawin, 0, "Compound options:");
3319     for (i = 0; compopt[i].name; i++) {
3320         Sprintf(buf2, "`%s'", compopt[i].name);
3321         Sprintf(buf, "%-20s - %s%c", buf2, compopt[i].descr,
3322                 compopt[i+1].name ? ',' : '.');
3323         putstr(datawin, 0, buf);
3324     }
3325
3326     for (i = 0; opt_epilog[i]; i++)
3327         putstr(datawin, 0, opt_epilog[i]);
3328
3329     display_nhwindow(datawin, FALSE);
3330     destroy_nhwindow(datawin);
3331     return;
3332 }
3333
3334 /*
3335  * prints the next boolean option, on the same line if possible, on a new
3336  * line if not. End with next_opt("").
3337  */
3338 void
3339 next_opt(datawin, str)
3340 winid datawin;
3341 const char *str;
3342 {
3343         static char *buf = 0;
3344         int i;
3345         char *s;
3346
3347         if (!buf) *(buf = (char *)alloc(BUFSZ)) = '\0';
3348
3349         if (!*str) {
3350                 s = eos(buf);
3351                 if (s > &buf[1] && s[-2] == ',')
3352                     Strcpy(s - 2, "."); /* replace last ", " */
3353                 i = COLNO;      /* (greater than COLNO - 2) */
3354         } else {
3355                 i = strlen(buf) + strlen(str) + 2;
3356         }
3357
3358         if (i > COLNO - 2) { /* rule of thumb */
3359                 putstr(datawin, 0, buf);
3360                 buf[0] = 0;
3361         }
3362         if (*str) {
3363                 Strcat(buf, str);
3364                 Strcat(buf, ", ");
3365         } else {
3366                 putstr(datawin, 0, str);
3367                 free(buf),  buf = 0;
3368         }
3369         return;
3370 }
3371
3372 /* Returns the fid of the fruit type; if that type already exists, it
3373  * returns the fid of that one; if it does not exist, it adds a new fruit
3374  * type to the chain and returns the new one.
3375  */
3376 int
3377 fruitadd(str)
3378 char *str;
3379 {
3380         register int i;
3381         register struct fruit *f;
3382         struct fruit *lastf = 0;
3383         int highest_fruit_id = 0;
3384         char buf[PL_FSIZ];
3385         boolean user_specified = (str == pl_fruit);
3386         /* if not user-specified, then it's a fruit name for a fruit on
3387          * a bones level...
3388          */
3389
3390         /* Note: every fruit has an id (spe for fruit objects) of at least
3391          * 1; 0 is an error.
3392          */
3393         if (user_specified) {
3394                 /* disallow naming after other foods (since it'd be impossible
3395                  * to tell the difference)
3396                  */
3397
3398                 boolean found = FALSE, numeric = FALSE;
3399
3400                 for (i = bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS;
3401                                                 i++) {
3402                         if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) {
3403                                 found = TRUE;
3404                                 break;
3405                         }
3406                 }
3407                 {
3408                     char *c;
3409
3410                     c = pl_fruit;
3411
3412                     for(c = pl_fruit; *c >= '0' && *c <= '9'; c++)
3413                         ;
3414                     if (isspace(*c) || *c == 0) numeric = TRUE;
3415                 }
3416                 if (found || numeric ||
3417                     !strncmp(str, "cursed ", 7) ||
3418                     !strncmp(str, "uncursed ", 9) ||
3419                     !strncmp(str, "blessed ", 8) ||
3420                     !strncmp(str, "partly eaten ", 13) ||
3421                     (!strncmp(str, "tin of ", 7) &&
3422                         (!strcmp(str+7, "spinach") ||
3423                          name_to_mon(str+7) >= LOW_PM)) ||
3424                     !strcmp(str, "empty tin") ||
3425                     ((!strncmp(eos(str)-7," corpse",7) ||
3426                             !strncmp(eos(str)-4, " egg",4)) &&
3427                         name_to_mon(str) >= LOW_PM))
3428                         {
3429                                 Strcpy(buf, pl_fruit);
3430                                 Strcpy(pl_fruit, "candied ");
3431                                 nmcpy(pl_fruit+8, buf, PL_FSIZ-8);
3432                 }
3433         }
3434         for(f=ffruit; f; f = f->nextf) {
3435                 lastf = f;
3436                 if(f->fid > highest_fruit_id) highest_fruit_id = f->fid;
3437                 if(!strncmp(str, f->fname, PL_FSIZ))
3438                         goto nonew;
3439         }
3440         /* if adding another fruit would overflow spe, use a random
3441            fruit instead... we've got a lot to choose from. */
3442         if (highest_fruit_id >= 127) return rnd(127);
3443         highest_fruit_id++;
3444         f = newfruit();
3445         if (ffruit) lastf->nextf = f;
3446         else ffruit = f;
3447         Strcpy(f->fname, str);
3448         f->fid = highest_fruit_id;
3449         f->nextf = 0;
3450 nonew:
3451         if (user_specified) current_fruit = highest_fruit_id;
3452         return f->fid;
3453 }
3454
3455 /*
3456  * This is a somewhat generic menu for taking a list of NetHack style
3457  * class choices and presenting them via a description
3458  * rather than the traditional NetHack characters.
3459  * (Benefits users whose first exposure to NetHack is via tiles).
3460  *
3461  * prompt
3462  *           The title at the top of the menu.
3463  *
3464  * category: 0 = monster class
3465  *           1 = object  class
3466  *
3467  * way
3468  *           FALSE = PICK_ONE, TRUE = PICK_ANY
3469  *
3470  * class_list
3471  *           a null terminated string containing the list of choices.
3472  *
3473  * class_selection
3474  *           a null terminated string containing the selected characters.
3475  *
3476  * Returns number selected.
3477  */
3478 int
3479 choose_classes_menu(prompt, category, way, class_list, class_select)
3480 const char *prompt;
3481 int category;
3482 boolean way;
3483 char *class_list;
3484 char *class_select;
3485 {
3486     menu_item *pick_list = (menu_item *)0;
3487     winid win;
3488     anything any;
3489     char buf[BUFSZ];
3490     int i, n;
3491     int ret;
3492     int next_accelerator, accelerator;
3493
3494     if (class_list == (char *)0 || class_select == (char *)0) return 0;
3495     accelerator = 0;
3496     next_accelerator = 'a';
3497     any.a_void = 0;
3498     win = create_nhwindow(NHW_MENU);
3499     start_menu(win);
3500     while (*class_list) {
3501         const char *text;
3502         boolean selected;
3503
3504         text = (char *)0;
3505         selected = FALSE;
3506         switch (category) {
3507                 case 0:
3508                         text = monexplain[def_char_to_monclass(*class_list)];
3509                         accelerator = *class_list;
3510                         Sprintf(buf, "%s", text);
3511                         break;
3512                 case 1:
3513                         text = objexplain[def_char_to_objclass(*class_list)];
3514                         accelerator = next_accelerator;
3515                         Sprintf(buf, "%c  %s", *class_list, text);
3516                         break;
3517                 default:
3518                         impossible("choose_classes_menu: invalid category %d",
3519                                         category);
3520         }
3521         if (way && *class_select) {     /* Selections there already */
3522                 if (index(class_select, *class_list)) {
3523                         selected = TRUE;
3524                 }
3525         }
3526         any.a_int = *class_list;
3527         add_menu(win, NO_GLYPH, &any, accelerator,
3528                   category ? *class_list : 0,
3529                   ATR_NONE, buf, selected);
3530         ++class_list;
3531         if (category > 0) {
3532                 ++next_accelerator;
3533                 if (next_accelerator == ('z' + 1)) next_accelerator = 'A';
3534                 if (next_accelerator == ('Z' + 1)) break;
3535         }
3536     }
3537     end_menu(win, prompt);
3538     n = select_menu(win, way ? PICK_ANY : PICK_ONE, &pick_list);
3539     destroy_nhwindow(win);
3540     if (n > 0) {
3541         for (i = 0; i < n; ++i)
3542             *class_select++ = (char)pick_list[i].item.a_int;
3543         free((genericptr_t)pick_list);
3544         ret = n;
3545     } else if (n == -1) {
3546         class_select = eos(class_select);
3547         ret = -1;
3548     } else
3549         ret = 0;
3550     *class_select = '\0';
3551     return ret;
3552 }
3553
3554 struct wc_Opt wc_options[] = {
3555         {"ascii_map", WC_ASCII_MAP},
3556         {"color", WC_COLOR},
3557         {"eight_bit_tty", WC_EIGHT_BIT_IN},
3558         {"hilite_pet", WC_HILITE_PET},
3559         {"popup_dialog", WC_POPUP_DIALOG},
3560         {"player_selection", WC_PLAYER_SELECTION},
3561         {"preload_tiles", WC_PRELOAD_TILES},
3562         {"tiled_map", WC_TILED_MAP},
3563         {"tile_file", WC_TILE_FILE},
3564         {"tile_width", WC_TILE_WIDTH},
3565         {"tile_height", WC_TILE_HEIGHT},
3566         {"use_inverse", WC_INVERSE},
3567         {"align_message", WC_ALIGN_MESSAGE},
3568         {"align_status", WC_ALIGN_STATUS},
3569         {"font_map", WC_FONT_MAP},
3570         {"font_menu", WC_FONT_MENU},
3571         {"font_message",WC_FONT_MESSAGE},
3572 #if 0
3573         {"perm_invent",WC_PERM_INVENT},
3574 #endif
3575         {"font_size_map", WC_FONTSIZ_MAP},
3576         {"font_size_menu", WC_FONTSIZ_MENU},
3577         {"font_size_message", WC_FONTSIZ_MESSAGE},
3578         {"font_size_status", WC_FONTSIZ_STATUS},
3579         {"font_size_text", WC_FONTSIZ_TEXT},
3580         {"font_status", WC_FONT_STATUS},
3581         {"font_text", WC_FONT_TEXT},
3582         {"map_mode", WC_MAP_MODE},
3583         {"scroll_amount", WC_SCROLL_AMOUNT},
3584         {"scroll_margin", WC_SCROLL_MARGIN},
3585         {"splash_screen", WC_SPLASH_SCREEN},
3586         {"vary_msgcount",WC_VARY_MSGCOUNT},
3587         {"windowcolors", WC_WINDOWCOLORS},
3588         {"mouse_support", WC_MOUSE_SUPPORT},
3589         {(char *)0, 0L}
3590 };
3591
3592 struct wc_Opt wc2_options[] = {
3593         {"fullscreen", WC2_FULLSCREEN},
3594         {"softkeyboard", WC2_SOFTKEYBOARD},
3595         {"wraptext", WC2_WRAPTEXT},
3596         {(char *)0, 0L}
3597 };
3598
3599
3600 /*
3601  * If a port wants to change or ensure that the
3602  * SET_IN_FILE, DISP_IN_GAME, or SET_IN_GAME status of an option is
3603  * correct (for controlling its display in the option menu) call
3604  * set_option_mod_status()
3605  * with the second argument of 0,2, or 3 respectively.
3606  */
3607 void
3608 set_option_mod_status(optnam, status)
3609 const char *optnam;
3610 int status;
3611 {
3612         int k;
3613         if (status < SET_IN_FILE || status > SET_IN_GAME) {
3614                 impossible("set_option_mod_status: status out of range %d.",
3615                            status);
3616                 return;
3617         }
3618         for (k = 0; boolopt[k].name; k++) {
3619                 if (!strncmpi(boolopt[k].name, optnam, strlen(optnam))) {
3620                         boolopt[k].optflags = status;
3621                         return;
3622                 }
3623         }
3624         for (k = 0; compopt[k].name; k++) {
3625                 if (!strncmpi(compopt[k].name, optnam, strlen(optnam))) {
3626                         compopt[k].optflags = status;
3627                         return;
3628                 }
3629         }
3630 }
3631
3632 /*
3633  * You can set several wc_options in one call to
3634  * set_wc_option_mod_status() by setting
3635  * the appropriate bits for each option that you
3636  * are setting in the optmask argument
3637  * prior to calling.
3638  *    example: set_wc_option_mod_status(WC_COLOR|WC_SCROLL_MARGIN, SET_IN_GAME);
3639  */
3640 void
3641 set_wc_option_mod_status(optmask, status)
3642 unsigned long optmask;
3643 int status;
3644 {
3645         int k = 0;
3646         if (status < SET_IN_FILE || status > SET_IN_GAME) {
3647                 impossible("set_wc_option_mod_status: status out of range %d.",
3648                            status);
3649                 return;
3650         }
3651         while (wc_options[k].wc_name) {
3652                 if (optmask & wc_options[k].wc_bit) {
3653                         set_option_mod_status(wc_options[k].wc_name, status);
3654                 }
3655                 k++;
3656         }
3657 }
3658
3659 STATIC_OVL boolean
3660 is_wc_option(optnam)
3661 const char *optnam;
3662 {
3663         int k = 0;
3664         while (wc_options[k].wc_name) {
3665                 if (strcmp(wc_options[k].wc_name, optnam) == 0)
3666                         return TRUE;
3667                 k++;
3668         }
3669         return FALSE;
3670 }
3671
3672 STATIC_OVL boolean
3673 wc_supported(optnam)
3674 const char *optnam;
3675 {
3676         int k = 0;
3677         while (wc_options[k].wc_name) {
3678                 if (!strcmp(wc_options[k].wc_name, optnam) &&
3679                     (windowprocs.wincap & wc_options[k].wc_bit))
3680                         return TRUE;
3681                 k++;
3682         }
3683         return FALSE;
3684 }
3685
3686
3687 /*
3688  * You can set several wc2_options in one call to
3689  * set_wc2_option_mod_status() by setting
3690  * the appropriate bits for each option that you
3691  * are setting in the optmask argument
3692  * prior to calling.
3693  *    example: set_wc2_option_mod_status(WC2_FULLSCREEN|WC2_SOFTKEYBOARD|WC2_WRAPTEXT, SET_IN_FILE);
3694  */
3695
3696 void
3697 set_wc2_option_mod_status(optmask, status)
3698 unsigned long optmask;
3699 int status;
3700 {
3701         int k = 0;
3702         if (status < SET_IN_FILE || status > SET_IN_GAME) {
3703                 impossible("set_wc2_option_mod_status: status out of range %d.",
3704                            status);
3705                 return;
3706         }
3707         while (wc2_options[k].wc_name) {
3708                 if (optmask & wc2_options[k].wc_bit) {
3709                         set_option_mod_status(wc2_options[k].wc_name, status);
3710                 }
3711                 k++;
3712         }
3713 }
3714
3715 STATIC_OVL boolean
3716 is_wc2_option(optnam)
3717 const char *optnam;
3718 {
3719         int k = 0;
3720         while (wc2_options[k].wc_name) {
3721                 if (strcmp(wc2_options[k].wc_name, optnam) == 0)
3722                         return TRUE;
3723                 k++;
3724         }
3725         return FALSE;
3726 }
3727
3728 STATIC_OVL boolean
3729 wc2_supported(optnam)
3730 const char *optnam;
3731 {
3732         int k = 0;
3733         while (wc2_options[k].wc_name) {
3734                 if (!strcmp(wc2_options[k].wc_name, optnam) &&
3735                     (windowprocs.wincap2 & wc2_options[k].wc_bit))
3736                         return TRUE;
3737                 k++;
3738         }
3739         return FALSE;
3740 }
3741
3742
3743 STATIC_OVL void
3744 wc_set_font_name(wtype, fontname)
3745 int wtype;
3746 char *fontname;
3747 {
3748         char **fn = (char **)0;
3749         if (!fontname) return;
3750         switch(wtype) {
3751             case NHW_MAP:
3752                         fn = &iflags.wc_font_map;
3753                         break;
3754             case NHW_MESSAGE:
3755                         fn = &iflags.wc_font_message;
3756                         break;
3757             case NHW_TEXT:
3758                         fn = &iflags.wc_font_text;
3759                         break;
3760             case NHW_MENU:
3761                         fn = &iflags.wc_font_menu;
3762                         break;
3763             case NHW_STATUS:
3764                         fn = &iflags.wc_font_status;
3765                         break;
3766             default:
3767                         return;
3768         }
3769         if (fn) {
3770                 if (*fn) free(*fn);
3771                 *fn = (char *)alloc(strlen(fontname) + 1);
3772                 Strcpy(*fn, fontname);
3773         }
3774         return;
3775 }
3776
3777 STATIC_OVL int
3778 wc_set_window_colors(op)
3779 char *op;
3780 {
3781         /* syntax:
3782          *  menu white/black message green/yellow status white/blue text white/black
3783          */
3784
3785         int j;
3786         char buf[BUFSZ];
3787         char *wn, *tfg, *tbg, *newop;
3788         static const char *wnames[] = { "menu", "message", "status", "text" };
3789         static const char *shortnames[] = { "mnu", "msg", "sts", "txt" };
3790         static char **fgp[] = {
3791                 &iflags.wc_foregrnd_menu,
3792                 &iflags.wc_foregrnd_message,
3793                 &iflags.wc_foregrnd_status,
3794                 &iflags.wc_foregrnd_text
3795         };
3796         static char **bgp[] = {
3797                 &iflags.wc_backgrnd_menu,
3798                 &iflags.wc_backgrnd_message,
3799                 &iflags.wc_backgrnd_status,
3800                 &iflags.wc_backgrnd_text
3801         };
3802
3803         Strcpy(buf, op);
3804         newop = mungspaces(buf);
3805         while (newop && *newop) {
3806
3807                 wn = tfg = tbg = (char *)0;
3808
3809                 /* until first non-space in case there's leading spaces - before colorname*/
3810                 while(*newop && isspace(*newop)) newop++;
3811                 if (*newop) wn = newop;
3812                 else return 0;
3813
3814                 /* until first space - colorname*/
3815                 while(*newop && !isspace(*newop)) newop++;
3816                 if (*newop) *newop = '\0';
3817                 else return 0;
3818                 newop++;
3819
3820                 /* until first non-space - before foreground*/
3821                 while(*newop && isspace(*newop)) newop++;
3822                 if (*newop) tfg = newop;
3823                 else return 0;
3824
3825                 /* until slash - foreground */
3826                 while(*newop && *newop != '/') newop++;
3827                 if (*newop) *newop = '\0';
3828                 else return 0;
3829                 newop++;
3830
3831                 /* until first non-space (in case there's leading space after slash) - before background */
3832                 while(*newop && isspace(*newop)) newop++;
3833                 if (*newop) tbg = newop;
3834                 else return 0;
3835
3836                 /* until first space - background */
3837                 while(*newop && !isspace(*newop)) newop++;
3838                 if (*newop) *newop++ = '\0';
3839
3840                 for (j = 0; j < 4; ++j) {
3841                         if (!strcmpi(wn, wnames[j]) ||
3842                             !strcmpi(wn, shortnames[j])) {
3843                                 if (tfg && !strstri(tfg, " ")) {
3844                                         if (*fgp[j]) free(*fgp[j]);
3845                                         *fgp[j] = (char *)alloc(strlen(tfg) + 1);
3846                                         Strcpy(*fgp[j], tfg);
3847                                 }
3848                                 if (tbg && !strstri(tbg, " ")) {
3849                                         if (*bgp[j]) free(*bgp[j]);
3850                                         *bgp[j] = (char *)alloc(strlen(tbg) + 1);
3851                                         Strcpy(*bgp[j], tbg);
3852                                 }
3853                                 break;
3854                         }
3855                 }
3856         }
3857         return 1;
3858 }
3859
3860 #endif  /* OPTION_LISTS_ONLY */
3861
3862 /*options.c*/