Source:NetHack 3.1.0/options.c

From NetHackWiki
Jump to navigation Jump to search

Below is the full text to options.c from the source code of NetHack 3.1.0. To link to a particular line, write [[NetHack 3.1.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.1	92/11/14	*/
2.    /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3.    /* NetHack may be freely redistributed.  See license for details. */
4.    
5.    #include "hack.h"
6.    #include "termcap.h"
7.    #include <ctype.h>
8.    
9.    /*
10.    *  NOTE:  If you add (or delete) an option, please update the short
11.    *  options help (option_help()), the long options help (dat/opthelp),
12.    *  and the current options setting display function (doset()).
13.    */
14.   
15.   #if defined(TOS) && defined(TEXTCOLOR)
16.   extern boolean colors_changed;	/* in tos.c */
17.   #endif
18.   
19.   extern const char *roles[];	/* from u_init.c */
20.   extern char inv_order[];	/* from invent.c */
21.   
22.   static boolean initial, from_file;
23.   static boolean NEARDATA set_order;
24.   
25.   static void FDECL(nmcpy, (char *, const char *, int));
26.   static void FDECL(escapes, (const char *, char *));
27.   static void FDECL(rejectoption, (const char *));
28.   static void FDECL(badoption, (const char *));
29.   static char *FDECL(string_for_env_opt, (const char *, char *));
30.   static int FDECL(change_inv_order, (char *, int));
31.   static void FDECL(oc_to_str, (char *, char *));
32.   
33.   static struct Bool_Opt
34.   {
35.   	const char *name;
36.   	boolean	*addr, initvalue;
37.   } boolopt[] = {
38.   #if defined(MICRO) && !defined(AMIGA)
39.   	{"BIOS", &flags.BIOS, FALSE},
40.   #endif
41.   #ifdef INSURANCE
42.   	{"checkpoint", &flags.ins_chkpt, TRUE},
43.   #endif
44.   #ifdef TEXTCOLOR
45.   # ifdef MICRO
46.   	{"color", &flags.use_color, TRUE},
47.   # else	/* systems that support multiple terminals, many monochrome */
48.   	{"color", &flags.use_color, FALSE},
49.   # endif
50.   #endif
51.   	{"confirm",&flags.confirm, TRUE},
52.   #ifdef TERMLIB
53.   	{"DECgraphics", &flags.DECgraphics, FALSE},
54.   #endif
55.   	{"disclose", &flags.end_disclose, TRUE},
56.   	{"female", &flags.female, FALSE},
57.   	{"fixinv", &flags.invlet_constant, TRUE},
58.   #ifdef AMIFLUSH
59.   	{"flush", &flags.amiflush, FALSE},
60.   #endif
61.   	{"help", &flags.help, TRUE},
62.   #ifdef TEXTCOLOR
63.   	{"hilite_pet", &flags.hilite_pet, FALSE},
64.   #endif
65.   #ifdef ASCIIGRAPH
66.   	{"IBMgraphics", &flags.IBMgraphics, FALSE},
67.   #endif
68.   	{"ignintr", &flags.ignintr, FALSE},
69.   #ifdef MAC_GRAPHICS_ENV
70.   	{"large_font", &flags.large_font, FALSE},
71.   #endif
72.   	{"legacy",&flags.legacy, TRUE},
73.   	{"lit_corridor", &flags.lit_corridor, FALSE},
74.   #ifdef MAC_GRAPHICS_ENV
75.   	{"MACgraphics", &flags.MACgraphics, TRUE},
76.   #endif
77.   #ifdef NEWS
78.   	{"news", &flags.news, TRUE},
79.   #endif
80.   	{"null", &flags.null, TRUE},
81.   	{"number_pad", &flags.num_pad, FALSE},
82.   	{"pickup", &flags.pickup, TRUE},
83.   #ifdef MAC
84.   	{"popup_dialog", &flags.popup_dialog, FALSE},
85.   #endif
86.   #if defined(MICRO) && !defined(AMIGA)
87.   	{"rawio", &flags.rawio, FALSE},
88.   #endif
89.   	{"rest_on_space", &flags.rest_on_space, FALSE},
90.   	{"safepet", &flags.safe_dog, TRUE},
91.   #ifdef EXP_ON_BOTL
92.   	{"showexp", &flags.showexp, FALSE},
93.   #endif
94.   #ifdef SCORE_ON_BOTL
95.   	{"showscore", &flags.showscore, FALSE},
96.   #endif
97.   	{"silent", &flags.silent, TRUE},
98.   	{"sortpack", &flags.sortpack, TRUE},
99.   	{"sound", &flags.soundok, TRUE},
100.  	{"standout", &flags.standout, FALSE},
101.  	{"time", &flags.time, FALSE},
102.  	{"tombstone",&flags.tombstone, TRUE},
103.  	{"verbose", &flags.verbose, TRUE},
104.  	{NULL, (boolean *)0, FALSE}
105.  };
106.  
107.  static boolean need_redraw; /* for doset() */
108.  
109.  void
110.  initoptions()
111.  {
112.  	register char *opts;
113.  	int i;
114.  
115.  	for (i = 0; boolopt[i].name; i++) {
116.  		if (boolopt[i].addr)
117.  			*(boolopt[i].addr) = boolopt[i].initvalue;
118.  	}
119.  	flags.end_own = FALSE;
120.  	flags.end_top = 3;
121.  	flags.end_around = 2;
122.  	flags.msg_history = 20;
123.  
124.  	/* Set the default monster and object class symbols.  Don't use */
125.  	/* memcpy() --- sizeof char != sizeof uchar on some machines.	*/
126.  	for (i = 0; i < MAXOCLASSES; i++)
127.  	    	oc_syms[i] = (uchar) def_oc_syms[i];
128.  	for (i = 0; i < MAXMCLASSES; i++)
129.  	    	monsyms[i] = (uchar) def_monsyms[i];
130.  
131.  	switch_graphics(ASCII_GRAPHICS);	/* set default characters */
132.  #ifdef UNIX
133.  	/*
134.  	 * Set defaults for some options depending on what we can
135.  	 * detect about the environment's capabilities.
136.  	 * This has to be done after the global initialization above
137.  	 * and before reading user-specific initialization via
138.  	 * config file/environment variable below.
139.  	 */
140.  	/* this detects the IBM-compatible console on most 386 boxes */
141.  	if (!strncmp(getenv("TERM"), "AT", 2)) {
142.  		switch_graphics(IBM_GRAPHICS);
143.  # ifdef TEXTCOLOR
144.  		flags.use_color = TRUE;
145.  # endif
146.  	}
147.  #endif /* UNIX */
148.  #if defined(UNIX) || defined(VMS)
149.  	/* detect whether a "vt" terminal can handle alternate charsets */
150.  	if (!strncmpi(getenv("TERM"), "vt", 2) && (AS && AE) &&
151.  	    !strcmp(AS, "\016") && !strcmp(AE, "\017")) {
152.  		switch_graphics(DEC_GRAPHICS);
153.  	}
154.  #endif /* UNIX || VMS */
155.  
156.  #ifdef MAC_GRAPHICS_ENV
157.  	switch_graphics(MAC_GRAPHICS);
158.  #endif /* MAC_GRAPHICS_ENV */
159.  
160.  #ifdef TUTTI_FRUTTI
161.  	/* since this is done before init_objects(), do partial init here */
162.  	objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD;
163.  	nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
164.  #endif
165.  	opts = getenv("NETHACKOPTIONS");
166.  	if (!opts) opts = getenv("HACKOPTIONS");
167.  	if (opts)
168.  		if (*opts == '/' || *opts == '\\' || *opts == '@') {
169.  			if (*opts == '@') opts++;	/* @filename */
170.  			/* looks like a filename */
171.  			read_config_file(opts);
172.  		} else {
173.  			read_config_file(NULL);
174.  			parseoptions(opts, TRUE, FALSE);
175.  		}
176.  	else
177.  		read_config_file(NULL);
178.  #ifdef AMIGA
179.  	ami_wbench_init();	/* must be here or can't set fruit */
180.  #endif
181.  #ifdef TUTTI_FRUTTI
182.  	(void)fruitadd(pl_fruit);
183.  	/* Remove "slime mold" from list of object names; this will	*/
184.  	/* prevent it from being wished unless it's actually present	*/
185.  	/* as a named (or default) fruit.  Wishing for "fruit" will	*/
186.  	/* result in the player's preferred fruit [better than "\033"].	*/
187.  	obj_descr[SLIME_MOLD].oc_name = "fruit";
188.  #endif
189.  	if(flags.female)  {	/* should have been set in NETHACKOPTIONS */
190.  		roles[2] = "Cavewoman";
191.  		roles[6] = "Priestess";
192.  	}
193.  }
194.  
195.  static void
196.  nmcpy(dest, src, maxlen)
197.  	char	*dest;
198.  	const char *src;
199.  	int	maxlen;
200.  {
201.  	int	count;
202.  
203.  	for(count = 1; count < maxlen; count++) {
204.  		if(*src == ',' || *src == '\0') break; /*exit on \0 terminator*/
205.  		*dest++ = *src++;
206.  	}
207.  	*dest = 0;
208.  }
209.  
210.  /*
211.   * escapes: escape expansion for showsyms. C-style escapes understood include
212.   * \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal). The ^-prefix
213.   * for control characters is also understood, and \[mM] followed by any of the
214.   * previous forms or by a character has the effect of 'meta'-ing the value (so
215.   * that the alternate character set will be enabled).
216.   */
217.  static void
218.  escapes(cp, tp)
219.  const char	*cp;
220.  char *tp;
221.  {
222.      while (*cp)
223.      {
224.  	int	cval = 0, meta = 0;
225.  
226.  	if (*cp == '\\' && index("mM", cp[1])) {
227.  		meta = 1;
228.  		cp += 2;
229.  	}
230.  	if (*cp == '\\' && index("0123456789xXoO", cp[1]))
231.  	{
232.  	    const char *dp, *hex = "00112233445566778899aAbBcCdDeEfF";
233.  	    int dcount = 0;
234.  
235.  	    cp++;
236.  	    if (*cp == 'x' || *cp == 'X')
237.  		for (++cp; (dp = index(hex, *cp)) && (dcount++ < 2); cp++)
238.  		    cval = (cval * 16) + (dp - hex) / 2;
239.  	    else if (*cp == 'o' || *cp == 'O')
240.  		for (++cp; (index("01234567",*cp)) && (dcount++ < 3); cp++)
241.  		    cval = (cval * 8) + (*cp - '0');
242.  	    else
243.  		for (; (index("0123456789",*cp)) && (dcount++ < 3); cp++)
244.  		    cval = (cval * 10) + (*cp - '0');
245.  	}
246.  	else if (*cp == '\\')		/* C-style character escapes */
247.  	{
248.  	    switch (*++cp)
249.  	    {
250.  	    case '\\': cval = '\\'; break;
251.  	    case 'n': cval = '\n'; break;
252.  	    case 't': cval = '\t'; break;
253.  	    case 'b': cval = '\b'; break;
254.  	    case 'r': cval = '\r'; break;
255.  	    default: cval = *cp;
256.  	    }
257.  	    cp++;
258.  	}
259.  	else if (*cp == '^')		/* expand control-character syntax */
260.  	{
261.  	    cval = (*++cp & 0x1f);
262.  	    cp++;
263.  	}
264.  	else
265.  	    cval = *cp++;
266.  	if (meta)
267.  	    cval |= 0x80;
268.  	*tp++ = cval;
269.      }
270.      *tp = '\0';
271.  }
272.  
273.  static void
274.  rejectoption(optname)
275.  const char *optname;
276.  {
277.  #ifdef MICRO
278.  # ifdef AMIGA
279.  	if(FromWBench){
280.  		pline("\"%s\" settable only from %s or in icon.",
281.  			optname, configfile);
282.  	} else
283.  # endif
284.  		pline("\"%s\" settable only from %s.", optname, configfile);
285.  #else
286.  	pline("%s can be set only from NETHACKOPTIONS or %s.", optname,
287.  			configfile);
288.  #endif
289.  }
290.  
291.  static void
292.  badoption(opts)
293.  const char *opts;
294.  {
295.  	if(!initial) {
296.  	    if(!strncmp(opts, "h", 1) || !strncmp(opts, "?", 1))
297.  		option_help();
298.  	    else
299.  		pline("Unknown option: %s.  Enter \"?g\" for help.", opts);
300.  	    return;
301.  	}
302.  # ifdef AMIGA
303.  	if(ami_wbench_badopt(opts)) {
304.  # endif
305.  	if(from_file)
306.  	    raw_printf("Bad syntax in OPTIONS in %s: %s.", configfile, opts);
307.  	else
308.  	    raw_printf("Bad syntax in NETHACKOPTIONS: %s.", opts);
309.  # ifdef AMIGA
310.  	}
311.  # endif
312.  	wait_synch();
313.  }
314.  
315.  static char *
316.  string_for_env_opt(optname, opts)
317.  const char *optname;
318.  char *opts;
319.  {
320.  	register char *colon;
321.  
322.  	if(!initial) {
323.  		rejectoption(optname);
324.  		return NULL;
325.  	}
326.  	colon = index(opts,':');
327.  	if(!colon) {
328.  		badoption(opts);
329.  		return NULL;
330.  	}
331.  	return ++colon;
332.  }
333.  
334.  /*
335.   * Change the inventory order, using the given string as the new order.
336.   * Missing characters in the new order are filled in at the end from
337.   * the current inv_order.
338.   *
339.   * This routine always returns 1 unless the parameter 'fail' is true
340.   * and there is a duplicate or bad char in the string.
341.   */
342.  static int
343.  change_inv_order(op, fail)
344.      char *op;
345.      int  fail;	/* If TRUE, return 0 if any duplicates or bad chars. */
346.  {
347.      int oc_sym, num;
348.      char *sp, *tmp, buf[BUFSZ];
349.  
350.      for (sp = op; *sp; sp++) {
351.  	oc_sym = def_char_to_objclass(*sp);
352.  
353.  	/* Remove bad or duplicate entries. */
354.  	if (oc_sym == MAXOCLASSES ||
355.  		(!index(inv_order, oc_sym)) || (index(sp+1, *sp))) {
356.  
357.  	    if (fail) return 0;
358.  	    for(tmp = sp; *tmp; tmp++)
359.  		tmp[0] = tmp[1];
360.  	    sp--;
361.  	} else
362.  	    *sp = (char) oc_sym;
363.      } 
364.      Strcpy(buf, op);
365.      for (sp = inv_order, num = strlen(buf); *sp; sp++)
366.  	if (!index(buf, *sp))
367.  	    buf[num++] = *sp;
368.  
369.      buf[num] = 0;
370.      Strcpy(inv_order, buf);
371.      return 1;
372.  }
373.  
374.  void
375.  parseoptions(opts, tinitial, tfrom_file)
376.  register char *opts;
377.  boolean tinitial, tfrom_file;
378.  {
379.  	register char *op;
380.  	unsigned num;
381.  	boolean negated;
382.  	int i;
383.  
384.  	initial = tinitial;
385.  	from_file = tfrom_file;
386.  	if ((op = index(opts, ',')) != 0) {
387.  		*op++ = 0;
388.  		parseoptions(op, initial, from_file);
389.  	}
390.  
391.  	/* strip leading and trailing white space */
392.  	while (isspace(*opts)) opts++;
393.  	op = eos(opts);
394.  	while (--op >= opts && isspace(*op)) *op = '\0';
395.  
396.  	if(!*opts) return;
397.  	negated = FALSE;
398.  	while((*opts == '!') || !strncmpi(opts, "no", 2)) {
399.  		if(*opts == '!') opts++; else opts += 2;
400.  		negated = !negated;
401.  	}
402.  	
403.  #if defined(MICRO) && !defined(AMIGA)
404.  	/* included for compatibility with old NetHack.cnf files */
405.  	if (!strncmp(opts, "IBM_", 4)) {
406.  		flags.BIOS = !negated;
407.  		return;
408.  	}
409.  
410.  	/* put here cause it has to come from the config file */
411.  	if (!strncmpi(opts, "raw", 3)) {
412.  		if (initial)
413.  			flags.rawio = !negated;
414.  		else
415.  			rejectoption("rawio");
416.  		return;
417.  	}
418.  #endif /* MICRO */
419.  
420.  #if defined(TOS) && defined(TEXTCOLOR)
421.  	if (!strncmpi(opts, "col", 3)) {
422.  		flags.use_color = !negated;
423.  		if (flags.BIOS && !initial) {
424.  			if (colors_changed)
425.  				restore_colors();
426.  			else
427.  				set_colors();
428.  		}
429.  	}
430.  #endif
431.  	/* other special-case boolean options */
432.  #ifdef TERMLIB
433.  	if (!strncmpi(opts, "DEC", 3)) {
434.  #ifdef REINCARNATION
435.  		if (!initial && Is_rogue_level(&u.uz))
436.  			assign_rogue_graphics(FALSE);
437.  #endif
438.  		flags.DECgraphics = !negated;
439.  		need_redraw = TRUE;
440.  		switch_graphics(flags.DECgraphics ?
441.  				DEC_GRAPHICS : ASCII_GRAPHICS);
442.  #ifdef REINCARNATION
443.  		if (!initial && Is_rogue_level(&u.uz))
444.  			assign_rogue_graphics(TRUE);
445.  #endif
446.  		return;
447.  	}
448.  #endif /* TERMLIB */
449.  #ifdef ASCIIGRAPH
450.  	if (!strncmpi(opts, "IBMg", 4)) {
451.  #ifdef REINCARNATION
452.  		if (!initial && Is_rogue_level(&u.uz))
453.  			assign_rogue_graphics(FALSE);
454.  #endif
455.  		flags.IBMgraphics = !negated;
456.  		need_redraw = TRUE;
457.  		switch_graphics(flags.IBMgraphics ?
458.  				IBM_GRAPHICS : ASCII_GRAPHICS);
459.  #ifdef REINCARNATION
460.  		if (!initial && Is_rogue_level(&u.uz))
461.  			assign_rogue_graphics(TRUE);
462.  #endif
463.  		return;
464.  	}
465.  #endif /* ASCIIGRAPH */
466.  #ifdef MAC_GRAPHICS_ENV
467.  	if (!strncmpi(opts, "MACg", 4)) {
468.  #ifdef REINCARNATION
469.  		if (!initial && Is_rogue_level(&u.uz))
470.  			assign_rogue_graphics(FALSE);
471.  #endif
472.  		flags.MACgraphics = !negated;
473.  		need_redraw = TRUE;
474.  		switch_graphics(flags.MACgraphics ?
475.  				MAC_GRAPHICS : ASCII_GRAPHICS);
476.  #ifdef REINCARNATION
477.  		if (!initial && Is_rogue_level(&u.uz))
478.  			assign_rogue_graphics(TRUE);
479.  #endif
480.  		return;
481.  	}
482.  #endif /* MAC_GRAPHICS_ENV */
483.  
484.  	/* common boolean options */
485.  
486.  	if (!strncmpi(opts, "fem", 3)) {
487.  		if(!initial && flags.female == negated)
488.  			pline("That is not anatomically possible.");
489.  		else
490.  			flags.female = !negated;
491.  		return;
492.  	}
493.  
494.  	if (!strncmpi(opts, "fix", 3)) {
495.  		flags.invlet_constant = !negated;
496.  		if (!initial && flags.invlet_constant) reassign();
497.  		return;
498.  	}
499.  
500.  	if (!strncmpi(opts, "male", 4)) {
501.  		if(!initial && flags.female != negated)
502.  			pline("That is not anatomically possible.");
503.  		else
504.  			flags.female = negated;
505.  		return;
506.  	}
507.  
508.  	if (!strncmpi(opts, "num", 3)) {
509.  		flags.num_pad = !negated;
510.  		if (!initial) number_pad(flags.num_pad ? 1 : 0);
511.  		return;
512.  	}
513.  #ifdef EXP_ON_BOTL
514.  	if (!strncmpi(opts, "showexp", 7)) {
515.  		flags.showexp = !negated;
516.  		flags.botl = 1;
517.  		return;
518.  	}
519.  #endif
520.  #ifdef SCORE_ON_BOTL
521.  	if (!strncmpi(opts, "showscore", 9)) {
522.  		flags.showscore = !negated;
523.  		flags.botl = 1;
524.  		return;
525.  	}
526.  #endif
527.  	if (!strncmpi(opts, "time", 4)) {
528.  		flags.time = !negated;
529.  		flags.botl = 1;
530.  		return;
531.  	}
532.  
533.  	if (!strncmpi(opts, "legacy", 6)) {
534.  	        if(!initial) rejectoption("legacy");
535.  		else flags.legacy = !negated;
536.  		return;
537.  	}
538.  
539.  	/* compound options */
540.  
541.  	if (!strncmpi(opts, "pet", 3)) {
542.  		if ((op = string_for_env_opt("pettype", opts)) != 0)
543.  		    switch (*op) {
544.  			case 'd':	/* dog */
545.  			case 'D':
546.  			    preferred_pet = 'd';
547.  			    break;
548.  			case 'c':	/* cat */
549.  			case 'C':
550.  			case 'f':	/* feline */
551.  			case 'F':
552.  			    preferred_pet = 'c';
553.  			    break;
554.  			default:
555.  			    pline("Unrecognized pettype '%s'", op);
556.  			    break;
557.  		    }
558.  		return;
559.  	}
560.  
561.  	if (!strncmpi(opts, "cat", 3)) {
562.  		if ((op = string_for_env_opt("catname", opts)) != 0)
563.  			nmcpy(catname, op, 62);
564.  		return;
565.  	}
566.  
567.  	if (!strncmpi(opts, "dog", 3)) {
568.  		if ((op = string_for_env_opt("dogname", opts)) != 0)
569.  			nmcpy(dogname, op, 62);
570.  		return;
571.  	}
572.  
573.  	if (!strncmpi(opts, "msg", 3)) {
574.  		if ((op = string_for_env_opt("msghistory", opts)) != 0) {
575.  			flags.msg_history = atoi(op);
576.  		}
577.  		return;
578.  	}
579.  #ifdef TUTTI_FRUTTI
580.  	if (!strncmpi(opts, "fr", 2)) {
581.  		op = index(opts, ':');
582.  		if (!op) {
583.  			badoption(opts);
584.  			return;
585.  		}
586.  		op++;
587.  		if (!initial) {
588.  		    struct fruit *f;
589.  		    int numfruits = 0;
590.  
591.  		    for(f=ffruit; f; f=f->nextf) {
592.  			if (!strcmp(op, f->fname)) goto goodfruit;
593.  			numfruits++;
594.  		    }
595.  		    if (numfruits >= 100) {
596.  			pline("Doing that so many times isn't very fruitful.");
597.  			return;
598.  		    }
599.  		}
600.  goodfruit:
601.  		nmcpy(pl_fruit, op, PL_FSIZ);
602.  		if (!*pl_fruit)
603.  		    nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
604.  		if (!initial)
605.  		    (void)fruitadd(pl_fruit);
606.  		/* If initial, then initoptions is allowed to do it instead
607.  		 * of here (initoptions always has to do it even if there's
608.  		 * no fruit option at all.  Also, we don't want people
609.  		 * setting multiple fruits in their options.)
610.  		 */
611.  		return;
612.  	}
613.  #endif
614.  	/* graphics:string */
615.  	if (!strncmpi(opts, "gr", 2)) {
616.  		uchar translate[MAXPCHARS+1];
617.  		int lth;
618.  
619.  		if (!(opts = string_for_env_opt("graphics", opts)))
620.  			return;
621.  		escapes(opts, opts);
622.  
623.  		lth = strlen(opts);
624.  		if (lth > MAXPCHARS) lth = MAXPCHARS;
625.  		/* match the form obtained from PC configuration files */
626.  		for (i = 0; i < lth; i++)
627.  			translate[i] = (uchar) opts[i];
628.  		assign_graphics(translate, lth);
629.  		return;
630.  	}
631.  
632.  	/* objects:string */
633.  	if (!strncmpi(opts, "objects", 7)) {
634.  		int k, length;
635.  
636.  		if (!(opts = string_for_env_opt("objects", opts)))
637.  			return;
638.  		escapes(opts, opts);
639.  
640.  		/*
641.  		 * Override the default object class symbols.  The first
642.  		 * object in the object class is the "random object".  I
643.  		 * don't want to use 0 as an object class, so the "random
644.  		 * object" is basically a place holder.
645.  		 *
646.  		 * The object class symbols have already been initialized in
647.  		 * initoptions().
648.  		 */
649.  		length = strlen(opts);
650.  		if (length >= MAXOCLASSES)
651.  		    length = MAXOCLASSES-1;	/* don't count RANDOM_OBJECT */
652.  
653.  		for (k = 0; k < length; k++)
654.  		    oc_syms[k+1] = (uchar) opts[k];
655.  		return;
656.  	}
657.  
658.  	/* monsters:string */
659.  	if (!strncmpi(opts, "monsters", 8)) {
660.  		int k, length;
661.  
662.  		if (!(opts = string_for_env_opt("monsters", opts)))
663.  			return;
664.  		escapes(opts, opts);
665.  
666.  		/* Override default mon class symbols set in initoptions(). */
667.  		length = strlen(opts);
668.  		if (length >= MAXMCLASSES)
669.  		    length = MAXMCLASSES-1;	/* mon class 0 unused */
670.  
671.  		for (k = 0; k < length; k++)
672.  		    monsyms[k+1] = (uchar) opts[k];
673.  		return;
674.  	}
675.  
676.  	/* name:string */
677.  	if (!strncmpi(opts, "name", 4)) {
678.  		if ((op = string_for_env_opt("name", opts)) != 0)
679.  			nmcpy(plname, op, (int)sizeof(plname)-1);
680.  		return;
681.  	}
682.  
683.  	/* the order to list the pack */
684.  	if (!strncmpi(opts, "pack", 4)) {
685.  		op = index(opts,':');
686.  		if(!op) {
687.  			badoption(opts);
688.  			return;
689.  		}
690.  		op++;			/* skip : */
691.  
692.  		if (!change_inv_order(op, 1))
693.  		    	set_order = TRUE;
694.  		else
695.  		    	badoption(opts);
696.  		return;
697.  	}
698.  
699.  	/* scores:5t[op] 5a[round] o[wn] */
700.  	if (!strncmpi(opts, "scores", 6)) {
701.  		op = index(opts,':');
702.  		if(!op) {
703.  			badoption(opts);
704.  			return;
705.  		}
706.  		op++;
707.  		while(*op) {
708.  			num = 1;
709.  			if(digit(*op)) {
710.  				num = atoi(op);
711.  				while(digit(*op)) op++;
712.  			} else if(*op == '!') {
713.  				negated = !negated;
714.  				op++;
715.  			}
716.  			while(*op == ' ') op++;
717.  
718.  			switch(*op) {
719.  				case 't':
720.  				case 'T':
721.  					flags.end_top = num;
722.  					break;
723.  				case 'a':
724.  				case 'A':
725.  					flags.end_around = num;
726.  					break;
727.  				case 'o':
728.  				case 'O':
729.  					flags.end_own = !negated;
730.  					break;
731.  				default:
732.  					badoption(opts);
733.  					return;
734.  			}
735.  			while(letter(*++op) || *op == ' ') ;
736.  			if(*op == '/') op++;
737.  		}
738.  		return;
739.  	}
740.  	if (!strncmpi(opts, "win", 3)) {
741.  	    if ((op = string_for_env_opt("windowtype", opts)) != 0) {
742.  		char buf[16];
743.  		nmcpy(buf, op, 15);
744.  		choose_windows(buf);
745.  	    }
746.  	    return;
747.  	}
748.  
749.  	/* OK, if we still haven't recognized the option, check the boolean
750.  	 * options list
751.  	 */
752.  	for (i = 0; boolopt[i].name; i++) {
753.  		if (boolopt[i].addr && !strncmpi(boolopt[i].name, opts, 3)) {
754.  			*(boolopt[i].addr) = !negated;
755.  #ifdef TEXTCOLOR
756.  			if((boolopt[i].addr) == &flags.use_color)
757.  			    need_redraw = TRUE;
758.  
759.  			if((boolopt[i].addr) == &flags.hilite_pet)
760.  			    need_redraw = TRUE;
761.  #endif
762.  			if (!initial && boolopt[i].addr==&flags.lit_corridor) {
763.  			    /*
764.  			     * All corridor squares seen via night vision or
765.  			     * candles & lamps change.  Update them by calling
766.  			     * newsym() on them.  Don't do this if we are
767.  			     * initializing the options --- the vision system
768.  			     * isn't set up yet.
769.  			     */
770.  			    vision_recalc(2);		/* shut down vision */
771.  			    vision_full_recalc = 1;	/* delayed recalc */
772.  			}
773.  			return;
774.  		}
775.  	}
776.  
777.  	/* out of valid options */
778.  	badoption(opts);
779.  }
780.  
781.  /*
782.   * Convert the given string of object classes to a string of default object
783.   * symbols.
784.   */
785.  static void
786.  oc_to_str(src,dest)
787.      char *src, *dest;
788.  {
789.      int i;
790.  
791.      while ((i = (int) *src++) != 0) {
792.  	if (i < 0 || i >= MAXOCLASSES)
793.  	    impossible("oc_to_str:  illegal object class %d", i);
794.  	else
795.  	    *dest++ = def_oc_syms[i];
796.      }
797.      *dest = '\0';
798.  }
799.  
800.  #ifdef MICRO
801.  # define OPTIONS_HEADING "OPTIONS"
802.  #else
803.  # define OPTIONS_HEADING "NETHACKOPTIONS"
804.  #endif
805.  
806.  int
807.  doset()
808.  {
809.  	char buf[BUFSZ], pack_order[MAXOCLASSES+1], on_off;
810.  	const char *opt_name;
811.  	int i;
812.  	winid tmpwin;
813.  
814.  	switch (yn_function("Show the current settings [c], or set options [s]?",
815.  			    "csq", 'q')) {
816.  	default:
817.  	case 'q':
818.  	    clear_nhwindow(WIN_MESSAGE);
819.  	    return 0;
820.  	case 'c':
821.  	    tmpwin = create_nhwindow(NHW_MENU);
822.  	    putstr(tmpwin, 0, OPTIONS_HEADING);
823.  	    putstr(tmpwin, 0, "");
824.  	    /* print the booleans */
825.  	    for (i = 0; boolopt[i].name; i++) {
826.  		if (!boolopt[i].addr) continue;
827.  		opt_name = boolopt[i].name;
828.  		if (*(boolopt[i].addr)) {
829.  		    on_off = ' ';               /* on */
830.  		} else {
831.  		    if (!strcmp(opt_name, "female"))
832.  			opt_name = "male",  on_off = ' ';
833.  		    else
834.  			on_off = '!';           /* off */
835.  		}
836.  		Sprintf(buf, "%c%s", on_off, opt_name);
837.  		putstr(tmpwin, 0, buf);
838.  	    }
839.  	    /* print the compounds */
840.  	    Sprintf(buf, " catname: %s",
841.  			(catname[0] != 0) ? catname : "(null)");
842.  	    putstr(tmpwin, 0, buf);
843.  	    Sprintf(buf, " dogname: %s",
844.  			(dogname[0] != 0) ? dogname : "(null)");
845.  	    putstr(tmpwin, 0, buf);
846.  #ifdef TUTTI_FRUTTI
847.  	    Sprintf(buf, " fruit: %s", pl_fruit);
848.  	    putstr(tmpwin, 0, buf);
849.  #endif
850.  	    Sprintf(buf, " msghistory: %u", flags.msg_history);
851.  	    putstr(tmpwin, 0, buf);
852.  	    Sprintf(buf, " name: %s", plname);
853.  	    putstr(tmpwin, 0, buf);
854.  	    oc_to_str(inv_order, pack_order);
855.  	    Sprintf(buf, " packorder: %s", pack_order);
856.  	    putstr(tmpwin, 0, buf);
857.  	    Sprintf(buf, " pettype: %s", preferred_pet == 'c' ? "cat" :
858.  				    preferred_pet == 'd' ? "dog" : "random");
859.  	    putstr(tmpwin, 0, buf);
860.  	    Sprintf(buf, " scores: %utop/%uaround%s",
861.  		        flags.end_top, flags.end_around,
862.  		        (flags.end_own ? "/own" : ""));
863.  	    putstr(tmpwin, 0, buf);
864.  	    Sprintf(buf, " windowtype: %s", windowprocs.name);
865.  	    putstr(tmpwin, 0, buf);
866.  	    display_nhwindow(tmpwin, TRUE);
867.  	    destroy_nhwindow(tmpwin);
868.  	    break;
869.  	case 's':
870.  	    clear_nhwindow(WIN_MESSAGE);
871.  	    getlin("What options do you want to set?", buf);
872.  	    clear_nhwindow(WIN_MESSAGE);
873.  	    if(buf[0] == '\033') return 0;
874.  	    need_redraw = FALSE;
875.  	    parseoptions(buf, FALSE, FALSE);
876.  	    if(need_redraw)
877.  		(void) doredraw();
878.  	    break;
879.  	}
880.  
881.  	return 0;
882.  }
883.  
884.  int
885.  dotogglepickup() {
886.  	flags.pickup = !flags.pickup;
887.  	pline("Pickup: %s.", flags.pickup ? "ON" : "OFF");
888.  	return 0;
889.  }
890.  
891.  /* data for option_help() */
892.  static const char *opt_intro[] = {
893.  	"",
894.  	"                 NetHack Options Help:",
895.  	"",
896.  #define CONFIG_SLOT 3	/* fill in next value at run-time */
897.  	NULL,
898.  #ifndef MICRO
899.  	"or use `NETHACKOPTIONS=\"<options>\"' in your environment;",
900.  # ifdef VMS
901.  	"-- for example, $ DEFINE NETHACKOPTIONS \"nopickup,fruit:kumquat\"",
902.  # endif
903.  #endif
904.  	"or press \"O\" while playing, and type your <options> at the prompt.",
905.  	"In either case, <options> is a list of options separated by commas.",
906.  	"",
907.   "Boolean options (which can be negated by prefixing them with '!' or \"no\"):",
908.  	NULL
909.  };
910.  static const char *opt_compound[] = {
911.  	"Compound options:",
912.  	"`catname'   - the name of your (first) cat (e.g., catname:Tabby),",
913.  	"`dogname'   - the name of your (first) dog (e.g., dogname:Fang),",
914.  #ifdef TUTTI_FRUTTI
915.  	"`fruit'     - the name of a fruit you enjoy eating,",
916.  # define FRUIT_OFFSET 1
917.  #else
918.  # define FRUIT_OFFSET 0
919.  #endif
920.  	"`graphics'  - defines the symbols to use in drawing the dungeon map,",
921.  	"`monsters'  - defines the symbols to use for monsters,",
922.  	"`msghistory'- number of top line messages to save,",
923.  	"`name'      - your character's name (e.g., name:Merlin-W),",
924.  	"`objects'   - defines the symbols to use for objects,",
925.  	"`packorder' - the inventory order of the items in your pack",
926.  #define PCKORD_SLOT 9+FRUIT_OFFSET
927.  	NULL,
928.  	"`pettype'   - your preferred initial pet type,",
929.  	"`scores'    - the parts of the score list you wish to see,",
930.  	"`windowtype'- windowing system to use.",
931.  	"",
932.   "Some of the options can be set only before the game is started.  You will",
933.  	"be so informed, if you attempt to set them while in the game.",
934.  	NULL
935.  };
936.  
937.  void
938.  option_help()
939.  {
940.      char	buf[BUFSZ], pack_order[MAXOCLASSES+1];
941.      register int	i;
942.      winid datawin;
943.  
944.      datawin = create_nhwindow(NHW_TEXT);
945.  #ifdef AMIGA
946.      if(FromWBench){
947.  	Sprintf(buf,"Set options as OPTIONS= in %s or in icon;",configfile);
948.      } else
949.  #endif
950.  	Sprintf(buf, "Set options as OPTIONS=<options> in %s;", configfile);
951.      opt_intro[CONFIG_SLOT] = (const char *) buf;
952.      for (i = 0; opt_intro[i]; i++)
953.  	putstr(datawin, 0, opt_intro[i]);
954.  
955.      /* Boolean options */
956.      for (i = 0; boolopt[i].name; i++) {
957.  	if (boolopt[i].addr)
958.  	    next_opt(datawin, boolopt[i].name);
959.      }
960.      next_opt(datawin, "");
961.  
962.      /* Compound options */
963.      oc_to_str(inv_order, pack_order);
964.      Sprintf(buf, "              (currently, packorder:%s ),", pack_order);
965.  #if 0
966.      assert( opt_compound[PCKORD_SLOT] == NULL );
967.  #endif
968.      opt_compound[PCKORD_SLOT] = (const char *) buf;
969.      for (i = 0; opt_compound[i]; i++)
970.  	putstr(datawin, 0, opt_compound[i]);
971.  
972.      display_nhwindow(datawin, FALSE);
973.      destroy_nhwindow(datawin);
974.      return;
975.  }
976.  
977.  /*
978.   * prints the next boolean option, on the same line if possible, on a new
979.   * line if not. End with next_opt("").
980.   */
981.  void
982.  next_opt(datawin, str)
983.  winid datawin;
984.  const char *str;
985.  {
986.  	static char buf[121];
987.  	int i;
988.  	char *s;
989.  
990.  	if (!*str) {
991.  		for (s = buf; *s; s++);	/* find end of string */
992.  		if (s > &buf[1] && s[-2] == ',')
993.  			s[-2] = 0;	/* strip last ", " */
994.  		i = 121;
995.  	}
996.  	else	
997.  		i = strlen(buf) + strlen(str) + 2;
998.  
999.  	if (i > COLNO - 2) { /* rule of thumb */
1000. 		putstr(datawin, 0, buf);
1001. 		buf[0] = 0;
1002. 	}
1003. 	if (*str) {
1004. 		Strcat(buf, str);
1005. 		Strcat(buf, ", ");
1006. 	}
1007. 	else
1008. 		putstr(datawin, 0, str);
1009. 	return;
1010. }
1011. 
1012. #ifdef TUTTI_FRUTTI
1013. /* Returns the fid of the fruit type; if that type already exists, it
1014.  * returns the fid of that one; if it does not exist, it adds a new fruit
1015.  * type to the chain and returns the new one.
1016.  */
1017. int
1018. fruitadd(str)
1019. char *str;
1020. {
1021. 	register int i,j;
1022. 	register struct fruit *f;
1023. #ifdef GCC_WARN
1024. 	struct fruit *lastf = (struct fruit *)0;
1025. #else
1026. 	struct fruit *lastf;
1027. #endif
1028. 	int highest_fruit_id = 0;
1029. 	char buf[PL_FSIZ];
1030. 	boolean user_specified = (str == pl_fruit);
1031. 	/* if not user-specified, then it's a fruit name for a fruit on
1032. 	 * a bones level...
1033. 	 */
1034. 
1035. 	/* Note: every fruit has an id (spe for fruit objects) of at least
1036. 	 * 1; 0 is an error.
1037. 	 */
1038. 	if (user_specified) {
1039. 		/* disallow naming after other foods (since it'd be impossible
1040. 		 * to tell the difference)
1041. 		 */
1042. 
1043. 		boolean found = FALSE;
1044. 
1045. 		for(i = bases[j=letindex(FOOD_CLASS)]; i < bases[j+1]; i++) {
1046. 			if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) {
1047. 				found = TRUE;
1048. 				break;
1049. 			}
1050. 		}
1051. 		if (found ||
1052. 		    (!strncmp(str, "tin of ", 7) && name_to_mon(str+7) > -1) ||
1053. 		    !strcmp(str, "empty tin") ||
1054. 		    !strcmp(str, "tin of spinach") ||
1055. 		    ((!strncmp(eos(str)-6," corpse",7) ||
1056. 						!strncmp(eos(str)-3, " egg",4))
1057. 			&& name_to_mon(str) > -1))
1058. 			{
1059. 				Strcpy(buf, pl_fruit);
1060. 				Strcpy(pl_fruit, "candied ");
1061. 				nmcpy(pl_fruit+8, buf, PL_FSIZ-8);
1062. 		}
1063. 	}
1064. 	for(f=ffruit; f; f = f->nextf) {
1065. 		lastf = f;
1066. 		if(f->fid > highest_fruit_id) highest_fruit_id = f->fid;
1067. 		if(!strncmp(str, f->fname, PL_FSIZ))
1068. 			goto nonew;
1069. 	}
1070. 	/* if adding another fruit would overflow spe, use a random
1071. 	   fruit instead... we've got a lot to choose from. */
1072. 	if (highest_fruit_id >= 127) return rnd(127);
1073. 	highest_fruit_id++;
1074. 	f = newfruit();
1075. 	if (ffruit) lastf->nextf = f;
1076. 	else ffruit = f;
1077. 	Strcpy(f->fname, str);
1078. 	f->fid = highest_fruit_id;
1079. 	f->nextf = 0;
1080. nonew:
1081. 	if (user_specified) current_fruit = highest_fruit_id;
1082. 	return f->fid;
1083. }
1084. #endif
1085. 
1086. /*options.c*/