/*	SCCS Id: @(#)options.c	3.1	92/11/14	*/
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed.  See license for details. */

#include "hack.h"
#include "termcap.h"
#include <ctype.h>

/*
 *  NOTE:  If you add (or delete) an option, please update the short
 *  options help (option_help()), the long options help (dat/opthelp),
 *  and the current options setting display function (doset()).
 */

#if defined(TOS) && defined(TEXTCOLOR)
extern boolean colors_changed;	/* in tos.c */
#endif

extern const char *roles[];	/* from u_init.c */
extern char inv_order[];	/* from invent.c */

static boolean initial, from_file;
static boolean NEARDATA set_order;

static void FDECL(nmcpy, (char *, const char *, int));
static void FDECL(escapes, (const char *, char *));
static void FDECL(rejectoption, (const char *));
static void FDECL(badoption, (const char *));
static char *FDECL(string_for_env_opt, (const char *, char *));
static int FDECL(change_inv_order, (char *, int));
static void FDECL(oc_to_str, (char *, char *));

static struct Bool_Opt
{
	const char *name;
	boolean	*addr, initvalue;
} boolopt[] = {
#if defined(MICRO) && !defined(AMIGA)
	{"BIOS", &flags.BIOS, FALSE},
#endif
#ifdef INSURANCE
	{"checkpoint", &flags.ins_chkpt, TRUE},
#endif
#ifdef TEXTCOLOR
# ifdef MICRO
	{"color", &flags.use_color, TRUE},
# else	/* systems that support multiple terminals, many monochrome */
	{"color", &flags.use_color, FALSE},
# endif
#endif
	{"confirm",&flags.confirm, TRUE},
#ifdef TERMLIB
	{"DECgraphics", &flags.DECgraphics, FALSE},
#endif
	{"disclose", &flags.end_disclose, TRUE},
	{"female", &flags.female, FALSE},
	{"fixinv", &flags.invlet_constant, TRUE},
#ifdef AMIFLUSH
	{"flush", &flags.amiflush, FALSE},
#endif
	{"help", &flags.help, TRUE},
#ifdef TEXTCOLOR
	{"hilite_pet", &flags.hilite_pet, FALSE},
#endif
#ifdef ASCIIGRAPH
	{"IBMgraphics", &flags.IBMgraphics, FALSE},
#endif
	{"ignintr", &flags.ignintr, FALSE},
#ifdef MAC_GRAPHICS_ENV
	{"large_font", &flags.large_font, FALSE},
#endif
	{"legacy",&flags.legacy, TRUE},
	{"lit_corridor", &flags.lit_corridor, FALSE},
#ifdef MAC_GRAPHICS_ENV
	{"MACgraphics", &flags.MACgraphics, TRUE},
#endif
#ifdef NEWS
	{"news", &flags.news, TRUE},
#endif
	{"null", &flags.null, TRUE},
	{"number_pad", &flags.num_pad, FALSE},
	{"pickup", &flags.pickup, TRUE},
#ifdef MAC
	{"popup_dialog", &flags.popup_dialog, FALSE},
#endif
#if defined(MICRO) && !defined(AMIGA)
	{"rawio", &flags.rawio, FALSE},
#endif
	{"rest_on_space", &flags.rest_on_space, FALSE},
	{"safepet", &flags.safe_dog, TRUE},
#ifdef EXP_ON_BOTL
	{"showexp", &flags.showexp, FALSE},
#endif
#ifdef SCORE_ON_BOTL
	{"showscore", &flags.showscore, FALSE},
#endif
	{"silent", &flags.silent, TRUE},
	{"sortpack", &flags.sortpack, TRUE},
	{"sound", &flags.soundok, TRUE},
	{"standout", &flags.standout, FALSE},
	{"time", &flags.time, FALSE},
	{"tombstone",&flags.tombstone, TRUE},
	{"verbose", &flags.verbose, TRUE},
	{NULL, (boolean *)0, FALSE}
};

static boolean need_redraw; /* for doset() */

void
initoptions()
{
	register char *opts;
	int i;

	for (i = 0; boolopt[i].name; i++) {
		if (boolopt[i].addr)
			*(boolopt[i].addr) = boolopt[i].initvalue;
	}
	flags.end_own = FALSE;
	flags.end_top = 3;
	flags.end_around = 2;
	flags.msg_history = 20;

	/* Set the default monster and object class symbols.  Don't use */
	/* memcpy() --- sizeof char != sizeof uchar on some machines.	*/
	for (i = 0; i < MAXOCLASSES; i++)
	    	oc_syms[i] = (uchar) def_oc_syms[i];
	for (i = 0; i < MAXMCLASSES; i++)
	    	monsyms[i] = (uchar) def_monsyms[i];

	switch_graphics(ASCII_GRAPHICS);	/* set default characters */
#ifdef UNIX
	/*
	 * Set defaults for some options depending on what we can
	 * detect about the environment's capabilities.
	 * This has to be done after the global initialization above
	 * and before reading user-specific initialization via
	 * config file/environment variable below.
	 */
	/* this detects the IBM-compatible console on most 386 boxes */
	if (!strncmp(getenv("TERM"), "AT", 2)) {
		switch_graphics(IBM_GRAPHICS);
# ifdef TEXTCOLOR
		flags.use_color = TRUE;
# endif
	}
#endif /* UNIX */
#if defined(UNIX) || defined(VMS)
	/* detect whether a "vt" terminal can handle alternate charsets */
	if (!strncmpi(getenv("TERM"), "vt", 2) && (AS && AE) &&
	    !strcmp(AS, "\016") && !strcmp(AE, "\017")) {
		switch_graphics(DEC_GRAPHICS);
	}
#endif /* UNIX || VMS */

#ifdef MAC_GRAPHICS_ENV
	switch_graphics(MAC_GRAPHICS);
#endif /* MAC_GRAPHICS_ENV */

#ifdef TUTTI_FRUTTI
	/* since this is done before init_objects(), do partial init here */
	objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD;
	nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
#endif
	opts = getenv("NETHACKOPTIONS");
	if (!opts) opts = getenv("HACKOPTIONS");
	if (opts)
		if (*opts == '/' || *opts == '\\' || *opts == '@') {
			if (*opts == '@') opts++;	/* @filename */
			/* looks like a filename */
			read_config_file(opts);
		} else {
			read_config_file(NULL);
			parseoptions(opts, TRUE, FALSE);
		}
	else
		read_config_file(NULL);
#ifdef AMIGA
	ami_wbench_init();	/* must be here or can't set fruit */
#endif
#ifdef TUTTI_FRUTTI
	(void)fruitadd(pl_fruit);
	/* Remove "slime mold" from list of object names; this will	*/
	/* prevent it from being wished unless it's actually present	*/
	/* as a named (or default) fruit.  Wishing for "fruit" will	*/
	/* result in the player's preferred fruit [better than "\033"].	*/
	obj_descr[SLIME_MOLD].oc_name = "fruit";
#endif
	if(flags.female)  {	/* should have been set in NETHACKOPTIONS */
		roles[2] = "Cavewoman";
		roles[6] = "Priestess";
	}
}

static void
nmcpy(dest, src, maxlen)
	char	*dest;
	const char *src;
	int	maxlen;
{
	int	count;

	for(count = 1; count < maxlen; count++) {
		if(*src == ',' || *src == '\0') break; /*exit on \0 terminator*/
		*dest++ = *src++;
	}
	*dest = 0;
}

/*
 * escapes: escape expansion for showsyms. C-style escapes understood include
 * \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal). The ^-prefix
 * for control characters is also understood, and \[mM] followed by any of the
 * previous forms or by a character has the effect of 'meta'-ing the value (so
 * that the alternate character set will be enabled).
 */
static void
escapes(cp, tp)
const char	*cp;
char *tp;
{
    while (*cp)
    {
	int	cval = 0, meta = 0;

	if (*cp == '\\' && index("mM", cp[1])) {
		meta = 1;
		cp += 2;
	}
	if (*cp == '\\' && index("0123456789xXoO", cp[1]))
	{
	    const char *dp, *hex = "00112233445566778899aAbBcCdDeEfF";
	    int dcount = 0;

	    cp++;
	    if (*cp == 'x' || *cp == 'X')
		for (++cp; (dp = index(hex, *cp)) && (dcount++ < 2); cp++)
		    cval = (cval * 16) + (dp - hex) / 2;
	    else if (*cp == 'o' || *cp == 'O')
		for (++cp; (index("01234567",*cp)) && (dcount++ < 3); cp++)
		    cval = (cval * 8) + (*cp - '0');
	    else
		for (; (index("0123456789",*cp)) && (dcount++ < 3); cp++)
		    cval = (cval * 10) + (*cp - '0');
	}
	else if (*cp == '\\')		/* C-style character escapes */
	{
	    switch (*++cp)
	    {
	    case '\\': cval = '\\'; break;
	    case 'n': cval = '\n'; break;
	    case 't': cval = '\t'; break;
	    case 'b': cval = '\b'; break;
	    case 'r': cval = '\r'; break;
	    default: cval = *cp;
	    }
	    cp++;
	}
	else if (*cp == '^')		/* expand control-character syntax */
	{
	    cval = (*++cp & 0x1f);
	    cp++;
	}
	else
	    cval = *cp++;
	if (meta)
	    cval |= 0x80;
	*tp++ = cval;
    }
    *tp = '\0';
}

static void
rejectoption(optname)
const char *optname;
{
#ifdef MICRO
# ifdef AMIGA
	if(FromWBench){
		pline("\"%s\" settable only from %s or in icon.",
			optname, configfile);
	} else
# endif
		pline("\"%s\" settable only from %s.", optname, configfile);
#else
	pline("%s can be set only from NETHACKOPTIONS or %s.", optname,
			configfile);
#endif
}

static void
badoption(opts)
const char *opts;
{
	if(!initial) {
	    if(!strncmp(opts, "h", 1) || !strncmp(opts, "?", 1))
		option_help();
	    else
		pline("Unknown option: %s.  Enter \"?g\" for help.", opts);
	    return;
	}
# ifdef AMIGA
	if(ami_wbench_badopt(opts)) {
# endif
	if(from_file)
	    raw_printf("Bad syntax in OPTIONS in %s: %s.", configfile, opts);
	else
	    raw_printf("Bad syntax in NETHACKOPTIONS: %s.", opts);
# ifdef AMIGA
	}
# endif
	wait_synch();
}

static char *
string_for_env_opt(optname, opts)
const char *optname;
char *opts;
{
	register char *colon;

	if(!initial) {
		rejectoption(optname);
		return NULL;
	}
	colon = index(opts,':');
	if(!colon) {
		badoption(opts);
		return NULL;
	}
	return ++colon;
}

/*
 * Change the inventory order, using the given string as the new order.
 * Missing characters in the new order are filled in at the end from
 * the current inv_order.
 *
 * This routine always returns 1 unless the parameter 'fail' is true
 * and there is a duplicate or bad char in the string.
 */
static int
change_inv_order(op, fail)
    char *op;
    int  fail;	/* If TRUE, return 0 if any duplicates or bad chars. */
{
    int oc_sym, num;
    char *sp, *tmp, buf[BUFSZ];

    for (sp = op; *sp; sp++) {
	oc_sym = def_char_to_objclass(*sp);

	/* Remove bad or duplicate entries. */
	if (oc_sym == MAXOCLASSES ||
		(!index(inv_order, oc_sym)) || (index(sp+1, *sp))) {

	    if (fail) return 0;
	    for(tmp = sp; *tmp; tmp++)
		tmp[0] = tmp[1];
	    sp--;
	} else
	    *sp = (char) oc_sym;
    } 
    Strcpy(buf, op);
    for (sp = inv_order, num = strlen(buf); *sp; sp++)
	if (!index(buf, *sp))
	    buf[num++] = *sp;

    buf[num] = 0;
    Strcpy(inv_order, buf);
    return 1;
}

void
parseoptions(opts, tinitial, tfrom_file)
register char *opts;
boolean tinitial, tfrom_file;
{
	register char *op;
	unsigned num;
	boolean negated;
	int i;

	initial = tinitial;
	from_file = tfrom_file;
	if ((op = index(opts, ',')) != 0) {
		*op++ = 0;
		parseoptions(op, initial, from_file);
	}

	/* strip leading and trailing white space */
	while (isspace(*opts)) opts++;
	op = eos(opts);
	while (--op >= opts && isspace(*op)) *op = '\0';

	if(!*opts) return;
	negated = FALSE;
	while((*opts == '!') || !strncmpi(opts, "no", 2)) {
		if(*opts == '!') opts++; else opts += 2;
		negated = !negated;
	}
	
#if defined(MICRO) && !defined(AMIGA)
	/* included for compatibility with old NetHack.cnf files */
	if (!strncmp(opts, "IBM_", 4)) {
		flags.BIOS = !negated;
		return;
	}

	/* put here cause it has to come from the config file */
	if (!strncmpi(opts, "raw", 3)) {
		if (initial)
			flags.rawio = !negated;
		else
			rejectoption("rawio");
		return;
	}
#endif /* MICRO */

#if defined(TOS) && defined(TEXTCOLOR)
	if (!strncmpi(opts, "col", 3)) {
		flags.use_color = !negated;
		if (flags.BIOS && !initial) {
			if (colors_changed)
				restore_colors();
			else
				set_colors();
		}
	}
#endif
	/* other special-case boolean options */
#ifdef TERMLIB
	if (!strncmpi(opts, "DEC", 3)) {
#ifdef REINCARNATION
		if (!initial && Is_rogue_level(&u.uz))
			assign_rogue_graphics(FALSE);
#endif
		flags.DECgraphics = !negated;
		need_redraw = TRUE;
		switch_graphics(flags.DECgraphics ?
				DEC_GRAPHICS : ASCII_GRAPHICS);
#ifdef REINCARNATION
		if (!initial && Is_rogue_level(&u.uz))
			assign_rogue_graphics(TRUE);
#endif
		return;
	}
#endif /* TERMLIB */
#ifdef ASCIIGRAPH
	if (!strncmpi(opts, "IBMg", 4)) {
#ifdef REINCARNATION
		if (!initial && Is_rogue_level(&u.uz))
			assign_rogue_graphics(FALSE);
#endif
		flags.IBMgraphics = !negated;
		need_redraw = TRUE;
		switch_graphics(flags.IBMgraphics ?
				IBM_GRAPHICS : ASCII_GRAPHICS);
#ifdef REINCARNATION
		if (!initial && Is_rogue_level(&u.uz))
			assign_rogue_graphics(TRUE);
#endif
		return;
	}
#endif /* ASCIIGRAPH */
#ifdef MAC_GRAPHICS_ENV
	if (!strncmpi(opts, "MACg", 4)) {
#ifdef REINCARNATION
		if (!initial && Is_rogue_level(&u.uz))
			assign_rogue_graphics(FALSE);
#endif
		flags.MACgraphics = !negated;
		need_redraw = TRUE;
		switch_graphics(flags.MACgraphics ?
				MAC_GRAPHICS : ASCII_GRAPHICS);
#ifdef REINCARNATION
		if (!initial && Is_rogue_level(&u.uz))
			assign_rogue_graphics(TRUE);
#endif
		return;
	}
#endif /* MAC_GRAPHICS_ENV */

	/* common boolean options */

	if (!strncmpi(opts, "fem", 3)) {
		if(!initial && flags.female == negated)
			pline("That is not anatomically possible.");
		else
			flags.female = !negated;
		return;
	}

	if (!strncmpi(opts, "fix", 3)) {
		flags.invlet_constant = !negated;
		if (!initial && flags.invlet_constant) reassign();
		return;
	}

	if (!strncmpi(opts, "male", 4)) {
		if(!initial && flags.female != negated)
			pline("That is not anatomically possible.");
		else
			flags.female = negated;
		return;
	}

	if (!strncmpi(opts, "num", 3)) {
		flags.num_pad = !negated;
		if (!initial) number_pad(flags.num_pad ? 1 : 0);
		return;
	}
#ifdef EXP_ON_BOTL
	if (!strncmpi(opts, "showexp", 7)) {
		flags.showexp = !negated;
		flags.botl = 1;
		return;
	}
#endif
#ifdef SCORE_ON_BOTL
	if (!strncmpi(opts, "showscore", 9)) {
		flags.showscore = !negated;
		flags.botl = 1;
		return;
	}
#endif
	if (!strncmpi(opts, "time", 4)) {
		flags.time = !negated;
		flags.botl = 1;
		return;
	}

	if (!strncmpi(opts, "legacy", 6)) {
	        if(!initial) rejectoption("legacy");
		else flags.legacy = !negated;
		return;
	}

	/* compound options */

	if (!strncmpi(opts, "pet", 3)) {
		if ((op = string_for_env_opt("pettype", opts)) != 0)
		    switch (*op) {
			case 'd':	/* dog */
			case 'D':
			    preferred_pet = 'd';
			    break;
			case 'c':	/* cat */
			case 'C':
			case 'f':	/* feline */
			case 'F':
			    preferred_pet = 'c';
			    break;
			default:
			    pline("Unrecognized pettype '%s'", op);
			    break;
		    }
		return;
	}

	if (!strncmpi(opts, "cat", 3)) {
		if ((op = string_for_env_opt("catname", opts)) != 0)
			nmcpy(catname, op, 62);
		return;
	}

	if (!strncmpi(opts, "dog", 3)) {
		if ((op = string_for_env_opt("dogname", opts)) != 0)
			nmcpy(dogname, op, 62);
		return;
	}

	if (!strncmpi(opts, "msg", 3)) {
		if ((op = string_for_env_opt("msghistory", opts)) != 0) {
			flags.msg_history = atoi(op);
		}
		return;
	}
#ifdef TUTTI_FRUTTI
	if (!strncmpi(opts, "fr", 2)) {
		op = index(opts, ':');
		if (!op) {
			badoption(opts);
			return;
		}
		op++;
		if (!initial) {
		    struct fruit *f;
		    int numfruits = 0;

		    for(f=ffruit; f; f=f->nextf) {
			if (!strcmp(op, f->fname)) goto goodfruit;
			numfruits++;
		    }
		    if (numfruits >= 100) {
			pline("Doing that so many times isn't very fruitful.");
			return;
		    }
		}
goodfruit:
		nmcpy(pl_fruit, op, PL_FSIZ);
		if (!*pl_fruit)
		    nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
		if (!initial)
		    (void)fruitadd(pl_fruit);
		/* If initial, then initoptions is allowed to do it instead
		 * of here (initoptions always has to do it even if there's
		 * no fruit option at all.  Also, we don't want people
		 * setting multiple fruits in their options.)
		 */
		return;
	}
#endif
	/* graphics:string */
	if (!strncmpi(opts, "gr", 2)) {
		uchar translate[MAXPCHARS+1];
		int lth;

		if (!(opts = string_for_env_opt("graphics", opts)))
			return;
		escapes(opts, opts);

		lth = strlen(opts);
		if (lth > MAXPCHARS) lth = MAXPCHARS;
		/* match the form obtained from PC configuration files */
		for (i = 0; i < lth; i++)
			translate[i] = (uchar) opts[i];
		assign_graphics(translate, lth);
		return;
	}

	/* objects:string */
	if (!strncmpi(opts, "objects", 7)) {
		int k, length;

		if (!(opts = string_for_env_opt("objects", opts)))
			return;
		escapes(opts, opts);

		/*
		 * Override the default object class symbols.  The first
		 * object in the object class is the "random object".  I
		 * don't want to use 0 as an object class, so the "random
		 * object" is basically a place holder.
		 *
		 * The object class symbols have already been initialized in
		 * initoptions().
		 */
		length = strlen(opts);
		if (length >= MAXOCLASSES)
		    length = MAXOCLASSES-1;	/* don't count RANDOM_OBJECT */

		for (k = 0; k < length; k++)
		    oc_syms[k+1] = (uchar) opts[k];
		return;
	}

	/* monsters:string */
	if (!strncmpi(opts, "monsters", 8)) {
		int k, length;

		if (!(opts = string_for_env_opt("monsters", opts)))
			return;
		escapes(opts, opts);

		/* Override default mon class symbols set in initoptions(). */
		length = strlen(opts);
		if (length >= MAXMCLASSES)
		    length = MAXMCLASSES-1;	/* mon class 0 unused */

		for (k = 0; k < length; k++)
		    monsyms[k+1] = (uchar) opts[k];
		return;
	}

	/* name:string */
	if (!strncmpi(opts, "name", 4)) {
		if ((op = string_for_env_opt("name", opts)) != 0)
			nmcpy(plname, op, (int)sizeof(plname)-1);
		return;
	}

	/* the order to list the pack */
	if (!strncmpi(opts, "pack", 4)) {
		op = index(opts,':');
		if(!op) {
			badoption(opts);
			return;
		}
		op++;			/* skip : */

		if (!change_inv_order(op, 1))
		    	set_order = TRUE;
		else
		    	badoption(opts);
		return;
	}

	/* scores:5t[op] 5a[round] o[wn] */
	if (!strncmpi(opts, "scores", 6)) {
		op = index(opts,':');
		if(!op) {
			badoption(opts);
			return;
		}
		op++;
		while(*op) {
			num = 1;
			if(digit(*op)) {
				num = atoi(op);
				while(digit(*op)) op++;
			} else if(*op == '!') {
				negated = !negated;
				op++;
			}
			while(*op == ' ') op++;

			switch(*op) {
				case 't':
				case 'T':
					flags.end_top = num;
					break;
				case 'a':
				case 'A':
					flags.end_around = num;
					break;
				case 'o':
				case 'O':
					flags.end_own = !negated;
					break;
				default:
					badoption(opts);
					return;
			}
			while(letter(*++op) || *op == ' ') ;
			if(*op == '/') op++;
		}
		return;
	}
	if (!strncmpi(opts, "win", 3)) {
	    if ((op = string_for_env_opt("windowtype", opts)) != 0) {
		char buf[16];
		nmcpy(buf, op, 15);
		choose_windows(buf);
	    }
	    return;
	}

	/* OK, if we still haven't recognized the option, check the boolean
	 * options list
	 */
	for (i = 0; boolopt[i].name; i++) {
		if (boolopt[i].addr && !strncmpi(boolopt[i].name, opts, 3)) {
			*(boolopt[i].addr) = !negated;
#ifdef TEXTCOLOR
			if((boolopt[i].addr) == &flags.use_color)
			    need_redraw = TRUE;

			if((boolopt[i].addr) == &flags.hilite_pet)
			    need_redraw = TRUE;
#endif
			if (!initial && boolopt[i].addr==&flags.lit_corridor) {
			    /*
			     * All corridor squares seen via night vision or
			     * candles & lamps change.  Update them by calling
			     * newsym() on them.  Don't do this if we are
			     * initializing the options --- the vision system
			     * isn't set up yet.
			     */
			    vision_recalc(2);		/* shut down vision */
			    vision_full_recalc = 1;	/* delayed recalc */
			}
			return;
		}
	}

	/* out of valid options */
	badoption(opts);
}

/*
 * Convert the given string of object classes to a string of default object
 * symbols.
 */
static void
oc_to_str(src,dest)
    char *src, *dest;
{
    int i;

    while ((i = (int) *src++) != 0) {
	if (i < 0 || i >= MAXOCLASSES)
	    impossible("oc_to_str:  illegal object class %d", i);
	else
	    *dest++ = def_oc_syms[i];
    }
    *dest = '\0';
}

#ifdef MICRO
# define OPTIONS_HEADING "OPTIONS"
#else
# define OPTIONS_HEADING "NETHACKOPTIONS"
#endif

int
doset()
{
	char buf[BUFSZ], pack_order[MAXOCLASSES+1], on_off;
	const char *opt_name;
	int i;
	winid tmpwin;

	switch (yn_function("Show the current settings [c], or set options [s]?",
			    "csq", 'q')) {
	default:
	case 'q':
	    clear_nhwindow(WIN_MESSAGE);
	    return 0;
	case 'c':
	    tmpwin = create_nhwindow(NHW_MENU);
	    putstr(tmpwin, 0, OPTIONS_HEADING);
	    putstr(tmpwin, 0, "");
	    /* print the booleans */
	    for (i = 0; boolopt[i].name; i++) {
		if (!boolopt[i].addr) continue;
		opt_name = boolopt[i].name;
		if (*(boolopt[i].addr)) {
		    on_off = ' ';               /* on */
		} else {
		    if (!strcmp(opt_name, "female"))
			opt_name = "male",  on_off = ' ';
		    else
			on_off = '!';           /* off */
		}
		Sprintf(buf, "%c%s", on_off, opt_name);
		putstr(tmpwin, 0, buf);
	    }
	    /* print the compounds */
	    Sprintf(buf, " catname: %s",
			(catname[0] != 0) ? catname : "(null)");
	    putstr(tmpwin, 0, buf);
	    Sprintf(buf, " dogname: %s",
			(dogname[0] != 0) ? dogname : "(null)");
	    putstr(tmpwin, 0, buf);
#ifdef TUTTI_FRUTTI
	    Sprintf(buf, " fruit: %s", pl_fruit);
	    putstr(tmpwin, 0, buf);
#endif
	    Sprintf(buf, " msghistory: %u", flags.msg_history);
	    putstr(tmpwin, 0, buf);
	    Sprintf(buf, " name: %s", plname);
	    putstr(tmpwin, 0, buf);
	    oc_to_str(inv_order, pack_order);
	    Sprintf(buf, " packorder: %s", pack_order);
	    putstr(tmpwin, 0, buf);
	    Sprintf(buf, " pettype: %s", preferred_pet == 'c' ? "cat" :
				    preferred_pet == 'd' ? "dog" : "random");
	    putstr(tmpwin, 0, buf);
	    Sprintf(buf, " scores: %utop/%uaround%s",
		        flags.end_top, flags.end_around,
		        (flags.end_own ? "/own" : ""));
	    putstr(tmpwin, 0, buf);
	    Sprintf(buf, " windowtype: %s", windowprocs.name);
	    putstr(tmpwin, 0, buf);
	    display_nhwindow(tmpwin, TRUE);
	    destroy_nhwindow(tmpwin);
	    break;
	case 's':
	    clear_nhwindow(WIN_MESSAGE);
	    getlin("What options do you want to set?", buf);
	    clear_nhwindow(WIN_MESSAGE);
	    if(buf[0] == '\033') return 0;
	    need_redraw = FALSE;
	    parseoptions(buf, FALSE, FALSE);
	    if(need_redraw)
		(void) doredraw();
	    break;
	}

	return 0;
}

int
dotogglepickup() {
	flags.pickup = !flags.pickup;
	pline("Pickup: %s.", flags.pickup ? "ON" : "OFF");
	return 0;
}

/* data for option_help() */
static const char *opt_intro[] = {
	"",
	"                 NetHack Options Help:",
	"",
#define CONFIG_SLOT 3	/* fill in next value at run-time */
	NULL,
#ifndef MICRO
	"or use `NETHACKOPTIONS=\"<options>\"' in your environment;",
# ifdef VMS
	"-- for example, $ DEFINE NETHACKOPTIONS \"nopickup,fruit:kumquat\"",
# endif
#endif
	"or press \"O\" while playing, and type your <options> at the prompt.",
	"In either case, <options> is a list of options separated by commas.",
	"",
 "Boolean options (which can be negated by prefixing them with '!' or \"no\"):",
	NULL
};
static const char *opt_compound[] = {
	"Compound options:",
	"`catname'   - the name of your (first) cat (e.g., catname:Tabby),",
	"`dogname'   - the name of your (first) dog (e.g., dogname:Fang),",
#ifdef TUTTI_FRUTTI
	"`fruit'     - the name of a fruit you enjoy eating,",
# define FRUIT_OFFSET 1
#else
# define FRUIT_OFFSET 0
#endif
	"`graphics'  - defines the symbols to use in drawing the dungeon map,",
	"`monsters'  - defines the symbols to use for monsters,",
	"`msghistory'- number of top line messages to save,",
	"`name'      - your character's name (e.g., name:Merlin-W),",
	"`objects'   - defines the symbols to use for objects,",
	"`packorder' - the inventory order of the items in your pack",
#define PCKORD_SLOT 9+FRUIT_OFFSET
	NULL,
	"`pettype'   - your preferred initial pet type,",
	"`scores'    - the parts of the score list you wish to see,",
	"`windowtype'- windowing system to use.",
	"",
 "Some of the options can be set only before the game is started.  You will",
	"be so informed, if you attempt to set them while in the game.",
	NULL
};

void
option_help()
{
    char	buf[BUFSZ], pack_order[MAXOCLASSES+1];
    register int	i;
    winid datawin;

    datawin = create_nhwindow(NHW_TEXT);
#ifdef AMIGA
    if(FromWBench){
	Sprintf(buf,"Set options as OPTIONS= in %s or in icon;",configfile);
    } else
#endif
	Sprintf(buf, "Set options as OPTIONS=<options> in %s;", configfile);
    opt_intro[CONFIG_SLOT] = (const char *) buf;
    for (i = 0; opt_intro[i]; i++)
	putstr(datawin, 0, opt_intro[i]);

    /* Boolean options */
    for (i = 0; boolopt[i].name; i++) {
	if (boolopt[i].addr)
	    next_opt(datawin, boolopt[i].name);
    }
    next_opt(datawin, "");

    /* Compound options */
    oc_to_str(inv_order, pack_order);
    Sprintf(buf, "              (currently, packorder:%s ),", pack_order);
#if 0
    assert( opt_compound[PCKORD_SLOT] == NULL );
#endif
    opt_compound[PCKORD_SLOT] = (const char *) buf;
    for (i = 0; opt_compound[i]; i++)
	putstr(datawin, 0, opt_compound[i]);

    display_nhwindow(datawin, FALSE);
    destroy_nhwindow(datawin);
    return;
}

/*
 * prints the next boolean option, on the same line if possible, on a new
 * line if not. End with next_opt("").
 */
void
next_opt(datawin, str)
winid datawin;
const char *str;
{
	static char buf[121];
	int i;
	char *s;

	if (!*str) {
		for (s = buf; *s; s++);	/* find end of string */
		if (s > &buf[1] && s[-2] == ',')
			s[-2] = 0;	/* strip last ", " */
		i = 121;
	}
	else	
		i = strlen(buf) + strlen(str) + 2;

	if (i > COLNO - 2) { /* rule of thumb */
		putstr(datawin, 0, buf);
		buf[0] = 0;
	}
	if (*str) {
		Strcat(buf, str);
		Strcat(buf, ", ");
	}
	else
		putstr(datawin, 0, str);
	return;
}

#ifdef TUTTI_FRUTTI
/* Returns the fid of the fruit type; if that type already exists, it
 * returns the fid of that one; if it does not exist, it adds a new fruit
 * type to the chain and returns the new one.
 */
int
fruitadd(str)
char *str;
{
	register int i,j;
	register struct fruit *f;
#ifdef GCC_WARN
	struct fruit *lastf = (struct fruit *)0;
#else
	struct fruit *lastf;
#endif
	int highest_fruit_id = 0;
	char buf[PL_FSIZ];
	boolean user_specified = (str == pl_fruit);
	/* if not user-specified, then it's a fruit name for a fruit on
	 * a bones level...
	 */

	/* Note: every fruit has an id (spe for fruit objects) of at least
	 * 1; 0 is an error.
	 */
	if (user_specified) {
		/* disallow naming after other foods (since it'd be impossible
		 * to tell the difference)
		 */

		boolean found = FALSE;

		for(i = bases[j=letindex(FOOD_CLASS)]; i < bases[j+1]; i++) {
			if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) {
				found = TRUE;
				break;
			}
		}
		if (found ||
		    (!strncmp(str, "tin of ", 7) && name_to_mon(str+7) > -1) ||
		    !strcmp(str, "empty tin") ||
		    !strcmp(str, "tin of spinach") ||
		    ((!strncmp(eos(str)-6," corpse",7) ||
						!strncmp(eos(str)-3, " egg",4))
			&& name_to_mon(str) > -1))
			{
				Strcpy(buf, pl_fruit);
				Strcpy(pl_fruit, "candied ");
				nmcpy(pl_fruit+8, buf, PL_FSIZ-8);
		}
	}
	for(f=ffruit; f; f = f->nextf) {
		lastf = f;
		if(f->fid > highest_fruit_id) highest_fruit_id = f->fid;
		if(!strncmp(str, f->fname, PL_FSIZ))
			goto nonew;
	}
	/* if adding another fruit would overflow spe, use a random
	   fruit instead... we've got a lot to choose from. */
	if (highest_fruit_id >= 127) return rnd(127);
	highest_fruit_id++;
	f = newfruit();
	if (ffruit) lastf->nextf = f;
	else ffruit = f;
	Strcpy(f->fname, str);
	f->fid = highest_fruit_id;
	f->nextf = 0;
nonew:
	if (user_specified) current_fruit = highest_fruit_id;
	return f->fid;
}
#endif

/*options.c*/
