Curses: fix extended command input
The extended command input prompt was behaving in an unintended way: Typing #a<enter> executed #adjust. Spaces in the entry prevented matching any command. No error message was given when no command was matched. Fix all of those, so it behaves more like the tty. Clean up the tty, curses, and X11 windowport code, so they don't use the extcmdlist array directly, but query with extcmds_match and extcmds_getentry.
This commit is contained in:
@@ -1050,6 +1050,7 @@ curses: sometimes entering a count during menu selection caused the menu to
|
|||||||
more digits typed followed by non-digit); in-out menu was still active
|
more digits typed followed by non-digit); in-out menu was still active
|
||||||
but no longer displayed
|
but no longer displayed
|
||||||
curses: support backspace/delete when entering a count during menu selection
|
curses: support backspace/delete when entering a count during menu selection
|
||||||
|
curses: make extended command prompt behave more sensibly
|
||||||
macOS: Xcode project was failing to build if the path to the NetHack source
|
macOS: Xcode project was failing to build if the path to the NetHack source
|
||||||
tree contained a space; the issue was within some shell script code
|
tree contained a space; the issue was within some shell script code
|
||||||
contained within the project
|
contained within the project
|
||||||
|
|||||||
@@ -248,6 +248,8 @@ extern char pgetchar(void);
|
|||||||
extern void pushch(char);
|
extern void pushch(char);
|
||||||
extern void savech(char);
|
extern void savech(char);
|
||||||
extern int doextcmd(void);
|
extern int doextcmd(void);
|
||||||
|
extern struct ext_func_tab *extcmds_getentry(int);
|
||||||
|
extern int extcmds_match(const char *, int, int **);
|
||||||
extern const char *key2extcmddesc(uchar);
|
extern const char *key2extcmddesc(uchar);
|
||||||
extern boolean bind_specialkey(uchar, const char *);
|
extern boolean bind_specialkey(uchar, const char *);
|
||||||
extern void parseautocomplete(char *, boolean);
|
extern void parseautocomplete(char *, boolean);
|
||||||
|
|||||||
@@ -18,6 +18,12 @@
|
|||||||
#define PREFIXCMD 0x100 /* prefix command, requires another one after it */
|
#define PREFIXCMD 0x100 /* prefix command, requires another one after it */
|
||||||
#define MOVEMENTCMD 0x200 /* used to move hero/cursor */
|
#define MOVEMENTCMD 0x200 /* used to move hero/cursor */
|
||||||
|
|
||||||
|
/* flags for extcmds_match() */
|
||||||
|
#define ECM_NOFLAGS 0
|
||||||
|
#define ECM_IGNOREAC 0x01 /* ignore !autocomplete commands */
|
||||||
|
#define ECM_EXACTMATCH 0x02 /* needs exact match of findstr */
|
||||||
|
#define ECM_NO1CHARCMD 0x04 /* ignore commands like '?' and '#' */
|
||||||
|
|
||||||
struct ext_func_tab {
|
struct ext_func_tab {
|
||||||
uchar key;
|
uchar key;
|
||||||
const char *ef_txt, *ef_desc;
|
const char *ef_txt, *ef_desc;
|
||||||
|
|||||||
52
src/cmd.c
52
src/cmd.c
@@ -2449,6 +2449,58 @@ static const struct {
|
|||||||
|
|
||||||
int extcmdlist_length = SIZE(extcmdlist) - 1;
|
int extcmdlist_length = SIZE(extcmdlist) - 1;
|
||||||
|
|
||||||
|
/* get entry i in the extended commands list. for windowport use. */
|
||||||
|
struct ext_func_tab *
|
||||||
|
extcmds_getentry(int i)
|
||||||
|
{
|
||||||
|
if (i < 0 || i > extcmdlist_length)
|
||||||
|
return 0;
|
||||||
|
return &extcmdlist[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find extended command entries matching findstr.
|
||||||
|
if findstr is NULL, returns all available entries.
|
||||||
|
returns: number of matching extended commands,
|
||||||
|
and the entry indexes in matchlist.
|
||||||
|
for windowport use. */
|
||||||
|
int
|
||||||
|
extcmds_match(const char *findstr, int ecmflags, int **matchlist)
|
||||||
|
{
|
||||||
|
static int retmatchlist[SIZE(extcmdlist)] = DUMMY;
|
||||||
|
int i, mi = 0;
|
||||||
|
int fslen = findstr ? strlen(findstr) : 0;
|
||||||
|
boolean ignoreac = (ecmflags & ECM_IGNOREAC) != 0;
|
||||||
|
boolean exactmatch = (ecmflags & ECM_EXACTMATCH) != 0;
|
||||||
|
boolean no1charcmd = (ecmflags & ECM_NO1CHARCMD) != 0;
|
||||||
|
|
||||||
|
for (i = 0; extcmdlist[i].ef_txt; i++) {
|
||||||
|
if (extcmdlist[i].flags & (CMD_NOT_AVAILABLE|INTERNALCMD))
|
||||||
|
continue;
|
||||||
|
if (!wizard && (extcmdlist[i].flags & WIZMODECMD))
|
||||||
|
continue;
|
||||||
|
if (!ignoreac && !(extcmdlist[i].flags & AUTOCOMPLETE))
|
||||||
|
continue;
|
||||||
|
if (no1charcmd && (strlen(extcmdlist[i].ef_txt) == 1))
|
||||||
|
continue;
|
||||||
|
if (!findstr) {
|
||||||
|
retmatchlist[mi++] = i;
|
||||||
|
} else if (exactmatch) {
|
||||||
|
if (!strcmpi(findstr, extcmdlist[i].ef_txt)) {
|
||||||
|
retmatchlist[mi++] = i;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!strncmpi(findstr, extcmdlist[i].ef_txt, fslen)) {
|
||||||
|
retmatchlist[mi++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchlist)
|
||||||
|
*matchlist = retmatchlist;
|
||||||
|
|
||||||
|
return mi;
|
||||||
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
key2extcmddesc(uchar key)
|
key2extcmddesc(uchar key)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1764,21 +1764,6 @@ ec_scroll_to_view(int ec_indx) /* might be greater than extended_cmd_selected */
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* decide whether extcmdlist[idx] should be part of extended commands menu */
|
|
||||||
static boolean
|
|
||||||
ignore_extcmd(int idx)
|
|
||||||
{
|
|
||||||
/* #shell or #suspect might not be available;
|
|
||||||
'extmenu' option controls whether we show full list
|
|
||||||
or just the traditional extended commands */
|
|
||||||
if ((extcmdlist[idx].flags & (CMD_NOT_AVAILABLE|INTERNALCMD)) != 0
|
|
||||||
|| ((extcmdlist[idx].flags & AUTOCOMPLETE) == 0 && !ec_full_list)
|
|
||||||
|| strlen(extcmdlist[idx].ef_txt) < 2) /* ignore "#" and "?" */
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ARGSUSED */
|
/* ARGSUSED */
|
||||||
void
|
void
|
||||||
ec_key(Widget w, XEvent *event, String *params, Cardinal *num_params)
|
ec_key(Widget w, XEvent *event, String *params, Cardinal *num_params)
|
||||||
@@ -1899,22 +1884,24 @@ ec_key(Widget w, XEvent *event, String *params, Cardinal *num_params)
|
|||||||
static void
|
static void
|
||||||
init_extended_commands_popup(void)
|
init_extended_commands_popup(void)
|
||||||
{
|
{
|
||||||
int i, j, num_commands, ignore_cmds = 0;
|
int i, j, num_commands;
|
||||||
|
int *matches;
|
||||||
|
int ecmflags = ECM_NO1CHARCMD;
|
||||||
|
|
||||||
/* count commands */
|
if (ec_full_list)
|
||||||
for (num_commands = 0; extcmdlist[num_commands].ef_txt; num_commands++)
|
ecmflags |= ECM_IGNOREAC;
|
||||||
if (ignore_extcmd(num_commands))
|
|
||||||
++ignore_cmds;
|
|
||||||
|
|
||||||
j = num_commands - ignore_cmds;
|
num_commands = extcmds_match(NULL, ecmflags, &matches);
|
||||||
|
|
||||||
|
j = num_commands;
|
||||||
command_list = (const char **) alloc((unsigned) (j * sizeof (char *) + 1));
|
command_list = (const char **) alloc((unsigned) (j * sizeof (char *) + 1));
|
||||||
command_indx = (short *) alloc((unsigned) (j * sizeof (short) + 1));
|
command_indx = (short *) alloc((unsigned) (j * sizeof (short) + 1));
|
||||||
|
|
||||||
for (i = j = 0; i < num_commands; i++) {
|
for (i = j = 0; i < num_commands; i++) {
|
||||||
if (ignore_extcmd(i))
|
struct ext_func_tab *ec = extcmds_getentry(matches[i]);
|
||||||
continue;
|
|
||||||
command_indx[j] = (short) i;
|
command_indx[j] = matches[i];
|
||||||
command_list[j++] = extcmdlist[i].ef_txt;
|
command_list[j++] = ec->ef_txt;
|
||||||
}
|
}
|
||||||
command_list[j] = (char *) 0;
|
command_list[j] = (char *) 0;
|
||||||
command_indx[j] = -1;
|
command_indx[j] = -1;
|
||||||
|
|||||||
@@ -368,11 +368,12 @@ curses_character_input_dialog(const char *prompt, const char *choices,
|
|||||||
int
|
int
|
||||||
curses_ext_cmd(void)
|
curses_ext_cmd(void)
|
||||||
{
|
{
|
||||||
int count, letter, prompt_width, startx, starty, winx, winy;
|
int letter, prompt_width, startx, starty, winx, winy;
|
||||||
int messageh, messagew, maxlen = BUFSZ - 1;
|
int messageh, messagew, maxlen = BUFSZ - 1;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
char cur_choice[BUFSZ];
|
char cur_choice[BUFSZ];
|
||||||
int matches = 0;
|
int matches = 0;
|
||||||
|
int *ecmatches;
|
||||||
WINDOW *extwin = NULL, *extwin2 = NULL;
|
WINDOW *extwin = NULL, *extwin2 = NULL;
|
||||||
|
|
||||||
if (iflags.extmenu) {
|
if (iflags.extmenu) {
|
||||||
@@ -421,10 +422,12 @@ curses_ext_cmd(void)
|
|||||||
|
|
||||||
/* if we have an autocomplete command, AND it matches uniquely */
|
/* if we have an autocomplete command, AND it matches uniquely */
|
||||||
if (matches == 1) {
|
if (matches == 1) {
|
||||||
|
struct ext_func_tab *ec = extcmds_getentry(ecmatches[0]);
|
||||||
|
|
||||||
curses_toggle_color_attr(extwin, NONE, A_UNDERLINE, ON);
|
curses_toggle_color_attr(extwin, NONE, A_UNDERLINE, ON);
|
||||||
wmove(extwin, starty, (int) strlen(cur_choice) + startx + 2);
|
wmove(extwin, starty, (int) strlen(cur_choice) + startx + 2);
|
||||||
wprintw(extwin, "%s",
|
wprintw(extwin, "%s",
|
||||||
extcmdlist[ret].ef_txt + (int) strlen(cur_choice));
|
ec->ef_txt + (int) strlen(cur_choice));
|
||||||
curses_toggle_color_attr(extwin, NONE, A_UNDERLINE, OFF);
|
curses_toggle_color_attr(extwin, NONE, A_UNDERLINE, OFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,13 +444,11 @@ curses_ext_cmd(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (letter == '\r' || letter == '\n') {
|
if (letter == '\r' || letter == '\n') {
|
||||||
|
(void) mungspaces(cur_choice);
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
for (count = 0; extcmdlist[count].ef_txt; count++) {
|
matches = extcmds_match(cur_choice, ECM_IGNOREAC|ECM_EXACTMATCH, &ecmatches);
|
||||||
if (!strcasecmp(cur_choice, extcmdlist[count].ef_txt)) {
|
if (matches == 1)
|
||||||
ret = count;
|
ret = ecmatches[0];
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -463,6 +464,7 @@ curses_ext_cmd(void)
|
|||||||
cur_choice[prompt_width - 1] = '\0';
|
cur_choice[prompt_width - 1] = '\0';
|
||||||
letter = '*';
|
letter = '*';
|
||||||
prompt_width--;
|
prompt_width--;
|
||||||
|
ret = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* honor kill_char if it's ^U or similar, but not if it's '@' */
|
/* honor kill_char if it's ^U or similar, but not if it's '@' */
|
||||||
@@ -478,30 +480,16 @@ curses_ext_cmd(void)
|
|||||||
cur_choice[prompt_width + 1] = '\0';
|
cur_choice[prompt_width + 1] = '\0';
|
||||||
ret = -1;
|
ret = -1;
|
||||||
}
|
}
|
||||||
for (count = 0; extcmdlist[count].ef_txt; count++) {
|
matches = extcmds_match(cur_choice, ECM_NOFLAGS, &ecmatches);
|
||||||
if (!wizard && (extcmdlist[count].flags & WIZMODECMD))
|
if (matches == 1)
|
||||||
continue;
|
ret = ecmatches[0];
|
||||||
if (!(extcmdlist[count].flags & AUTOCOMPLETE))
|
|
||||||
continue;
|
|
||||||
if ((int) strlen(extcmdlist[count].ef_txt) > prompt_width) {
|
|
||||||
if (strncasecmp(cur_choice, extcmdlist[count].ef_txt,
|
|
||||||
prompt_width) == 0) {
|
|
||||||
if ((extcmdlist[count].ef_txt[prompt_width] ==
|
|
||||||
lowc(letter)) || letter == '*') {
|
|
||||||
if (matches == 0) {
|
|
||||||
ret = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
matches++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
curses_destroy_win(extwin);
|
curses_destroy_win(extwin);
|
||||||
if (extwin2)
|
if (extwin2)
|
||||||
curses_destroy_win(extwin2);
|
curses_destroy_win(extwin2);
|
||||||
|
if (ret == -1 && *cur_choice)
|
||||||
|
pline("%s: unknown extended command.", cur_choice);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -248,23 +248,13 @@ xwaitforspace(register const char *s) /* chars allowed besides return */
|
|||||||
static boolean
|
static boolean
|
||||||
ext_cmd_getlin_hook(char *base)
|
ext_cmd_getlin_hook(char *base)
|
||||||
{
|
{
|
||||||
int oindex, com_index;
|
int *ecmatches;
|
||||||
|
int nmatches = extcmds_match(base, ECM_NOFLAGS, &ecmatches);
|
||||||
|
|
||||||
com_index = -1;
|
if (nmatches == 1) {
|
||||||
for (oindex = 0; extcmdlist[oindex].ef_txt != (char *) 0; oindex++) {
|
struct ext_func_tab *ec = extcmds_getentry(ecmatches[0]);
|
||||||
if (extcmdlist[oindex].flags & (CMD_NOT_AVAILABLE|INTERNALCMD))
|
|
||||||
continue;
|
Strcpy(base, ec->ef_txt);
|
||||||
if ((extcmdlist[oindex].flags & AUTOCOMPLETE)
|
|
||||||
&& !(!wizard && (extcmdlist[oindex].flags & WIZMODECMD))
|
|
||||||
&& !strncmpi(base, extcmdlist[oindex].ef_txt, strlen(base))) {
|
|
||||||
if (com_index == -1) /* no matches yet */
|
|
||||||
com_index = oindex;
|
|
||||||
else /* more than 1 match */
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (com_index >= 0) {
|
|
||||||
Strcpy(base, extcmdlist[com_index].ef_txt);
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,8 +268,9 @@ ext_cmd_getlin_hook(char *base)
|
|||||||
int
|
int
|
||||||
tty_get_ext_cmd(void)
|
tty_get_ext_cmd(void)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
char buf[BUFSZ];
|
char buf[BUFSZ];
|
||||||
|
int nmatches;
|
||||||
|
int *ecmatches;
|
||||||
|
|
||||||
if (iflags.extmenu)
|
if (iflags.extmenu)
|
||||||
return extcmd_via_menu();
|
return extcmd_via_menu();
|
||||||
@@ -298,9 +289,7 @@ tty_get_ext_cmd(void)
|
|||||||
if (buf[0] == 0 || buf[0] == '\033')
|
if (buf[0] == 0 || buf[0] == '\033')
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
for (i = 0; extcmdlist[i].ef_txt != (char *) 0; i++)
|
nmatches = extcmds_match(buf, ECM_IGNOREAC|ECM_EXACTMATCH, &ecmatches);
|
||||||
if (!strcmpi(buf, extcmdlist[i].ef_txt))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (!g.in_doagain) {
|
if (!g.in_doagain) {
|
||||||
int j;
|
int j;
|
||||||
@@ -309,12 +298,12 @@ tty_get_ext_cmd(void)
|
|||||||
savech('\n');
|
savech('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extcmdlist[i].ef_txt == (char *) 0) {
|
if (nmatches != 1) {
|
||||||
pline("%s: unknown extended command.", buf);
|
pline("%s: unknown extended command.", buf);
|
||||||
i = -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return i;
|
return ecmatches[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* TTY_GRAPHICS */
|
#endif /* TTY_GRAPHICS */
|
||||||
|
|||||||
Reference in New Issue
Block a user