view xrogue/options.c @ 165:2d94c32a709e

arogue7: comment out a problematic message. This line causes a segfault on x64. I suspect one of the char* arguments to msg() is being corrupted. But gdb doesn't make it easy to debug variadic functions, and the crash doesn't occur when running under valgrind. So the message is being removed until I can discover the root of the problem.
author John "Elwin" Edwards
date Fri, 26 Jun 2015 11:32:37 -0400
parents aac28331e71d
children f54901b9c39b
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 */
    int         *o_opt;         /* pointer to thing to set */
    int         (*o_putfunc)(); /* function to print value */
    int         (*o_getfunc)(); /* function to get value interactively */
};

typedef struct optstruct        OPTION;

int     put_bool(), 
        get_bool(),
        put_str(),
        get_str(),
        put_abil(),
        get_abil(),
        get_quest(),
        put_quest(),
    get_default();

int get_str_prot(char *opt, WINDOW *win);
int get_score(char *opt, WINDOW *win);
bool allowchange(OPTION *op);

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        },
    {"pickup",  "Pick things up automatically: ",
                (int *) &auto_pickup,   put_bool,       get_bool        },
    {"overlay", "Overlay menu: ",
                (int *) &menu_overlay,  put_bool,       get_bool        },
    {"name",    "Name: ",
                (int *) whoami,         put_str,        get_str_prot    },
    {"file",    "Save file: ",
                (int *) file_name,      put_str,        get_str_prot    },
    {"score",   "Score file: ",
                (int *) score_file,     put_str,        get_score       },
    {"class",   "Character type: ",
                (int *) &char_type,     put_abil,       get_abil        },
    {"quest",   "Quest item: ",
                (int *) &quest_item,    put_quest,      get_quest       },
    {"default", "Default Attributes: ",
                (int *) &def_attr,      put_bool,    get_default     }
};

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

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

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

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

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

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

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

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

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

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

get_ro(win, oy, ox)
WINDOW *win;
register int oy, 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
 */

get_bool(bp, win)
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
 */

get_str(opt, win)
register 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
 */

option()
{
    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.
 */

parse_opts(str)
register 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 (*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)) {
                                *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
 */

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

/*
 * print out the quest
 */

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

/*
 * put out a boolean
 */

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

/*
 * put out a string
 */

put_str(str, win)
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;
}