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
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);
#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;
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)
{
}
}
-/* 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)
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;
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) {
/* 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);
}
}
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;
}
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 '@' */
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;
}
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;
}
int
tty_get_ext_cmd(void)
{
- int i;
char buf[BUFSZ];
+ int nmatches;
+ int *ecmatches;
if (iflags.extmenu)
return extcmd_via_menu();
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;
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 */