From: PatR Date: Wed, 10 Mar 2021 20:28:09 +0000 (-0800) Subject: options key parsing X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d8bef900090ab3623cd46588726e0d1a6ff34e8e;p=nethack options key parsing OPTIONS=menu_previous_page:\mv BINDINGS=M-v:menu_previous_page both worked, but OPTIONS=menu_previous_page:M-v BINDINGS=\mv:menu_previous_page both failed. Make all four variations work. Tiny change made large by the need to move some things around. The option definition for menu_first_page had a couple of its flag bits swapped. I didn't try to figure out whether that had any impact. --- diff --git a/doc/fixes37.0 b/doc/fixes37.0 index fe6096e4a..0accbe778 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -398,6 +398,11 @@ hero would be blinded and stunned by an Archon's radiance (gaze attack) even applying a polearm to attempt to attack a hidden monster would report "wait! there's a monster hidden there" and display the "remembered, unseen monster" glyph but only use a turn if polearm wasn't already wielded +key parsing during options processing was inconsistent between OPTIONS=foo:k + BINDINGS=k:foo where k represents a key designation; the OPTIONS form + recognized backslash escape sequences but not M-x meta characters, + vice versa for BINDINGS (most noticeable for menu interaction keys + such as menu_next_page because those can be set via either directive) Fixes to 3.7.0-x Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 1e597124d..90ff05575 100644 --- a/include/extern.h +++ b/include/extern.h @@ -210,7 +210,6 @@ extern void pushch(char); extern void savech(char); extern const char *key2extcmddesc(uchar); extern boolean bind_specialkey(uchar, const char *); -extern uchar txt2key(char *); extern void parseautocomplete(char *, boolean); extern void reset_commands(boolean); extern void rhack(char *); @@ -1784,6 +1783,7 @@ extern int shiny_obj(char); /* ### options.c ### */ extern boolean match_optname(const char *, const char *, int, boolean); +extern uchar txt2key(char *); extern void initoptions(void); extern void initoptions_init(void); extern void initoptions_finish(void); diff --git a/include/global.h b/include/global.h index 2163e7659..5b6e7e3ac 100644 --- a/include/global.h +++ b/include/global.h @@ -443,4 +443,23 @@ struct savefile_info { (nhassert_failed(#expression, __FILE__, __LINE__), 0)) #endif +/* Macros for meta and ctrl modifiers: + * M and C return the meta/ctrl code for the given character; + * e.g., (C('c') is ctrl-c + */ +#ifndef M +#ifndef NHSTDC +#define M(c) (0x80 | (c)) +#else +#define M(c) ((c) - 128) +#endif /* NHSTDC */ +#endif + +#ifndef C +#define C(c) (0x1f & (c)) +#endif + +#define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c)) +#define unmeta(c) (0x7f & (c)) + #endif /* GLOBAL_H */ diff --git a/include/optlist.h b/include/optlist.h index fc667ce33..a1b363330 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -263,8 +263,8 @@ opt_##a, NoAlias, "deselect all items in a menu") NHOPTC(menu_deselect_page, 4, opt_in, set_in_config, No, Yes, No, No, NoAlias, "deselect all items on this page of a menu") - NHOPTC(menu_first_page, 4, opt_in, set_in_config, No, No, Yes, No, NoAlias, - "jump to the first page in a menu") + NHOPTC(menu_first_page, 4, opt_in, set_in_config, No, Yes, No, No, + NoAlias, "jump to the first page in a menu") NHOPTC(menu_headings, 4, opt_in, set_in_game, No, Yes, No, Yes, NoAlias, "display style for menu headings") NHOPTC(menu_invert_all, 4, opt_in, set_in_config, No, Yes, No, No, NoAlias, diff --git a/src/cmd.c b/src/cmd.c index 6d42dceae..09156831d 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -6,25 +6,6 @@ #include "hack.h" #include "func_tab.h" -/* Macros for meta and ctrl modifiers: - * M and C return the meta/ctrl code for the given character; - * e.g., (C('c') is ctrl-c - */ -#ifndef M -#ifndef NHSTDC -#define M(c) (0x80 | (c)) -#else -#define M(c) ((c) - 128) -#endif /* NHSTDC */ -#endif - -#ifndef C -#define C(c) (0x1f & (c)) -#endif - -#define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c)) -#define unmeta(c) (0x7f & (c)) - #ifdef ALTMETA static boolean alt_esc = FALSE; #endif @@ -3045,96 +3026,6 @@ spkey_name(int nhkf) return name; } -/* returns a one-byte character from the text; may change txt[] */ -uchar -txt2key(char *txt) -{ - uchar uc; - boolean makemeta = FALSE; - - txt = trimspaces(txt); - if (!*txt) - return '\0'; - - /* simple character */ - if (!txt[1]) - return (uchar) txt[0]; - - /* a few special entries */ - if (!strcmp(txt, "")) - return '\n'; - if (!strcmp(txt, "")) - return ' '; - if (!strcmp(txt, "")) - return '\033'; - - /* control and meta keys */ - if (highc(*txt) == 'M') { - /* - * M return 'M' - * M - return M-'-' - * M return M- - * otherwise M is pending until after ^/C- processing. - * Since trailing spaces are discarded, the only way to - * specify M-' ' is via "160". - */ - if (!txt[1]) - return (uchar) *txt; - /* skip past 'M' or 'm' and maybe '-' */ - ++txt; - if (*txt == '-' && txt[1]) - ++txt; - if (!txt[1]) - return M((uchar) *txt); - makemeta = TRUE; - } - if (*txt == '^' || highc(*txt) == 'C') { - /* - * C return 'C' or M-'C' - * C - return '-' or M-'-' - * C [-] return C- or M-C- - * C [-] ? return - * otherwise return C- or M-C- - */ - uc = (uchar) *txt; - if (!txt[1]) - return makemeta ? M(uc) : uc; - ++txt; - /* unlike M-x, lots of values of x are invalid for C-x; - checking and rejecting them is not worthwhile; GIGO; - we do accept "^-x" as synonym for "^x" or "C-x" */ - if (*txt == '-' && txt[1]) - ++txt; - /* and accept ^?, which gets used despite not being a control char */ - if (*txt == '?') - return (uchar) (makemeta ? '\377' : '\177'); /* rubout/delete */ - uc = C((uchar) *txt); - return makemeta ? M(uc) : uc; - } - if (makemeta && *txt) - return M((uchar) *txt); - - /* FIXME: should accept single-quote single-character single-quote - and probably single-quote backslash octal-digits single-quote; - if we do that, the M- and C- results should be pending until - after, so that C-'X' becomes valid for ^X */ - - /* ascii codes: must be three-digit decimal */ - if (*txt >= '0' && *txt <= '9') { - uchar key = 0; - int i; - - for (i = 0; i < 3; i++) { - if (txt[i] < '0' || txt[i] > '9') - return '\0'; - key = 10 * key + txt[i] - '0'; - } - return key; - } - - return '\0'; -} - /* returns the text for a one-byte encoding; * must be shorter than a tab for proper formatting */ char * diff --git a/src/options.c b/src/options.c index 9e662026a..9d62633b3 100644 --- a/src/options.c +++ b/src/options.c @@ -4307,10 +4307,7 @@ spcfn_misc_menu_cmd(int midx, int req, boolean negated, char *opts, char *op) bad_negation(default_menu_cmd_info[midx].name, FALSE); return optn_err; } else if ((op = string_for_opt(opts, FALSE)) != empty_optstr) { - char c, op_buf[BUFSZ]; - - escapes(op, op_buf); - c = *op_buf; + char c = txt2key(op); if (illegal_menu_cmd_key((uchar) c)) return optn_err; @@ -5617,6 +5614,107 @@ escapes(const char *cp, /* might be 'tp', updating in place */ *tp = '\0'; } +/* returns a one-byte character from the text; may change txt[]; + moved from cmd.c in order to get access to escapes() */ +uchar +txt2key(char *txt) +{ + uchar uc; + boolean makemeta = FALSE; + + txt = trimspaces(txt); + if (!*txt) + return '\0'; + + /* simple character */ + if (!txt[1]) + return (uchar) txt[0]; + + /* a few special entries */ + if (!strcmp(txt, "")) + return '\n'; + if (!strcmp(txt, "")) + return ' '; + if (!strcmp(txt, "")) + return '\033'; + + /* handle things like \b and \7 and \mX */ + if (*txt == '\\') { + char tbuf[QBUFSZ]; + + if (strlen(txt) >= sizeof tbuf) + txt[sizeof tbuf - 1] = '\0'; + escapes(txt, tbuf); + return *tbuf; + } + + /* control and meta keys */ + if (highc(*txt) == 'M') { + /* + * M return 'M' + * M - return M-'-' + * M return M- + * otherwise M is pending until after ^/C- processing. + * Since trailing spaces are discarded, the only way to + * specify M-' ' is via "160". + */ + if (!txt[1]) + return (uchar) *txt; + /* skip past 'M' or 'm' and maybe '-' */ + ++txt; + if (*txt == '-' && txt[1]) + ++txt; + if (!txt[1]) + return M((uchar) *txt); + makemeta = TRUE; + } + if (*txt == '^' || highc(*txt) == 'C') { + /* + * C return 'C' or M-'C' + * C - return '-' or M-'-' + * C [-] return C- or M-C- + * C [-] ? return + * otherwise return C- or M-C- + */ + uc = (uchar) *txt; + if (!txt[1]) + return makemeta ? M(uc) : uc; + ++txt; + /* unlike M-x, lots of values of x are invalid for C-x; + checking and rejecting them is not worthwhile; GIGO; + we do accept "^-x" as synonym for "^x" or "C-x" */ + if (*txt == '-' && txt[1]) + ++txt; + /* and accept ^?, which gets used despite not being a control char */ + if (*txt == '?') + return (uchar) (makemeta ? '\377' : '\177'); /* rubout/delete */ + uc = C((uchar) *txt); + return makemeta ? M(uc) : uc; + } + if (makemeta && *txt) + return M((uchar) *txt); + + /* FIXME: should accept single-quote single-character single-quote + and probably single-quote backslash octal-digits single-quote; + if we do that, the M- and C- results should be pending until + after, so that C-'X' becomes valid for ^X */ + + /* ascii codes: must be three-digit decimal */ + if (*txt >= '0' && *txt <= '9') { + uchar key = 0; + int i; + + for (i = 0; i < 3; i++) { + if (txt[i] < '0' || txt[i] > '9') + return '\0'; + key = 10 * key + txt[i] - '0'; + } + return key; + } + + return '\0'; +} + /* ********************************** *