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"
9NEARDATA struct flag flags;	/* provide linkage */
10NEARDATA 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
36static 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 */
203static 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
343static boolean need_redraw; /* for doset() */
344
345#if defined(TOS) && defined(TEXTCOLOR)
346extern boolean colors_changed;	/* in tos.c */
347#endif
348
349#ifdef VIDEOSHADES
350extern char *shade[3];		  /* in sys/msdos/video.c */
351extern char ttycolors[CLR_MAX];	  /* in sys/msdos/video.c */
352#endif
353
354static 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 */
386typedef struct {
387    const char *name;
388    char cmd;
389} menu_cmd_t;
390
391#define NUM_MENU_CMDS 11
392static 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 */
412static char mapped_menu_op[MAX_MENU_MAPPED_CMDS+1];
413static short n_menu_mapped = 0;
414
415
416static boolean initial, from_file;
417
418STATIC_DCL void FDECL(doset_add_menu, (winid,const char *,int));
419STATIC_DCL void FDECL(nmcpy, (char *, const char *, int));
420STATIC_DCL void FDECL(escapes, (const char *, char *));
421STATIC_DCL void FDECL(rejectoption, (const char *));
422STATIC_DCL void FDECL(badoption, (const char *));
423STATIC_DCL char *FDECL(string_for_opt, (char *,BOOLEAN_P));
424STATIC_DCL char *FDECL(string_for_env_opt, (const char *, char *,BOOLEAN_P));
425STATIC_DCL void FDECL(bad_negation, (const char *,BOOLEAN_P));
426STATIC_DCL int FDECL(change_inv_order, (char *));
427STATIC_DCL void FDECL(oc_to_str, (char *, char *));
428STATIC_DCL void FDECL(graphics_opts, (char *,const char *,int,int));
429STATIC_DCL int FDECL(feature_alert_opts, (char *, const char *));
430STATIC_DCL const char *FDECL(get_compopt_value, (const char *, char *));
431STATIC_DCL boolean FDECL(special_handling, (const char *, BOOLEAN_P, BOOLEAN_P));
432STATIC_DCL void FDECL(warning_opts, (char *,const char *));
433STATIC_DCL void FDECL(duplicate_opt_detection, (const char *, int));
434
435STATIC_OVL void FDECL(wc_set_font_name, (int, char *));
436STATIC_OVL int FDECL(wc_set_window_colors, (char *));
437STATIC_OVL boolean FDECL(is_wc_option, (const char *));
438STATIC_OVL boolean FDECL(wc_supported, (const char *));
439STATIC_OVL boolean FDECL(is_wc2_option, (const char *));
440STATIC_OVL boolean FDECL(wc2_supported, (const char *));
441#ifdef AUTOPICKUP_EXCEPTIONS
442STATIC_DCL void FDECL(remove_autopickup_exception, (struct autopickup_exception *));
443STATIC_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 */
449boolean
450match_optname(user_string, opt_name, min_length, val_allowed)
451const char *user_string, *opt_name;
452int min_length;
453boolean 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 */
476char *
477nh_getenv(ev)
478const 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
488void
489initoptions()
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
609STATIC_OVL void
610nmcpy(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 */
631STATIC_OVL void
632escapes(cp, tp)
633const char	*cp;
634char *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
687STATIC_OVL void
688rejectoption(optname)
689const 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
699STATIC_OVL void
700badoption(opts)
701const 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
722STATIC_OVL char *
723string_for_opt(opts, val_optional)
724char *opts;
725boolean 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
740STATIC_OVL char *
741string_for_env_opt(optname, opts, val_optional)
742const char *optname;
743char *opts;
744boolean val_optional;
745{
746	if(!initial) {
747		rejectoption(optname);
748		return (char *)0;
749	}
750	return string_for_opt(opts, val_optional);
751}
752
753STATIC_OVL void
754bad_negation(optname, with_parameter)
755const char *optname;
756boolean 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 */
772STATIC_OVL int
773change_inv_order(op)
774char *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
810STATIC_OVL void
811graphics_opts(opts, optype, maxlen, offset)
812register char *opts;
813const char *optype;
814int 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
831STATIC_OVL void
832warning_opts(opts, optype)
833register char *opts;
834const 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
852void
853assign_warnings(graph_chars)
854register 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
861STATIC_OVL int
862feature_alert_opts(op, optn)
863char *op;
864const 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
894void
895set_duplicate_opt_detection(on_or_off)
896int 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
923STATIC_OVL void
924duplicate_opt_detection(opts, bool_or_comp)
925const char *opts;
926int 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
967void
968parseoptions(opts, tinitial, tfrom_file)
969register char *opts;
970boolean 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		}
1352goodfruit:
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
2253static NEARDATA const char *menutype[] = {
2254	"traditional", "combination", "partial", "full"
2255};
2256
2257static NEARDATA const char *burdentype[] = {
2258	"unencumbered", "burdened", "stressed",
2259	"strained", "overtaxed", "overloaded"
2260};
2261
2262static 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 */
2270STATIC_OVL void
2271oc_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 */
2289void
2290add_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 */
2308char
2309map_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
2327static char fmtstr_doset_add_menu[] = "%s%-15s [%s]   ";
2328static char fmtstr_doset_add_menu_tab[] = "%s\t[%s]";
2329
2330STATIC_OVL void
2331doset_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 */
2369int
2370doset()
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
2531STATIC_OVL boolean
2532special_handling(optname, setinitial, setfromfile)
2533const char *optname;
2534boolean 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	};
2809ape_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. */
2886STATIC_OVL const char *
2887get_compopt_value(optname, buf)
2888const char *optname;
2889char *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
3147int
3148dotogglepickup()
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
3171int
3172add_autopickup_exception(mapping)
3173const 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
3207STATIC_OVL void
3208remove_autopickup_exception(whichape)
3209struct 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
3229STATIC_OVL int
3230count_ape_maps(leave, grab)
3231int *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
3249void
3250free_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() */
3266static 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
3285static 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
3292void
3293option_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 */
3338void
3339next_opt(datawin, str)
3340winid datawin;
3341const 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 */
3376int
3377fruitadd(str)
3378char *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;
3450nonew:
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 */
3478int
3479choose_classes_menu(prompt, category, way, class_list, class_select)
3480const char *prompt;
3481int category;
3482boolean way;
3483char *class_list;
3484char *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
3554struct 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
3592struct 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 */
3607void
3608set_option_mod_status(optnam, status)
3609const char *optnam;
3610int 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 */
3640void
3641set_wc_option_mod_status(optmask, status)
3642unsigned long optmask;
3643int 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
3659STATIC_OVL boolean
3660is_wc_option(optnam)
3661const 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
3672STATIC_OVL boolean
3673wc_supported(optnam)
3674const 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
3696void
3697set_wc2_option_mod_status(optmask, status)
3698unsigned long optmask;
3699int 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
3715STATIC_OVL boolean
3716is_wc2_option(optnam)
3717const 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
3728STATIC_OVL boolean
3729wc2_supported(optnam)
3730const 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
3743STATIC_OVL void
3744wc_set_font_name(wtype, fontname)
3745int wtype;
3746char *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
3777STATIC_OVL int
3778wc_set_window_colors(op)
3779char *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*/
3863