view rogue3/options.c @ 111:7f8f43943b1f

Fix some terribly depressing corruption during restore. In rogue5/state.c, rs_read_daemons() zeroes out the argument and delay if the daemon slot is empty. Unfortunately that code ended up on the wrong side of the brace that closes the for loop, so instead of running after each daemon, it got run once after the loop exited, when the index was of course out of bounds. This tended to manifest, when compiled with -O2, by overwriting hw and setting it to NULL. When inventory() next ran, hw would be passed to wgetch(), which returns ERR when it gets a NULL argument. This made md_readchar() think something was wrong and autosave the game. Upon investigation, rogue3 was found to commit the same mistake. rogue4 and srogue don't zero the data. arogue5 already does it properly. Someday I am going to run all this through Valgrind. Someday when I am a kinder person who will not be driven to invoke hordes of trolls and centaurs upon the original authors.
author John "Elwin" Edwards
date Wed, 08 Jan 2014 16:44:16 -0500
parents e676d52b5d09
children e52a8a7ad4c5
line wrap: on
line source

/*
 * This file has all the code for the option command.
 * I would rather this command were not necessary, but
 * it is the only way to keep the wolves off of my back.
 *
 * @(#)options.c	3.3 (Berkeley) 5/25/81
 *
 * Rogue: Exploring the Dungeons of Doom
 * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
 * All rights reserved.
 *
 * See the file LICENSE.TXT for full copyright and licensing information.
 */

#include <stdlib.h>
#include "curses.h"
#include <ctype.h>
#include <string.h>
#include "machdep.h"
#include "rogue.h"

#define	NUM_OPTS	(sizeof optlist / sizeof (OPTION))

/*
 * description of an option and what to do with it
 */
struct optstruct {
    char	*o_name;	/* option name */
    char	*o_prompt;	/* prompt for interactive entry */
    void	*o_opt;		/* pointer to thing to set */
    void	(*o_putfunc)();	/* function to print value */
    int		(*o_getfunc)();	/* function to get value interactively */
};

typedef struct optstruct	OPTION;

int allowchange(OPTION *opt); /* doesn't need to be externally visible */

OPTION	optlist[] = {
    {"terse",	 "Terse output: ",
		 (int *) &terse,	put_bool,	get_bool	},
    {"flush",	 "Flush typeahead during battle: ",
		 (int *) &fight_flush,	put_bool,	get_bool	},
    {"jump",	 "Show position only at end of run: ",
		 (int *) &jump,		put_bool,	get_bool	},
    {"step",	"Do inventories one line at a time: ",
		(int *) &slow_invent,	put_bool,	get_bool	},
    {"askme",	"Ask me about unidentified things: ",
		(int *) &askme,		put_bool,	get_bool	},
    {"name",	 "Name: ",
		 (int *) whoami,	put_str,	get_str		},
    {"fruit",	 "Fruit: ",
		 (int *) fruit,		put_str,	get_str		},
    {"file",	 "Save file: ",
		 (int *) file_name,	put_str,	get_str		}
};

/*
 * print and then set options from the terminal
 */
void
option()
{
    OPTION	*op;
    int	retval;

    wclear(hw);
    touchwin(hw);
    /*
     * Display current values of options
     */
    for (op = optlist; op <= &optlist[NUM_OPTS-1]; op++)
    {
        if (allowchange(op))
        {
	    waddstr(hw, op->o_prompt);
	    (*op->o_putfunc)(op->o_opt);
	    waddch(hw, '\n');
        }
    }
    /*
     * Set values
     */
    wmove(hw, 0, 0);
    for (op = optlist; op <= &optlist[NUM_OPTS-1]; op++)
    {
        if (!allowchange(op))
            continue;
	waddstr(hw, op->o_prompt);
	if ((retval = (*op->o_getfunc)(op->o_opt, hw)))
        {
	    if (retval == QUIT)
		break;
#if 0
/* Support for MINUS has been removed because making it work with 
 * hidden unchangable options without underflowing optlist will
 * require a complete rewrite.  And it should also be rewritten 
 * so option() doesn't count on get_str() to put the cursor in the
 * right place, because that makes it a pain to understand.
 */
	    else if (op > optlist) {	/* retval == MINUS */
		wmove(hw, (int)(op - optlist) - 1, 0);
		op -= 2;
	    }
	    else	/* trying to back up beyond the top */
	    {
		beep();
		wmove(hw, 0, 0);
		op--;
	    }
#else
            break;
#endif
        }
    }
    /*
     * Switch back to original screen
     */
    mvwaddstr(hw, LINES-1, 0, "--Press space to continue--");
    draw(hw);
    wait_for(hw,' ');
    clearok(cw, TRUE);
    touchwin(cw);
    after = FALSE;
}

/*
 * put out a boolean
 */
void
put_bool(void *b)
{
    waddstr(hw, *(int *)b ? "True" : "False");
}

/*
 * put out a string
 */
void
put_str(void *str)
{
    waddstr(hw, (char *) str);
}

/*
 * allow changing a boolean option and print it out
 */

int
get_bool(void *vp, WINDOW *win)
{
    int *bp = (int *) vp;
    int oy, ox;
    int op_bad;

    op_bad = TRUE;
    getyx(win, oy, ox);
    waddstr(win, *bp ? "True" : "False");
    while(op_bad)	
    {
	wmove(win, oy, ox);
	draw(win);
	switch (readchar(win))
	{
	    case 't':
	    case 'T':
		*bp = TRUE;
		op_bad = FALSE;
		break;
	    case 'f':
	    case 'F':
		*bp = FALSE;
		op_bad = FALSE;
		break;
	    case '\n':
	    case '\r':
		op_bad = FALSE;
		break;
	    case '\033':
	    case '\007':
		return QUIT;
	    case '-':
		return MINUS;
	    default:
		mvwaddstr(win, oy, ox + 10, "(T or F)");
	}
    }
    wmove(win, oy, ox);
    waddstr(win, *bp ? "True" : "False");
    waddch(win, '\n');
    return NORM;
}

/*
 * set a string option
 */
int
get_str(void *vopt, WINDOW *win)
{
    char *opt = (char *) vopt;
    char *sp;
    int c, oy, ox;
    char buf[80];

    draw(win);
    getyx(win, oy, ox);
    /*
     * loop reading in the string, and put it in a temporary buffer
     */
    for (sp = buf;
	(c = readchar(win)) != '\n' && c != '\r' && c != '\033' && c != '\007';
	wclrtoeol(win), draw(win))
    {
	if (c == -1)
	    continue;
	else if (c == md_erasechar())	/* process erase character */
	{
	    if (sp > buf)
	    {
		int i;
		int myx, myy;

		sp--;

		for (i = (int) strlen(unctrl(*sp)); i; i--)
		{
		    getyx(win,myy,myx);
		    if ((myx == 0)&& (myy > 0))
		    {
			wmove(win,myy-1,getmaxx(win)-1);
			waddch(win,' ');
			wmove(win,myy-1,getmaxx(win)-1);
		    }
		    else
			waddch(win, '\b');
		}
	    }
	    continue;
	}
	else if (c == md_killchar())	/* process kill character */
	{
	    sp = buf;
	    wmove(win, oy, ox);
	    continue;
	}
	else if (sp == buf)
	    if (c == '-')
		break;
	    else if (c == '~')
	    {
		strcpy(buf, home);
		waddstr(win, home);
		sp += strlen(home);
		continue;
	    }

	if ((sp - buf) < 78) /* Avoid overflow */
	{
	    *sp++ = c;
	    waddstr(win, unctrl(c));
	}
    }
    *sp = '\0';
    if (sp > buf)	/* only change option if something has been typed */
	strucpy(opt, buf, strlen(buf));
    wmove(win, oy, ox);
    waddstr(win, opt);
    waddch(win, '\n');
    draw(win);
    if (win == cw)
	mpos += (int)(sp - buf);
    if (c == '-')
	return MINUS;
    else if (c == '\033' || c == '\007')
	return QUIT;
    else
	return NORM;
}

/*
 * parse options from string, usually taken from the environment.
 * the string is a series of comma seperated values, with booleans
 * being stated as "name" (true) or "noname" (false), and strings
 * being "name=....", with the string being defined up to a comma
 * or the end of the entire option string.
 */

void
parse_opts(char *str)
{
    char *sp;
    OPTION *op;
    int len;

    while (*str)
    {
	/*
	 * Get option name
	 */
	for (sp = str; isalpha(*sp); sp++)
	    continue;
	len = (int)(sp - str);
	/*
	 * Look it up and deal with it
	 */
	for (op = optlist; op <= &optlist[NUM_OPTS-1]; op++)
        {
            /* If using system savefiles, changing your name or the 
               save file is not allowed. */
            if (!allowchange(op))
              continue;
	    if (EQSTR(str, op->o_name, len))
	    {
		if (op->o_putfunc == put_bool)	/* if option is a boolean */
		    *(int *)op->o_opt = TRUE;
		else				/* string option */
		{
		    char *start;
		    /*
		     * Skip to start of string value
		     */
		    for (str = sp + 1; *str == '='; str++)
			continue;
		    if (*str == '~')
		    {
			strcpy((char *) op->o_opt, home);
			start = (char *) op->o_opt + strlen(home);
			while (*++str == '/')
			    continue;
		    }
		    else
			start = (char *) op->o_opt;
		    /*
		     * Skip to end of string value
		     */
		    for (sp = str + 1; *sp && *sp != ','; sp++)
			continue;
		    strucpy(start, str, sp - str);
		}
		break;
	    }
	    /*
	     * check for "noname" for booleans
	     */
	    else if (op->o_putfunc == put_bool
	      && EQSTR(str, "no", 2) && EQSTR(str + 2, op->o_name, len - 2))
	    {
		*(int *)op->o_opt = FALSE;
		break;
	    }
        }

	/*
	 * skip to start of next option name
	 */
	while (*sp && !isalpha(*sp))
	    sp++;
	str = sp;
    }
}

/*
 * copy string using unctrl for things
 */
void
strucpy(char *s1, char *s2, size_t len)
{
    const char *sp;

    while (len--)
    {
    	sp = unctrl(*s2);
	strcpy(s1, sp);
	s1 += strlen(sp);
	s2++;
    }
    *s1 = '\0';
}

/* Tells whether the user is allowed to change the option. */
int allowchange(OPTION *opt)
{
  if (!use_savedir)
    return 1;
  if (!strcmp(opt->o_name, "name"))
    return 0;
  if (!strcmp(opt->o_name, "file"))
    return 0;
  return 1;
}