diff --git a/doc/fixes37.0 b/doc/fixes37.0 index b788905ff..5b5656fd1 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -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 but no longer displayed 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 tree contained a space; the issue was within some shell script code contained within the project diff --git a/include/extern.h b/include/extern.h index 12fdb986d..32e9a2370 100644 --- a/include/extern.h +++ b/include/extern.h @@ -248,6 +248,8 @@ extern char pgetchar(void); extern void pushch(char); extern void savech(char); 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 boolean bind_specialkey(uchar, const char *); extern void parseautocomplete(char *, boolean); diff --git a/include/func_tab.h b/include/func_tab.h index da2b3cca9..73d3ba223 100644 --- a/include/func_tab.h +++ b/include/func_tab.h @@ -18,6 +18,12 @@ #define PREFIXCMD 0x100 /* prefix command, requires another one after it */ #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 { uchar key; const char *ef_txt, *ef_desc; diff --git a/src/cmd.c b/src/cmd.c index 50b3ee862..ba8fd773c 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -2449,6 +2449,58 @@ static const struct { 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 * key2extcmddesc(uchar key) { diff --git a/win/X11/winmisc.c b/win/X11/winmisc.c index 17f366ba4..644d1cd3f 100644 --- a/win/X11/winmisc.c +++ b/win/X11/winmisc.c @@ -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 */ void 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 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 */ - for (num_commands = 0; extcmdlist[num_commands].ef_txt; num_commands++) - if (ignore_extcmd(num_commands)) - ++ignore_cmds; + if (ec_full_list) + ecmflags |= ECM_IGNOREAC; - 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_indx = (short *) alloc((unsigned) (j * sizeof (short) + 1)); for (i = j = 0; i < num_commands; i++) { - if (ignore_extcmd(i)) - continue; - command_indx[j] = (short) i; - command_list[j++] = extcmdlist[i].ef_txt; + struct ext_func_tab *ec = extcmds_getentry(matches[i]); + + command_indx[j] = matches[i]; + command_list[j++] = ec->ef_txt; } command_list[j] = (char *) 0; command_indx[j] = -1; diff --git a/win/curses/cursdial.c b/win/curses/cursdial.c index 2d2d5f047..86f2a1356 100644 --- a/win/curses/cursdial.c +++ b/win/curses/cursdial.c @@ -368,11 +368,12 @@ curses_character_input_dialog(const char *prompt, const char *choices, int 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 ret = -1; char cur_choice[BUFSZ]; int matches = 0; + int *ecmatches; WINDOW *extwin = NULL, *extwin2 = NULL; if (iflags.extmenu) { @@ -421,10 +422,12 @@ curses_ext_cmd(void) /* if we have an autocomplete command, AND it matches uniquely */ if (matches == 1) { + struct ext_func_tab *ec = extcmds_getentry(ecmatches[0]); + curses_toggle_color_attr(extwin, NONE, A_UNDERLINE, ON); wmove(extwin, starty, (int) strlen(cur_choice) + startx + 2); 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); } @@ -441,13 +444,11 @@ curses_ext_cmd(void) } if (letter == '\r' || letter == '\n') { + (void) mungspaces(cur_choice); if (ret == -1) { - for (count = 0; extcmdlist[count].ef_txt; count++) { - if (!strcasecmp(cur_choice, extcmdlist[count].ef_txt)) { - ret = count; - break; - } - } + matches = extcmds_match(cur_choice, ECM_IGNOREAC|ECM_EXACTMATCH, &ecmatches); + if (matches == 1) + ret = ecmatches[0]; } break; } @@ -463,6 +464,7 @@ curses_ext_cmd(void) cur_choice[prompt_width - 1] = '\0'; letter = '*'; prompt_width--; + ret = -1; } /* 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'; ret = -1; } - for (count = 0; extcmdlist[count].ef_txt; count++) { - if (!wizard && (extcmdlist[count].flags & WIZMODECMD)) - continue; - 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++; - } - } - } - } + matches = extcmds_match(cur_choice, ECM_NOFLAGS, &ecmatches); + if (matches == 1) + ret = ecmatches[0]; } curses_destroy_win(extwin); if (extwin2) curses_destroy_win(extwin2); + if (ret == -1 && *cur_choice) + pline("%s: unknown extended command.", cur_choice); return ret; } diff --git a/win/tty/getline.c b/win/tty/getline.c index 2eefd05c5..f94359bdc 100644 --- a/win/tty/getline.c +++ b/win/tty/getline.c @@ -248,23 +248,13 @@ xwaitforspace(register const char *s) /* chars allowed besides return */ static boolean ext_cmd_getlin_hook(char *base) { - int oindex, com_index; + int *ecmatches; + int nmatches = extcmds_match(base, ECM_NOFLAGS, &ecmatches); - com_index = -1; - for (oindex = 0; extcmdlist[oindex].ef_txt != (char *) 0; oindex++) { - if (extcmdlist[oindex].flags & (CMD_NOT_AVAILABLE|INTERNALCMD)) - continue; - 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); + if (nmatches == 1) { + struct ext_func_tab *ec = extcmds_getentry(ecmatches[0]); + + Strcpy(base, ec->ef_txt); return TRUE; } @@ -278,8 +268,9 @@ ext_cmd_getlin_hook(char *base) int tty_get_ext_cmd(void) { - int i; char buf[BUFSZ]; + int nmatches; + int *ecmatches; if (iflags.extmenu) return extcmd_via_menu(); @@ -298,9 +289,7 @@ tty_get_ext_cmd(void) if (buf[0] == 0 || buf[0] == '\033') return -1; - for (i = 0; extcmdlist[i].ef_txt != (char *) 0; i++) - if (!strcmpi(buf, extcmdlist[i].ef_txt)) - break; + nmatches = extcmds_match(buf, ECM_IGNOREAC|ECM_EXACTMATCH, &ecmatches); if (!g.in_doagain) { int j; @@ -309,12 +298,12 @@ tty_get_ext_cmd(void) savech('\n'); } - if (extcmdlist[i].ef_txt == (char *) 0) { + if (nmatches != 1) { pline("%s: unknown extended command.", buf); - i = -1; + return -1; } - return i; + return ecmatches[0]; } #endif /* TTY_GRAPHICS */