Mercurial > hg > early-roguelike
view arogue5/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 | d4bf99f82ea0 |
children | aac28331e71d |
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. * * 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. */ #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(), get_restr(), put_abil(), get_abil(), get_quest(), put_quest(); 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 }, {"name", "Name: ", (int *) whoami, put_str, get_restr }, {"fruit", "Fruit: ", (int *) fruit, put_str, get_str }, {"file", "Save file: ", (int *) file_name, put_str, get_restr }, {"score", "Score file: ", (int *) score_file, put_str, get_restr }, {"class", "Character class: ", (int *)&char_type, put_abil, get_abil }, {"quest", "Quest item: ", (int *) &quest_item, put_quest, get_quest } }; /* For fields that would be restricted if use_savedir is set. */ int get_restr(char *optstr, WINDOW *win) { int oy, ox; if (use_savedir) { getyx(win, oy, ox); put_str(optstr, win); return get_ro(win, oy, ox); } else return get_str(optstr, win); } /* * The ability 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 = md_readchar(win)) != '\n' && c != '\r' && c != '\033' && c != '\007' && sp < &buf[LINELEN-1]; wclrtoeol(win), draw(win)) { if (c == -1) continue; else if (c == md_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 == md_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 == cw) mpos += (int)(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-1]; 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-1]; op++) { waddstr(hw, op->o_prompt); if ((retval = (*op->o_getfunc)(op->o_opt, hw))) if (retval == QUIT) break; else if (op > optlist) { /* MINUS */ wmove(hw, (int)(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(hw,' '); clearok(cw, TRUE); touchwin(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; 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++) /* None of these can be changed if using system savefiles. */ if (use_savedir && (!strcmp(op->o_name, "name") || !strcmp(op->o_name, "file") || !strcmp(op->o_name, "score") )) continue; 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[80]; /* * Skip to start of string value */ for (str = sp + 1; *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++) continue; strucpy(start, str, sp - str); /* Put the value into the option field */ if (op->o_putfunc != put_abil) strcpy((char *)op->o_opt, value); else if (*op->o_opt == -1) { /* Only init ability once */ register int len = strlen(value); if (isupper(value[0])) value[0] = tolower(value[0]); if (EQSTR(value, "fighter", len)) *op->o_opt = C_FIGHTER; else if (EQSTR(value, "magic", min(len, 5))) *op->o_opt = C_MAGICIAN; else if (EQSTR(value, "cleric", len)) *op->o_opt = C_CLERIC; else if (EQSTR(value, "thief", len)) *op->o_opt = C_THIEF; } } 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 character type */ put_abil(ability, win) int *ability; WINDOW *win; { char *abil; switch (*ability) { case C_MAGICIAN:abil = "Magic User"; when C_FIGHTER: abil = "Fighter"; when C_CLERIC: abil = "Cleric"; when C_THIEF: abil = "Thief"; otherwise: abil = "??"; } waddstr(win, abil); } /* * 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); }