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