view xrogue/options.c @ 254:e940e6c00751

Add some braces to a loop in parse_opts(). A for loop had no braces around its body, which was a single if-else statement. In Advanced Rogue 5, another statement had been added, accidentally removing the if-else from the loop. This could have resulted in an out-of-bounds access to the options array. In the other games, the added braces are only for clarity.
author John "Elwin" Edwards
date Fri, 10 Feb 2017 09:02:58 -0500
parents 50b89f165a34
children e52a8a7ad4c5
line wrap: on
line source

/*
    options.c - This file has all the code for the option command
    
    XRogue: Expeditions into the Dungeons of Doom
    Copyright (C) 1991 Robert Pietkivitch
    All rights reserved.
    
    Based on "Advanced Rogue"
    Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka and AT&T
    All rights reserved.

    Based on "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.
*/  
    
 /*
 * I would rather this command were not necessary, but
 * it is the only way to keep the wolves off of my back.
 */

#include <curses.h>
#include <ctype.h>
#include <string.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 get_ro(WINDOW *win, int oy, int ox);
void put_bool(bool *b, WINDOW *win);
int get_bool(bool *bp, WINDOW *win);
void put_str(char *str, WINDOW *win);
int get_str(char *opt, WINDOW *win);
void put_abil(int *ability, WINDOW *win);
int get_abil(int *abil, WINDOW *win);
void put_quest(int *quest, WINDOW *win);
int get_quest(int *quest, WINDOW *win);
int get_default(bool *bp, WINDOW *win);
int get_str_prot(char *opt, WINDOW *win);
int get_score(char *opt, WINDOW *win);
bool allowchange(OPTION *op);

OPTION  optlist[] = {
    {"terse",   "Terse output: ",
                (void *) &terse,         put_bool,       get_bool        },
    {"flush",   "Flush typeahead during battle: ",
                (void *) &fight_flush,   put_bool,       get_bool        },
    {"jump",    "Show position only at end of run: ",
                (void *) &jump,          put_bool,       get_bool        },
    {"step",    "Do inventories one line at a time: ",
                (void *) &slow_invent,   put_bool,       get_bool        },
    {"askme",   "Ask me about unidentified things: ",
                (void *) &askme,         put_bool,       get_bool        },
    {"pickup",  "Pick things up automatically: ",
                (void *) &auto_pickup,   put_bool,       get_bool        },
    {"overlay", "Overlay menu: ",
                (void *) &menu_overlay,  put_bool,       get_bool        },
    {"name",    "Name: ",
                (void *) whoami,         put_str,        get_str_prot    },
    {"file",    "Save file: ",
                (void *) file_name,      put_str,        get_str_prot    },
    {"score",   "Score file: ",
                (void *) score_file,     put_str,        get_score       },
    {"class",   "Character type: ",
                (void *) &char_type,     put_abil,       get_abil        },
    {"quest",   "Quest item: ",
                (void *) &quest_item,    put_quest,      get_quest       },
    {"default", "Default Attributes: ",
                (void *) &def_attr,      put_bool,    get_default     }
};

/*
 * The default attribute field is read-only
 */

int
get_default(bool *bp, WINDOW *win)
{
    register int oy, ox;

    getyx(win, oy, ox);
    put_bool(bp, win);
    return get_ro(win, oy, ox);
}

/*
 * The ability (class) field is read-only
 */

int
get_abil(int *abil, WINDOW *win)
{
    register int oy, ox;

    getyx(win, oy, ox);
    put_abil(abil, win);
    return get_ro(win, oy, ox);
}

/*
 * The quest field is read-only
 */

int
get_quest(int *quest, WINDOW *win)
{
    register int oy, ox;

    getyx(win, oy, ox);
    waddstr(win, rel_magic[*quest].mi_name);
    return get_ro(win, oy, ox);
}

/*
 * get_ro:
 *      "Get" a read-only value.
 */

int
get_ro(WINDOW *win, int oy, int ox)
{
    register int ny, nx;
    register bool op_bad;
    
    op_bad = TRUE;
    getyx(win, ny, nx);
    while(op_bad)       
    {
        wmove(win, oy, ox);
        draw(win);
        switch (wgetch(win))
        {
            case '\n':
            case '\r':
                op_bad = FALSE;
                break;
            case '\033':
            case '\007':
                return QUIT;
            case '-':
                return MINUS;
            default:
                mvwaddstr(win, ny, nx + 5, "(no change allowed)");
        }
    }
    wmove(win, ny, nx + 5);
    wclrtoeol(win);
    wmove(win, ny, nx);
    waddch(win, '\n');
    return NORM;
}

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

int
get_bool(bool *bp, WINDOW *win)
{
    register int oy, ox;
    register bool op_bad;

    op_bad = TRUE;
    getyx(win, oy, ox);
    waddstr(win, *bp ? "True" : "False");
    while(op_bad)       
    {
        wmove(win, oy, ox);
        draw(win);
        switch (wgetch(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);
    wclrtoeol(win);
    waddstr(win, *bp ? "True" : "False");
    waddch(win, '\n');
    return NORM;
}

/*
 * set a string option
 */

int
get_str(char *opt, WINDOW *win)
{
    register char *sp;
    register int c, oy, ox;
    char buf[LINELEN];

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

                sp--;
                for (i = strlen(unctrl(*sp)); i; i--)
                    waddch(win, '\b');
            }
            continue;
        }
        else if (c == killchar()) /* process kill character */
        {
            sp = buf;
            wmove(win, oy, ox);
            continue;
        }
        else if (sp == buf)
            if (c == '-' && win == hw)  /* To move back a line in hw */
                break;
            else if (c == '~')
            {
                strcpy(buf, home);
                waddstr(win, home);
                sp += strlen(home);
                continue;
            }
        *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 == msgw)
        mpos += sp - buf;
    if (c == '-')
        return MINUS;
    else if (c == '\033' || c == '\007')
        return QUIT;
    else
        return NORM;
}

/*
 * print and then set options from the terminal
 */

void
option(void)
{
    register OPTION     *op;
    register int        retval;

    wclear(hw);
    touchwin(hw);
    /*
     * Display current values of options
     */
    for (op = optlist; op < &optlist[NUM_OPTS]; op++)
    {
        waddstr(hw, op->o_prompt);
        (*op->o_putfunc)(op->o_opt, hw);
        waddch(hw, '\n');
    }
    /*
     * Set values
     */
    wmove(hw, 0, 0);
    for (op = optlist; op < &optlist[NUM_OPTS]; op++)
    {
        waddstr(hw, op->o_prompt);

		retval = (*op->o_getfunc)(op->o_opt, hw);

        if (retval)
            if (retval == QUIT)
                break;
            else if (op > optlist) {    /* MINUS */
                wmove(hw, (op - optlist) - 1, 0);
                op -= 2;
            }
            else        /* trying to back up beyond the top */
            {
                putchar('\007');
                wmove(hw, 0, 0);
                op--;
            }
    }
    /*
     * Switch back to original screen
     */
    mvwaddstr(hw, lines-1, 0, spacemsg);
    draw(hw);
    wait_for(' ');
    restscr(cw);
    after = FALSE;
}

/*
 * 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)
{
    register char *sp;
    register OPTION *op;
    register int len;

    if (*str == '\"')
       str++;

    while (*str)
    {
        /*
         * Get option name
         */

        for (sp = str; isalpha(*sp); sp++)
            continue;
        len = (char *)sp - str;
        /*
         * Look it up and deal with it
         */
        for (op = optlist; op < &optlist[NUM_OPTS]; op++)
	{
            if (EQSTR(str, op->o_name, len))
            {
                if (op->o_putfunc == put_bool)  /* if option is a boolean */
                    *(bool *)op->o_opt = TRUE;
                else                            /* string option */
                {
                    register char *start;
                    char value[LINELEN];

                    /*
                     * Skip to start of string value
                     */
                    for (str = sp + 1; *str == '=' || *str == ':'; str++)
                        continue;

                    if (*str == '~')
                    {
                        strcpy((char *) value, home);
                        start = (char *) value + strlen(home);
                        while (*++str == '/')
                            continue;
                    }
                    else
                        start = (char *) value;
                    /*
                     * Skip to end of string value
                     */
                    for (sp = str + 1; *sp && *sp != ',' && *sp != '\"'; sp++)
                        continue;
                    strucpy(start, str, (char *) sp - str);

                    /* Put the value into the option field */
                    if (op->o_putfunc != put_abil) {
                        if (allowchange(op))
                            strcpy((char *)op->o_opt, value);
                    }

                    else if (*(int *)op->o_opt == -1) {
                        /* Only init ability once */
                        register int len = strlen(value);
                        register int i;

                        for (i=0; i<NUM_CHARTYPES-1; i++) {
                            if (EQSTR(value, char_class[i].name, len)) {
                                *(int *)op->o_opt = i;
                                break;
                            }
                        }
                    }
                }
                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))
            {
                *(bool *)op->o_opt = FALSE;
                break;
            }
	}

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


/*
 * print the default attributes
 */

/* put_default(b, win)
 * bool *b;
 * WINDOW *win;
 * {
 *     waddstr(win, *b ? "True" : "False");
 * }
 */

/*
 * print the character type
 */

void
put_abil(int *ability, WINDOW *win)
{
    waddstr(win, char_class[*ability].name);
}

/*
 * print out the quest
 */

void
put_quest(int *quest, WINDOW *win)
{
    waddstr(win, rel_magic[*quest].mi_name);
}

/*
 * put out a boolean
 */

void
put_bool(bool *b, WINDOW *win)
{
    waddstr(win, *b ? "True" : "False");
}

/*
 * put out a string
 */

void
put_str(char *str, WINDOW *win)
{
    waddstr(win, str);
}

/* Like get_str, but disallows changes when use_savedir is set. */
int
get_str_prot(char *opt, WINDOW *win)
{
    int oy, ox;

    if (use_savedir) {
        getyx(win, oy, ox);
        waddstr(win, opt);
        return get_ro(win, oy, ox);
    }
    else {
        return get_str(opt, win);
    }
}

/* When getting the scorefile, the new file must be opened. */
int
get_score(char *optstr, WINDOW *win)
{
    char old_score_file[LINELEN];
    int status;

    if (use_savedir)
        return get_str_prot(optstr, win);

    strcpy(old_score_file, optstr);
    status = get_str(optstr, win);
    if (status == NORM && strcmp(old_score_file, optstr))
    {
        if (scorefi != NULL)
            fclose(scorefi);
        scorefi = fopen(score_file, "rb+");
        if (scorefi == NULL)
            scorefi = fopen(score_file, "wb+");
    }
    return status;
}

bool
allowchange(OPTION *op)
{
    if (!use_savedir)
        return TRUE;
    if (!strcmp(op->o_name, "name"))
        return FALSE;
    if (!strcmp(op->o_name, "file"))
        return FALSE;
    if (!strcmp(op->o_name, "score"))
        return FALSE;
    return TRUE;
}