1    | /*	SCCS Id: @(#)options.c	3.3	2000/08/01	*/
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   | /*
21   |  *  NOTE:  If you add (or delete) an option, please update the short
22   |  *  options help (option_help()), the long options help (dat/opthelp),
23   |  *  and the current options setting display function (doset()),
24   |  *  and also the Guidebooks.
25   |  */
26   | 
27   | static struct Bool_Opt
28   | {
29   | 	const char *name;
30   | 	boolean	*addr, initvalue;
31   | } boolopt[] = {
32   | #ifdef AMIGA
33   | 	{"altmeta", &flags.altmeta, TRUE},
34   | #else
35   | 	{"altmeta", (boolean *)0, TRUE},
36   | #endif
37   | #ifdef MFLOPPY
38   | 	{"asksavedisk", &flags.asksavedisk, FALSE},
39   | #else
40   | 	{"asksavedisk", (boolean *)0, FALSE},
41   | #endif
42   | 	{"autopickup", &flags.pickup, TRUE},
43   | 	{"autoquiver", &flags.autoquiver, FALSE},
44   | #if defined(MICRO) && !defined(AMIGA)
45   | 	{"BIOS", &iflags.BIOS, FALSE},
46   | #else
47   | 	{"BIOS", (boolean *)0, FALSE},
48   | #endif
49   | #ifdef INSURANCE
50   | 	{"checkpoint", &flags.ins_chkpt, TRUE},
51   | #else
52   | 	{"checkpoint", (boolean *)0, FALSE},
53   | #endif
54   | #ifdef MFLOPPY
55   | 	{"checkspace", &iflags.checkspace, TRUE},
56   | #else
57   | 	{"checkspace", (boolean *)0, FALSE},
58   | #endif
59   | #ifdef TEXTCOLOR
60   | # ifdef MICRO
61   | 	{"color", &iflags.use_color, TRUE},
62   | # else	/* systems that support multiple terminals, many monochrome */
63   | 	{"color", &iflags.use_color, FALSE},
64   | # endif
65   | #else
66   | 	{"color", (boolean *)0, FALSE},
67   | #endif
68   | 	{"confirm",&flags.confirm, TRUE},
69   | #ifdef TERMLIB
70   | 	{"DECgraphics", &iflags.DECgraphics, FALSE},
71   | #else
72   | 	{"DECgraphics", (boolean *)0, FALSE},
73   | #endif
74   | #ifdef TTY_GRAPHICS
75   | 	{"eight_bit_tty", &iflags.eight_bit_tty, FALSE},
76   | 	{"extmenu", &iflags.extmenu, FALSE},
77   | #else
78   | 	{"eight_bit_tty", (boolean *)0, FALSE},
79   | 	{"extmenu", (boolean *)0, FALSE},
80   | #endif
81   | #ifdef OPT_DISPMAP
82   | 	{"fast_map", &flags.fast_map, TRUE},
83   | #else
84   | 	{"fast_map", (boolean *)0, TRUE},
85   | #endif
86   | 	{"female", &flags.female, FALSE},
87   | 	{"fixinv", &flags.invlet_constant, TRUE},
88   | #ifdef AMIFLUSH
89   | 	{"flush", &flags.amiflush, FALSE},
90   | #else
91   | 	{"flush", (boolean *)0, FALSE},
92   | #endif
93   | 	{"help", &flags.help, TRUE},
94   | #ifdef TEXTCOLOR
95   | 	{"hilite_pet", &iflags.hilite_pet, FALSE},
96   | #else
97   | 	{"hilite_pet", (boolean *)0, FALSE},
98   | #endif
99   | #ifdef ASCIIGRAPH
100  | 	{"IBMgraphics", &iflags.IBMgraphics, FALSE},
101  | #else
102  | 	{"IBMgraphics", (boolean *)0, FALSE},
103  | #endif
104  | 	{"ignintr", &flags.ignintr, FALSE},
105  | #ifdef MAC_GRAPHICS_ENV
106  | 	{"large_font", &iflags.large_font, FALSE},
107  | #else
108  | 	{"large_font", (boolean *)0, FALSE},
109  | #endif
110  | 	{"legacy", &flags.legacy, TRUE},
111  | 	{"lit_corridor", &flags.lit_corridor, FALSE},
112  | #ifdef MAC_GRAPHICS_ENV
113  | 	{"Macgraphics", &iflags.MACgraphics, TRUE},
114  | #else
115  | 	{"Macgraphics", (boolean *)0, FALSE},
116  | #endif
117  | #ifdef MAIL
118  | 	{"mail", &flags.biff, TRUE},
119  | #else
120  | 	{"mail", (boolean *)0, TRUE},
121  | #endif
122  | #ifdef NEWS
123  | 	{"news", &iflags.news, TRUE},
124  | #else
125  | 	{"news", (boolean *)0, FALSE},
126  | #endif
127  | 	{"null", &flags.null, TRUE},
128  | 	{"number_pad", &iflags.num_pad, FALSE},
129  | #ifdef MAC
130  | 	{"page_wait", &flags.page_wait, TRUE},
131  | #else
132  | 	{"page_wait", (boolean *)0, FALSE},
133  | #endif
134  | 	{"perm_invent", &flags.perm_invent, FALSE},
135  | #ifdef MAC
136  | 	{"popup_dialog", &iflags.popup_dialog, FALSE},
137  | #else
138  | 	{"popup_dialog", (boolean *)0, FALSE},
139  | #endif
140  | 	{"prayconfirm", &flags.prayconfirm, TRUE},
141  | #if defined(MSDOS) && defined(USE_TILES)
142  | 	{"preload_tiles", &iflags.preload_tiles, TRUE},
143  | #else
144  | 	{"preload_tiles", (boolean *)0, FALSE},
145  | #endif
146  | 	{"pushweapon", &flags.pushweapon, FALSE},
147  | #if defined(MICRO) && !defined(AMIGA)
148  | 	{"rawio", &iflags.rawio, FALSE},
149  | #else
150  | 	{"rawio", (boolean *)0, FALSE},
151  | #endif
152  | 	{"rest_on_space", &flags.rest_on_space, FALSE},
153  | 	{"safe_pet", &flags.safe_dog, TRUE},
154  | #ifdef WIZARD
155  | 	{"sanity_check", &iflags.sanity_check, FALSE},
156  | #else
157  | 	{"sanity_check", (boolean *)0, FALSE},
158  | #endif
159  | #ifdef EXP_ON_BOTL
160  | 	{"showexp", &flags.showexp, FALSE},
161  | #else
162  | 	{"showexp", (boolean *)0, FALSE},
163  | #endif
164  | #ifdef SCORE_ON_BOTL
165  | 	{"showscore", &flags.showscore, FALSE},
166  | #else
167  | 	{"showscore", (boolean *)0, FALSE},
168  | #endif
169  | 	{"silent", &flags.silent, TRUE},
170  | 	{"sortpack", &flags.sortpack, TRUE},
171  | 	{"sound", &flags.soundok, TRUE},
172  | 	{"standout", &flags.standout, FALSE},
173  | 	{"time", &flags.time, FALSE},
174  | #ifdef TIMED_DELAY
175  | 	{"timed_delay", &flags.nap, TRUE},
176  | #else
177  | 	{"timed_delay", (boolean *)0, FALSE},
178  | #endif
179  | 	{"tombstone",&flags.tombstone, TRUE},
180  | 	{"toptenwin",&flags.toptenwin, FALSE},
181  | 	{"verbose", &flags.verbose, TRUE},
182  | 	{(char *)0, (boolean *)0, FALSE}
183  | };
184  | 
185  | /* compound options, for option_help() and external programs like Amiga
186  |  * frontend */
187  | #define SET_IN_FILE	0 /* config file option only, not visible in game
188  | 			   * or via program */
189  | #define SET_VIA_PROG	1 /* may be set via extern program, not seen in game */
190  | #define DISP_IN_GAME	2 /* may be set via extern program, displayed in game */
191  | #define SET_IN_GAME	3 /* may be set via extern program or set in the game */
192  | static struct Comp_Opt
193  | {
194  | 	const char *name, *descr;
195  | 	int size;	/* for frontends and such allocating space --
196  | 			 * usually allowed size of data in game, but
197  | 			 * occasionally maximum reasonable size for
198  | 			 * typing when game maintains information in
199  | 			 * a different format */
200  | 	int optflags;
201  | } compopt[] = {
202  | 	{ "align",    "your starting alignment (lawful, neutral, or chaotic)",
203  | 						8, DISP_IN_GAME },
204  | #ifdef MAC
205  | 	{ "background", "the color of the background (black or white)",
206  | 						6, SET_IN_FILE },
207  | #endif
208  | 	{ "catname",  "the name of your (first) cat (e.g., catname:Tabby)",
209  | 						PL_PSIZ, DISP_IN_GAME },
210  | 	{ "disclose", "the kinds of information to disclose at end of game",
211  | 						sizeof(flags.end_disclose),
212  | 						SET_IN_GAME },
213  | 	{ "dogname",  "the name of your (first) dog (e.g., dogname:Fang)",
214  | 						PL_PSIZ, DISP_IN_GAME },
215  | 	{ "dungeon",  "the symbols to use in drawing the dungeon map",
216  | 						MAXDCHARS+1, SET_IN_FILE },
217  | 	{ "effects",  "the symbols to use in drawing special effects",
218  | 						MAXECHARS+1, SET_IN_FILE },
219  | #ifdef MAC
220  | 	{ "fontmap", "the font to use in the map window", 40, SET_IN_FILE },
221  | 	{ "fontmessage", "the font to use in the message window",
222  | 						40, SET_IN_FILE },
223  | 	{ "fonttext", "the font to use in text windows", 40, SET_IN_FILE },
224  | #endif
225  | 	{ "fruit",    "the name of a fruit you enjoy eating",
226  | 						PL_FSIZ, SET_IN_GAME },
227  | 	{ "gender",   "your starting gender (male or female)",
228  | 						8, DISP_IN_GAME },
229  | 	{ "horsename", "the name of your (first) horse (e.g., horsename:Silver)",
230  | 						PL_PSIZ, DISP_IN_GAME },
231  | 	{ "menustyle", "user interface for object selection",
232  | 						MENUTYPELEN, SET_IN_GAME },
233  | 	{ "menu_deselect_all", "deselect all items in a menu", 4, SET_IN_FILE },
234  | 	{ "menu_deselect_page", "deselect all items on this page of a menu",
235  | 						4, SET_IN_FILE },
236  | 	{ "menu_first_page", "jump to the first page in a menu",
237  | 						4, SET_IN_FILE },
238  | 	{ "menu_invert_all", "invert all items in a menu", 4, SET_IN_FILE },
239  | 	{ "menu_invert_page", "invert all items on this page of a menu",
240  | 						4, SET_IN_FILE },
241  | 	{ "menu_last_page", "jump to the last page in a menu", 4, SET_IN_FILE },
242  | 	{ "menu_next_page", "goto the next menu page", 4, SET_IN_FILE },
243  | 	{ "menu_previous_page", "goto the previous menu page", 4, SET_IN_FILE },
244  | 	{ "menu_search", "search for a menu item", 4, SET_IN_FILE },
245  | 	{ "menu_select_all", "select all items in a menu", 4, SET_IN_FILE },
246  | 	{ "menu_select_page", "select all items on this page of a menu",
247  | 						4, SET_IN_FILE },
248  | 	{ "monsters", "the symbols to use for monsters",
249  | 						MAXMCLASSES, SET_IN_FILE },
250  | 	{ "msghistory", "number of top line messages to save",
251  | 						5, DISP_IN_GAME },
252  | 	{ "name",     "your character's name (e.g., name:Merlin-W)",
253  | 						PL_NSIZ, DISP_IN_GAME },
254  | 	{ "objects",  "the symbols to use for objects",
255  | 						MAXOCLASSES, SET_IN_FILE },
256  | 	{ "packorder", "the inventory order of the items in your pack",
257  | 						MAXOCLASSES, SET_IN_GAME },
258  | #ifdef CHANGE_COLOR
259  | 	{ "palette",  "palette (00c/880/-fff is blue/yellow/reverse white)",
260  | 						15 , SET_IN_GAME },
261  | # if defined(MAC)
262  | 	{ "hicolor",  "same as palette, only order is reversed",
263  | 						15, SET_IN_FILE },
264  | # endif
265  | #endif
266  | 	{ "pettype",  "your preferred initial pet type", 4, DISP_IN_GAME },
267  | 	{ "pickup_burden",  "maximum burden picked up before prompt",
268  | 						20, SET_IN_GAME },
269  | 	{ "pickup_types", "types of objects to pick up automatically",
270  | 						MAXOCLASSES, SET_IN_GAME },
271  | 	{ "race",     "your starting race (e.g., Human, Elf)",
272  | 						PL_CSIZ, DISP_IN_GAME },
273  | 	{ "role",     "your starting role (e.g., Barbarian, Valkyrie)",
274  | 						PL_CSIZ, DISP_IN_GAME },
275  | 	{ "scores",   "the parts of the score list you wish to see",
276  | 						32, SET_IN_GAME },
277  | #ifdef MSDOS
278  | 	{ "soundcard", "type of sound card to use", 20, SET_IN_FILE },
279  | #endif
280  | 	{ "suppress_alert", "suppress alerts about version-specific features",
281  | 						8, SET_IN_GAME },
282  | 	{ "traps",    "the symbols to use in drawing traps",
283  | 						MAXTCHARS+1, SET_IN_FILE },
284  | #ifdef MAC
285  | 	{"use_stone", "use stone background patterns", 8, SET_IN_FILE },
286  | #endif
287  | #ifdef MSDOS
288  | 	{ "video",    "method of video updating", 20, SET_IN_FILE },
289  | #endif
290  | #ifdef VIDEOSHADES
291  | 	{ "videocolors", "color mappings for internal screen routines",
292  | 						40, DISP_IN_GAME },
293  | 	{ "videoshades", "gray shades to map to black/gray/white",
294  | 						32, DISP_IN_GAME },
295  | #endif
296  | #if 0
297  | 	{ "warnlevel", "minimum monster level to trigger warning", 4, SET_IN_GAME },
298  | #endif
299  | 	{ "windowtype", "windowing system to use", WINTYPELEN, DISP_IN_GAME },
300  | 	{ (char *)0, (char *)0, 0 }
301  | };
302  | 
303  | #ifdef OPTION_LISTS_ONLY
304  | #undef static
305  | 
306  | #else	/* use rest of file */
307  | 
308  | static boolean need_redraw; /* for doset() */
309  | 
310  | #if defined(TOS) && defined(TEXTCOLOR)
311  | extern boolean colors_changed;	/* in tos.c */
312  | #endif
313  | 
314  | #ifdef VIDEOSHADES
315  | extern char *shade[3];		  /* in sys/msdos/video.c */
316  | extern char ttycolors[CLR_MAX];	  /* in sys/msdos/video.c */
317  | #endif
318  | 
319  | static char def_inv_order[MAXOCLASSES] = {
320  | 	GOLD_CLASS, AMULET_CLASS, WEAPON_CLASS, ARMOR_CLASS, FOOD_CLASS,
321  | 	SCROLL_CLASS, SPBOOK_CLASS, POTION_CLASS, RING_CLASS, WAND_CLASS,
322  | 	TOOL_CLASS, GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, 0,
323  | };
324  | 
325  | /*
326  |  * Default menu manipulation command accelerators.  These may _not_ be:
327  |  *
328  |  *	+ a number - reserved for counts
329  |  *	+ an upper or lower case US ASCII letter - used for accelerators
330  |  *	+ ESC - reserved for escaping the menu
331  |  *	+ NULL, CR or LF - reserved for commiting the selection(s).  NULL
332  |  *	  is kind of odd, but the tty's xwaitforspace() will return it if
333  |  *	  someone hits a <ret>.
334  |  *	+ a default object class symbol - used for object class accelerators
335  |  *
336  |  * Standard letters (for now) are:
337  |  *
338  |  *		<  back 1 page
339  |  *		>  forward 1 page
340  |  *		^  first page
341  |  *		|  last page
342  |  *		:  search
343  |  *
344  |  *		page		all
345  |  *		 ,    select	 .
346  |  *		 \    deselect	 -
347  |  *		 ~    invert	 @
348  |  *
349  |  * The command name list is duplicated in the compopt array.
350  |  */
351  | typedef struct {
352  |     const char *name;
353  |     char cmd;
354  | } menu_cmd_t;
355  | 
356  | #define NUM_MENU_CMDS 11
357  | static const menu_cmd_t default_menu_cmd_info[NUM_MENU_CMDS] = {
358  | /* 0*/	{ "menu_first_page",	MENU_FIRST_PAGE },
359  | 	{ "menu_last_page",	MENU_LAST_PAGE },
360  | 	{ "menu_next_page",	MENU_NEXT_PAGE },
361  | 	{ "menu_previous_page",	MENU_PREVIOUS_PAGE },
362  | 	{ "menu_select_all",	MENU_SELECT_ALL },
363  | /* 5*/	{ "menu_deselect_all",	MENU_UNSELECT_ALL },
364  | 	{ "menu_invert_all",	MENU_INVERT_ALL },
365  | 	{ "menu_select_page",	MENU_SELECT_PAGE },
366  | 	{ "menu_deselect_page",	MENU_UNSELECT_PAGE },
367  | 	{ "menu_invert_page",	MENU_INVERT_PAGE },
368  | /*10*/	{ "menu_search",		MENU_SEARCH },
369  | };
370  | 
371  | /*
372  |  * Allow the user to map incoming characters to various menu commands.
373  |  * The accelerator list must be a valid C string.
374  |  */
375  | #define MAX_MENU_MAPPED_CMDS 32	/* some number */
376  |        char mapped_menu_cmds[MAX_MENU_MAPPED_CMDS+1];	/* exported */
377  | static char mapped_menu_op[MAX_MENU_MAPPED_CMDS+1];
378  | static short n_menu_mapped = 0;
379  | 
380  | 
381  | static boolean initial, from_file;
382  | 
383  | STATIC_DCL void FDECL(doset_add_menu, (winid,const char *,int));
384  | STATIC_DCL void FDECL(nmcpy, (char *, const char *, int));
385  | STATIC_DCL void FDECL(escapes, (const char *, char *));
386  | STATIC_DCL int FDECL(boolopt_only_initial, (int));
387  | STATIC_DCL void FDECL(rejectoption, (const char *));
388  | STATIC_DCL void FDECL(badoption, (const char *));
389  | STATIC_DCL char *FDECL(string_for_opt, (char *,BOOLEAN_P));
390  | STATIC_DCL char *FDECL(string_for_env_opt, (const char *, char *,BOOLEAN_P));
391  | STATIC_DCL void FDECL(bad_negation, (const char *,BOOLEAN_P));
392  | STATIC_DCL int FDECL(change_inv_order, (char *));
393  | STATIC_DCL void FDECL(oc_to_str, (char *, char *));
394  | STATIC_DCL void FDECL(graphics_opts, (char *,const char *,int,int));
395  | STATIC_DCL int FDECL(feature_alert_opts, (char *, const char *));
396  | STATIC_DCL const char *FDECL(get_compopt_value, (const char *, char *));
397  | STATIC_DCL boolean FDECL(special_handling, (const char *, BOOLEAN_P, BOOLEAN_P));
398  | STATIC_DCL void FDECL(warning_opts, (char *,const char *));
399  | #if 0
400  | STATIC_DCL int FDECL(warnlevel_opts, (char *, const char *));
401  | #endif
402  | 
403  | /* check whether a user-supplied option string is a proper leading
404  |    substring of a particular option name; option string might have
405  |    a colon or equals sign and arbitrary value appended to it */
406  | boolean
407  | match_optname(user_string, opt_name, min_length, val_allowed)
408  | const char *user_string, *opt_name;
409  | int min_length;
410  | boolean val_allowed;
411  | {
412  | 	int len = (int)strlen(user_string);
413  | 
414  | 	if (val_allowed) {
415  | 	    const char *p = index(user_string, ':'),
416  | 		       *q = index(user_string, '=');
417  | 
418  | 	    if (!p || (q && q < p)) p = q;
419  | 	    while(p && p > user_string && isspace(*(p-1))) p--;
420  | 	    if (p) len = (int)(p - user_string);
421  | 	}
422  | 
423  | 	return (len >= min_length) && !strncmpi(opt_name, user_string, len);
424  | }
425  | 
426  | /* most environment variables will eventually be printed in an error
427  |  * message if they don't work, and most error message paths go through
428  |  * BUFSZ buffers, which could be overflowed by a maliciously long
429  |  * environment variable.  if a variable can legitimately be long, or
430  |  * if it's put in a smaller buffer, the responsible code will have to
431  |  * bounds-check itself.
432  |  */
433  | char *
434  | nh_getenv(ev)
435  | const char *ev;
436  | {
437  | 	char *getev = getenv(ev);
438  | 
439  | 	if (getev && strlen(getev) <= (BUFSZ / 2))
440  | 		return getev;
441  | 	else
442  | 		return (char *)0;
443  | }
444  | 
445  | void
446  | initoptions()
447  | {
448  | 	char *opts;
449  | 	int i;
450  | 
451  | 	/* initialize the random number generator */
452  | 	setrandom();
453  | 
454  | 	for (i = 0; boolopt[i].name; i++) {
455  | 		if (boolopt[i].addr)
456  | 			*(boolopt[i].addr) = boolopt[i].initvalue;
457  | 	}
458  | 	flags.end_own = FALSE;
459  | 	flags.end_top = 3;
460  | 	flags.end_around = 2;
461  | 	iflags.msg_history = 20;
462  | 
463  | 	/* Use negative indices to indicate not yet selected */
464  | 	flags.initrole = -1;
465  | 	flags.initrace = -1;
466  | 	flags.initgend = -1;
467  | 	flags.initalign = -1;
468  | 
469  | 	/* Set the default monster and object class symbols.  Don't use */
470  | 	/* memcpy() --- sizeof char != sizeof uchar on some machines.	*/
471  | 	for (i = 0; i < MAXOCLASSES; i++)
472  | 		oc_syms[i] = (uchar) def_oc_syms[i];
473  | 	for (i = 0; i < MAXMCLASSES; i++)
474  | 		monsyms[i] = (uchar) def_monsyms[i];
475  | 	for (i = 0; i < WARNCOUNT; i++)
476  | 		warnsyms[i] = def_warnsyms[i].sym;
477  | 	flags.warnlevel = 1;
478  | 	flags.warntype = 0L;
479  | 
480  |      /* assert( sizeof flags.inv_order == sizeof def_inv_order ); */
481  | 	(void)memcpy((genericptr_t)flags.inv_order,
482  | 		     (genericptr_t)def_inv_order, sizeof flags.inv_order);
483  | 	flags.pickup_types[0] = '\0';
484  | 	flags.pickup_burden = MOD_ENCUMBER;
485  | 
486  | 	switch_graphics(ASCII_GRAPHICS);	/* set default characters */
487  | #if defined(UNIX) && defined(TTY_GRAPHICS)
488  | 	/*
489  | 	 * Set defaults for some options depending on what we can
490  | 	 * detect about the environment's capabilities.
491  | 	 * This has to be done after the global initialization above
492  | 	 * and before reading user-specific initialization via
493  | 	 * config file/environment variable below.
494  | 	 */
495  | 	/* this detects the IBM-compatible console on most 386 boxes */
496  | 	if (!strncmp(nh_getenv("TERM"), "AT", 2)) {
497  | 		switch_graphics(IBM_GRAPHICS);
498  | # ifdef TEXTCOLOR
499  | 		iflags.use_color = TRUE;
500  | # endif
501  | 	}
502  | #endif /* UNIX && TTY_GRAPHICS */
503  | #if defined(UNIX) || defined(VMS)
504  | # ifdef TTY_GRAPHICS
505  | 	/* detect whether a "vt" terminal can handle alternate charsets */
506  | 	if (!strncmpi(nh_getenv("TERM"), "vt", 2) && (AS && AE) &&
507  | 	    index(AS, '\016') && index(AE, '\017')) {
508  | 		switch_graphics(DEC_GRAPHICS);
509  | 	}
510  | # endif
511  | #endif /* UNIX || VMS */
512  | 
513  | #ifdef MAC_GRAPHICS_ENV
514  | 	switch_graphics(MAC_GRAPHICS);
515  | #endif /* MAC_GRAPHICS_ENV */
516  | 	flags.menu_style = MENU_FULL;
517  | 
518  | 	/* since this is done before init_objects(), do partial init here */
519  | 	objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD;
520  | 	nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
521  | #ifndef MAC
522  | 	opts = getenv("NETHACKOPTIONS");
523  | 	if (!opts) opts = getenv("HACKOPTIONS");
524  | 	if (opts) {
525  | 		if (*opts == '/' || *opts == '\\' || *opts == '@') {
526  | 			if (*opts == '@') opts++;	/* @filename */
527  | 			/* looks like a filename */
528  | 			if (strlen(opts) < BUFSZ/2)
529  | 			    read_config_file(opts);
530  | 		} else {
531  | 			read_config_file((char *)0);
532  | 			/* let the total length of options be long;
533  | 			 * parseoptions() will check each individually
534  | 			 */
535  | 			parseoptions(opts, TRUE, FALSE);
536  | 		}
537  | 	} else
538  | #endif
539  | 		read_config_file((char *)0);
540  | #ifdef AMIGA
541  | 	ami_wbench_init();	/* must be here or can't set fruit */
542  | #endif
543  | 	(void)fruitadd(pl_fruit);
544  | 	/* Remove "slime mold" from list of object names; this will	*/
545  | 	/* prevent it from being wished unless it's actually present	*/
546  | 	/* as a named (or default) fruit.  Wishing for "fruit" will	*/
547  | 	/* result in the player's preferred fruit [better than "\033"].	*/
548  | 	obj_descr[SLIME_MOLD].oc_name = "fruit";
549  | 
550  | 	return;
551  | }
552  | 
553  | STATIC_OVL void
554  | nmcpy(dest, src, maxlen)
555  | 	char	*dest;
556  | 	const char *src;
557  | 	int	maxlen;
558  | {
559  | 	int	count;
560  | 
561  | 	for(count = 1; count < maxlen; count++) {
562  | 		if(*src == ',' || *src == '\0') break; /*exit on \0 terminator*/
563  | 		*dest++ = *src++;
564  | 	}
565  | 	*dest = 0;
566  | }
567  | 
568  | /*
569  |  * escapes: escape expansion for showsyms. C-style escapes understood include
570  |  * \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal). The ^-prefix
571  |  * for control characters is also understood, and \[mM] followed by any of the
572  |  * previous forms or by a character has the effect of 'meta'-ing the value (so
573  |  * that the alternate character set will be enabled).
574  |  */
575  | STATIC_OVL void
576  | escapes(cp, tp)
577  | const char	*cp;
578  | char *tp;
579  | {
580  |     while (*cp)
581  |     {
582  | 	int	cval = 0, meta = 0;
583  | 
584  | 	if (*cp == '\\' && index("mM", cp[1])) {
585  | 		meta = 1;
586  | 		cp += 2;
587  | 	}
588  | 	if (*cp == '\\' && index("0123456789xXoO", cp[1]))
589  | 	{
590  | 	    const char *dp, *hex = "00112233445566778899aAbBcCdDeEfF";
591  | 	    int dcount = 0;
592  | 
593  | 	    cp++;
594  | 	    if (*cp == 'x' || *cp == 'X')
595  | 		for (++cp; (dp = index(hex, *cp)) && (dcount++ < 2); cp++)
596  | 		    cval = (cval * 16) + (dp - hex) / 2;
597  | 	    else if (*cp == 'o' || *cp == 'O')
598  | 		for (++cp; (index("01234567",*cp)) && (dcount++ < 3); cp++)
599  | 		    cval = (cval * 8) + (*cp - '0');
600  | 	    else
601  | 		for (; (index("0123456789",*cp)) && (dcount++ < 3); cp++)
602  | 		    cval = (cval * 10) + (*cp - '0');
603  | 	}
604  | 	else if (*cp == '\\')		/* C-style character escapes */
605  | 	{
606  | 	    switch (*++cp)
607  | 	    {
608  | 	    case '\\': cval = '\\'; break;
609  | 	    case 'n': cval = '\n'; break;
610  | 	    case 't': cval = '\t'; break;
611  | 	    case 'b': cval = '\b'; break;
612  | 	    case 'r': cval = '\r'; break;
613  | 	    default: cval = *cp;
614  | 	    }
615  | 	    cp++;
616  | 	}
617  | 	else if (*cp == '^')		/* expand control-character syntax */
618  | 	{
619  | 	    cval = (*++cp & 0x1f);
620  | 	    cp++;
621  | 	}
622  | 	else
623  | 	    cval = *cp++;
624  | 	if (meta)
625  | 	    cval |= 0x80;
626  | 	*tp++ = cval;
627  |     }
628  |     *tp = '\0';
629  | }
630  | 
631  | /* some boolean options can only be set on start-up */
632  | STATIC_OVL int
633  | boolopt_only_initial(i)
634  | int i;
635  | {
636  | 	return (boolopt[i].addr == &flags.female
637  | 	     || boolopt[i].addr == &flags.legacy
638  | #if defined(MICRO) && !defined(AMIGA)
639  | 	     || boolopt[i].addr == &iflags.rawio
640  | 	     || boolopt[i].addr == &iflags.BIOS
641  | #endif
642  | #if defined(MSDOS) && defined(USE_TILES)
643  | 	     || boolopt[i].addr == &iflags.preload_tiles
644  | #endif
645  | 	);
646  | }
647  | 
648  | STATIC_OVL void
649  | rejectoption(optname)
650  | const char *optname;
651  | {
652  | #ifdef MICRO
653  | # ifdef AMIGA
654  | 	if(FromWBench){
655  | 		pline("\"%s\" settable only from %s or in icon.",
656  | 			optname, configfile);
657  | 	} else
658  | # endif
659  | 		pline("\"%s\" settable only from %s.", optname, configfile);
660  | #else
661  | 	pline("%s can be set only from NETHACKOPTIONS or %s.", optname,
662  | 			configfile);
663  | #endif
664  | }
665  | 
666  | STATIC_OVL void
667  | badoption(opts)
668  | const char *opts;
669  | {
670  | 	if (!initial) {
671  | 	    if (!strncmp(opts, "h", 1) || !strncmp(opts, "?", 1))
672  | 		option_help();
673  | 	    else
674  | 		pline("Bad syntax: %s.  Enter \"?g\" for help.", opts);
675  | 	    return;
676  | 	}
677  | #ifdef MAC
678  | 	else return;
679  | #endif
680  | 
681  | # ifdef AMIGA
682  | 	if(ami_wbench_badopt(opts)) {
683  | # endif
684  | 	if(from_file)
685  | 	    raw_printf("Bad syntax in OPTIONS in %s: %s.", configfile, opts);
686  | 	else
687  | 	    raw_printf("Bad syntax in NETHACKOPTIONS: %s.", opts);
688  | # ifdef AMIGA
689  | 	}
690  | # endif
691  | 	wait_synch();
692  | }
693  | 
694  | STATIC_OVL char *
695  | string_for_opt(opts, val_optional)
696  | char *opts;
697  | boolean val_optional;
698  | {
699  | 	char *colon, *equals;
700  | 
701  | 	colon = index(opts, ':');
702  | 	equals = index(opts, '=');
703  | 	if (!colon || (equals && equals < colon)) colon = equals;
704  | 
705  | 	if (!colon || !*++colon) {
706  | 		if (!val_optional) badoption(opts);
707  | 		return (char *)0;
708  | 	}
709  | 	return colon;
710  | }
711  | 
712  | STATIC_OVL char *
713  | string_for_env_opt(optname, opts, val_optional)
714  | const char *optname;
715  | char *opts;
716  | boolean val_optional;
717  | {
718  | 	if(!initial) {
719  | 		rejectoption(optname);
720  | 		return (char *)0;
721  | 	}
722  | 	return string_for_opt(opts, val_optional);
723  | }
724  | 
725  | STATIC_OVL void
726  | bad_negation(optname, with_parameter)
727  | const char *optname;
728  | boolean with_parameter;
729  | {
730  | 	pline_The("%s option may not %sbe negated.",
731  | 		optname,
732  | 		with_parameter ? "both have a value and " : "");
733  | }
734  | 
735  | /*
736  |  * Change the inventory order, using the given string as the new order.
737  |  * Missing characters in the new order are filled in at the end from
738  |  * the current inv_order, except for gold, which is forced to be first
739  |  * if not explicitly present.
740  |  *
741  |  * This routine returns 1 unless there is a duplicate or bad char in
742  |  * the string.
743  |  */
744  | STATIC_OVL int
745  | change_inv_order(op)
746  | char *op;
747  | {
748  |     int oc_sym, num;
749  |     char *sp, buf[BUFSZ];
750  | 
751  |     num = 0;
752  |     if (!index(op, GOLD_SYM))
753  | 	buf[num++] = GOLD_CLASS;
754  | 
755  |     for (sp = op; *sp; sp++) {
756  | 	oc_sym = def_char_to_objclass(*sp);
757  | 	/* reject bad or duplicate entries */
758  | 	if (oc_sym == MAXOCLASSES ||
759  | 		oc_sym == RANDOM_CLASS || oc_sym == ILLOBJ_CLASS ||
760  | 		!index(flags.inv_order, oc_sym) || index(sp+1, *sp))
761  | 	    return 0;
762  | 	/* retain good ones */
763  | 	buf[num++] = (char) oc_sym;
764  |     }
765  |     buf[num] = '\0';
766  | 
767  |     /* fill in any omitted classes, using previous ordering */
768  |     for (sp = flags.inv_order; *sp; sp++)
769  | 	if (!index(buf, *sp)) {
770  | 	    buf[num++] = *sp;
771  | 	    buf[num] = '\0';	/* explicitly terminate for next index() */
772  | 	}
773  | 
774  |     Strcpy(flags.inv_order, buf);
775  |     return 1;
776  | }
777  | 
778  | STATIC_OVL void
779  | graphics_opts(opts, optype, maxlen, offset)
780  | register char *opts;
781  | const char *optype;
782  | int maxlen, offset;
783  | {
784  | 	uchar translate[MAXPCHARS+1];
785  | 	int length, i;
786  | 
787  | 	if (!(opts = string_for_env_opt(optype, opts, FALSE)))
788  | 		return;
789  | 	escapes(opts, opts);
790  | 
791  | 	length = strlen(opts);
792  | 	if (length > maxlen) length = maxlen;
793  | 	/* match the form obtained from PC configuration files */
794  | 	for (i = 0; i < length; i++)
795  | 		translate[i] = (uchar) opts[i];
796  | 	assign_graphics(translate, length, maxlen, offset);
797  | }
798  | 
799  | STATIC_OVL void
800  | warning_opts(opts, optype)
801  | register char *opts;
802  | const char *optype;
803  | {
804  | 	uchar translate[MAXPCHARS+1];
805  | 	int length, i;
806  | 
807  | 	if (!(opts = string_for_env_opt(optype, opts, FALSE)))
808  | 		return;
809  | 	escapes(opts, opts);
810  | 
811  | 	length = strlen(opts);
812  | 	if (length > WARNCOUNT) length = WARNCOUNT;
813  | 	/* match the form obtained from PC configuration files */
814  | 	for (i = 0; i < length; i++)
815  | 	     translate[i] = (((i < WARNCOUNT) && opts[i]) ?
816  | 			   (uchar) opts[i] : def_warnsyms[i].sym);
817  | 	assign_warnings(translate);
818  | }
819  | 
820  | void
821  | assign_warnings(graph_chars)
822  | register uchar *graph_chars;
823  | {
824  | 	int i;
825  | 	for (i = 0; i < WARNCOUNT; i++)
826  | 	    warnsyms[i] = graph_chars[i];
827  | }
828  | 
829  | #if 0
830  | /* warnlevel is unnecessary with the new warning introduced in 3.3.1 */
831  | STATIC_OVL int
832  | warnlevel_opts(op, optn)
833  | char *op;
834  | const char *optn;
835  | {
836  | 	char buf[BUFSZ];
837  | 	int twarnlevel;
838  | 	boolean rejectlevel = FALSE;
839  | 
840  | 	if (op) {
841  | 		twarnlevel = atoi(op);
842  | 		if (twarnlevel >= WARNCOUNT || twarnlevel < 1)
843  | 			rejectlevel = TRUE;
844  | 		else {
845  | 			flags.warnlevel = twarnlevel;
846  | 			see_monsters();
847  | 		}
848  | 	}
849  | 	if (rejectlevel) {
850  | 		if (!initial)
851  | 			pline("warnlevel must be 1 to %d.", WARNCOUNT - 1);
852  | 		else {
853  | 			Sprintf(buf,
854  | 			    "\n%s=%s Invalid warnlevel ignored (must be 1 to %d)",
855  | 				optn, op, WARNCOUNT - 1);
856  | 			badoption(buf);
857  | 		}
858  | 		return 0;
859  | 	}
860  | 	if (!initial) {
861  | 		if (flags.warnlevel < WARNCOUNT -1)
862  | 			Sprintf(buf, "s %d to %d", flags.warnlevel, WARNCOUNT - 1);
863  | 		else
864  | 			Sprintf(buf, " %d", flags.warnlevel);
865  | 		pline("Warning level%s will be displayed.", buf);
866  | 	}
867  | 	return 1;
868  | }
869  | #endif
870  | 
871  | STATIC_OVL int
872  | feature_alert_opts(op, optn)
873  | char *op;
874  | const char *optn;
875  | {
876  | 	char buf[BUFSZ];
877  | 	boolean rejectver = FALSE;
878  | 	unsigned long fnv = get_feature_notice_ver(op);		/* version.c */
879  | 	if (fnv == 0L) return 0;
880  | 	if (fnv > get_current_feature_ver())
881  | 		rejectver = TRUE;
882  | 	else
883  | 		flags.suppress_alert = fnv;
884  | 	if (rejectver) {
885  | 		if (!initial)
886  | 			You_cant("disable new feature alerts for future versions.");
887  | 		else {
888  | 			Sprintf(buf,
889  | 				"\n%s=%s Invalid reference to a future version ignored",
890  | 				optn, op);
891  | 			badoption(buf);
892  | 		}
893  | 		return 0;
894  | 	}
895  | 	if (!initial) {
896  | 		Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
897  | 			FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
898  | 		pline("Feature change alerts disabled for NetHack %s features and prior.",
899  | 			buf);
900  | 	}
901  | 	return 1;
902  | }
903  | 
904  | void
905  | parseoptions(opts, tinitial, tfrom_file)
906  | register char *opts;
907  | boolean tinitial, tfrom_file;
908  | {
909  | 	register char *op;
910  | 	unsigned num;
911  | 	boolean negated;
912  | 	int i;
913  | 	const char *fullname;
914  | 
915  | 	initial = tinitial;
916  | 	from_file = tfrom_file;
917  | 	if ((op = index(opts, ',')) != 0) {
918  | 		*op++ = 0;
919  | 		parseoptions(op, initial, from_file);
920  | 	}
921  | 	if (strlen(opts) > BUFSZ/2) {
922  | 		badoption("option too long");
923  | 		return;
924  | 	}
925  | 
926  | 	/* strip leading and trailing white space */
927  | 	while (isspace(*opts)) opts++;
928  | 	op = eos(opts);
929  | 	while (--op >= opts && isspace(*op)) *op = '\0';
930  | 
931  | 	if (!*opts) return;
932  | 	negated = FALSE;
933  | 	while ((*opts == '!') || !strncmpi(opts, "no", 2)) {
934  | 		if (*opts == '!') opts++; else opts += 2;
935  | 		negated = !negated;
936  | 	}
937  | 
938  | 	/* variant spelling */
939  | 
940  | 	if (match_optname(opts, "colour", 5, FALSE))
941  | 		Strcpy(opts, "color");	/* fortunately this isn't longer */
942  | 
943  | 	/* special boolean options */
944  | 
945  | 	if (match_optname(opts, "female", 3, FALSE)) {
946  | 		if(!initial && flags.female == negated)
947  | 			pline("That is not anatomically possible.");
948  | 		else
949  | 			flags.initgend = flags.female = !negated;
950  | 		return;
951  | 	}
952  | 
953  | 	if (match_optname(opts, "male", 4, FALSE)) {
954  | 		if(!initial && flags.female != negated)
955  | 			pline("That is not anatomically possible.");
956  | 		else
957  | 			flags.initgend = flags.female = negated;
958  | 		return;
959  | 	}
960  | 
961  | #if defined(MICRO) && !defined(AMIGA)
962  | 	/* included for compatibility with old NetHack.cnf files */
963  | 	if (match_optname(opts, "IBM_", 4, FALSE)) {
964  | 		iflags.BIOS = !negated;
965  | 		return;
966  | 	}
967  | #endif /* MICRO */
968  | 
969  | 	/* compound options */
970  | 
971  | 	fullname = "pettype";
972  | 	if (match_optname(opts, fullname, 3, TRUE)) {
973  | 		if ((op = string_for_env_opt(fullname, opts, negated)) != 0) {
974  | 		    if (negated) bad_negation(fullname, TRUE);
975  | 		    else switch (*op) {
976  | 			case 'd':	/* dog */
977  | 			case 'D':
978  | 			    preferred_pet = 'd';
979  | 			    break;
980  | 			case 'c':	/* cat */
981  | 			case 'C':
982  | 			case 'f':	/* feline */
983  | 			case 'F':
984  | 			    preferred_pet = 'c';
985  | 			    break;
986  | 			default:
987  | 			    pline("Unrecognized pet type '%s'", op);
988  | 			    break;
989  | 		    }
990  | 		} else if (negated) preferred_pet = 0;
991  | 		return;
992  | 	}
993  | 
994  | 	fullname = "catname";
995  | 	if (match_optname(opts, fullname, 3, TRUE)) {
996  | 		if (negated) bad_negation(fullname, FALSE);
997  | 		else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
998  | 			nmcpy(catname, op, PL_PSIZ);
999  | 		return;
1000 | 	}
1001 | 
1002 | 	fullname = "dogname";
1003 | 	if (match_optname(opts, fullname, 3, TRUE)) {
1004 | 		if (negated) bad_negation(fullname, FALSE);
1005 | 		else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1006 | 			nmcpy(dogname, op, PL_PSIZ);
1007 | 		return;
1008 | 	}
1009 | 
1010 | 	fullname = "horsename";
1011 | 	if (match_optname(opts, fullname, 5, TRUE)) {
1012 | 		if (negated) bad_negation(fullname, FALSE);
1013 | 		else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1014 | 			nmcpy(horsename, op, PL_PSIZ);
1015 | 		return;
1016 | 	}
1017 | 
1018 | 	fullname = "msghistory";
1019 | 	if (match_optname(opts, fullname, 3, TRUE)) {
1020 | 		op = string_for_env_opt(fullname, opts, negated);
1021 | 		if ((negated && !op) || (!negated && op)) {
1022 | 			iflags.msg_history = negated ? 0 : atoi(op);
1023 | 		} else if (negated) bad_negation(fullname, TRUE);
1024 | 		return;
1025 | 	}
1026 | 
1027 | #ifdef CHANGE_COLOR
1028 | #ifdef MAC
1029 | 	fullname = "use_stone";
1030 | 	if (match_optname(opts, fullname, 6, TRUE)) {
1031 | 		op = string_for_env_opt(fullname, opts, negated);
1032 | 		if ((negated && !op) || (!negated && op)) {
1033 | 			iflags.use_stone = negated ? 0 : atoi(op);
1034 | 		} else if (negated) bad_negation(fullname, TRUE);
1035 | 		return;
1036 | 	}
1037 | 
1038 | 	fullname = "background";
1039 | 	if (match_optname(opts, fullname, 5,TRUE))
1040 | 	{
1041 | 		if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1042 | 		{
1043 | 			if (!strncmpi (op, "white", 5))
1044 | 				change_background (1);
1045 | 			else if (!strncmpi (op, "black", 5))
1046 | 				change_background (0);
1047 | 		}
1048 | 		return;
1049 | 	}
1050 | 	
1051 | 	fullname = "font";
1052 | 	if (!strncmpi(opts, fullname, 4))
1053 | 	{	int wintype = -1;
1054 | 	
1055 | 		opts += 4;
1056 | 		if (!strncmpi (opts, "map", 3))
1057 | 			wintype = NHW_MAP;
1058 | 		else if (!strncmpi (opts, "message", 7))
1059 | 			wintype = NHW_MESSAGE;
1060 | 		else if (!strncmpi (opts, "text", 4))
1061 | 			wintype = NHW_TEXT;
1062 | 				
1063 | 		if (wintype > 0 && (op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1064 | 		{	set_font_name (wintype, op);
1065 | 		}
1066 | 		return;
1067 | 	}
1068 | #endif
1069 | 	if (match_optname(opts, "palette", 3, TRUE)
1070 | # ifdef MAC
1071 | 					|| match_optname(opts, "hicolor", 3, TRUE)
1072 | # endif
1073 | 									) {
1074 | 	    int color_number, color_incr;
1075 | 
1076 | # ifdef MAC
1077 | 	    if (match_optname(opts, "hicolor", 3, TRUE)) {
1078 | 		if (negated) {
1079 | 		    bad_negation("hicolor", FALSE);
1080 | 		    return;
1081 | 		}
1082 | 		color_number = CLR_MAX + 4;	/* HARDCODED inverse number */
1083 | 		color_incr = -1;
1084 | 	    } else {
1085 | # endif
1086 | 		if (negated) {
1087 | 		    bad_negation("palette", FALSE);
1088 | 		    return;
1089 | 		}
1090 | 		color_number = 0;
1091 | 		color_incr = 1;
1092 | # ifdef MAC
1093 | 	    }
1094 | # endif
1095 | 	    if ((op = string_for_opt(opts, FALSE)) != (char *)0) {
1096 | 		char *pt = op;
1097 | 		int cnt, tmp, reverse;
1098 | 		long rgb;
1099 | 
1100 | 		while (*pt && color_number >= 0) {
1101 | 		    cnt = 3;
1102 | 		    rgb = 0L;
1103 | 		    if (*pt == '-') {
1104 | 			reverse = 1;
1105 | 			pt++;
1106 | 		    } else {
1107 | 			reverse = 0;
1108 | 		    }
1109 | 		    while (cnt-- > 0) {
1110 | 			if (*pt && *pt != '/') {
1111 | # ifdef AMIGA
1112 | 			    rgb <<= 4;
1113 | # else
1114 | 			    rgb <<= 8;
1115 | # endif
1116 | 			    tmp = *(pt++);
1117 | 			    if (isalpha(tmp)) {
1118 | 				tmp = (tmp + 9) & 0xf;	/* Assumes ASCII... */
1119 | 			    } else {
1120 | 				tmp &= 0xf;	/* Digits in ASCII too... */
1121 | 			    }
1122 | # ifndef AMIGA
1123 | 			    /* Add an extra so we fill f -> ff and 0 -> 00 */
1124 | 			    rgb += tmp << 4;
1125 | # endif
1126 | 			    rgb += tmp;
1127 | 			}
1128 | 		    }
1129 | 		    if (*pt == '/') {
1130 | 			pt++;
1131 | 		    }
1132 | 		    change_color(color_number, rgb, reverse);
1133 | 		    color_number += color_incr;
1134 | 		}
1135 | 	    }
1136 | 	    if (!initial) {
1137 | 		need_redraw = TRUE;
1138 | 	    }
1139 | 	    return;
1140 | 	}
1141 | #endif
1142 | 
1143 | 	if (match_optname(opts, "fruit", 2, TRUE)) {
1144 | 		char empty_str = '\0';
1145 | 		op = string_for_opt(opts, negated);
1146 | 		if (negated) {
1147 | 		    if (op) {
1148 | 			bad_negation("fruit", TRUE);
1149 | 			return;
1150 | 		    }
1151 | 		    op = &empty_str;
1152 | 		    goto goodfruit;
1153 | 		}
1154 | 		if (!op) return;
1155 | 		if (!initial) {
1156 | 		    struct fruit *f;
1157 | 
1158 | 		    num = 0;
1159 | 		    for(f=ffruit; f; f=f->nextf) {
1160 | 			if (!strcmp(op, f->fname)) goto goodfruit;
1161 | 			num++;
1162 | 		    }
1163 | 		    if (num >= 100) {
1164 | 			pline("Doing that so many times isn't very fruitful.");
1165 | 			return;
1166 | 		    }
1167 | 		}
1168 | goodfruit:
1169 | 		nmcpy(pl_fruit, op, PL_FSIZ);
1170 | 	/* OBJ_NAME(objects[SLIME_MOLD]) won't work after initialization */
1171 | 		if (!*pl_fruit)
1172 | 		    nmcpy(pl_fruit, "slime mold", PL_FSIZ);
1173 | 		if (!initial)
1174 | 		    (void)fruitadd(pl_fruit);
1175 | 		/* If initial, then initoptions is allowed to do it instead
1176 | 		 * of here (initoptions always has to do it even if there's
1177 | 		 * no fruit option at all.  Also, we don't want people
1178 | 		 * setting multiple fruits in their options.)
1179 | 		 */
1180 | 		return;
1181 | 	}
1182 | 
1183 | 	/* graphics:string */
1184 | 	fullname = "graphics";
1185 | 	if (match_optname(opts, fullname, 2, TRUE)) {
1186 | 		if (negated) bad_negation(fullname, FALSE);
1187 | 		else graphics_opts(opts, fullname, MAXPCHARS, 0);
1188 | 		return;
1189 | 	}
1190 | 	fullname = "dungeon";
1191 | 	if (match_optname(opts, fullname, 2, TRUE)) {
1192 | 		if (negated) bad_negation(fullname, FALSE);
1193 | 		else graphics_opts(opts, fullname, MAXDCHARS, 0);
1194 | 		return;
1195 | 	}
1196 | 	fullname = "traps";
1197 | 	if (match_optname(opts, fullname, 2, TRUE)) {
1198 | 		if (negated) bad_negation(fullname, FALSE);
1199 | 		else graphics_opts(opts, fullname, MAXTCHARS, MAXDCHARS);
1200 | 		return;
1201 | 	}
1202 | 	fullname = "effects";
1203 | 	if (match_optname(opts, fullname, 2, TRUE)) {
1204 | 		if (negated) bad_negation(fullname, FALSE);
1205 | 		else
1206 | 		 graphics_opts(opts, fullname, MAXECHARS, MAXDCHARS+MAXTCHARS);
1207 | 		return;
1208 | 	}
1209 | 
1210 | 	/* objects:string */
1211 | 	fullname = "objects";
1212 | 	if (match_optname(opts, fullname, 7, TRUE)) {
1213 | 		int length;
1214 | 
1215 | 		if (negated) {
1216 | 		    bad_negation(fullname, FALSE);
1217 | 		    return;
1218 | 		}
1219 | 		if (!(opts = string_for_env_opt(fullname, opts, FALSE)))
1220 | 			return;
1221 | 		escapes(opts, opts);
1222 | 
1223 | 		/*
1224 | 		 * Override the default object class symbols.  The first
1225 | 		 * object in the object class is the "random object".  I
1226 | 		 * don't want to use 0 as an object class, so the "random
1227 | 		 * object" is basically a place holder.
1228 | 		 *
1229 | 		 * The object class symbols have already been initialized in
1230 | 		 * initoptions().
1231 | 		 */
1232 | 		length = strlen(opts);
1233 | 		if (length >= MAXOCLASSES)
1234 | 		    length = MAXOCLASSES-1;	/* don't count RANDOM_OBJECT */
1235 | 
1236 | 		for (i = 0; i < length; i++)
1237 | 		    oc_syms[i+1] = (uchar) opts[i];
1238 | 		return;
1239 | 	}
1240 | 
1241 | 	/* monsters:string */
1242 | 	fullname = "monsters";
1243 | 	if (match_optname(opts, fullname, 8, TRUE)) {
1244 | 		int length;
1245 | 
1246 | 		if (negated) {
1247 | 		    bad_negation(fullname, FALSE);
1248 | 		    return;
1249 | 		}
1250 | 		if (!(opts = string_for_env_opt(fullname, opts, FALSE)))
1251 | 			return;
1252 | 		escapes(opts, opts);
1253 | 
1254 | 		/* Override default mon class symbols set in initoptions(). */
1255 | 		length = strlen(opts);
1256 | 		if (length >= MAXMCLASSES)
1257 | 		    length = MAXMCLASSES-1;	/* mon class 0 unused */
1258 | 
1259 | 		for (i = 0; i < length; i++)
1260 | 		    monsyms[i+1] = (uchar) opts[i];
1261 | 		return;
1262 | 	}
1263 | 	fullname = "warnings";
1264 | 	if (match_optname(opts, fullname, 5, TRUE)) {
1265 | 		if (negated) bad_negation(fullname, FALSE);
1266 | 		else warning_opts(opts, fullname);
1267 | 		return;
1268 | 	}
1269 | #if 0
1270 | 	fullname = "warnlevel";
1271 | 	if (match_optname(opts, fullname, 5, TRUE)) {
1272 | 	    op = string_for_opt(opts, negated);
1273 | 	    if (negated) bad_negation(fullname, FALSE);
1274 | 	    else if (op) (void) warnlevel_opts(op,fullname);
1275 | 	    return;
1276 | 	}
1277 | #endif
1278 | 	/* name:string */
1279 | 	fullname = "name";
1280 | 	if (match_optname(opts, fullname, 4, TRUE)) {
1281 | 		if (negated) bad_negation(fullname, FALSE);
1282 | 		else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1283 | 			nmcpy(plname, op, PL_NSIZ);
1284 | 		return;
1285 | 	}
1286 | 
1287 | 	/* role:string or character:string */
1288 | 	fullname = "role";
1289 | 	if (match_optname(opts, fullname, 4, TRUE) ||
1290 | 	    match_optname(opts, (fullname = "character"), 4, TRUE)) {
1291 | 		if (negated) bad_negation(fullname, FALSE);
1292 | 		else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1293 | 			if ((flags.initrole = str2role(op)) == ROLE_NONE)
1294 | 				badoption(opts);
1295 | 			else  /* Backwards compatibility */
1296 | 				nmcpy(pl_character, op, PL_NSIZ);
1297 | 		}
1298 | 		return;
1299 | 	}
1300 | 
1301 | 	/* race:string */
1302 | 	fullname = "race";
1303 | 	if (match_optname(opts, fullname, 4, TRUE)) {
1304 | 		if (negated) bad_negation(fullname, FALSE);
1305 | 		else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1306 | 			if ((flags.initrace = str2race(op)) == ROLE_NONE)
1307 | 				badoption(opts);
1308 | 			else /* Backwards compatibility */
1309 | 				pl_race = *op;
1310 | 		}
1311 | 		return;
1312 | 	}
1313 | 
1314 | 	/* gender:string */
1315 | 	fullname = "gender";
1316 | 	if (match_optname(opts, fullname, 4, TRUE)) {
1317 | 		if (negated) bad_negation(fullname, FALSE);
1318 | 		else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1319 | 			if ((flags.initgend = str2gend(op)) == ROLE_NONE)
1320 | 				badoption(opts);
1321 | 			else
1322 | 				flags.female = flags.initgend;
1323 | 		}
1324 | 		return;
1325 | 	}
1326 | 
1327 | 	/* align:string */
1328 | 	fullname = "align";
1329 | 	if (match_optname(opts, fullname, 4, TRUE)) {
1330 | 		if (negated) bad_negation(fullname, FALSE);
1331 | 		else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1332 | 			if ((flags.initalign = str2align(op)) == ROLE_NONE)
1333 | 				badoption(opts);
1334 | 		return;
1335 | 	}
1336 | 
1337 | 	/* the order to list the pack */
1338 | 	fullname = "packorder";
1339 | 	if (match_optname(opts, fullname, 4, TRUE)) {
1340 | 		if (negated) {
1341 | 		    bad_negation(fullname, FALSE);
1342 | 		    return;
1343 | 		} else if (!(op = string_for_opt(opts, FALSE))) return;
1344 | 
1345 | 		if (!change_inv_order(op))
1346 | 			badoption(opts);
1347 | 		return;
1348 | 	}
1349 | 
1350 | 	/* maximum burden picked up before prompt (Warren Cheung) */
1351 | 	fullname = "pickup_burden";
1352 | 	if (match_optname(opts, fullname, 8, TRUE)) {
1353 | 		if (negated) {
1354 | 			bad_negation(fullname, FALSE);
1355 | 			return;
1356 | 		} else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1357 | 		    switch (tolower(*op)) {
1358 | 				/* Unencumbered */
1359 | 				case 'u':
1360 | 					flags.pickup_burden = UNENCUMBERED;
1361 | 					break;
1362 | 				/* Burdened (slight encumberance) */
1363 | 				case 'b':
1364 | 					flags.pickup_burden = SLT_ENCUMBER;
1365 | 					break;
1366 | 				/* streSsed (moderate encumberance) */
1367 | 				case 's':
1368 | 					flags.pickup_burden = MOD_ENCUMBER;
1369 | 					break;
1370 | 				/* straiNed (heavy encumberance) */
1371 | 				case 'n':
1372 | 					flags.pickup_burden = HVY_ENCUMBER;
1373 | 					break;
1374 | 				/* OverTaxed (extreme encumberance) */
1375 | 				case 'o':
1376 | 				case 't':
1377 | 					flags.pickup_burden = EXT_ENCUMBER;
1378 | 					break;
1379 | 				/* overLoaded */
1380 | 				case 'l':
1381 | 					flags.pickup_burden = OVERLOADED;
1382 | 					break;
1383 | 				default:
1384 | 				badoption(opts);
1385 | 		    }
1386 | 		}
1387 | 		return;
1388 | 	}
1389 | 
1390 | 	/* types of objects to pick up automatically */
1391 | 	if (match_optname(opts, "pickup_types", 8, TRUE)) {
1392 | 		char ocl[MAXOCLASSES + 1], tbuf[MAXOCLASSES + 1],
1393 | 		     qbuf[QBUFSZ], abuf[BUFSZ];
1394 | 		int oc_sym;
1395 | 		boolean badopt = FALSE, compat = (strlen(opts) <= 6), use_menu;
1396 | 
1397 | 		oc_to_str(flags.pickup_types, tbuf);
1398 | 		flags.pickup_types[0] = '\0';	/* all */
1399 | 		op = string_for_opt(opts, (compat || !initial));
1400 | 		if (!op) {
1401 | 		    if (compat || negated || initial) {
1402 | 			/* for backwards compatibility, "pickup" without a
1403 | 			   value is a synonym for autopickup of all types
1404 | 			   (and during initialization, we can't prompt yet) */
1405 | 			flags.pickup = !negated;
1406 | 			return;
1407 | 		    }
1408 | 		    oc_to_str(flags.inv_order, ocl);
1409 | 		    use_menu = TRUE;
1410 | 		    if (flags.menu_style == MENU_TRADITIONAL ||
1411 | 			    flags.menu_style == MENU_COMBINATION) {
1412 | 			use_menu = FALSE;
1413 | 			Sprintf(qbuf, "New pickup_types: [%s am] (%s)",
1414 | 				ocl, *tbuf ? tbuf : "all");
1415 | 			getlin(qbuf, abuf);
1416 | 			op = mungspaces(abuf);
1417 | 			if (abuf[0] == '\0' || abuf[0] == '\033')
1418 | 			    op = tbuf;		/* restore */
1419 | 			else if (abuf[0] == 'm')
1420 | 			    use_menu = TRUE;
1421 | 		    }
1422 | 		    if (use_menu) {
1423 | 			(void) choose_classes_menu("Auto-Pickup what?", 1,
1424 | 						   TRUE, ocl, tbuf);
1425 | 			op = tbuf;
1426 | 		    }
1427 | 		}
1428 | 		if (negated) {
1429 | 		    bad_negation("pickup_types", TRUE);
1430 | 		    return;
1431 | 		}
1432 | 		while (*op == ' ') op++;
1433 | 		if (*op != 'a' && *op != 'A') {
1434 | 		    num = 0;
1435 | 		    while (*op) {
1436 | 			oc_sym = def_char_to_objclass(*op);
1437 | 			/* make sure all are valid obj symbols occuring once */
1438 | 			if (oc_sym != MAXOCLASSES &&
1439 | 			    !index(flags.pickup_types, oc_sym)) {
1440 | 			    flags.pickup_types[num] = (char)oc_sym;
1441 | 			    flags.pickup_types[++num] = '\0';
1442 | 			} else
1443 | 			    badopt = TRUE;
1444 | 			op++;
1445 | 		    }
1446 | 		    if (badopt) badoption(opts);
1447 | 		}
1448 | 		return;
1449 | 	}
1450 | 
1451 | 	/* things to disclose at end of game */
1452 | 	if (match_optname(opts, "disclose", 4, TRUE)) {
1453 | 		flags.end_disclose[0] = '\0';	/* all */
1454 | 		if (!(op = string_for_opt(opts, TRUE))) {
1455 | 			/* for backwards compatibility, "disclose" without a
1456 | 			 * value means all (was inventory and attributes,
1457 | 			 * the only things available then), but negated
1458 | 			 * it means "none"
1459 | 			 * (note "none" contains none of "iavkgc")
1460 | 			 */
1461 | 			if (negated) Strcpy(flags.end_disclose, "none");
1462 | 			return;
1463 | 		}
1464 | 		if (negated) {
1465 | 			bad_negation("disclose", TRUE);
1466 | 			return;
1467 | 		}
1468 | 		num = 0;
1469 | 		while (*op && num < sizeof flags.end_disclose - 1) {
1470 | 			register char c;
1471 | 			c = lowc(*op);
1472 | 			if (c == 'k') c = 'v';	/* killed -> vanquished */
1473 | 			if (!index(flags.end_disclose, c)) {
1474 | 				flags.end_disclose[num++] = c;
1475 | 				flags.end_disclose[num] = '\0';	/* for index */
1476 | 			}
1477 | 			op++;
1478 | 		}
1479 | 		return;
1480 | 	}
1481 | 
1482 | 	/* scores:5t[op] 5a[round] o[wn] */
1483 | 	if (match_optname(opts, "scores", 4, TRUE)) {
1484 | 	    if (negated) {
1485 | 		bad_negation("scores", FALSE);
1486 | 		return;
1487 | 	    }
1488 | 	    if (!(op = string_for_opt(opts, FALSE))) return;
1489 | 
1490 | 	    while (*op) {
1491 | 		int inum = 1;
1492 | 
1493 | 		if (digit(*op)) {
1494 | 		    inum = atoi(op);
1495 | 		    while (digit(*op)) op++;
1496 | 		} else if (*op == '!') {
1497 | 		    negated = !negated;
1498 | 		    op++;
1499 | 		}
1500 | 		while (*op == ' ') op++;
1501 | 
1502 | 		switch (*op) {
1503 | 		 case 't':
1504 | 		 case 'T':  flags.end_top = inum;
1505 | 			    break;
1506 | 		 case 'a':
1507 | 		 case 'A':  flags.end_around = inum;
1508 | 			    break;
1509 | 		 case 'o':
1510 | 		 case 'O':  flags.end_own = !negated;
1511 | 			    break;
1512 | 		 default:   badoption(opts);
1513 | 			    return;
1514 | 		}
1515 | 		while (letter(*++op) || *op == ' ') continue;
1516 | 		if (*op == '/') op++;
1517 | 	    }
1518 | 	    return;
1519 | 	}
1520 | 
1521 | 	fullname = "suppress_alert";
1522 | 	if (match_optname(opts, fullname, 4, TRUE)) {
1523 | 		op = string_for_opt(opts, negated);
1524 | 		if (negated) bad_negation(fullname, FALSE);
1525 | 		else if (op) (void) feature_alert_opts(op,fullname);
1526 | 		return;
1527 | 	}
1528 | 	
1529 | #ifdef VIDEOSHADES
1530 | 	/* videocolors:string */
1531 | 	fullname = "videocolors";
1532 | 	if (match_optname(opts, fullname, 6, TRUE) ||
1533 | 	    match_optname(opts, "videocolours", 10, TRUE)) {
1534 | 		if (negated) {
1535 | 			bad_negation(fullname, FALSE);
1536 | 			return;
1537 | 		}
1538 | 		else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
1539 | 			return;
1540 | 		}
1541 | 		if (!assign_videocolors(opts))
1542 | 			badoption(opts);
1543 | 		return;
1544 | 	}
1545 | 	/* videoshades:string */
1546 | 	fullname = "videoshades";
1547 | 	if (match_optname(opts, fullname, 6, TRUE)) {
1548 | 		if (negated) {
1549 | 			bad_negation(fullname, FALSE);
1550 | 			return;
1551 | 		}
1552 | 		else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
1553 | 			return;
1554 | 		}
1555 | 		if (!assign_videoshades(opts))
1556 | 			badoption(opts);
1557 | 		return;
1558 | 	}
1559 | #endif /* VIDEOSHADES */
1560 | #ifdef MSDOS
1561 | # ifdef NO_TERMS
1562 | 	/* video:string -- must be after longer tests */
1563 | 	fullname = "video";
1564 | 	if (match_optname(opts, fullname, 5, TRUE)) {
1565 | 		if (negated) {
1566 | 			bad_negation(fullname, FALSE);
1567 | 			return;
1568 | 		}
1569 | 		else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
1570 | 			return;
1571 | 		}
1572 | 		if (!assign_video(opts))
1573 | 			badoption(opts);
1574 | 		return;
1575 | 	}
1576 | # endif /* NO_TERMS */
1577 | 	/* soundcard:string -- careful not to match boolean 'sound' */
1578 | 	fullname = "soundcard";
1579 | 	if (match_optname(opts, fullname, 6, TRUE)) {
1580 | 		if (negated) {
1581 | 			bad_negation(fullname, FALSE);
1582 | 			return;
1583 | 		}
1584 | 		else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
1585 | 			return;
1586 | 		}
1587 | 		if (!assign_soundcard(opts))
1588 | 			badoption(opts);
1589 | 		return;
1590 | 	}
1591 | #endif /* MSDOS */
1592 | 
1593 | 
1594 | 	fullname = "windowtype";
1595 | 	if (match_optname(opts, fullname, 3, TRUE)) {
1596 | 	    if (negated) {
1597 | 		bad_negation(fullname, FALSE);
1598 | 		return;
1599 | 	    } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1600 | 		char buf[WINTYPELEN];
1601 | 		nmcpy(buf, op, WINTYPELEN);
1602 | 		choose_windows(buf);
1603 | 	    }
1604 | 	    return;
1605 | 	}
1606 | 
1607 | 	/* menustyle:traditional or combo or full or partial */
1608 | 	if (match_optname(opts, "menustyle", 4, TRUE)) {
1609 | 		int tmp;
1610 | 		boolean val_required = (strlen(opts) > 5 && !negated);
1611 | 
1612 | 		if (!(op = string_for_opt(opts, !val_required))) {
1613 | 		    if (val_required) return; /* string_for_opt gave feedback */
1614 | 		    tmp = negated ? 'n' : 'f';
1615 | 		} else {
1616 | 		    tmp = tolower(*op);
1617 | 		}
1618 | 		switch (tmp) {
1619 | 			case 'n':	/* none */
1620 | 			case 't':	/* traditional */
1621 | 				flags.menu_style = MENU_TRADITIONAL;
1622 | 				break;
1623 | 			case 'c':	/* combo: trad.class sel+menu */
1624 | 				flags.menu_style = MENU_COMBINATION;
1625 | 				break;
1626 | 			case 'p':	/* partial: no class menu */
1627 | 				flags.menu_style = MENU_PARTIAL;
1628 | 				break;
1629 | 			case 'f':	/* full: class menu + menu */
1630 | 				flags.menu_style = MENU_FULL;
1631 | 				break;
1632 | 			default:
1633 | 				badoption(opts);
1634 | 		}
1635 | 		return;
1636 | 	}
1637 | 
1638 | 	/* check for menu command mapping */
1639 | 	for (i = 0; i < NUM_MENU_CMDS; i++) {
1640 | 	    fullname = default_menu_cmd_info[i].name;
1641 | 	    if (match_optname(opts, fullname, (int)strlen(fullname), TRUE)) {
1642 | 		if (negated)
1643 | 		    bad_negation(fullname, FALSE);
1644 | 		else if ((op = string_for_opt(opts, FALSE)) != 0) {
1645 | 		    int j;
1646 | 		    char c, op_buf[BUFSZ];
1647 | 		    boolean isbad = FALSE;
1648 | 
1649 | 		    escapes(op, op_buf);
1650 | 		    c = *op_buf;
1651 | 
1652 | 		    if (c == 0 || c == '\r' || c == '\n' || c == '\033' ||
1653 | 			    c == ' ' || digit(c) || (letter(c) && c != '@'))
1654 | 			isbad = TRUE;
1655 | 		    else	/* reject default object class symbols */
1656 | 			for (j = 1; j < MAXOCLASSES; j++)
1657 | 			    if (c == def_oc_syms[i]) {
1658 | 				isbad = TRUE;
1659 | 				break;
1660 | 			    }
1661 | 
1662 | 		    if (isbad)
1663 | 			badoption(opts);
1664 | 		    else
1665 | 			add_menu_cmd_alias(c, default_menu_cmd_info[i].cmd);
1666 | 		}
1667 | 		return;
1668 | 	    }
1669 | 	}
1670 | 
1671 | 	/* OK, if we still haven't recognized the option, check the boolean
1672 | 	 * options list
1673 | 	 */
1674 | 	for (i = 0; boolopt[i].name; i++) {
1675 | 		if (match_optname(opts, boolopt[i].name, 3, FALSE)) {
1676 | 			/* options that don't exist */
1677 | 			if (!boolopt[i].addr) {
1678 | 			    if (!initial && !negated)
1679 | 				pline_The("\"%s\" option is not available.",
1680 | 					boolopt[i].name);
1681 | 			    return;
1682 | 			}
1683 | 			/* options that must come from config file */
1684 | 			if (!initial && boolopt_only_initial(i)) {
1685 | 			    rejectoption(boolopt[i].name);
1686 | 			    return;
1687 | 			}
1688 | 
1689 | 			*(boolopt[i].addr) = !negated;
1690 | 
1691 | #if defined(TERMLIB) || defined(ASCIIGRAPH) || defined(MAC_GRAPHICS_ENV)
1692 | 			if (FALSE
1693 | # ifdef TERMLIB
1694 | 				 || (boolopt[i].addr) == &iflags.DECgraphics
1695 | # endif
1696 | # ifdef ASCIIGRAPH
1697 | 				 || (boolopt[i].addr) == &iflags.IBMgraphics
1698 | # endif
1699 | # ifdef MAC_GRAPHICS_ENV
1700 | 				 || (boolopt[i].addr) == &iflags.MACgraphics
1701 | # endif
1702 | 				) {
1703 | # ifdef REINCARNATION
1704 | 			    if (!initial && Is_rogue_level(&u.uz))
1705 | 				assign_rogue_graphics(FALSE);
1706 | # endif
1707 | 			    need_redraw = TRUE;
1708 | # ifdef TERMLIB
1709 | 			    if ((boolopt[i].addr) == &iflags.DECgraphics)
1710 | 				switch_graphics(iflags.DECgraphics ?
1711 | 						DEC_GRAPHICS : ASCII_GRAPHICS);
1712 | # endif
1713 | # ifdef ASCIIGRAPH
1714 | 			    if ((boolopt[i].addr) == &iflags.IBMgraphics)
1715 | 				switch_graphics(iflags.IBMgraphics ?
1716 | 						IBM_GRAPHICS : ASCII_GRAPHICS);
1717 | # endif
1718 | # ifdef MAC_GRAPHICS_ENV
1719 | 			    if ((boolopt[i].addr) == &iflags.MACgraphics)
1720 | 				switch_graphics(iflags.MACgraphics ?
1721 | 						MAC_GRAPHICS : ASCII_GRAPHICS);
1722 | # endif
1723 | # ifdef REINCARNATION
1724 | 			    if (!initial && Is_rogue_level(&u.uz))
1725 | 				assign_rogue_graphics(TRUE);
1726 | # endif
1727 | 			}
1728 | #endif /* TERMLIB || ASCIIGRAPH || MAC_GRAPHICS_ENV */
1729 | 
1730 | 			/* only do processing below if setting with doset() */
1731 | 			if (initial) return;
1732 | 
1733 | 			if ((boolopt[i].addr) == &flags.time
1734 | #ifdef EXP_ON_BOTL
1735 | 			 || (boolopt[i].addr) == &flags.showexp
1736 | #endif
1737 | #ifdef SCORE_ON_BOTL
1738 | 			 || (boolopt[i].addr) == &flags.showscore
1739 | #endif
1740 | 			    )
1741 | 			    flags.botl = TRUE;
1742 | 
1743 | 			else if ((boolopt[i].addr) == &flags.invlet_constant) {
1744 | 			    if (flags.invlet_constant) reassign();
1745 | 			}
1746 | #ifdef LAN_MAIL
1747 | 			else if ((boolopt[i].addr) == &flags.biff) {
1748 | 			    if (flags.biff) lan_mail_init();
1749 | 			    else lan_mail_finish();
1750 | 			}
1751 | #endif
1752 | 			else if ((boolopt[i].addr) == &iflags.num_pad)
1753 | 			    number_pad(iflags.num_pad ? 1 : 0);
1754 | 
1755 | 			else if ((boolopt[i].addr) == &flags.lit_corridor) {
1756 | 			    /*
1757 | 			     * All corridor squares seen via night vision or
1758 | 			     * candles & lamps change.  Update them by calling
1759 | 			     * newsym() on them.  Don't do this if we are
1760 | 			     * initializing the options --- the vision system
1761 | 			     * isn't set up yet.
1762 | 			     */
1763 | 			    vision_recalc(2);		/* shut down vision */
1764 | 			    vision_full_recalc = 1;	/* delayed recalc */
1765 | 			}
1766 | 
1767 | #ifdef TEXTCOLOR
1768 | 			else if ((boolopt[i].addr) == &iflags.use_color
1769 | 			      || (boolopt[i].addr) == &iflags.hilite_pet) {
1770 | 			    need_redraw = TRUE;
1771 | # ifdef TOS
1772 | 			    if ((boolopt[i].addr) == &iflags.use_color
1773 | 				&& iflags.BIOS) {
1774 | 				if (colors_changed)
1775 | 				    restore_colors();
1776 | 				else
1777 | 				    set_colors();
1778 | 			    }
1779 | # endif
1780 | 			}
1781 | #endif
1782 | 
1783 | 			return;
1784 | 		}
1785 | 	}
1786 | 
1787 | 	/* out of valid options */
1788 | 	badoption(opts);
1789 | }
1790 | 
1791 | 
1792 | static NEARDATA const char *menutype[] = {
1793 | 	"traditional", "combination", "partial", "full"
1794 | };
1795 | 
1796 | static NEARDATA const char *burdentype[] = {
1797 | 	"unencumbered", "burdened", "stressed",
1798 | 	"strained", "overtaxed", "overloaded"
1799 | };
1800 | 
1801 | 
1802 | /*
1803 |  * Convert the given string of object classes to a string of default object
1804 |  * symbols.
1805 |  */
1806 | STATIC_OVL void
1807 | oc_to_str(src,dest)
1808 |     char *src, *dest;
1809 | {
1810 |     int i;
1811 | 
1812 |     while ((i = (int) *src++) != 0) {
1813 | 	if (i < 0 || i >= MAXOCLASSES)
1814 | 	    impossible("oc_to_str:  illegal object class %d", i);
1815 | 	else
1816 | 	    *dest++ = def_oc_syms[i];
1817 |     }
1818 |     *dest = '\0';
1819 | }
1820 | 
1821 | /*
1822 |  * Add the given mapping to the menu command map list.  Always keep the
1823 |  * maps valid C strings.
1824 |  */
1825 | void
1826 | add_menu_cmd_alias(from_ch, to_ch)
1827 |     char from_ch, to_ch;
1828 | {
1829 |     if (n_menu_mapped >= MAX_MENU_MAPPED_CMDS)
1830 | 	pline("out of menu map space");
1831 |     else {
1832 | 	mapped_menu_cmds[n_menu_mapped] = from_ch;
1833 | 	mapped_menu_op[n_menu_mapped] = to_ch;
1834 | 	n_menu_mapped++;
1835 | 	mapped_menu_cmds[n_menu_mapped] = 0;
1836 | 	mapped_menu_op[n_menu_mapped] = 0;
1837 |     }
1838 | }
1839 | 
1840 | /*
1841 |  * Map the given character to its corresponding menu command.  If it
1842 |  * doesn't match anything, just return the original.
1843 |  */
1844 | char
1845 | map_menu_cmd(ch)
1846 |     char ch;
1847 | {
1848 |     char *found = index(mapped_menu_cmds, ch);
1849 |     if (found) {
1850 | 	int idx = found - mapped_menu_cmds;
1851 | 	ch = mapped_menu_op[idx];
1852 |     }
1853 |     return ch;
1854 | }
1855 | 
1856 | 
1857 | #if defined(MICRO) || defined(MAC)
1858 | # define OPTIONS_HEADING "OPTIONS"
1859 | #else
1860 | # define OPTIONS_HEADING "NETHACKOPTIONS"
1861 | #endif
1862 | 
1863 | static char fmtstr_doset_add_menu[] = "%s%-15s [%s]   "; 
1864 | 
1865 | STATIC_OVL void
1866 | doset_add_menu(win, option, indexoffset)
1867 |     winid win;			/* window to add to */
1868 |     const char *option;		/* option name */
1869 |     int indexoffset;		/* value to add to index in compopt[], or zero
1870 | 				   if option cannot be changed */
1871 | {
1872 |     const char *value = "unknown";		/* current value */
1873 |     char buf[BUFSZ], buf2[BUFSZ];
1874 |     anything any;
1875 |     int i;
1876 | 
1877 |     any.a_void = 0;
1878 |     if (indexoffset == 0) {
1879 | 	any.a_int = 0;
1880 | 	value = get_compopt_value(option, buf2);
1881 |     } else {
1882 | 	for (i=0; compopt[i].name; i++)
1883 | 	    if (strcmp(option, compopt[i].name) == 0) break;
1884 | 
1885 | 	if (compopt[i].name) {
1886 | 	    any.a_int = i + 1 + indexoffset;
1887 | 	    value = get_compopt_value(option, buf2);
1888 | 	} else {
1889 | 	    /* We are trying to add an option not found in compopt[].
1890 | 	       This is almost certainly bad, but we'll let it through anyway
1891 | 	       (with a zero value, so it can't be selected). */
1892 | 	    any.a_int = 0;
1893 | 	}
1894 |     }
1895 |     /* "    " replaces "a - " -- assumes menus follow that style */
1896 |     Sprintf(buf, fmtstr_doset_add_menu, (any.a_int ? "" : "    "), option, value);
1897 |     add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
1898 | }
1899 | 
1900 | /* Changing options via menu by Per Liboriussen */
1901 | int
1902 | doset()
1903 | {
1904 | 	char buf[BUFSZ], buf2[BUFSZ];
1905 | 	int i, pass, boolcount, pick_cnt, pick_idx, opt_indx;
1906 | 	boolean *bool_p;
1907 | 	winid tmpwin;
1908 | 	anything any;
1909 | 	menu_item *pick_list;
1910 | 	int indexoffset, startpass, endpass;
1911 | 	boolean setinitial = FALSE, fromfile = FALSE;
1912 | 	int biggest_name = 0;
1913 | 
1914 | 	tmpwin = create_nhwindow(NHW_MENU);
1915 | 	start_menu(tmpwin);
1916 | 
1917 | 	any.a_void = 0;
1918 | 	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1919 | 		 "Booleans (selecting will toggle value):", MENU_UNSELECTED);
1920 | 	any.a_int = 0;
1921 | 	/* first list any other non-modifiable booleans, then modifiable ones */
1922 | 	for (pass = 0; pass <= 1; pass++)
1923 | 	    for (i = 0; boolopt[i].name; i++)
1924 | 		if ((bool_p = boolopt[i].addr) != 0 &&
1925 | 			(boolopt_only_initial(i) ^ pass)) {
1926 | 		    if (bool_p == &flags.female) continue;  /* obsolete */
1927 | #ifdef WIZARD
1928 | 		    if (bool_p == &iflags.sanity_check && !wizard) continue;
1929 | #endif
1930 | 		    any.a_int = (pass == 0) ? 0 : i + 1;
1931 | 		    Sprintf(buf, "%s%-13s [%s]",
1932 | 			    pass == 0 ? "    " : "",
1933 | 			    boolopt[i].name, *bool_p ? "true" : "false");
1934 | 		    add_menu(tmpwin, NO_GLYPH, &any, 0, 0,
1935 | 			     ATR_NONE, buf, MENU_UNSELECTED);
1936 | 		}
1937 | 
1938 | 	boolcount = i;
1939 | 	indexoffset = boolcount;
1940 | 	any.a_void = 0;
1941 | 	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
1942 | 	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1943 | 		 "Compounds (selecting will prompt for new value):",
1944 | 		 MENU_UNSELECTED);
1945 | 
1946 | 	startpass = DISP_IN_GAME;
1947 | 	endpass = SET_IN_GAME;
1948 | 
1949 | 	/* spin through the options to find the biggest name
1950 |            and adjust the format string accordingly if needed */
1951 | 	biggest_name = 0;
1952 | 	for (i = 0; compopt[i].name; i++)
1953 | 		if (compopt[i].optflags >= startpass && compopt[i].optflags <= endpass &&
1954 | 		    strlen(compopt[i].name) > (unsigned) biggest_name)
1955 | 			biggest_name = (int) strlen(compopt[i].name);
1956 | 	if (biggest_name > 30) biggest_name = 30;
1957 | 	Sprintf(fmtstr_doset_add_menu, "%%s%%-%ds [%%s]", biggest_name);
1958 | 
1959 | 	/* deliberately put `name', `role', `race', `gender' first */
1960 | 	doset_add_menu(tmpwin, "name", 0);
1961 | 	doset_add_menu(tmpwin, "role", 0);
1962 | 	doset_add_menu(tmpwin, "race", 0);
1963 | 	doset_add_menu(tmpwin, "gender", 0);
1964 | 
1965 | 	for (pass = startpass; pass <= endpass; pass++) 
1966 | 	    for (i = 0; compopt[i].name; i++)
1967 | 		if (compopt[i].optflags == pass) {
1968 |  		    	if (!strcmp(compopt[i].name, "name") ||
1969 | 		    	    !strcmp(compopt[i].name, "role") ||
1970 | 		    	    !strcmp(compopt[i].name, "race") ||
1971 | 		    	    !strcmp(compopt[i].name, "gender"))
1972 | 		    	    	continue;
1973 | 		    	else
1974 | 				doset_add_menu(tmpwin, compopt[i].name,
1975 | 					(pass == DISP_IN_GAME) ? 0 : indexoffset);
1976 | 		}
1977 | #ifdef PREFIXES_IN_USE
1978 | 	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
1979 | 	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1980 | 		 "Variable playground locations:", MENU_UNSELECTED);
1981 | 	for (i = 0; i < PREFIX_COUNT; i++)
1982 | 		doset_add_menu(tmpwin, fqn_prefix_names[i], 0);
1983 | #endif
1984 | 	end_menu(tmpwin, "Set what options?");
1985 | 	need_redraw = FALSE;
1986 | 	if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &pick_list)) > 0) {
1987 | 	    /*
1988 | 	     * Walk down the selection list and either invert the booleans
1989 | 	     * or prompt for new values. In most cases, call parseoptions()
1990 | 	     * to take care of options that require special attention, like
1991 | 	     * redraws.
1992 | 	     */
1993 | 	    for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
1994 | 		opt_indx = pick_list[pick_idx].item.a_int - 1;
1995 | 		if (opt_indx < boolcount) {
1996 | 		    /* boolean option */
1997 | 		    Sprintf(buf, "%s%s", *boolopt[opt_indx].addr ? "!" : "",
1998 | 			    boolopt[opt_indx].name);
1999 | 		    parseoptions(buf, setinitial, fromfile);
2000 | 		} else {
2001 | 		    /* compound option */
2002 | 		    opt_indx -= boolcount;
2003 | 
2004 | 		    if (!special_handling(compopt[opt_indx].name,
2005 | 							setinitial, fromfile)) {
2006 | 			Sprintf(buf, "Set %s to what?", compopt[opt_indx].name);
2007 | 			getlin(buf, buf2);
2008 | 			Sprintf(buf, "%s:%s", compopt[opt_indx].name, buf2);
2009 | 			/* pass the buck */
2010 | 			parseoptions(buf, setinitial, fromfile);
2011 | 		    }
2012 | 		}
2013 | 	    }
2014 | 	    free((genericptr_t)pick_list);
2015 | 	    pick_list = (menu_item *)0;
2016 | 	}
2017 | 
2018 | 	destroy_nhwindow(tmpwin);
2019 | 	if (need_redraw)
2020 | 	    (void) doredraw();
2021 | 	return 0;
2022 | }
2023 | 
2024 | STATIC_OVL boolean
2025 | special_handling(optname, setinitial, setfromfile)
2026 | const char *optname;
2027 | boolean setinitial,setfromfile;
2028 | {
2029 |     winid tmpwin;
2030 |     anything any;
2031 |     int i;
2032 |     char buf[BUFSZ];
2033 |     boolean retval = FALSE;
2034 |     
2035 |     /* Special handling of menustyle, pickup_burden, and pickup_types. */
2036 |     if (!strcmp("menustyle", optname)) {
2037 | 	const char *style_name;
2038 | 	menu_item *style_pick = (menu_item *)0;
2039 |         tmpwin = create_nhwindow(NHW_MENU);
2040 | 	start_menu(tmpwin);
2041 | 	for (i = 0; i < SIZE(menutype); i++) {
2042 | 		style_name = menutype[i];
2043 |     		/* note: separate `style_name' variable used
2044 | 		   to avoid an optimizer bug in VAX C V2.3 */
2045 | 		any.a_int = i + 1;
2046 | 		add_menu(tmpwin, NO_GLYPH, &any, *style_name, 0,
2047 | 			 ATR_NONE, style_name, MENU_UNSELECTED);
2048 |         }
2049 | 	end_menu(tmpwin, "Select menustyle:");
2050 | 	if (select_menu(tmpwin, PICK_ONE, &style_pick) > 0) {
2051 | 		flags.menu_style = style_pick->item.a_int - 1;
2052 | 		free((genericptr_t)style_pick);
2053 |         }
2054 | 	destroy_nhwindow(tmpwin);
2055 |         retval = TRUE;
2056 |     } else if (!strcmp("pickup_burden", optname)) {
2057 | 	const char *burden_name, *burden_letters = "ubsntl";
2058 | 	menu_item *burden_pick = (menu_item *)0;
2059 |         tmpwin = create_nhwindow(NHW_MENU);
2060 | 	start_menu(tmpwin);
2061 | 	for (i = 0; i < SIZE(burdentype); i++) {
2062 | 		burden_name = burdentype[i];
2063 | 		any.a_int = i + 1;
2064 | 		add_menu(tmpwin, NO_GLYPH, &any, burden_letters[i], 0,
2065 | 			 ATR_NONE, burden_name, MENU_UNSELECTED);
2066 |         }
2067 | 	end_menu(tmpwin, "Select encumberence level:");
2068 | 	if (select_menu(tmpwin, PICK_ONE, &burden_pick) > 0) {
2069 | 		flags.pickup_burden = burden_pick->item.a_int - 1;
2070 | 		free((genericptr_t)burden_pick);
2071 | 	}
2072 | 	destroy_nhwindow(tmpwin);
2073 | 	retval = TRUE;
2074 |     } else if (!strcmp("pickup_types", optname)) {
2075 | 	/* parseoptions will prompt for the list of types */
2076 | 	parseoptions(strcpy(buf, "pickup_types"), setinitial, setfromfile);
2077 | 	retval = TRUE;
2078 |     }
2079 |     return retval;
2080 | }
2081 | 
2082 | #define rolestring(val,array,field) ((val >= 0) ? array[val].field : \
2083 | 				     (val == ROLE_RANDOM) ? randomrole : none)
2084 | 
2085 | /* This is ugly. We have all the option names in the compopt[] array,
2086 |    but we need to look at each option individually to get the value. */
2087 | STATIC_OVL const char *
2088 | get_compopt_value(optname, buf)
2089 | const char *optname;
2090 | char *buf;
2091 | {
2092 | 	char ocl[MAXOCLASSES+1];
2093 | 	static const char none[] = "(none)", randomrole[] = "random",
2094 | 		     to_be_done[] = "(to be done)";
2095 | #ifdef PREFIXES_IN_USE
2096 | 	int i;
2097 | #endif
2098 | 
2099 | 	buf[0] = '\0';
2100 | 	if (!strcmp(optname,"align"))
2101 | 		Sprintf(buf, "%s", rolestring(flags.initalign, aligns, adj));
2102 | 	else if (!strcmp(optname, "catname")) 
2103 | 		Sprintf(buf, "%s", catname[0] ? catname : none );
2104 | 	else if (!strcmp(optname, "disclose")) 
2105 | 		Sprintf(buf, "%s",
2106 | 			flags.end_disclose[0] ? flags.end_disclose : "all" );
2107 | 	else if (!strcmp(optname, "dogname")) 
2108 | 		Sprintf(buf, "%s", dogname[0] ? dogname : none );
2109 | 	else if (!strcmp(optname, "dungeon"))
2110 | 		Sprintf(buf, "%s", to_be_done);
2111 | 	else if (!strcmp(optname, "effects"))
2112 | 		Sprintf(buf, "%s", to_be_done);
2113 | 	else if (!strcmp(optname, "fruit")) 
2114 | 		Sprintf(buf, "%s", pl_fruit);
2115 | 	else if (!strcmp(optname, "gender"))
2116 | 		Sprintf(buf, "%s", rolestring(flags.initgend, genders, adj));
2117 | 	else if (!strcmp(optname, "horsename")) 
2118 | 		Sprintf(buf, "%s", horsename[0] ? horsename : none);
2119 | 	else if (!strcmp(optname, "menustyle")) 
2120 | 		Sprintf(buf, "%s", menutype[(int)flags.menu_style] );
2121 | 	else if (!strcmp(optname, "menu_deselect_all"))
2122 | 		Sprintf(buf, "%s", to_be_done);
2123 | 	else if (!strcmp(optname, "menu_deselect_page"))
2124 | 		Sprintf(buf, "%s", to_be_done);
2125 | 	else if (!strcmp(optname, "menu_first_page"))
2126 | 		Sprintf(buf, "%s", to_be_done);
2127 | 	else if (!strcmp(optname, "menu_invert_all"))
2128 | 		Sprintf(buf, "%s", to_be_done);
2129 | 	else if (!strcmp(optname, "menu_invert_page"))
2130 | 		Sprintf(buf, "%s", to_be_done);
2131 | 	else if (!strcmp(optname, "menu_last_page"))
2132 | 		Sprintf(buf, "%s", to_be_done);
2133 | 	else if (!strcmp(optname, "menu_next_page"))
2134 | 		Sprintf(buf, "%s", to_be_done);
2135 | 	else if (!strcmp(optname, "menu_previous_page"))
2136 | 		Sprintf(buf, "%s", to_be_done);
2137 | 	else if (!strcmp(optname, "menu_search"))
2138 | 		Sprintf(buf, "%s", to_be_done);
2139 | 	else if (!strcmp(optname, "menu_select_all"))
2140 | 		Sprintf(buf, "%s", to_be_done);
2141 | 	else if (!strcmp(optname, "menu_select_page"))
2142 | 		Sprintf(buf, "%s", to_be_done);
2143 | 	else if (!strcmp(optname, "monsters"))
2144 | 		Sprintf(buf, "%s", to_be_done);
2145 | 	else if (!strcmp(optname, "msghistory"))
2146 | 		Sprintf(buf, "%u", iflags.msg_history);
2147 | 	else if (!strcmp(optname, "name"))
2148 | 		Sprintf(buf, "%s", plname);
2149 | 	else if (!strcmp(optname, "objects"))
2150 | 		Sprintf(buf, "%s", to_be_done);
2151 | 	else if (!strcmp(optname, "packorder")) {
2152 | 		oc_to_str(flags.inv_order, ocl);
2153 | 		Sprintf(buf, "%s", ocl);
2154 | 	     }
2155 | #ifdef CHANGE_COLOR
2156 | 	else if (!strcmp(optname, "palette")) 
2157 | 		Sprintf(buf, "%s", get_color_string());
2158 | #endif
2159 | 	else if (!strcmp(optname, "pettype")) 
2160 | 		Sprintf(buf, "%s", (preferred_pet == 'c') ? "cat" :
2161 | 				(preferred_pet == 'd') ? "dog" : "random" );
2162 | 	else if (!strcmp(optname, "pickup_burden"))
2163 | 		Sprintf(buf, "%s", burdentype[flags.pickup_burden] );
2164 | 	else if (!strcmp(optname, "pickup_types")) {
2165 | 		oc_to_str(flags.pickup_types, ocl);
2166 | 		Sprintf(buf, "%s", ocl[0] ? ocl : "all" );
2167 | 	     }
2168 | 	else if (!strcmp(optname, "race"))
2169 | 		Sprintf(buf, "%s", rolestring(flags.initrace, races, noun));
2170 | 	else if (!strcmp(optname, "role"))
2171 | 		Sprintf(buf, "%s", rolestring(flags.initrole, roles, name.m));
2172 | 	else if (!strcmp(optname, "scores")) {
2173 | 		Sprintf(buf, "%d top/%d around%s", flags.end_top,
2174 | 				flags.end_around, flags.end_own ? "/own" : "");
2175 | 	     }
2176 | #ifdef MSDOS
2177 | 	else if (!strcmp(optname, "soundcard"))
2178 | 		Sprintf(buf, "%s", to_be_done);
2179 | #endif
2180 | 	else if (!strcmp(optname, "suppress_alert")) {
2181 | 	    if (flags.suppress_alert == 0L)
2182 | 		Strcpy(buf, none);
2183 | 	    else
2184 | 		Sprintf(buf, "%lu.%lu.%lu",
2185 | 			FEATURE_NOTICE_VER_MAJ,
2186 | 			FEATURE_NOTICE_VER_MIN,
2187 | 			FEATURE_NOTICE_VER_PATCH);
2188 | 	} else if (!strcmp(optname, "traps"))
2189 | 		Sprintf(buf, "%s", to_be_done);
2190 | #ifdef MSDOS
2191 | 	else if (!strcmp(optname, "video"))
2192 | 		Sprintf(buf, "%s", to_be_done);
2193 | #endif
2194 | #ifdef VIDEOSHADES
2195 | 	else if (!strcmp(optname, "videoshades"))
2196 | 		Sprintf(buf, "%s-%s-%s", shade[0],shade[1],shade[2]);
2197 | 	else if (!strcmp(optname, "videocolors"))
2198 | 		Sprintf(buf, "%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d",
2199 | 			ttycolors[CLR_RED], ttycolors[CLR_GREEN],
2200 | 			ttycolors[CLR_BROWN], ttycolors[CLR_BLUE],
2201 | 			ttycolors[CLR_MAGENTA], ttycolors[CLR_CYAN],
2202 | 			ttycolors[CLR_ORANGE], ttycolors[CLR_BRIGHT_GREEN],
2203 | 			ttycolors[CLR_YELLOW], ttycolors[CLR_BRIGHT_BLUE],
2204 | 			ttycolors[CLR_BRIGHT_MAGENTA],
2205 | 			ttycolors[CLR_BRIGHT_CYAN]);
2206 | #endif /* VIDEOSHADES */
2207 | #if 0
2208 | 	else if (!strcmp(optname, "warnlevel"))
2209 | 		Sprintf(buf, "%d", flags.warnlevel);
2210 | #endif
2211 | 	else if (!strcmp(optname, "windowtype"))
2212 | 		Sprintf(buf, "%s", windowprocs.name);
2213 | #ifdef PREFIXES_IN_USE
2214 | 	else {
2215 | 	    for (i = 0; i < PREFIX_COUNT; ++i)
2216 | 		if (!strcmp(optname, fqn_prefix_names[i]) && fqn_prefix[i])
2217 | 			Sprintf(buf, "%s", fqn_prefix[i]);
2218 | 	}
2219 | #endif
2220 | 
2221 | 	if (buf[0]) return buf;
2222 | 	else return "unknown";
2223 | }
2224 | 
2225 | int
2226 | dotogglepickup()
2227 | {
2228 | 	char buf[BUFSZ], ocl[MAXOCLASSES+1];
2229 | 
2230 | 	flags.pickup = !flags.pickup;
2231 | 	if (flags.pickup) {
2232 | 	    oc_to_str(flags.pickup_types, ocl);
2233 | 	    Sprintf(buf, "ON, for %s objects", ocl[0] ? ocl : "all");
2234 | 	} else {
2235 | 	    Strcpy(buf, "OFF");
2236 | 	}
2237 | 	pline("Autopickup: %s.", buf);
2238 | 	return 0;
2239 | }
2240 | 
2241 | /* data for option_help() */
2242 | static const char *opt_intro[] = {
2243 | 	"",
2244 | 	"                 NetHack Options Help:",
2245 | 	"",
2246 | #define CONFIG_SLOT 3	/* fill in next value at run-time */
2247 | 	(char *)0,
2248 | #if !defined(MICRO) && !defined(MAC)
2249 | 	"or use `NETHACKOPTIONS=\"<options>\"' in your environment",
2250 | #endif
2251 | 	"(<options> is a list of options separated by commas)",
2252 | #ifdef VMS
2253 | 	"-- for example, $ DEFINE NETHACKOPTIONS \"noautopickup,fruit:kumquat\"",
2254 | #endif
2255 | 	"or press \"O\" while playing and use the menu.",
2256 | 	"",
2257 |  "Boolean options (which can be negated by prefixing them with '!' or \"no\"):",
2258 | 	(char *)0
2259 | };
2260 | 
2261 | static const char *opt_epilog[] = {
2262 | 	"",
2263 |  "Some of the options can be set only before the game is started; those",
2264 | 	"items will not be selectable in the 'O' command's menu.",
2265 | 	(char *)0
2266 | };
2267 | 
2268 | void
2269 | option_help()
2270 | {
2271 |     char buf[BUFSZ], buf2[BUFSZ];
2272 |     register int i;
2273 |     winid datawin;
2274 | 
2275 |     datawin = create_nhwindow(NHW_TEXT);
2276 | #ifdef AMIGA
2277 |     if (FromWBench)
2278 | 	Sprintf(buf, "Set options as OPTIONS= in %s or in icon", configfile);
2279 |     else
2280 | #endif
2281 | 	Sprintf(buf, "Set options as OPTIONS=<options> in %s", configfile);
2282 |     opt_intro[CONFIG_SLOT] = (const char *) buf;
2283 |     for (i = 0; opt_intro[i]; i++)
2284 | 	putstr(datawin, 0, opt_intro[i]);
2285 | 
2286 |     /* Boolean options */
2287 |     for (i = 0; boolopt[i].name; i++) {
2288 | 	if (boolopt[i].addr) {
2289 | #ifdef WIZARD
2290 | 	    if (boolopt[i].addr == &iflags.sanity_check && !wizard) continue;
2291 | #endif
2292 | 	    next_opt(datawin, boolopt[i].name);
2293 | 	}
2294 |     }
2295 |     next_opt(datawin, "");
2296 | 
2297 |     /* Compound options */
2298 |     putstr(datawin, 0, "Compound options:");
2299 |     for (i = 0; compopt[i].name; i++) {
2300 | 	Sprintf(buf2, "`%s'", compopt[i].name);
2301 | 	Sprintf(buf, "%-20s - %s%c", buf2, compopt[i].descr,
2302 | 		compopt[i+1].name ? ',' : '.');
2303 | 	putstr(datawin, 0, buf);
2304 |     }
2305 | 
2306 |     for (i = 0; opt_epilog[i]; i++)
2307 | 	putstr(datawin, 0, opt_epilog[i]);
2308 | 
2309 |     display_nhwindow(datawin, FALSE);
2310 |     destroy_nhwindow(datawin);
2311 |     return;
2312 | }
2313 | 
2314 | /*
2315 |  * prints the next boolean option, on the same line if possible, on a new
2316 |  * line if not. End with next_opt("").
2317 |  */
2318 | void
2319 | next_opt(datawin, str)
2320 | winid datawin;
2321 | const char *str;
2322 | {
2323 | 	static char *buf = 0;
2324 | 	int i;
2325 | 	char *s;
2326 | 
2327 | 	if (!buf) *(buf = (char *)alloc(BUFSZ)) = '\0';
2328 | 
2329 | 	if (!*str) {
2330 | 		s = eos(buf);
2331 | 		if (s > &buf[1] && s[-2] == ',')
2332 | 		    Strcpy(s - 2, ".");	/* replace last ", " */
2333 | 		i = COLNO;	/* (greater than COLNO - 2) */
2334 | 	} else {
2335 | 		i = strlen(buf) + strlen(str) + 2;
2336 | 	}
2337 | 
2338 | 	if (i > COLNO - 2) { /* rule of thumb */
2339 | 		putstr(datawin, 0, buf);
2340 | 		buf[0] = 0;
2341 | 	}
2342 | 	if (*str) {
2343 | 		Strcat(buf, str);
2344 | 		Strcat(buf, ", ");
2345 | 	} else {
2346 | 		putstr(datawin, 0, str);
2347 | 		free(buf),  buf = 0;
2348 | 	}
2349 | 	return;
2350 | }
2351 | 
2352 | /* Returns the fid of the fruit type; if that type already exists, it
2353 |  * returns the fid of that one; if it does not exist, it adds a new fruit
2354 |  * type to the chain and returns the new one.
2355 |  */
2356 | int
2357 | fruitadd(str)
2358 | char *str;
2359 | {
2360 | 	register int i;
2361 | 	register struct fruit *f;
2362 | 	struct fruit *lastf = 0;
2363 | 	int highest_fruit_id = 0;
2364 | 	char buf[PL_FSIZ];
2365 | 	boolean user_specified = (str == pl_fruit);
2366 | 	/* if not user-specified, then it's a fruit name for a fruit on
2367 | 	 * a bones level...
2368 | 	 */
2369 | 
2370 | 	/* Note: every fruit has an id (spe for fruit objects) of at least
2371 | 	 * 1; 0 is an error.
2372 | 	 */
2373 | 	if (user_specified) {
2374 | 		/* disallow naming after other foods (since it'd be impossible
2375 | 		 * to tell the difference)
2376 | 		 */
2377 | 
2378 | 		boolean found = FALSE, numeric = FALSE;
2379 | 
2380 | 		for (i = bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS;
2381 | 						i++) {
2382 | 			if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) {
2383 | 				found = TRUE;
2384 | 				break;
2385 | 			}
2386 | 		}
2387 | 		{
2388 | 		    char *c;
2389 | 
2390 | 		    c = pl_fruit;
2391 | 
2392 | 		    for(c = pl_fruit; *c >= '0' && *c <= '9'; c++)
2393 | 			;
2394 | 		    if (isspace(*c) || *c == 0) numeric = TRUE;
2395 | 		}
2396 | 		if (found || numeric ||
2397 | 		    !strncmp(str, "cursed ", 7) ||
2398 | 		    !strncmp(str, "uncursed ", 9) ||
2399 | 		    !strncmp(str, "blessed ", 8) ||
2400 | 		    !strncmp(str, "partly eaten ", 13) ||
2401 | 		    (!strncmp(str, "tin of ", 7) &&
2402 | 			(!strcmp(str+7, "spinach") ||
2403 | 			 name_to_mon(str+7) >= LOW_PM)) ||
2404 | 		    !strcmp(str, "empty tin") ||
2405 | 		    ((!strncmp(eos(str)-7," corpse",7) ||
2406 | 			    !strncmp(eos(str)-4, " egg",4)) &&
2407 | 			name_to_mon(str) >= LOW_PM))
2408 | 			{
2409 | 				Strcpy(buf, pl_fruit);
2410 | 				Strcpy(pl_fruit, "candied ");
2411 | 				nmcpy(pl_fruit+8, buf, PL_FSIZ-8);
2412 | 		}
2413 | 	}
2414 | 	for(f=ffruit; f; f = f->nextf) {
2415 | 		lastf = f;
2416 | 		if(f->fid > highest_fruit_id) highest_fruit_id = f->fid;
2417 | 		if(!strncmp(str, f->fname, PL_FSIZ))
2418 | 			goto nonew;
2419 | 	}
2420 | 	/* if adding another fruit would overflow spe, use a random
2421 | 	   fruit instead... we've got a lot to choose from. */
2422 | 	if (highest_fruit_id >= 127) return rnd(127);
2423 | 	highest_fruit_id++;
2424 | 	f = newfruit();
2425 | 	if (ffruit) lastf->nextf = f;
2426 | 	else ffruit = f;
2427 | 	Strcpy(f->fname, str);
2428 | 	f->fid = highest_fruit_id;
2429 | 	f->nextf = 0;
2430 | nonew:
2431 | 	if (user_specified) current_fruit = highest_fruit_id;
2432 | 	return f->fid;
2433 | }
2434 | 
2435 | /*
2436 |  * This is a somewhat generic menu for taking a list of NetHack style
2437 |  * class choices and presenting them via a description
2438 |  * rather than the traditional NetHack characters.
2439 |  * (Benefits users whose first exposure to NetHack is via tiles).
2440 |  *
2441 |  * prompt
2442 |  *	     The title at the top of the menu.
2443 |  *
2444 |  * category: 0 = monster class
2445 |  *           1 = object  class
2446 |  *
2447 |  * way
2448 |  *	     FALSE = PICK_ONE, TRUE = PICK_ANY
2449 |  *
2450 |  * class_list
2451 |  *	     a null terminated string containing the list of choices.
2452 |  *
2453 |  * class_selection
2454 |  *	     a null terminated string containing the selected characters.
2455 |  *
2456 |  * Returns number selected.
2457 |  */
2458 | int
2459 | choose_classes_menu(prompt, category, way, class_list, class_select)
2460 | const char *prompt;
2461 | int category;
2462 | boolean way;
2463 | char *class_list;
2464 | char *class_select;
2465 | {
2466 |     menu_item *pick_list = (menu_item *)0;
2467 |     winid win;
2468 |     anything any;
2469 |     char buf[BUFSZ];
2470 |     int i, n;
2471 |     int ret;
2472 |     int next_accelerator, accelerator;
2473 | 
2474 |     if (class_list == (char *)0 || class_select == (char *)0) return 0;
2475 |     accelerator = 0;
2476 |     next_accelerator = 'a';
2477 |     any.a_void = 0;
2478 |     win = create_nhwindow(NHW_MENU);
2479 |     start_menu(win);
2480 |     while (*class_list) {
2481 | 	const char *text;
2482 | 	boolean selected;
2483 | 
2484 | 	text = (char *)0;
2485 | 	selected = FALSE;
2486 | 	switch (category) {
2487 | 		case 0:
2488 | 			text = monexplain[def_char_to_monclass(*class_list)];
2489 | 			accelerator = *class_list;
2490 | 			Sprintf(buf, "%s", text);
2491 | 			break;
2492 | 		case 1:
2493 | 			text = objexplain[def_char_to_objclass(*class_list)];
2494 | 			accelerator = next_accelerator;
2495 | 			Sprintf(buf, "%c  %s", *class_list, text);
2496 | 			break;
2497 | 		default:
2498 | 			impossible("choose_classes_menu: invalid category %d",
2499 | 					category);
2500 | 	}
2501 | 	if (way && *class_select) {	/* Selections there already */
2502 | 		if (index(class_select, *class_list)) {
2503 | 			selected = TRUE;
2504 | 		}
2505 | 	}
2506 | 	any.a_int = *class_list;
2507 | 	add_menu(win, NO_GLYPH, &any, accelerator,
2508 | 		  category ? *class_list : 0,
2509 | 		  ATR_NONE, buf, selected);
2510 | 	++class_list;
2511 | 	if (category > 0) {
2512 | 		++next_accelerator;
2513 | 		if (next_accelerator == ('z' + 1)) next_accelerator = 'A';
2514 | 		if (next_accelerator == ('Z' + 1)) break;
2515 | 	}
2516 |     }
2517 |     end_menu(win, prompt);
2518 |     n = select_menu(win, way ? PICK_ANY : PICK_ONE, &pick_list);
2519 |     destroy_nhwindow(win);
2520 |     if (n > 0) {
2521 | 	for (i = 0; i < n; ++i)
2522 | 	    *class_select++ = (char)pick_list[i].item.a_int;
2523 | 	free((genericptr_t)pick_list);
2524 | 	ret = n;
2525 |     } else if (n == -1) {
2526 | 	class_select = eos(class_select);
2527 | 	ret = -1;
2528 |     } else
2529 | 	ret = 0;
2530 |     *class_select = '\0';
2531 |     return ret;
2532 | }
2533 | 
2534 | #endif	/* OPTION_LISTS_ONLY */
2535 | 
2536 | /*options.c*/