]> granicus.if.org Git - nethack/commitdiff
Curses: fix extended command input
authorPasi Kallinen <paxed@alt.org>
Sat, 22 Jan 2022 12:32:50 +0000 (14:32 +0200)
committerPasi Kallinen <paxed@alt.org>
Sat, 22 Jan 2022 12:32:53 +0000 (14:32 +0200)
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.

doc/fixes37.0
include/extern.h
include/func_tab.h
src/cmd.c
win/X11/winmisc.c
win/curses/cursdial.c
win/tty/getline.c

index b788905ff53c0b5ef73eefbdda90d0664e813f11..5b5656fd17c4dc013145e300c924721b5fcfa6ea 100644 (file)
@@ -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
index 12fdb986d1cd1f777f5e2ad588e2c41126d559a4..32e9a23706c7e9326c923d7c0105c577ddcf777d 100644 (file)
@@ -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);
index da2b3cca9d1c6d26ce86af2ca0db89c4831af85a..73d3ba2239a10ef744bbd487b00aa9e7bb746a6b 100644 (file)
 #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;
index 50b3ee862661c92bfee6b02f810a3789f00a95c8..ba8fd773c2916527cb11ec89b081a5da6e0f1679 100644 (file)
--- 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)
 {
index 17f366ba45709d914576267b8603608357b1a240..644d1cd3f298cdf442af7e5360226e667ab8e7e9 100644 (file)
@@ -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;
index 2d2d5f04791ed8bb59137e334385b5b5d5fecd84..86f2a1356c3454555e33334377e67fe9842018cf 100644 (file)
@@ -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;
 }
 
index 2eefd05c515f88f946bc6288e0783aa4576cec01..f94359bdcf0be829cc1379ad15cdd1825db9b643 100644 (file)
@@ -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;
-
-    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);
+    int *ecmatches;
+    int nmatches = extcmds_match(base, ECM_NOFLAGS, &ecmatches);
+
+    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 */