Source:NetHack 3.2.0/options.c

From NetHackWiki
(Redirected from NetHack 3.2.0/options.c)
Jump to navigation Jump to search

Below is the full text to options.c from the source code of NetHack 3.2.0. To link to a particular line, write [[NetHack 3.2.0/options.c#line123]], for example.

Warning! This is the source code from an old release. For the latest release, see Source code

The NetHack General Public License applies to screenshots, source code and other content from NetHack.

This content was modified from the original NetHack source code distribution (by splitting up NetHack content between wiki pages, and possibly further editing). See the page history for a list of who changed it, and on what dates.

1.    /*	SCCS Id: @(#)options.c	3.2	96/02/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.   #define static
11.   #else
12.   #include "hack.h"
13.   #include "termcap.h"
14.   #include <ctype.h>
15.   #endif
16.   
17.   #define WINTYPELEN 16
18.   
19.   /*
20.    *  NOTE:  If you add (or delete) an option, please update the short
21.    *  options help (option_help()), the long options help (dat/opthelp),
22.    *  and the current options setting display function (doset()),
23.    *  and also the Guidebooks.
24.    */
25.   
26.   static struct Bool_Opt
27.   {
28.   	const char *name;
29.   	boolean	*addr, initvalue;
30.   } boolopt[] = {
31.   #ifdef AMIGA
32.   	{"altmeta", &flags.altmeta, TRUE},
33.   #else
34.   	{"altmeta", (boolean *)0, TRUE},
35.   #endif
36.   #ifdef MFLOPPY
37.   	{"asksavedisk", &flags.asksavedisk, FALSE},
38.   #else
39.   	{"asksavedisk", (boolean *)0, FALSE},
40.   #endif
41.   	{"autopickup", &flags.pickup, TRUE},
42.   #if defined(MICRO) && !defined(AMIGA)
43.   	{"BIOS", &flags.BIOS, FALSE},
44.   #else
45.   	{"BIOS", (boolean *)0, FALSE},
46.   #endif
47.   #ifdef INSURANCE
48.   	{"checkpoint", &flags.ins_chkpt, TRUE},
49.   #else
50.   	{"checkpoint", (boolean *)0, FALSE},
51.   #endif
52.   #ifdef TEXTCOLOR
53.   # ifdef MICRO
54.   	{"color", &flags.use_color, TRUE},
55.   # else	/* systems that support multiple terminals, many monochrome */
56.   	{"color", &flags.use_color, FALSE},
57.   # endif
58.   #else
59.   	{"color", (boolean *)0, FALSE},
60.   #endif
61.   	{"confirm",&flags.confirm, TRUE},
62.   #ifdef TERMLIB
63.   	{"DECgraphics", &flags.DECgraphics, FALSE},
64.   #else
65.   	{"DECgraphics", (boolean *)0, FALSE},
66.   #endif
67.   #ifdef OPT_DISPMAP
68.   	{"fast_map", &flags.fast_map, TRUE},
69.   #else
70.   	{"fast_map", (boolean *)0, TRUE},
71.   #endif
72.   	{"female", &flags.female, FALSE},
73.   	{"fixinv", &flags.invlet_constant, TRUE},
74.   #ifdef AMIFLUSH
75.   	{"flush", &flags.amiflush, FALSE},
76.   #else
77.   	{"flush", (boolean *)0, FALSE},
78.   #endif
79.   	{"help", &flags.help, TRUE},
80.   #ifdef TEXTCOLOR
81.   	{"hilite_pet", &flags.hilite_pet, FALSE},
82.   #else
83.   	{"hilite_pet", (boolean *)0, FALSE},
84.   #endif
85.   #ifdef ASCIIGRAPH
86.   	{"IBMgraphics", &flags.IBMgraphics, FALSE},
87.   #else
88.   	{"IBMgraphics", (boolean *)0, FALSE},
89.   #endif
90.   	{"ignintr", &flags.ignintr, FALSE},
91.   #ifdef MAC_GRAPHICS_ENV
92.   	{"large_font", &flags.large_font, FALSE},
93.   #else
94.   	{"large_font", (boolean *)0, FALSE},
95.   #endif
96.   	{"legacy",&flags.legacy, TRUE},
97.   	{"lit_corridor", &flags.lit_corridor, FALSE},
98.   #ifdef MAC_GRAPHICS_ENV
99.   	{"Macgraphics", &flags.MACgraphics, TRUE},
100.  #else
101.  	{"Macgraphics", (boolean *)0, FALSE},
102.  #endif
103.  #ifdef MAIL
104.  	{"mail", &flags.biff, TRUE},
105.  #else
106.  	{"mail", (boolean *)0, TRUE},
107.  #endif
108.  #ifdef NEWS
109.  	{"news", &flags.news, TRUE},
110.  #else
111.  	{"news", (boolean *)0, FALSE},
112.  #endif
113.  	{"null", &flags.null, TRUE},
114.  	{"number_pad", &flags.num_pad, FALSE},
115.  #ifdef MAC
116.  	{"page_wait", &flags.page_wait, TRUE},
117.  #else
118.  	{"page_wait", (boolean *)0, FALSE},
119.  #endif
120.  	{"perm_invent", &flags.perm_invent, FALSE},
121.  #ifdef MAC
122.  	{"popup_dialog", &flags.popup_dialog, FALSE},
123.  #else
124.  	{"popup_dialog", (boolean *)0, FALSE},
125.  #endif
126.  #if defined(MICRO) && !defined(AMIGA)
127.  	{"rawio", &flags.rawio, FALSE},
128.  #else
129.  	{"rawio", (boolean *)0, FALSE},
130.  #endif
131.  	{"rest_on_space", &flags.rest_on_space, FALSE},
132.  	{"safe_pet", &flags.safe_dog, TRUE},
133.  #ifdef WIZARD
134.  	{"sanity_check", &flags.sanity_check, FALSE},
135.  #else
136.  	{"sanity_check", (boolean *)0, FALSE},
137.  #endif
138.  #ifdef EXP_ON_BOTL
139.  	{"showexp", &flags.showexp, FALSE},
140.  #else
141.  	{"showexp", (boolean *)0, FALSE},
142.  #endif
143.  #ifdef SCORE_ON_BOTL
144.  	{"showscore", &flags.showscore, FALSE},
145.  #else
146.  	{"showscore", (boolean *)0, FALSE},
147.  #endif
148.  	{"silent", &flags.silent, TRUE},
149.  	{"sortpack", &flags.sortpack, TRUE},
150.  	{"sound", &flags.soundok, TRUE},
151.  	{"standout", &flags.standout, FALSE},
152.  	{"time", &flags.time, FALSE},
153.  #ifdef TIMED_DELAY
154.  	{"timed_delay", &flags.nap, TRUE},
155.  #else
156.  	{"timed_delay", (boolean *)0, FALSE},
157.  #endif
158.  	{"tombstone",&flags.tombstone, TRUE},
159.  	{"toptenwin",&flags.toptenwin, FALSE},
160.  	{"verbose", &flags.verbose, TRUE},
161.  	{(char *)0, (boolean *)0, FALSE}
162.  };
163.  
164.  /* compound options, for option_help() and external programs like Amiga
165.   * frontend */
166.  static struct Comp_Opt
167.  {
168.  	const char *name, *descr;
169.  	int size;	/* for frontends and such allocating space --
170.  			 * usually allowed size of data in game, but
171.  			 * occasionally maximum reasonable size for
172.  			 * typing when game maintains information in
173.  			 * a different format */
174.  } compopt[] = {
175.  	{ "catname",  "the name of your (first) cat (e.g., catname:Tabby),",
176.  						PL_PSIZ },
177.  	{ "disclose", "the kinds of information to disclose at end of game,",
178.  						sizeof(flags.end_disclose) },
179.  	{ "dogname",  "the name of your (first) dog (e.g., dogname:Fang),",
180.  						PL_PSIZ },
181.  	{ "dungeon",  "the symbols to use in drawing the dungeon map,",
182.  						MAXDCHARS+1 },
183.  	{ "effects",  "the symbols to use in drawing special effects,",
184.  						MAXECHARS+1 },
185.  	{ "fruit",    "the name of a fruit you enjoy eating,", PL_FSIZ },
186.  	{ "menustyle", "user interface for object selection,", MENUTYPELEN },
187.  	{ "monsters", "the symbols to use for monsters,", MAXMCLASSES },
188.  	{ "msghistory", "number of top line messages to save,", 5 },
189.  	{ "name",     "your character's name (e.g., name:Merlin-W),", PL_NSIZ },
190.  	{ "objects",  "the symbols to use for objects,", MAXOCLASSES },
191.  	{ "packorder", "the inventory order of the items in your pack,",
192.  						MAXOCLASSES },
193.  #ifdef CHANGE_COLOR
194.  	{ "palette",  "palette (00c/880/-fff is blue/yellow/reverse white),",
195.  						15 },
196.  # if defined(MAC)
197.  	{ "hicolor",  "same as palette, only order is reversed,", 15 },
198.  # endif
199.  #endif
200.  	{ "pettype",  "your preferred initial pet type,", 4 },
201.  	{ "pickup_types", "types of objects to pick up automatically,",
202.  						MAXOCLASSES },
203.  	{ "scores",   "the parts of the score list you wish to see,", 32 },
204.  #ifdef MSDOS
205.  	{ "soundcard", "type of sound card to use,", 20 },
206.  #endif
207.  	{ "traps",    "the symbols to use in drawing traps,", MAXTCHARS+1 },
208.  #ifdef MSDOS
209.  	{ "video",    "method of video updating,", 20 },
210.  #endif
211.  #ifdef VIDEOSHADES
212.  	{ "videocolors", "color mappings for internal screen routines,", 40 },
213.  	{ "videoshades", "gray shades to map to black/gray/white,", 32 },
214.  #endif
215.  	{ "windowtype", "windowing system to use.", WINTYPELEN },
216.  	{ (char *)0, (char *)0, 0 }
217.  };
218.  
219.  #ifdef OPTION_LISTS_ONLY
220.  #undef static
221.  
222.  #else	/* use rest of file */
223.  
224.  static boolean need_redraw; /* for doset() */
225.  
226.  #if defined(TOS) && defined(TEXTCOLOR)
227.  extern boolean colors_changed;	/* in tos.c */
228.  #endif
229.  
230.  #ifdef VIDEOSHADES
231.  extern char *shade[3];		  /* in sys/msdos/video.c */
232.  extern char ttycolors[CLR_MAX];	/* in sys/msdos/video.c */
233.  #endif
234.  
235.  extern const char *roles[];	/* from u_init.c */
236.  
237.  static char def_inv_order[MAXOCLASSES] = {
238.  	GOLD_CLASS, AMULET_CLASS, WEAPON_CLASS, ARMOR_CLASS, FOOD_CLASS,
239.  	SCROLL_CLASS, SPBOOK_CLASS, POTION_CLASS, RING_CLASS, WAND_CLASS,
240.  	TOOL_CLASS, GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, 0,
241.  };
242.  
243.  static boolean initial, from_file;
244.  
245.  static void FDECL(nmcpy, (char *, const char *, int));
246.  static void FDECL(escapes, (const char *, char *));
247.  static void FDECL(rejectoption, (const char *));
248.  static void FDECL(badoption, (const char *));
249.  static char *FDECL(string_for_opt, (char *,BOOLEAN_P));
250.  static char *FDECL(string_for_env_opt, (const char *, char *,BOOLEAN_P));
251.  static void FDECL(bad_negation, (const char *,BOOLEAN_P));
252.  static int FDECL(change_inv_order, (char *));
253.  static void FDECL(oc_to_str, (char *, char *));
254.  static void FDECL(graphics_opts, (char *,const char *,int,int));
255.  
256.  void
257.  initoptions()
258.  {
259.  	register char *opts;
260.  	int i;
261.  
262.  	for (i = 0; boolopt[i].name; i++) {
263.  		if (boolopt[i].addr)
264.  			*(boolopt[i].addr) = boolopt[i].initvalue;
265.  	}
266.  	flags.end_own = FALSE;
267.  	flags.end_top = 3;
268.  	flags.end_around = 2;
269.  	flags.msg_history = 20;
270.  
271.  	/* Set the default monster and object class symbols.  Don't use */
272.  	/* memcpy() --- sizeof char != sizeof uchar on some machines.	*/
273.  	for (i = 0; i < MAXOCLASSES; i++)
274.  		oc_syms[i] = (uchar) def_oc_syms[i];
275.  	for (i = 0; i < MAXMCLASSES; i++)
276.  		monsyms[i] = (uchar) def_monsyms[i];
277.  
278.       /* assert( sizeof flags.inv_order == sizeof def_inv_order ); */
279.  	(void)memcpy((genericptr_t)flags.inv_order,
280.  		     (genericptr_t)def_inv_order, sizeof flags.inv_order);
281.  	flags.pickup_types[0] = '\0';
282.  
283.  	switch_graphics(ASCII_GRAPHICS);	/* set default characters */
284.  #if defined(UNIX) && defined(TTY_GRAPHICS)
285.  	/*
286.  	 * Set defaults for some options depending on what we can
287.  	 * detect about the environment's capabilities.
288.  	 * This has to be done after the global initialization above
289.  	 * and before reading user-specific initialization via
290.  	 * config file/environment variable below.
291.  	 */
292.  	/* this detects the IBM-compatible console on most 386 boxes */
293.  	if (!strncmp(getenv("TERM"), "AT", 2)) {
294.  		switch_graphics(IBM_GRAPHICS);
295.  # ifdef TEXTCOLOR
296.  		flags.use_color = TRUE;
297.  # endif
298.  	}
299.  #endif /* UNIX && TTY_GRAPHICS */
300.  #if defined(UNIX) || defined(VMS)
301.  # ifdef TTY_GRAPHICS
302.  	/* detect whether a "vt" terminal can handle alternate charsets */
303.  	if (!strncmpi(getenv("TERM"), "vt", 2) && (AS && AE) &&
304.  	    index(AS, '\016') && index(AE, '\017')) {
305.  		switch_graphics(DEC_GRAPHICS);
306.  	}
307.  # endif
308.  #endif /* UNIX || VMS */
309.  
310.  #ifdef MAC_GRAPHICS_ENV
311.  	switch_graphics(MAC_GRAPHICS);
312.  #endif /* MAC_GRAPHICS_ENV */
313.  	flags.menu_style = MENU_FULL;
314.  
315.  	/* since this is done before init_objects(), do partial init here */
316.  	objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD;
317.  	nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
318.  	opts = getenv("NETHACKOPTIONS");
319.  	if (!opts) opts = getenv("HACKOPTIONS");
320.  	if (opts) {
321.  		if (*opts == '/' || *opts == '\\' || *opts == '@') {
322.  			if (*opts == '@') opts++;	/* @filename */
323.  			/* looks like a filename */
324.  			read_config_file(opts);
325.  		} else {
326.  			read_config_file((char *)0);
327.  			parseoptions(opts, TRUE, FALSE);
328.  		}
329.  	} else {
330.  		read_config_file((char *)0);
331.  	}
332.  #ifdef AMIGA
333.  	ami_wbench_init();	/* must be here or can't set fruit */
334.  #endif
335.  	(void)fruitadd(pl_fruit);
336.  	/* Remove "slime mold" from list of object names; this will	*/
337.  	/* prevent it from being wished unless it's actually present	*/
338.  	/* as a named (or default) fruit.  Wishing for "fruit" will	*/
339.  	/* result in the player's preferred fruit [better than "\033"].	*/
340.  	obj_descr[SLIME_MOLD].oc_name = "fruit";
341.  
342.  	if (flags.female)  {	/* should have been set in NETHACKOPTIONS */
343.  		roles[2] = "Cavewoman";
344.  		roles[6] = "Priestess";
345.  	}
346.  }
347.  
348.  static void
349.  nmcpy(dest, src, maxlen)
350.  	char	*dest;
351.  	const char *src;
352.  	int	maxlen;
353.  {
354.  	int	count;
355.  
356.  	for(count = 1; count < maxlen; count++) {
357.  		if(*src == ',' || *src == '\0') break; /*exit on \0 terminator*/
358.  		*dest++ = *src++;
359.  	}
360.  	*dest = 0;
361.  }
362.  
363.  /*
364.   * escapes: escape expansion for showsyms. C-style escapes understood include
365.   * \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal). The ^-prefix
366.   * for control characters is also understood, and \[mM] followed by any of the
367.   * previous forms or by a character has the effect of 'meta'-ing the value (so
368.   * that the alternate character set will be enabled).
369.   */
370.  static void
371.  escapes(cp, tp)
372.  const char	*cp;
373.  char *tp;
374.  {
375.      while (*cp)
376.      {
377.  	int	cval = 0, meta = 0;
378.  
379.  	if (*cp == '\\' && index("mM", cp[1])) {
380.  		meta = 1;
381.  		cp += 2;
382.  	}
383.  	if (*cp == '\\' && index("0123456789xXoO", cp[1]))
384.  	{
385.  	    const char *dp, *hex = "00112233445566778899aAbBcCdDeEfF";
386.  	    int dcount = 0;
387.  
388.  	    cp++;
389.  	    if (*cp == 'x' || *cp == 'X')
390.  		for (++cp; (dp = index(hex, *cp)) && (dcount++ < 2); cp++)
391.  		    cval = (cval * 16) + (dp - hex) / 2;
392.  	    else if (*cp == 'o' || *cp == 'O')
393.  		for (++cp; (index("01234567",*cp)) && (dcount++ < 3); cp++)
394.  		    cval = (cval * 8) + (*cp - '0');
395.  	    else
396.  		for (; (index("0123456789",*cp)) && (dcount++ < 3); cp++)
397.  		    cval = (cval * 10) + (*cp - '0');
398.  	}
399.  	else if (*cp == '\\')		/* C-style character escapes */
400.  	{
401.  	    switch (*++cp)
402.  	    {
403.  	    case '\\': cval = '\\'; break;
404.  	    case 'n': cval = '\n'; break;
405.  	    case 't': cval = '\t'; break;
406.  	    case 'b': cval = '\b'; break;
407.  	    case 'r': cval = '\r'; break;
408.  	    default: cval = *cp;
409.  	    }
410.  	    cp++;
411.  	}
412.  	else if (*cp == '^')		/* expand control-character syntax */
413.  	{
414.  	    cval = (*++cp & 0x1f);
415.  	    cp++;
416.  	}
417.  	else
418.  	    cval = *cp++;
419.  	if (meta)
420.  	    cval |= 0x80;
421.  	*tp++ = cval;
422.      }
423.      *tp = '\0';
424.  }
425.  
426.  static void
427.  rejectoption(optname)
428.  const char *optname;
429.  {
430.  #ifdef MICRO
431.  # ifdef AMIGA
432.  	if(FromWBench){
433.  		pline("\"%s\" settable only from %s or in icon.",
434.  			optname, configfile);
435.  	} else
436.  # endif
437.  		pline("\"%s\" settable only from %s.", optname, configfile);
438.  #else
439.  	pline("%s can be set only from NETHACKOPTIONS or %s.", optname,
440.  			configfile);
441.  #endif
442.  }
443.  
444.  static void
445.  badoption(opts)
446.  const char *opts;
447.  {
448.  	if (!initial) {
449.  	    if (!strncmp(opts, "h", 1) || !strncmp(opts, "?", 1))
450.  		option_help();
451.  	    else
452.  		pline("Bad syntax: %s.  Enter \"?g\" for help.", opts);
453.  	    return;
454.  	}
455.  # ifdef AMIGA
456.  	if(ami_wbench_badopt(opts)) {
457.  # endif
458.  	if(from_file)
459.  	    raw_printf("Bad syntax in OPTIONS in %s: %s.", configfile, opts);
460.  	else
461.  	    raw_printf("Bad syntax in NETHACKOPTIONS: %s.", opts);
462.  # ifdef AMIGA
463.  	}
464.  # endif
465.  	wait_synch();
466.  }
467.  
468.  static char *
469.  string_for_opt(opts, val_optional)
470.  char *opts;
471.  boolean val_optional;
472.  {
473.  	register char *colon;
474.  
475.  	colon = index(opts, ':');
476.  	if (!colon || !*++colon) {
477.  		if (!val_optional) badoption(opts);
478.  		return (char *)0;
479.  	}
480.  	return colon;
481.  }
482.  
483.  static char *
484.  string_for_env_opt(optname, opts, val_optional)
485.  const char *optname;
486.  char *opts;
487.  boolean val_optional;
488.  {
489.  	if(!initial) {
490.  		rejectoption(optname);
491.  		return (char *)0;
492.  	}
493.  	return string_for_opt(opts, val_optional);
494.  }
495.  
496.  static void
497.  bad_negation(optname, with_parameter)
498.  const char *optname;
499.  boolean with_parameter;
500.  {
501.  	pline_The("%s option may not %sbe negated.",
502.  		optname,
503.  		with_parameter ? "both have a value and " : "");
504.  }
505.  
506.  /*
507.   * Change the inventory order, using the given string as the new order.
508.   * Missing characters in the new order are filled in at the end from
509.   * the current inv_order, except for gold, which is forced to be first
510.   * if not explicitly present.
511.   *
512.   * This routine returns 1 unless there is a duplicate or bad char in
513.   * the string.
514.   */
515.  static int
516.  change_inv_order(op)
517.  char *op;
518.  {
519.      int oc_sym, num;
520.      char *sp, buf[BUFSZ];
521.  
522.      num = 0;
523.      if (!index(op, GOLD_SYM))
524.  	buf[num++] = GOLD_CLASS;
525.  
526.      for (sp = op; *sp; sp++) {
527.  	oc_sym = def_char_to_objclass(*sp);
528.  	/* reject bad or duplicate entries */
529.  	if (oc_sym == MAXOCLASSES ||
530.  		oc_sym == RANDOM_CLASS || oc_sym == ILLOBJ_CLASS ||
531.  		!index(flags.inv_order, oc_sym) || index(sp+1, *sp))
532.  	    return 0;
533.  	/* retain good ones */
534.  	buf[num++] = (char) oc_sym;
535.      }
536.      buf[num] = '\0';
537.  
538.      /* fill in any omitted classes, using previous ordering */
539.      for (sp = flags.inv_order; *sp; sp++)
540.  	if (!index(buf, *sp)) {
541.  	    buf[num++] = *sp;
542.  	    buf[num] = '\0';	/* explicitly terminate for next index() */
543.  	}
544.  
545.      Strcpy(flags.inv_order, buf);
546.      return 1;
547.  }
548.  
549.  static void
550.  graphics_opts(opts, optype, maxlen, offset)
551.  register char *opts;
552.  const char *optype;
553.  int maxlen, offset;
554.  {
555.  	uchar translate[MAXPCHARS+1];
556.  	int length, i;
557.  
558.  	if (!(opts = string_for_env_opt(optype, opts, FALSE)))
559.  		return;
560.  	escapes(opts, opts);
561.  
562.  	length = strlen(opts);
563.  	if (length > maxlen) length = maxlen;
564.  	/* match the form obtained from PC configuration files */
565.  	for (i = 0; i < length; i++)
566.  		translate[i] = (uchar) opts[i];
567.  	assign_graphics(translate, length, maxlen, offset);
568.  }
569.  
570.  void
571.  parseoptions(opts, tinitial, tfrom_file)
572.  register char *opts;
573.  boolean tinitial, tfrom_file;
574.  {
575.  	register char *op;
576.  	unsigned num;
577.  	boolean negated;
578.  	int i;
579.  	char tbuf[MAXOCLASSES + 1];
580.  	const char *fullname;
581.  
582.  	initial = tinitial;
583.  	from_file = tfrom_file;
584.  	if ((op = index(opts, ',')) != 0) {
585.  		*op++ = 0;
586.  		parseoptions(op, initial, from_file);
587.  	}
588.  
589.  	/* strip leading and trailing white space */
590.  	while (isspace(*opts)) opts++;
591.  	op = eos(opts);
592.  	while (--op >= opts && isspace(*op)) *op = '\0';
593.  
594.  	if (!*opts) return;
595.  	negated = FALSE;
596.  	while ((*opts == '!') || !strncmpi(opts, "no", 2)) {
597.  		if (*opts == '!') opts++; else opts += 2;
598.  		negated = !negated;
599.  	}
600.  
601.  	/* variant spelling */
602.  
603.  	if (!strcmpi(opts, "colour"))
604.  		Strcpy(opts, "color");	/* fortunately this is shorter */
605.  
606.  	/* special boolean options */
607.  
608.  	if (!strncmpi(opts, "female", 3)) {
609.  		if(!initial && flags.female == negated)
610.  			pline("That is not anatomically possible.");
611.  		else
612.  			flags.female = !negated;
613.  		return;
614.  	}
615.  
616.  	if (!strncmpi(opts, "male", 4)) {
617.  		if(!initial && flags.female != negated)
618.  			pline("That is not anatomically possible.");
619.  		else
620.  			flags.female = negated;
621.  		return;
622.  	}
623.  
624.  #if defined(MICRO) && !defined(AMIGA)
625.  	/* included for compatibility with old NetHack.cnf files */
626.  	if (!strncmp(opts, "IBM_", 4)) {
627.  		flags.BIOS = !negated;
628.  		return;
629.  	}
630.  #endif /* MICRO */
631.  
632.  	/* compound options */
633.  	
634.  	fullname = "pettype";
635.  	if (!strncmpi(opts, fullname, 3)) {
636.  		if ((op = string_for_env_opt(fullname, opts, negated)) != 0) {
637.  		    if (negated) bad_negation(fullname, TRUE);
638.  		    else switch (*op) {
639.  			case 'd':	/* dog */
640.  			case 'D':
641.  			    preferred_pet = 'd';
642.  			    break;
643.  			case 'c':	/* cat */
644.  			case 'C':
645.  			case 'f':	/* feline */
646.  			case 'F':
647.  			    preferred_pet = 'c';
648.  			    break;
649.  			default:
650.  			    pline("Unrecognized pet type '%s'", op);
651.  			    break;
652.  		    }
653.  		} else if (negated) preferred_pet = 0;
654.  		return;
655.  	}
656.  
657.  	fullname = "catname";
658.  	if (!strncmpi(opts, fullname, 3)) {
659.  		if (negated) bad_negation(fullname, FALSE);
660.  		else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
661.  			nmcpy(catname, op, PL_PSIZ);
662.  		return;
663.  	}
664.  
665.  	fullname = "dogname";
666.  	if (!strncmpi(opts, fullname, 3)) {
667.  		if (negated) bad_negation(fullname, FALSE);
668.  		else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
669.  			nmcpy(dogname, op, PL_PSIZ);
670.  		return;
671.  	}
672.  
673.  	fullname = "msghistory";
674.  	if (!strncmpi(opts, fullname, 3)) {
675.  		op = string_for_env_opt(fullname, opts, negated);
676.  		if ((negated && !op) || (!negated && op)) {
677.  			flags.msg_history = negated ? 0 : atoi(op);
678.  		} else if (negated) bad_negation(fullname, TRUE);
679.  		return;
680.  	}
681.  
682.  #ifdef CHANGE_COLOR
683.  	if (!strncmpi(opts, "palette", 3)
684.  # ifdef MAC
685.  					|| !strncmpi(opts, "hicolor", 3)
686.  # endif
687.  									) {
688.  	    int color_number, color_incr;
689.  
690.  # ifdef MAC
691.  	    if (!strncmpi(opts, "hicolor", 3)) {
692.  		if (negated) {
693.  		    bad_negation("hicolor", FALSE);
694.  		    return;
695.  		}
696.  		color_number = CLR_MAX + 4;	/* HARDCODED inverse number */
697.  		color_incr = -1;
698.  	    } else {
699.  # endif
700.  		if (negated) {
701.  		    bad_negation("palette", FALSE);
702.  		    return;
703.  		}
704.  		color_number = 0;
705.  		color_incr = 1;
706.  # ifdef MAC
707.  	    }
708.  # endif
709.  	    if ((op = string_for_opt(opts, FALSE)) != (char *)0) {
710.  		char *pt = op;
711.  		int cnt, tmp, reverse;
712.  		long rgb;
713.  
714.  		while (*pt && color_number >= 0) {
715.  		    cnt = 3;
716.  		    rgb = 0L;
717.  		    if (*pt == '-') {
718.  			reverse = 1;
719.  			pt++;
720.  		    } else {
721.  			reverse = 0;
722.  		    }
723.  		    while (cnt-- > 0) {
724.  			if (*pt && *pt != '/') {
725.  # ifdef AMIGA
726.  			    rgb <<= 4;
727.  # else
728.  			    rgb <<= 8;
729.  # endif
730.  			    tmp = *(pt++);
731.  			    if (isalpha(tmp)) {
732.  				tmp = (tmp + 9) & 0xf;	/* Assumes ASCII... */
733.  			    } else {
734.  				tmp &= 0xf;	/* Digits in ASCII too... */
735.  			    }
736.  # ifndef AMIGA
737.  			    /* Add an extra so we fill f -> ff and 0 -> 00 */
738.  			    rgb += tmp << 4;
739.  # endif
740.  			    rgb += tmp;
741.  			}
742.  		    }
743.  		    if (*pt == '/') {
744.  			pt++;
745.  		    }
746.  		    change_color(color_number, rgb, reverse);
747.  		    color_number += color_incr;
748.  		}
749.  	    }
750.  	    if (!initial) {
751.  		need_redraw = TRUE;
752.  	    }
753.  	    return;
754.  	}
755.  #endif
756.  
757.  	if (!strncmpi(opts, "fruit", 2)) {
758.  		char empty_str = '\0';
759.  		op = string_for_opt(opts, negated);
760.  		if (negated) {
761.  		    if (op) {
762.  			bad_negation("fruit", TRUE);
763.  			return;
764.  		    }
765.  		    op = &empty_str;
766.  		    goto goodfruit;
767.  		}
768.  		if (!op) return;
769.  		if (!initial) {
770.  		    struct fruit *f;
771.  
772.  		    num = 0;
773.  		    for(f=ffruit; f; f=f->nextf) {
774.  			if (!strcmp(op, f->fname)) goto goodfruit;
775.  			num++;
776.  		    }
777.  		    if (num >= 100) {
778.  			pline("Doing that so many times isn't very fruitful.");
779.  			return;
780.  		    }
781.  		}
782.  goodfruit:
783.  		nmcpy(pl_fruit, op, PL_FSIZ);
784.  	/* OBJ_NAME(objects[SLIME_MOLD]) won't work after initialization */
785.  		if (!*pl_fruit)
786.  		    nmcpy(pl_fruit, "slime mold", PL_FSIZ);
787.  		if (!initial)
788.  		    (void)fruitadd(pl_fruit);
789.  		/* If initial, then initoptions is allowed to do it instead
790.  		 * of here (initoptions always has to do it even if there's
791.  		 * no fruit option at all.  Also, we don't want people
792.  		 * setting multiple fruits in their options.)
793.  		 */
794.  		return;
795.  	}
796.  
797.  	/* graphics:string */
798.  	fullname = "graphics";
799.  	if (!strncmpi(opts, fullname, 2)) {
800.  		if (negated) bad_negation(fullname, FALSE);
801.  		else graphics_opts(opts, fullname, MAXPCHARS, 0);
802.  		return;
803.  	}
804.  	fullname = "dungeon";
805.  	if (!strncmpi(opts, fullname, 2)) {
806.  		if (negated) bad_negation(fullname, FALSE);
807.  		else graphics_opts(opts, fullname, MAXDCHARS, 0);
808.  		return;
809.  	}
810.  	fullname = "traps";
811.  	if (!strncmpi(opts, fullname, 2)) {
812.  		if (negated) bad_negation(fullname, FALSE);
813.  		else graphics_opts(opts, fullname, MAXTCHARS, MAXDCHARS);
814.  		return;
815.  	}
816.  	fullname = "effects";
817.  	if (!strncmpi(opts, fullname, 2)) {
818.  		if (negated) bad_negation(fullname, FALSE);
819.  		else
820.  		 graphics_opts(opts, fullname, MAXECHARS, MAXDCHARS+MAXTCHARS);
821.  		return;
822.  	}
823.  
824.  	/* objects:string */
825.  	fullname = "objects";
826.  	if (!strncmpi(opts, fullname, 7)) {
827.  		int length;
828.  
829.  		if (negated) {
830.  		    bad_negation(fullname, FALSE);
831.  		    return;
832.  		}
833.  		if (!(opts = string_for_env_opt(fullname, opts, FALSE)))
834.  			return;
835.  		escapes(opts, opts);
836.  
837.  		/*
838.  		 * Override the default object class symbols.  The first
839.  		 * object in the object class is the "random object".  I
840.  		 * don't want to use 0 as an object class, so the "random
841.  		 * object" is basically a place holder.
842.  		 *
843.  		 * The object class symbols have already been initialized in
844.  		 * initoptions().
845.  		 */
846.  		length = strlen(opts);
847.  		if (length >= MAXOCLASSES)
848.  		    length = MAXOCLASSES-1;	/* don't count RANDOM_OBJECT */
849.  
850.  		for (i = 0; i < length; i++)
851.  		    oc_syms[i+1] = (uchar) opts[i];
852.  		return;
853.  	}
854.  
855.  	/* monsters:string */
856.  	fullname = "monsters";
857.  	if (!strncmpi(opts, fullname, 8)) {
858.  		int length;
859.  
860.  		if (negated) {
861.  		    bad_negation(fullname, FALSE);
862.  		    return;
863.  		}
864.  		if (!(opts = string_for_env_opt(fullname, opts, FALSE)))
865.  			return;
866.  		escapes(opts, opts);
867.  
868.  		/* Override default mon class symbols set in initoptions(). */
869.  		length = strlen(opts);
870.  		if (length >= MAXMCLASSES)
871.  		    length = MAXMCLASSES-1;	/* mon class 0 unused */
872.  
873.  		for (i = 0; i < length; i++)
874.  		    monsyms[i+1] = (uchar) opts[i];
875.  		return;
876.  	}
877.  
878.  	/* name:string */
879.  	fullname = "name";
880.  	if (!strncmpi(opts, fullname, 4)) {
881.  		if (negated) bad_negation(fullname, FALSE);
882.  		else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
883.  			nmcpy(plname, op, PL_NSIZ);
884.  		return;
885.  	}
886.  
887.  	/* the order to list the pack */
888.  	fullname = "packorder";
889.  	if (!strncmpi(opts, fullname, 4)) {
890.  		if (negated) {
891.  		    bad_negation(fullname, FALSE);
892.  		    return;
893.  		} else if (!(op = string_for_opt(opts, FALSE))) return;
894.  
895.  		if (!change_inv_order(op))
896.  			badoption(opts);
897.  		return;
898.  	}
899.  
900.  	/* types of objects to pick up automatically */
901.  	if (!strncmpi(opts, "pickup_types", 4)) {
902.  		int oc_sym;
903.  		boolean badopt = FALSE, compat = (strlen(opts) <= 6);
904.  
905.  		oc_to_str(flags.pickup_types, tbuf);
906.  		flags.pickup_types[0] = '\0';	/* all */
907.  		op = string_for_opt(opts, TRUE);
908.  		if (!op) {
909.  		    if (!compat && !negated) {
910.  			char ocl[MAXOCLASSES + 1];
911.  			
912.  			oc_to_str(flags.inv_order, ocl);
913.  		        if (choose_classes_menu("Auto-Pickup what?", 1,
914.  						TRUE, ocl, tbuf))
915.  				op = tbuf;
916.  		        else
917.  				return;
918.  		    } else if (!compat) {
919.  		    /* !pickup_types means no pickup types, but we can't do
920.  		       that by just emptying pickup_types, because that's a
921.  		       special case which means all types rather than none  */
922.  			flags.pickup = 0;
923.  			return;
924.  		    } else {
925.  		    /* for backwards compatibility, "pickup" without a value
926.  		       (as opposed to "pickup_types" without a value)
927.  		       is a synonym for boolean autopickup, and pickup_types
928.  		       gets reset to "all"				    */
929.  		        flags.pickup = !negated;
930.  		        return;
931.  		    }
932.  		}
933.  		if (negated) {
934.  		    bad_negation("pickup_types", TRUE);
935.  		    return;
936.  		}
937.  		while (*op == ' ') op++;
938.  		if (*op != 'a' && *op != 'A') {
939.  		    num = 0;
940.  		    while (*op) {
941.  			oc_sym = def_char_to_objclass(*op);
942.  			/* make sure all are valid obj symbols occuring once */
943.  			if (oc_sym != MAXOCLASSES &&
944.  			    !index(flags.pickup_types, oc_sym)) {
945.  			    flags.pickup_types[num] = (char)oc_sym;
946.  			    flags.pickup_types[++num] = '\0';
947.  			} else
948.  			    badopt = TRUE;
949.  			op++;
950.  		    }
951.  		    if (badopt) badoption(opts);
952.  		}
953.  		return;
954.  	}
955.  
956.  	/* things to disclose at end of game */
957.  	if (!strncmpi(opts, "disclose", 4)) {
958.  		flags.end_disclose[0] = '\0';	/* all */
959.  		if (!(op = string_for_opt(opts, TRUE))) {
960.  			/* for backwards compatibility, "disclose" without a
961.  			 * value means all (was inventory and attributes,
962.  			 * the only things available then), but negated
963.  			 * it means "none"
964.  			 * (note "none" contains none of "iavkg")
965.  			 */
966.  			if (negated) Strcpy(flags.end_disclose, "none");
967.  			return;
968.  		}
969.  		if (negated) {
970.  			bad_negation("disclose", TRUE);
971.  			return;
972.  		}
973.  		num = 0;
974.  		while (*op && num < sizeof flags.end_disclose - 1) {
975.  			register char c;
976.  			c = lowc(*op);
977.  			if (c == 'k') c = 'v';	/* killed -> vanquished */
978.  			if (!index(flags.end_disclose, c)) {
979.  				flags.end_disclose[num++] = c;
980.  				flags.end_disclose[num] = '\0';	/* for index */
981.  			}
982.  			op++;
983.  		}
984.  		return;
985.  	}
986.  
987.  	/* scores:5t[op] 5a[round] o[wn] */
988.  	if (!strncmpi(opts, "scores", 6)) {
989.  	    if (negated) {
990.  		bad_negation("scores", FALSE);
991.  		return;
992.  	    }
993.  	    if (!(op = string_for_opt(opts, FALSE))) return;
994.  
995.  	    while (*op) {
996.  		int inum = 1;
997.  
998.  		if (digit(*op)) {
999.  		    inum = atoi(op);
1000. 		    while (digit(*op)) op++;
1001. 		} else if (*op == '!') {
1002. 		    negated = !negated;
1003. 		    op++;
1004. 		}
1005. 		while (*op == ' ') op++;
1006. 
1007. 		switch (*op) {
1008. 		 case 't':
1009. 		 case 'T':  flags.end_top = inum;
1010. 			    break;
1011. 		 case 'a':
1012. 		 case 'A':  flags.end_around = inum;
1013. 			    break;
1014. 		 case 'o':
1015. 		 case 'O':  flags.end_own = !negated;
1016. 			    break;
1017. 		 default:   badoption(opts);
1018. 			    return;
1019. 		}
1020. 		while (letter(*++op) || *op == ' ') continue;
1021. 		if (*op == '/') op++;
1022. 	    }
1023. 	    return;
1024. 	}
1025. 
1026. #ifdef VIDEOSHADES
1027. 	/* videocolors:string */
1028. 	fullname = "videocolors";
1029. 	if (!strncmpi(opts, fullname, 6)) {
1030. 		if (negated) {
1031. 			bad_negation(fullname, FALSE);
1032. 			return;
1033. 		}
1034. 		else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
1035. 			return;
1036. 		}
1037. 		if (!assign_videocolors(opts))
1038. 			badoption(opts);
1039. 		return;
1040. 	}
1041. 	/* videoshades:string */
1042. 	fullname = "videoshades";
1043. 	if (!strncmpi(opts, fullname, 6)) {
1044. 		if (negated) {
1045. 			bad_negation(fullname, FALSE);
1046. 			return;
1047. 		}
1048. 		else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
1049. 			return;
1050. 		}
1051. 		if (!assign_videoshades(opts))
1052. 			badoption(opts);
1053. 		return;
1054. 	}
1055. #endif /* VIDEOSHADES */
1056. #ifdef MSDOS
1057. # ifdef NO_TERMS
1058. 	/* video:string -- must be after longer tests */
1059. 	fullname = "video";
1060. 	if (!strncmpi(opts, fullname, 5)) {
1061. 		if (negated) {
1062. 			bad_negation(fullname, FALSE);
1063. 			return;
1064. 		}
1065. 		else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
1066. 			return;
1067. 		}
1068. 		if (!assign_video(opts))
1069. 			badoption(opts);
1070. 		return;
1071. 	}
1072. # endif
1073. 	/* soundcard:string -- careful not to match boolean 'sound' */
1074. 	fullname = "soundcard";
1075. 	if (!strncmpi(opts, fullname, 6)) {
1076. 		if (negated) {
1077. 			bad_negation(fullname, FALSE);
1078. 			return;
1079. 		}
1080. 		else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
1081. 			return;
1082. 		}
1083. 		if (!assign_soundcard(opts))
1084. 			badoption(opts);
1085. 		return;
1086. 	}
1087. #endif
1088. 	fullname = "windowtype";
1089. 	if (!strncmpi(opts, fullname, 3)) {
1090. 	    if (negated) {
1091. 		bad_negation(fullname, FALSE);
1092. 		return;
1093. 	    } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1094. 		char buf[WINTYPELEN];
1095. 		nmcpy(buf, op, WINTYPELEN);
1096. 		choose_windows(buf);
1097. 	    }
1098. 	    return;
1099. 	}
1100. 
1101. 	/* menustyle:traditional or combo or full or partial */
1102. 	if (!strncmpi(opts, "menustyle", 4)) {
1103. 		int tmp;
1104. 		boolean val_required = (strlen(opts) > 5 && !negated);
1105. 
1106. 		if (!(op = string_for_opt(opts, !val_required))) {
1107. 		    if (val_required) return; /* string_for_opt gave feedback */
1108. 		    tmp = negated ? 'n' : 'f';
1109. 		} else {
1110. 		    tmp = tolower(*op);
1111. 		}
1112. 		switch (tmp) {
1113. 			case 'n':	/* none */
1114. 			case 't':	/* traditional */
1115. 				flags.menu_style = MENU_TRADITIONAL;
1116. 				break;
1117. 			case 'c':	/* combo: trad.class sel+menu */
1118. 				flags.menu_style = MENU_COMBINATION;
1119. 				break;
1120. 			case 'p':	/* partial: no class menu */
1121. 				flags.menu_style = MENU_PARTIAL;
1122. 				break;
1123. 			case 'f':	/* full: class menu + menu */
1124. 				flags.menu_style = MENU_FULL;
1125. 				break;
1126. 			default:
1127. 				badoption(opts);
1128. 		}
1129. 		return;
1130. 	}
1131. 	/* OK, if we still haven't recognized the option, check the boolean
1132. 	 * options list
1133. 	 */
1134. 	for (i = 0; boolopt[i].name; i++) {
1135. 		if (strlen(opts) >= 3 &&
1136. 		    !strncmpi(boolopt[i].name, opts, strlen(opts))) {
1137. 			/* options that don't exist */
1138. 			if (!boolopt[i].addr) {
1139. 			    if (!initial && !negated)
1140. 				pline_The("\"%s\" option is not available.",
1141. 					boolopt[i].name);
1142. 			    return;
1143. 			}
1144. 			/* options that must come from config file */
1145. 			if (!initial &&
1146. 			    ((boolopt[i].addr) == &flags.legacy
1147. #if defined(MICRO) && !defined(AMIGA)
1148. 			  || (boolopt[i].addr) == &flags.rawio
1149. #endif
1150. 			     )) {
1151. 			    rejectoption(boolopt[i].name);
1152. 			    return;
1153. 			}
1154. 
1155. 			*(boolopt[i].addr) = !negated;
1156. 
1157. #if defined(TERMLIB) || defined(ASCIIGRAPH) || defined(MAC_GRAPHICS_ENV)
1158. 			if (FALSE
1159. # ifdef TERMLIB
1160. 				 || (boolopt[i].addr) == &flags.DECgraphics
1161. # endif
1162. # ifdef ASCIIGRAPH
1163. 				 || (boolopt[i].addr) == &flags.IBMgraphics
1164. # endif
1165. # ifdef MAC_GRAPHICS_ENV
1166. 				 || (boolopt[i].addr) == &flags.MACgraphics
1167. # endif
1168. 				) {
1169. # ifdef REINCARNATION
1170. 			    if (!initial && Is_rogue_level(&u.uz))
1171. 				assign_rogue_graphics(FALSE);
1172. # endif
1173. 			    need_redraw = TRUE;
1174. # ifdef TERMLIB
1175. 			    if ((boolopt[i].addr) == &flags.DECgraphics)
1176. 				switch_graphics(flags.DECgraphics ?
1177. 						DEC_GRAPHICS : ASCII_GRAPHICS);
1178. # endif
1179. # ifdef ASCIIGRAPH
1180. 			    if ((boolopt[i].addr) == &flags.IBMgraphics)
1181. 				switch_graphics(flags.IBMgraphics ?
1182. 						IBM_GRAPHICS : ASCII_GRAPHICS);
1183. # endif
1184. # ifdef MAC_GRAPHICS_ENV
1185. 			    if ((boolopt[i].addr) == &flags.MACgraphics)
1186. 				switch_graphics(flags.MACgraphics ?
1187. 						MAC_GRAPHICS : ASCII_GRAPHICS);
1188. # endif
1189. # ifdef REINCARNATION
1190. 			    if (!initial && Is_rogue_level(&u.uz))
1191. 				assign_rogue_graphics(TRUE);
1192. # endif
1193. 			}
1194. #endif /* TERMLIB || ASCIIGRAPH || MAC_GRAPHICS_ENV */
1195. 
1196. 			/* only do processing below if setting with doset() */
1197. 			if (initial) return;
1198. 
1199. 			if ((boolopt[i].addr) == &flags.time
1200. #ifdef EXP_ON_BOTL
1201. 			 || (boolopt[i].addr) == &flags.showexp
1202. #endif
1203. #ifdef SCORE_ON_BOTL
1204. 			 || (boolopt[i].addr) == &flags.showscore
1205. #endif
1206. 			    )
1207. 			    flags.botl = TRUE;
1208. 
1209. 			else if ((boolopt[i].addr) == &flags.invlet_constant) {
1210. 			    if (flags.invlet_constant) reassign();
1211. 			}
1212. 
1213. 			else if ((boolopt[i].addr) == &flags.num_pad)
1214. 			    number_pad(flags.num_pad ? 1 : 0);
1215. 
1216. 			else if ((boolopt[i].addr) == &flags.lit_corridor) {
1217. 			    /*
1218. 			     * All corridor squares seen via night vision or
1219. 			     * candles & lamps change.  Update them by calling
1220. 			     * newsym() on them.  Don't do this if we are
1221. 			     * initializing the options --- the vision system
1222. 			     * isn't set up yet.
1223. 			     */
1224. 			    vision_recalc(2);		/* shut down vision */
1225. 			    vision_full_recalc = 1;	/* delayed recalc */
1226. 			}
1227. 
1228. #ifdef TEXTCOLOR
1229. 			else if ((boolopt[i].addr) == &flags.use_color
1230. 			      || (boolopt[i].addr) == &flags.hilite_pet) {
1231. 			    need_redraw = TRUE;
1232. # ifdef TOS
1233. 			    if ((boolopt[i].addr) == &flags.use_color
1234. 				&& flags.BIOS) {
1235. 				if (colors_changed)
1236. 				    restore_colors();
1237. 				else
1238. 				    set_colors();
1239. 			    }
1240. # endif
1241. 			}
1242. #endif
1243. 
1244. 			return;
1245. 		}
1246. 	}
1247. 
1248. 	/* out of valid options */
1249. 	badoption(opts);
1250. }
1251. 
1252. static NEARDATA const char *menutype[] = {
1253. 	"traditional", "combination", "partial", "full"
1254. };
1255. 
1256. /*
1257.  * Convert the given string of object classes to a string of default object
1258.  * symbols.
1259.  */
1260. static void
1261. oc_to_str(src,dest)
1262.     char *src, *dest;
1263. {
1264.     int i;
1265. 
1266.     while ((i = (int) *src++) != 0) {
1267. 	if (i < 0 || i >= MAXOCLASSES)
1268. 	    impossible("oc_to_str:  illegal object class %d", i);
1269. 	else
1270. 	    *dest++ = def_oc_syms[i];
1271.     }
1272.     *dest = '\0';
1273. }
1274. 
1275. #if defined(MICRO) || defined(MAC)
1276. # define OPTIONS_HEADING "OPTIONS"
1277. #else
1278. # define OPTIONS_HEADING "NETHACKOPTIONS"
1279. #endif
1280. 
1281. int
1282. doset()
1283. {
1284. 	char buf[BUFSZ], ocl[MAXOCLASSES+1], on_off;
1285. 	const char *opt_name;
1286. 	int i;
1287. 	winid tmpwin;
1288. 
1289. 	switch (yn_function("Show the current settings [c], or set options [s]?",
1290. 			    "csq", 'q')) {
1291. 	default:
1292. 	case 'q':
1293. 	    clear_nhwindow(WIN_MESSAGE);
1294. 	    return 0;
1295. 	case 'c':
1296. 	    tmpwin = create_nhwindow(NHW_MENU);
1297. 	    putstr(tmpwin, 0, OPTIONS_HEADING);
1298. 	    putstr(tmpwin, 0, "");
1299. 	    /* print the booleans */
1300. 	    for (i = 0; boolopt[i].name; i++) {
1301. 		if (!boolopt[i].addr) continue;
1302. 		opt_name = boolopt[i].name;
1303. 		if (*(boolopt[i].addr)) {
1304. 		    on_off = ' ';		/* on */
1305. 		} else {
1306. 		    if (!strcmp(opt_name, "female"))
1307. 			opt_name = "male",  on_off = ' ';
1308. 		    else
1309. 			on_off = '!';		/* off */
1310. 		}
1311. 		Sprintf(buf, "%c%s", on_off, opt_name);
1312. 		putstr(tmpwin, 0, buf);
1313. 	    }
1314. 	    /* print the compounds */
1315. 	    Sprintf(buf, " catname: %s",
1316. 			(catname[0] != 0) ? catname : "(null)");
1317. 	    putstr(tmpwin, 0, buf);
1318. 	    Sprintf(buf, " disclose: %s",
1319. 			(flags.end_disclose[0]) ? flags.end_disclose : "all");
1320. 	    putstr(tmpwin, 0, buf);
1321. 	    Sprintf(buf, " dogname: %s",
1322. 			(dogname[0] != 0) ? dogname : "(null)");
1323. 	    putstr(tmpwin, 0, buf);
1324. 	    Sprintf(buf, " fruit: %s", pl_fruit);
1325. 	    putstr(tmpwin, 0, buf);
1326. 	    Sprintf(buf, " menustyle: %s", menutype[(int)flags.menu_style]);
1327. 	    putstr(tmpwin, 0, buf);
1328. 	    Sprintf(buf, " msghistory: %u", flags.msg_history);
1329. 	    putstr(tmpwin, 0, buf);
1330. 	    Sprintf(buf, " name: %s", plname);
1331. 	    putstr(tmpwin, 0, buf);
1332. 	    oc_to_str(flags.inv_order, ocl);
1333. 	    Sprintf(buf, " packorder: %s", ocl);
1334. 	    putstr(tmpwin, 0, buf);
1335. #ifdef CHANGE_COLOR
1336. 	    Sprintf(buf, " palette: %s", get_color_string());
1337. 	    putstr(tmpwin, 0, buf);
1338. #endif
1339. 	    Sprintf(buf, " pettype: %s", preferred_pet == 'c' ? "cat" :
1340. 				    preferred_pet == 'd' ? "dog" : "random");
1341. 	    putstr(tmpwin, 0, buf);
1342. 	    oc_to_str(flags.pickup_types, ocl);
1343. 	    Sprintf(buf, " pickup_types: %s", (ocl[0]) ? ocl : "all");
1344. 	    putstr(tmpwin, 0, buf);
1345. 	    Sprintf(buf, " scores: %d top/%d around%s",
1346. 			flags.end_top, flags.end_around,
1347. 			(flags.end_own ? "/own" : ""));
1348. 	    putstr(tmpwin, 0, buf);
1349. #ifdef VIDEOSHADES
1350. 	    Sprintf(buf, " videoshades: %s-%s-%s",
1351. 				shade[0],shade[1],shade[2]);
1352. 	    putstr(tmpwin, 0, buf);
1353. 	    Sprintf(buf, " videocolors: %d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d",
1354. 		 ttycolors[CLR_RED],ttycolors[CLR_GREEN],ttycolors[CLR_BROWN],
1355. 		 ttycolors[CLR_BLUE],ttycolors[CLR_MAGENTA],ttycolors[CLR_CYAN],
1356. 		 ttycolors[CLR_ORANGE],ttycolors[CLR_BRIGHT_GREEN],
1357. 		 ttycolors[CLR_YELLOW],ttycolors[CLR_BRIGHT_BLUE],
1358. 		 ttycolors[CLR_BRIGHT_MAGENTA],ttycolors[CLR_BRIGHT_CYAN]);
1359. 	    putstr(tmpwin, 0, buf);
1360. #endif /* VIDEOSHADES */
1361. 	    Sprintf(buf, " windowtype: %s", windowprocs.name);
1362. 	    putstr(tmpwin, 0, buf);
1363. 	    display_nhwindow(tmpwin, TRUE);
1364. 	    destroy_nhwindow(tmpwin);
1365. 	    break;
1366. 	case 's':
1367. 	    clear_nhwindow(WIN_MESSAGE);
1368. 	    getlin("What options do you want to set?", buf);
1369. 	    if(buf[0] == '\033') return 0;
1370. 	    need_redraw = FALSE;
1371. 	    parseoptions(buf, FALSE, FALSE);
1372. 	    if(need_redraw)
1373. 		(void) doredraw();
1374. 	    break;
1375. 	}
1376. 
1377. 	return 0;
1378. }
1379. 
1380. int
1381. dotogglepickup()
1382. {
1383. 	char buf[BUFSZ], ocl[MAXOCLASSES+1];
1384. 
1385. 	flags.pickup = !flags.pickup;
1386. 	if (flags.pickup) {
1387. 	    oc_to_str(flags.pickup_types, ocl);
1388. 	    Sprintf(buf, "ON, for %s objects", ocl[0] ? ocl : "all");
1389. 	} else {
1390. 	    Strcpy(buf, "OFF");
1391. 	}
1392. 	pline("Autopickup: %s.", buf);
1393. 	return 0;
1394. }
1395. 
1396. /* data for option_help() */
1397. static const char *opt_intro[] = {
1398. 	"",
1399. 	"                 NetHack Options Help:",
1400. 	"",
1401. #define CONFIG_SLOT 3	/* fill in next value at run-time */
1402. 	(char *)0,
1403. #if !defined(MICRO) && !defined(MAC)
1404. 	"or use `NETHACKOPTIONS=\"<options>\"' in your environment;",
1405. # ifdef VMS
1406. 	"-- for example, $ DEFINE NETHACKOPTIONS \"noautopickup,fruit:kumquat\"",
1407. # endif
1408. #endif
1409. 	"or press \"O\" while playing, and type your <options> at the prompt.",
1410. 	"In all cases, <options> is a list of options separated by commas.",
1411. 	"",
1412.  "Boolean options (which can be negated by prefixing them with '!' or \"no\"):",
1413. 	(char *)0
1414. };
1415. 
1416. static const char *opt_epilog[] = {
1417. 	"",
1418.  "Some of the options can be set only before the game is started.  You will",
1419. 	"be so informed, if you attempt to set them while in the game.",
1420. 	(char *)0
1421. };
1422. 
1423. void
1424. option_help()
1425. {
1426.     char buf[BUFSZ], buf2[BUFSZ];
1427.     register int i;
1428.     winid datawin;
1429. 
1430.     datawin = create_nhwindow(NHW_TEXT);
1431. #ifdef AMIGA
1432.     if(FromWBench){
1433. 	Sprintf(buf,"Set options as OPTIONS= in %s or in icon;",configfile);
1434.     } else
1435. #endif
1436. 	Sprintf(buf, "Set options as OPTIONS=<options> in %s;", configfile);
1437.     opt_intro[CONFIG_SLOT] = (const char *) buf;
1438.     for (i = 0; opt_intro[i]; i++)
1439. 	putstr(datawin, 0, opt_intro[i]);
1440. 
1441.     /* Boolean options */
1442.     for (i = 0; boolopt[i].name; i++) {
1443. 	if (boolopt[i].addr)
1444. 	    next_opt(datawin, boolopt[i].name);
1445.     }
1446.     next_opt(datawin, "");
1447. 
1448.     /* Compound options */
1449.     putstr(datawin, 0, "Compound options:");
1450.     for (i = 0; compopt[i].name; i++) {
1451. 	Sprintf(buf2, "`%s'", compopt[i].name);
1452. 	Sprintf(buf, "%-14s - %s", buf2, compopt[i].descr);
1453. 	putstr(datawin, 0, buf);
1454.     }
1455. 
1456.     for (i = 0; opt_epilog[i]; i++)
1457. 	putstr(datawin, 0, opt_epilog[i]);
1458. 
1459.     display_nhwindow(datawin, FALSE);
1460.     destroy_nhwindow(datawin);
1461.     return;
1462. }
1463. 
1464. /*
1465.  * prints the next boolean option, on the same line if possible, on a new
1466.  * line if not. End with next_opt("").
1467.  */
1468. void
1469. next_opt(datawin, str)
1470. winid datawin;
1471. const char *str;
1472. {
1473. 	static char buf[121];
1474. 	int i;
1475. 	char *s;
1476. 
1477. 	if (!*str) {
1478. 		for (s = buf; *s; s++);	/* find end of string */
1479. 		if (s > &buf[1] && s[-2] == ',')
1480. 			s[-2] = 0;	/* strip last ", " */
1481. 		i = 121;
1482. 	}
1483. 	else
1484. 		i = strlen(buf) + strlen(str) + 2;
1485. 
1486. 	if (i > COLNO - 2) { /* rule of thumb */
1487. 		putstr(datawin, 0, buf);
1488. 		buf[0] = 0;
1489. 	}
1490. 	if (*str) {
1491. 		Strcat(buf, str);
1492. 		Strcat(buf, ", ");
1493. 	}
1494. 	else
1495. 		putstr(datawin, 0, str);
1496. 	return;
1497. }
1498. 
1499. /* Returns the fid of the fruit type; if that type already exists, it
1500.  * returns the fid of that one; if it does not exist, it adds a new fruit
1501.  * type to the chain and returns the new one.
1502.  */
1503. int
1504. fruitadd(str)
1505. char *str;
1506. {
1507. 	register int i;
1508. 	register struct fruit *f;
1509. 	struct fruit *lastf = 0;
1510. 	int highest_fruit_id = 0;
1511. 	char buf[PL_FSIZ];
1512. 	boolean user_specified = (str == pl_fruit);
1513. 	/* if not user-specified, then it's a fruit name for a fruit on
1514. 	 * a bones level...
1515. 	 */
1516. 
1517. 	/* Note: every fruit has an id (spe for fruit objects) of at least
1518. 	 * 1; 0 is an error.
1519. 	 */
1520. 	if (user_specified) {
1521. 		/* disallow naming after other foods (since it'd be impossible
1522. 		 * to tell the difference)
1523. 		 */
1524. 
1525. 		boolean found = FALSE;
1526. 
1527. 		for (i = bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS;
1528. 						i++) {
1529. 			if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) {
1530. 				found = TRUE;
1531. 				break;
1532. 			}
1533. 		}
1534. 		if (found ||
1535. 		    (!strncmp(str, "tin of ", 7) &&
1536. 			(!strcmp(str+7, "spinach") ||
1537. 			 name_to_mon(str+7) >= LOW_PM)) ||
1538. 		    !strcmp(str, "empty tin") ||
1539. 		    ((!strncmp(eos(str)-6," corpse",7) ||
1540. 			    !strncmp(eos(str)-3, " egg",4)) &&
1541. 			name_to_mon(str) >= LOW_PM))
1542. 			{
1543. 				Strcpy(buf, pl_fruit);
1544. 				Strcpy(pl_fruit, "candied ");
1545. 				nmcpy(pl_fruit+8, buf, PL_FSIZ-8);
1546. 		}
1547. 	}
1548. 	for(f=ffruit; f; f = f->nextf) {
1549. 		lastf = f;
1550. 		if(f->fid > highest_fruit_id) highest_fruit_id = f->fid;
1551. 		if(!strncmp(str, f->fname, PL_FSIZ))
1552. 			goto nonew;
1553. 	}
1554. 	/* if adding another fruit would overflow spe, use a random
1555. 	   fruit instead... we've got a lot to choose from. */
1556. 	if (highest_fruit_id >= 127) return rnd(127);
1557. 	highest_fruit_id++;
1558. 	f = newfruit();
1559. 	if (ffruit) lastf->nextf = f;
1560. 	else ffruit = f;
1561. 	Strcpy(f->fname, str);
1562. 	f->fid = highest_fruit_id;
1563. 	f->nextf = 0;
1564. nonew:
1565. 	if (user_specified) current_fruit = highest_fruit_id;
1566. 	return f->fid;
1567. }
1568. 
1569. /*
1570.  * This is a somewhat generic menu for taking a list of NetHack style
1571.  * class choices and presenting them via a description
1572.  * rather than the traditional NetHack characters.
1573.  * (Benefits users whose first exposure to NetHack is via tiles).
1574.  *
1575.  * prompt
1576.  *	     The title at the top of the menu.
1577.  *
1578.  * category: 0 = monster class
1579.  *           1 = object  class
1580.  *
1581.  * way
1582.  *	     FALSE = PICK_ONE, TRUE = PICK_ANY
1583.  *
1584.  * class_list
1585.  *	     a null terminated string containing the list of choices.
1586.  *
1587.  * class_selection
1588.  *	     a null terminated string containing the selected characters.
1589.  *
1590.  * Returns number selected (or ESC if aborted).
1591.  */
1592. int
1593. choose_classes_menu(prompt, category, way, class_list, class_select)
1594. const char *prompt;
1595. int category;
1596. boolean way;
1597. char *class_list;
1598. char *class_select;
1599. {
1600.     menu_item *pick_list = (menu_item *)0;
1601.     winid win;
1602.     anything any;
1603.     char buf[BUFSZ];
1604.     int i, n;
1605.     int ret;
1606.     int next_accelerator, accelerator;
1607. 
1608.     if (class_list == (char *)0 || class_select == (char *)0) return 0;
1609.     accelerator = 0;
1610.     next_accelerator = 'a';
1611.     any.a_void = 0;
1612.     win = create_nhwindow(NHW_MENU);
1613.     start_menu(win);
1614.     while (*class_list) {
1615.     	const char *text;
1616.     	boolean selected;
1617. 
1618. 	text = (char *)0;
1619. 	selected = FALSE;
1620.     	switch (category) {
1621. 		case 0:
1622. 			text = monexplain[def_char_to_monclass(*class_list)];
1623. 			accelerator = *class_list;
1624. 			Sprintf(buf, "%s", text);
1625. 			break;
1626. 		case 1:
1627. 			text = objexplain[def_char_to_objclass(*class_list)];
1628. 			accelerator = next_accelerator;
1629. 			Sprintf(buf, "%c  %s", *class_list, text);
1630. 			break;
1631. 		default:
1632. 			impossible("choose_classes_menu: invalid category %d",
1633. 					category);
1634. 	}
1635. 	if (way && *class_select) {	/* Selections there already */
1636. 		if (index(class_select, *class_list)) {
1637. 			selected = TRUE;
1638. 		}
1639. 	}
1640. 	any.a_int = *class_list;
1641. 	add_menu(win, NO_GLYPH, &any, accelerator, ATR_NONE, buf, selected);
1642. 	++class_list;
1643. 	if (category > 0) {
1644. 		++next_accelerator;
1645. 		if (next_accelerator == ('z' + 1)) next_accelerator = 'A';
1646. 		if (next_accelerator == ('Z' + 1)) break;
1647. 	}
1648.     }
1649.     end_menu(win, prompt);
1650.     n = select_menu(win, way ? PICK_ANY : PICK_ONE, &pick_list);
1651.     destroy_nhwindow(win);
1652.     if (n > 0) {
1653.     	for (i = 0; i < n; ++i) {
1654. 	    	*class_select++ = (char)pick_list[i].item.a_int;
1655. 	}
1656.     	free((genericptr_t)pick_list);
1657. 	ret = n;
1658.     } else if (n == -1) {
1659. 	class_select = eos(class_select);
1660. 	ret = -1;
1661.     } else
1662.     	ret = 0;
1663.     *class_select = '\0';
1664.     return ret;
1665. }
1666. 
1667. #endif	/* OPTION_LISTS_ONLY */
1668. 
1669. /*options.c*/