6 * Copyright (C) 1996-2000,2002,2010-2011 Michael R. Elkins <me@mutt.org>
9 * This program is free software: you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 2 of the License, or (at your option) any later
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
19 * You should have received a copy of the GNU General Public License along with
20 * this program. If not, see <http://www.gnu.org/licenses/>.
24 * @page keymap Manage keymappings
36 #include "mutt/mutt.h"
40 #include "functions.h"
42 #include "mutt_commands.h"
43 #include "mutt_curses.h"
44 #include "mutt_logging.h"
45 #include "mutt_window.h"
46 #include "ncrypt/ncrypt.h"
49 #ifndef USE_SLANG_CURSES
53 #include "imap/imap.h"
60 * Menus - Menu name lookup table
62 const struct Mapping Menus[] = {
63 { "alias", MENU_ALIAS },
64 { "attach", MENU_ATTACH },
65 { "browser", MENU_FOLDER },
66 { "compose", MENU_COMPOSE },
67 { "editor", MENU_EDITOR },
68 { "index", MENU_MAIN },
69 { "pager", MENU_PAGER },
70 { "postpone", MENU_POSTPONE },
72 { "smime", MENU_SMIME },
73 #ifdef CRYPT_BACKEND_GPGME
74 { "key_select_pgp", MENU_KEY_SELECT_PGP },
75 { "key_select_smime", MENU_KEY_SELECT_SMIME },
80 { "query", MENU_QUERY },
81 { "generic", MENU_GENERIC },
86 * KeyNames - Key name lookup table
88 static struct Mapping KeyNames[] = {
89 { "<PageUp>", KEY_PPAGE },
90 { "<PageDown>", KEY_NPAGE },
92 { "<Down>", KEY_DOWN },
93 { "<Right>", KEY_RIGHT },
94 { "<Left>", KEY_LEFT },
95 { "<Delete>", KEY_DC },
96 { "<BackSpace>", KEY_BACKSPACE },
97 { "<Insert>", KEY_IC },
98 { "<Home>", KEY_HOME },
101 { "<Return>", '\r' },
102 { "<Esc>", '\033' }, // Escape
106 { "<BackTab>", KEY_BTAB },
109 { "<Next>", KEY_NEXT },
111 #ifdef NCURSES_VERSION
112 /* extensions supported by ncurses. values are filled in during initialization */
143 #endif /* NCURSES_VERSION */
147 int LastKey; /**< contains the last key the user pressed */
149 struct Keymap *Keymaps[MENU_MAX];
151 #ifdef NCURSES_VERSION
153 * struct Extkey - Map key names from NeoMutt's style to Curses style
161 static const struct Extkey ExtKeys[] = {
162 { "<c-up>", "kUP5" },
164 { "<a-up>", "kUP3" },
166 { "<s-down>", "kDN" },
167 { "<a-down>", "kDN3" },
168 { "<c-down>", "kDN5" },
170 { "<c-right>", "kRIT5" },
171 { "<s-right>", "kRIT" },
172 { "<a-right>", "kRIT3" },
174 { "<s-left>", "kLFT" },
175 { "<a-left>", "kLFT3" },
176 { "<c-left>", "kLFT5" },
178 { "<s-home>", "kHOM" },
179 { "<a-home>", "kHOM3" },
180 { "<c-home>", "kHOM5" },
182 { "<s-end>", "kEND" },
183 { "<a-end>", "kEND3" },
184 { "<c-end>", "kEND5" },
186 { "<s-next>", "kNXT" },
187 { "<a-next>", "kNXT3" },
188 { "<c-next>", "kNXT5" },
190 { "<s-prev>", "kPRV" },
191 { "<a-prev>", "kPRV3" },
192 { "<c-prev>", "kPRV5" },
199 * alloc_keys - Allocate space for a sequence of keys
200 * @param len Number of keys
201 * @param keys Array of keys
202 * @retval ptr Sequence of keys
204 static struct Keymap *alloc_keys(size_t len, keycode_t *keys)
206 struct Keymap *p = mutt_mem_calloc(1, sizeof(struct Keymap));
208 p->keys = mutt_mem_malloc(len * sizeof(keycode_t));
209 memcpy(p->keys, keys, len * sizeof(keycode_t));
214 * parse_fkey - Parse a function key string
215 * @param s String to parse
216 * @retval num Number of the key
218 * Given "<f8>", it will return 8.
220 static int parse_fkey(char *s)
225 if ((s[0] != '<') || (tolower(s[1]) != 'f'))
228 for (t = s + 2; *t && isdigit((unsigned char) *t); t++)
240 * parse_keycode - Parse a numeric keycode
241 * @param s String to parse
242 * @retval num Number of the key
244 * This function parses the string `<NNN>` and uses the octal value as the key
247 static int parse_keycode(const char *s)
249 char *end_char = NULL;
250 long int result = strtol(s + 1, &end_char, 8);
251 /* allow trailing whitespace, eg. < 1001 > */
252 while (IS_SPACE(*end_char))
254 /* negative keycodes don't make sense, also detect overflow */
255 if ((*end_char != '>') || (result < 0) || (result == LONG_MAX))
264 * parsekeys - Parse a key string into key codes
265 * @param str Key string
266 * @param d Array for key codes
267 * @param max Maximum length of key sequence
268 * @retval num Length of key sequence
270 static size_t parsekeys(const char *str, keycode_t *d, size_t max)
278 mutt_str_strfcpy(buf, str, sizeof(buf));
284 if ((*s == '<') && (t = strchr(s, '>')))
290 n = mutt_map_get_value(s, KeyNames);
296 else if ((n = parse_fkey(s)) > 0)
301 else if ((n = parse_keycode(s)) > 0)
312 *d = (unsigned char) *s;
323 * km_bind_err - Set up a key binding
324 * @param s Key string
325 * @param menu Menu id, e.g. #MENU_EDITOR
326 * @param op Operation, e.g. OP_DELETE
327 * @param macro Macro string
328 * @param desc Description of macro (OPTIONAL)
329 * @param err Buffer for error message
330 * @retval #CommandResult Result e.g. #MUTT_CMD_SUCCESS
332 * Insert a key sequence into the specified map.
333 * The map is sorted by ASCII value (lowest to highest)
335 static enum CommandResult km_bind_err(const char *s, enum MenuType menu, int op,
336 char *macro, char *desc, struct Buffer *err)
338 enum CommandResult rc = MUTT_CMD_SUCCESS;
339 struct Keymap *last = NULL, *next = NULL;
340 keycode_t buf[MAX_SEQ];
341 size_t pos = 0, lastpos = 0;
343 size_t len = parsekeys(s, buf, MAX_SEQ);
345 struct Keymap *map = alloc_keys(len, buf);
347 map->macro = mutt_str_strdup(macro);
348 map->desc = mutt_str_strdup(desc);
350 struct Keymap *tmp = Keymaps[menu];
354 if ((pos >= len) || (pos >= tmp->len))
356 /* map and tmp match so overwrite */
359 /* Don't warn on overwriting a 'noop' binding */
360 if ((tmp->len != len) && (tmp->op != OP_NULL))
362 /* Overwrite with the different lengths, warn */
363 /* TODO: MAX_SEQ here is wrong */
364 char old_binding[MAX_SEQ];
365 char new_binding[MAX_SEQ];
366 km_expand_key(old_binding, MAX_SEQ, map);
367 km_expand_key(new_binding, MAX_SEQ, tmp);
370 /* err was passed, put the string there */
372 err->data, err->dsize,
373 _("Binding '%s' will alias '%s' Before, try: 'bind %s %s "
375 "https://neomutt.org/guide/configuration.html#bind-warnings"),
376 old_binding, new_binding, mutt_map_get_name(menu, Menus), new_binding);
381 _("Binding '%s' will alias '%s' Before, try: 'bind %s %s "
383 "https://neomutt.org/guide/configuration.html#bind-warnings"),
384 old_binding, new_binding, mutt_map_get_name(menu, Menus), new_binding);
386 rc = MUTT_CMD_WARNING;
395 } while (tmp && len >= pos);
399 else if (buf[pos] == tmp->keys[pos])
401 else if (buf[pos] < tmp->keys[pos])
403 /* found location to insert between last and tmp */
407 else /* buf[pos] > tmp->keys[pos] */
432 * km_bind - Bind a key to a macro
433 * @param s Key string
434 * @param menu Menu id, e.g. #MENU_EDITOR
435 * @param op Operation, e.g. OP_DELETE
436 * @param macro Macro string
437 * @param desc Description of macro (OPTIONAL)
438 * @retval #CommandResult Result e.g. #MUTT_CMD_SUCCESS
440 enum CommandResult km_bind(char *s, enum MenuType menu, int op, char *macro, char *desc)
442 return km_bind_err(s, menu, op, macro, desc, NULL);
446 * km_bindkey_err - Bind a key in a Menu to an operation (with error message)
447 * @param s Key string
448 * @param menu Menu id, e.g. #MENU_PAGER
449 * @param op Operation, e.g. OP_DELETE
450 * @param err Buffer for error message
451 * @retval #CommandResult Result e.g. #MUTT_CMD_SUCCESS
453 static enum CommandResult km_bindkey_err(const char *s, enum MenuType menu,
454 int op, struct Buffer *err)
456 return km_bind_err(s, menu, op, NULL, NULL, err);
460 * km_bindkey - Bind a key in a Menu to an operation
461 * @param s Key string
462 * @param menu Menu id, e.g. #MENU_PAGER
463 * @param op Operation, e.g. OP_DELETE
464 * @retval #CommandResult Result e.g. #MUTT_CMD_SUCCESS
466 static enum CommandResult km_bindkey(const char *s, enum MenuType menu, int op)
468 return km_bindkey_err(s, menu, op, NULL);
472 * get_op - Get the function by its name
473 * @param bindings Key bindings table
474 * @param start Name of function to find
475 * @param len Length of string to match
476 * @retval num Operation, e.g. OP_DELETE
478 static int get_op(const struct Binding *bindings, const char *start, size_t len)
480 for (int i = 0; bindings[i].name; i++)
482 if ((mutt_str_strncasecmp(start, bindings[i].name, len) == 0) &&
483 (mutt_str_strlen(bindings[i].name) == len))
485 return bindings[i].op;
493 * mutt_get_func - Get the name of a function
494 * @param bindings Key bindings table
495 * @param op Operation, e.g. OP_DELETE
496 * @retval ptr Name of function
497 * @retval NULL Operation not found
499 * @note This returns a static string.
501 const char *mutt_get_func(const struct Binding *bindings, int op)
503 for (int i = 0; bindings[i].name; i++)
505 if (bindings[i].op == op)
506 return bindings[i].name;
513 * generic_tokenize_push_string - Parse and queue a 'push' command
514 * @param s String to push into the key queue
515 * @param generic_push Callback function to add events to macro queue
517 * Parses s for `<function>` syntax and adds the whole sequence to either the
518 * macro or unget buffer. This function is invoked by the next two defines
521 static void generic_tokenize_push_string(char *s, void (*generic_push)(int, int))
524 char *p = s + mutt_str_strlen(s) - 1;
530 /* if we see something like "<PageUp>", look to see if it is a real
531 * function name and return the corresponding value */
534 for (pp = p - 1; pp >= s && *pp != '<'; pp--)
541 generic_push(KEY_F(i), 0);
547 for (i = 0; KeyNames[i].name; i++)
549 if (mutt_str_strncasecmp(pp, KeyNames[i].name, l) == 0)
552 if (KeyNames[i].name)
555 generic_push(KeyNames[i].value, 0);
560 /* See if it is a valid command
561 * skip the '<' and the '>' when comparing */
562 for (enum MenuType j = 0; Menus[j].name; j++)
564 const struct Binding *binding = km_get_table(Menus[j].value);
567 op = get_op(binding, pp + 1, l - 2);
581 generic_push((unsigned char) *p--, 0); /* independent 8 bits chars */
586 * retry_generic - Try to find the key in the generic menu bindings
587 * @param menu Menu id, e.g. #MENU_PAGER
588 * @param keys Array of keys to return to the input queue
589 * @param keyslen Number of keys in the array
590 * @param lastkey Last key pressed (to return to input queue)
591 * @retval num Operation, e.g. OP_DELETE
593 static int retry_generic(enum MenuType menu, keycode_t *keys, int keyslen, int lastkey)
595 if ((menu != MENU_EDITOR) && (menu != MENU_GENERIC) && (menu != MENU_PAGER))
598 mutt_unget_event(lastkey, 0);
599 for (; keyslen; keyslen--)
600 mutt_unget_event(keys[keyslen - 1], 0);
601 return km_dokey(MENU_GENERIC);
603 if (menu != MENU_EDITOR)
605 /* probably a good idea to flush input here so we can abort macros */
612 * km_dokey - Determine what a keypress should do
613 * @param menu Menu ID, e.g. #MENU_EDITOR
614 * @retval >0 Function to execute
615 * @retval OP_NULL No function bound to key sequence
616 * @retval -1 Error occurred while reading input
617 * @retval -2 A timeout or sigwinch occurred
619 int km_dokey(enum MenuType menu)
622 struct Keymap *map = Keymaps[menu];
626 if (!map && (menu != MENU_EDITOR))
627 return retry_generic(menu, NULL, 0, 0);
631 int i = (C_Timeout > 0) ? C_Timeout : 60;
633 /* keepalive may need to run more frequently than C_Timeout allows */
636 if (C_ImapKeepalive >= i)
640 while (C_ImapKeepalive && (C_ImapKeepalive < i))
642 mutt_getch_timeout(C_ImapKeepalive * 1000);
644 mutt_getch_timeout(-1);
645 /* If a timeout was not received, or the window was resized, exit the
646 * loop now. Otherwise, continue to loop until reaching a total of
647 * $timeout seconds. */
648 if ((tmp.ch != -2) || SigWinch)
651 if (MonitorFilesChanged)
654 i -= C_ImapKeepalive;
661 mutt_getch_timeout(i * 1000);
663 mutt_getch_timeout(-1);
668 /* hide timeouts, but not window resizes, from the line editor. */
669 if ((menu == MENU_EDITOR) && (tmp.ch == -2) && !SigWinch)
676 /* do we have an op already? */
679 const char *func = NULL;
680 const struct Binding *bindings = NULL;
682 /* is this a valid op for this menu? */
683 if ((bindings = km_get_table(menu)) && (func = mutt_get_func(bindings, tmp.op)))
686 if ((menu == MENU_EDITOR) && mutt_get_func(OpEditor, tmp.op))
689 if ((menu != MENU_EDITOR) && (menu != MENU_PAGER))
691 /* check generic menu */
692 bindings = OpGeneric;
693 func = mutt_get_func(bindings, tmp.op);
698 /* Sigh. Valid function but not in this context.
699 * Find the literal string and push it back */
700 for (i = 0; Menus[i].name; i++)
702 bindings = km_get_table(Menus[i].value);
705 func = mutt_get_func(bindings, tmp.op);
708 mutt_unget_event('>', 0);
709 mutt_unget_string(func);
710 mutt_unget_event('<', 0);
715 /* continue to chew */
723 /* Nope. Business as usual */
724 while (LastKey > map->keys[pos])
726 if ((pos > map->eq) || !map->next)
727 return retry_generic(menu, map->keys, pos, LastKey);
731 if (LastKey != map->keys[pos])
732 return retry_generic(menu, map->keys, pos, LastKey);
734 if (++pos == map->len)
736 if (map->op != OP_MACRO)
739 /* OptIgnoreMacroEvents turns off processing the MacroEvents buffer
740 * in mutt_getch(). Generating new macro events during that time would
741 * result in undesired behavior once the option is turned off.
743 * Originally this returned -1, however that results in an unbuffered
744 * username or password prompt being aborted. Returning OP_NULL allows
745 * mutt_enter_string_full() to display the keybinding pressed instead.
747 * It may be unexpected for a macro's keybinding to be returned,
748 * but less so than aborting the prompt. */
749 if (OptIgnoreMacroEvents)
757 mutt_error(_("Macro loop detected"));
761 generic_tokenize_push_string(map->macro, mutt_push_macro_event);
771 * create_bindings - Attach a set of keybindings to a Menu
772 * @param map Key bindings
773 * @param menu Menu id, e.g. #MENU_PAGER
775 static void create_bindings(const struct Binding *map, enum MenuType menu)
777 for (int i = 0; map[i].name; i++)
779 km_bindkey(map[i].seq, menu, map[i].op);
783 * km_keyname - Get the human name for a key
785 * @retval ptr Name of the key
787 * @note This returns a pointer to a static buffer.
789 static const char *km_keyname(int c)
793 const char *p = mutt_map_get_name(c, KeyNames);
797 if ((c < 256) && (c > -128) && iscntrl((unsigned char) c))
805 buf[1] = (c + '@') & 0x7f;
809 snprintf(buf, sizeof(buf), "\\%d%d%d", c >> 6, (c >> 3) & 7, c & 7);
811 else if ((c >= KEY_F0) && (c < KEY_F(256))) /* this maximum is just a guess */
812 sprintf(buf, "<F%d>", c - KEY_F0);
814 snprintf(buf, sizeof(buf), "%c", (unsigned char) c);
816 snprintf(buf, sizeof(buf), "\\x%hx", (unsigned short) c);
821 * km_expand_key - Get the key string bound to a Keymap
822 * @param s Buffer for the key string
823 * @param len Length of buffer
824 * @param map Keybinding map
828 int km_expand_key(char *s, size_t len, struct Keymap *map)
837 mutt_str_strfcpy(s, km_keyname(map->keys[p]), len);
838 const size_t l = mutt_str_strlen(s);
841 if ((++p >= map->len) || !len)
851 * km_find_func - Find a function's mapping in a Menu
852 * @param menu Menu id, e.g. #MENU_PAGER
853 * @param func Function, e.g. OP_DELETE
854 * @retval ptr Keymap for the function
856 struct Keymap *km_find_func(enum MenuType menu, int func)
858 struct Keymap *map = Keymaps[menu];
860 for (; map; map = map->next)
866 #ifdef NCURSES_VERSION
868 * find_ext_name - Find the curses name for a key
869 * @param key Key name
870 * @retval ptr Curses name
872 * Look up NeoMutt's name for a key and find the ncurses extended name for it.
874 * @note This returns a static string.
876 static const char *find_ext_name(const char *key)
878 for (int j = 0; ExtKeys[j].name; j++)
880 if (strcasecmp(key, ExtKeys[j].name) == 0)
881 return ExtKeys[j].sym;
885 #endif /* NCURSES_VERSION */
888 * init_extended_keys - Initialise map of ncurses extended keys
890 * Determine the keycodes for ncurses extended keys and fill in the KeyNames array.
892 * This function must be called *after* initscr(), or tigetstr() returns -1.
893 * This creates a bit of a chicken-and-egg problem because km_init() is called
894 * prior to start_curses(). This means that the default keybindings can't
895 * include any of the extended keys because they won't be defined until later.
897 void init_extended_keys(void)
899 #ifdef NCURSES_VERSION
901 use_extended_names(true);
903 for (int j = 0; KeyNames[j].name; j++)
905 if (KeyNames[j].value == -1)
907 const char *keyname = find_ext_name(KeyNames[j].name);
911 char *s = tigetstr((char *) keyname);
912 if (s && ((long) (s) != -1))
914 int code = key_defined(s);
916 KeyNames[j].value = code;
925 * km_init - Initialise all the menu keybindings
929 memset(Keymaps, 0, sizeof(struct Keymap *) * MENU_MAX);
931 create_bindings(OpAttach, MENU_ATTACH);
932 create_bindings(OpBrowser, MENU_FOLDER);
933 create_bindings(OpCompose, MENU_COMPOSE);
934 create_bindings(OpMain, MENU_MAIN);
935 create_bindings(OpPager, MENU_PAGER);
936 create_bindings(OpPost, MENU_POSTPONE);
937 create_bindings(OpQuery, MENU_QUERY);
938 create_bindings(OpAlias, MENU_ALIAS);
940 if (WithCrypto & APPLICATION_PGP)
941 create_bindings(OpPgp, MENU_PGP);
943 if (WithCrypto & APPLICATION_SMIME)
944 create_bindings(OpSmime, MENU_SMIME);
946 #ifdef CRYPT_BACKEND_GPGME
947 create_bindings(OpPgp, MENU_KEY_SELECT_PGP);
948 create_bindings(OpSmime, MENU_KEY_SELECT_SMIME);
952 create_bindings(OpMix, MENU_MIX);
954 km_bindkey("<space>", MENU_MIX, OP_GENERIC_SELECT_ENTRY);
955 km_bindkey("h", MENU_MIX, OP_MIX_CHAIN_PREV);
956 km_bindkey("l", MENU_MIX, OP_MIX_CHAIN_NEXT);
960 create_bindings(OpAutocryptAcct, MENU_AUTOCRYPT_ACCT);
963 /* bindings for the line editor */
964 create_bindings(OpEditor, MENU_EDITOR);
966 km_bindkey("<up>", MENU_EDITOR, OP_EDITOR_HISTORY_UP);
967 km_bindkey("<down>", MENU_EDITOR, OP_EDITOR_HISTORY_DOWN);
968 km_bindkey("<left>", MENU_EDITOR, OP_EDITOR_BACKWARD_CHAR);
969 km_bindkey("<right>", MENU_EDITOR, OP_EDITOR_FORWARD_CHAR);
970 km_bindkey("<home>", MENU_EDITOR, OP_EDITOR_BOL);
971 km_bindkey("<end>", MENU_EDITOR, OP_EDITOR_EOL);
972 km_bindkey("<backspace>", MENU_EDITOR, OP_EDITOR_BACKSPACE);
973 km_bindkey("<delete>", MENU_EDITOR, OP_EDITOR_DELETE_CHAR);
974 km_bindkey("\177", MENU_EDITOR, OP_EDITOR_BACKSPACE);
976 /* generic menu keymap */
977 create_bindings(OpGeneric, MENU_GENERIC);
979 km_bindkey("<home>", MENU_GENERIC, OP_FIRST_ENTRY);
980 km_bindkey("<end>", MENU_GENERIC, OP_LAST_ENTRY);
981 km_bindkey("<pagedown>", MENU_GENERIC, OP_NEXT_PAGE);
982 km_bindkey("<pageup>", MENU_GENERIC, OP_PREV_PAGE);
983 km_bindkey("<right>", MENU_GENERIC, OP_NEXT_PAGE);
984 km_bindkey("<left>", MENU_GENERIC, OP_PREV_PAGE);
985 km_bindkey("<up>", MENU_GENERIC, OP_PREV_ENTRY);
986 km_bindkey("<down>", MENU_GENERIC, OP_NEXT_ENTRY);
987 km_bindkey("1", MENU_GENERIC, OP_JUMP);
988 km_bindkey("2", MENU_GENERIC, OP_JUMP);
989 km_bindkey("3", MENU_GENERIC, OP_JUMP);
990 km_bindkey("4", MENU_GENERIC, OP_JUMP);
991 km_bindkey("5", MENU_GENERIC, OP_JUMP);
992 km_bindkey("6", MENU_GENERIC, OP_JUMP);
993 km_bindkey("7", MENU_GENERIC, OP_JUMP);
994 km_bindkey("8", MENU_GENERIC, OP_JUMP);
995 km_bindkey("9", MENU_GENERIC, OP_JUMP);
997 km_bindkey("<return>", MENU_GENERIC, OP_GENERIC_SELECT_ENTRY);
998 km_bindkey("<enter>", MENU_GENERIC, OP_GENERIC_SELECT_ENTRY);
1000 /* Miscellaneous extra bindings */
1002 km_bindkey(" ", MENU_MAIN, OP_DISPLAY_MESSAGE);
1003 km_bindkey("<up>", MENU_MAIN, OP_MAIN_PREV_UNDELETED);
1004 km_bindkey("<down>", MENU_MAIN, OP_MAIN_NEXT_UNDELETED);
1005 km_bindkey("J", MENU_MAIN, OP_NEXT_ENTRY);
1006 km_bindkey("K", MENU_MAIN, OP_PREV_ENTRY);
1007 km_bindkey("x", MENU_MAIN, OP_EXIT);
1009 km_bindkey("<return>", MENU_MAIN, OP_DISPLAY_MESSAGE);
1010 km_bindkey("<enter>", MENU_MAIN, OP_DISPLAY_MESSAGE);
1012 km_bindkey("x", MENU_PAGER, OP_EXIT);
1013 km_bindkey("i", MENU_PAGER, OP_EXIT);
1014 km_bindkey("<backspace>", MENU_PAGER, OP_PREV_LINE);
1015 km_bindkey("<pagedown>", MENU_PAGER, OP_NEXT_PAGE);
1016 km_bindkey("<pageup>", MENU_PAGER, OP_PREV_PAGE);
1017 km_bindkey("<up>", MENU_PAGER, OP_MAIN_PREV_UNDELETED);
1018 km_bindkey("<right>", MENU_PAGER, OP_MAIN_NEXT_UNDELETED);
1019 km_bindkey("<down>", MENU_PAGER, OP_MAIN_NEXT_UNDELETED);
1020 km_bindkey("<left>", MENU_PAGER, OP_MAIN_PREV_UNDELETED);
1021 km_bindkey("<home>", MENU_PAGER, OP_PAGER_TOP);
1022 km_bindkey("<end>", MENU_PAGER, OP_PAGER_BOTTOM);
1023 km_bindkey("1", MENU_PAGER, OP_JUMP);
1024 km_bindkey("2", MENU_PAGER, OP_JUMP);
1025 km_bindkey("3", MENU_PAGER, OP_JUMP);
1026 km_bindkey("4", MENU_PAGER, OP_JUMP);
1027 km_bindkey("5", MENU_PAGER, OP_JUMP);
1028 km_bindkey("6", MENU_PAGER, OP_JUMP);
1029 km_bindkey("7", MENU_PAGER, OP_JUMP);
1030 km_bindkey("8", MENU_PAGER, OP_JUMP);
1031 km_bindkey("9", MENU_PAGER, OP_JUMP);
1033 km_bindkey("<return>", MENU_PAGER, OP_NEXT_LINE);
1034 km_bindkey("<enter>", MENU_PAGER, OP_NEXT_LINE);
1036 km_bindkey("<return>", MENU_ALIAS, OP_GENERIC_SELECT_ENTRY);
1037 km_bindkey("<enter>", MENU_ALIAS, OP_GENERIC_SELECT_ENTRY);
1038 km_bindkey("<space>", MENU_ALIAS, OP_TAG);
1040 km_bindkey("<return>", MENU_ATTACH, OP_VIEW_ATTACH);
1041 km_bindkey("<enter>", MENU_ATTACH, OP_VIEW_ATTACH);
1042 km_bindkey("<return>", MENU_COMPOSE, OP_VIEW_ATTACH);
1043 km_bindkey("<enter>", MENU_COMPOSE, OP_VIEW_ATTACH);
1045 /* edit-to (default "t") hides generic tag-entry in Compose menu
1046 * This will bind tag-entry to "T" in the Compose menu */
1047 km_bindkey("T", MENU_COMPOSE, OP_TAG);
1051 * km_error_key - Handle an unbound key sequence
1052 * @param menu Menu id, e.g. #MENU_PAGER
1054 void km_error_key(enum MenuType menu)
1059 struct Keymap *key = km_find_func(menu, OP_HELP);
1060 if (!key && (menu != MENU_EDITOR) && (menu != MENU_PAGER))
1061 key = km_find_func(MENU_GENERIC, OP_HELP);
1064 mutt_error(_("Key is not bound"));
1068 /* Make sure the key is really the help key in this menu.
1070 * OP_END_COND is used as a barrier to ensure nothing extra
1071 * is left in the unget buffer.
1073 * Note that km_expand_key() + tokenize_unget_string() should
1074 * not be used here: control sequences are expanded to a form
1075 * (e.g. "^H") not recognized by km_dokey(). */
1076 mutt_unget_event(0, OP_END_COND);
1079 mutt_unget_event(key->keys[p], 0);
1081 /* Note, e.g. for the index menu:
1082 * bind generic ? noop
1083 * bind generic ,a help
1084 * bind index ,ab quit
1085 * The index keybinding shadows the generic binding.
1086 * OP_END_COND will be read and returned as the op.
1088 * bind generic ? noop
1089 * bind generic dq help
1090 * bind index d delete-message
1091 * OP_DELETE will be returned as the op, leaving "q" + OP_END_COND
1092 * in the unget buffer.
1094 op = km_dokey(menu);
1095 if (op != OP_END_COND)
1096 mutt_flush_unget_to_endcond();
1099 mutt_error(_("Key is not bound"));
1103 km_expand_key(buf, sizeof(buf), key);
1104 mutt_error(_("Key is not bound. Press '%s' for help."), buf);
1108 * mutt_parse_push - Parse the 'push' command - Implements ::command_t
1110 enum CommandResult mutt_parse_push(struct Buffer *buf, struct Buffer *s,
1111 unsigned long data, struct Buffer *err)
1113 mutt_extract_token(buf, s, MUTT_TOKEN_CONDENSE);
1116 mutt_buffer_printf(err, _("%s: too many arguments"), "push");
1117 return MUTT_CMD_ERROR;
1120 generic_tokenize_push_string(buf->data, mutt_push_macro_event);
1121 return MUTT_CMD_SUCCESS;
1125 * parse_keymap - Parse a user-config key binding
1126 * @param menu Array for results
1127 * @param s Buffer containing config string
1128 * @param max_menus Total number of menus
1129 * @param num_menus Number of menus this config applies to
1130 * @param err Buffer for error messages
1131 * @param bind If true 'bind', otherwise 'macro'
1132 * @retval ptr Key string for the binding
1134 * Expects to see: <menu-string>,<menu-string>,... <key-string>
1136 * @note Caller needs to free the returned string
1138 static char *parse_keymap(enum MenuType *menu, struct Buffer *s, int max_menus,
1139 int *num_menus, struct Buffer *err, bool bind)
1145 mutt_buffer_init(&buf);
1148 mutt_extract_token(&buf, s, MUTT_TOKEN_NO_FLAGS);
1152 while (i < max_menus)
1158 int val = mutt_map_get_value(p, Menus);
1161 mutt_buffer_printf(err, _("%s: no such menu"), p);
1173 mutt_extract_token(&buf, s, MUTT_TOKEN_NO_FLAGS);
1175 if (buf.data[0] == '\0')
1177 mutt_buffer_printf(err, _("%s: null key sequence"), bind ? "bind" : "macro");
1179 else if (MoreArgs(s))
1184 mutt_buffer_printf(err, _("%s: too few arguments"), bind ? "bind" : "macro");
1192 * try_bind - Try to make a key binding
1193 * @param key Key name
1194 * @param menu Menu id, e.g. #MENU_PAGER
1195 * @param func Function name
1196 * @param bindings Key bindings table
1197 * @param err Buffer for error message
1198 * @retval #CommandResult Result e.g. #MUTT_CMD_SUCCESS
1200 static enum CommandResult try_bind(char *key, enum MenuType menu, char *func,
1201 const struct Binding *bindings, struct Buffer *err)
1203 for (int i = 0; bindings[i].name; i++)
1205 if (mutt_str_strcmp(func, bindings[i].name) == 0)
1207 return km_bindkey_err(key, menu, bindings[i].op, err);
1212 mutt_buffer_printf(err, _("Function '%s' not available for menu '%s'"),
1213 func, mutt_map_get_name(menu, Menus));
1215 return MUTT_CMD_ERROR; /* Couldn't find an existing function with this name */
1219 * km_get_table - Lookup a menu's keybindings
1220 * @param menu Menu id, e.g. #MENU_EDITOR
1221 * @retval ptr Array of keybindings
1223 const struct Binding *km_get_table(enum MenuType menu)
1231 #ifdef USE_AUTOCRYPT
1232 case MENU_AUTOCRYPT_ACCT:
1233 return OpAutocryptAcct;
1243 #ifdef CRYPT_BACKEND_GPGME
1244 case MENU_KEY_SELECT_PGP:
1246 case MENU_KEY_SELECT_SMIME:
1258 return (WithCrypto & APPLICATION_PGP) ? OpPgp : NULL;
1269 * mutt_parse_bind - Parse the 'bind' command - Implements ::command_t
1271 * bind menu-name `<key_sequence>` function-name
1273 enum CommandResult mutt_parse_bind(struct Buffer *buf, struct Buffer *s,
1274 unsigned long data, struct Buffer *err)
1276 const struct Binding *bindings = NULL;
1277 enum MenuType menu[sizeof(Menus) / sizeof(struct Mapping) - 1];
1279 enum CommandResult rc = MUTT_CMD_SUCCESS;
1281 char *key = parse_keymap(menu, s, mutt_array_size(menu), &num_menus, err, true);
1283 return MUTT_CMD_ERROR;
1285 /* function to execute */
1286 mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
1289 mutt_buffer_printf(err, _("%s: too many arguments"), "bind");
1290 rc = MUTT_CMD_ERROR;
1292 else if (mutt_str_strcasecmp("noop", buf->data) == 0)
1294 for (int i = 0; i < num_menus; i++)
1296 km_bindkey(key, menu[i], OP_NULL); /* the 'unbind' command */
1301 for (int i = 0; i < num_menus; i++)
1303 /* The pager and editor menus don't use the generic map,
1304 * however for other menus try generic first. */
1305 if ((menu[i] != MENU_PAGER) && (menu[i] != MENU_EDITOR) && (menu[i] != MENU_GENERIC))
1307 rc = try_bind(key, menu[i], buf->data, OpGeneric, err);
1314 /* Clear any error message, we're going to try again */
1315 err->data[0] = '\0';
1316 bindings = km_get_table(menu[i]);
1319 rc = try_bind(key, menu[i], buf->data, bindings, err);
1328 * parse_menu - Parse menu-names into an array
1329 * @param menu Array for results
1330 * @param s String containing menu-names
1331 * @param err Buffer for error messages
1333 * Expects to see: <menu-string>[,<menu-string>]
1335 static void *parse_menu(bool *menu, char *s, struct Buffer *err)
1337 char *menu_names_dup = mutt_str_strdup(s);
1338 char *marker = menu_names_dup;
1339 char *menu_name = NULL;
1341 while ((menu_name = strsep(&marker, ",")))
1343 int value = mutt_map_get_value(menu_name, Menus);
1346 mutt_buffer_printf(err, _("%s: no such menu"), menu_name);
1353 FREE(&menu_names_dup);
1358 * km_unbind_all - Free all the keys in the supplied Keymap
1359 * @param map Keymap mapping
1360 * @param mode Undo bind or macro, e.g. #MUTT_UNBIND, #MUTT_UNMACRO
1362 * Iterate through Keymap and free keys defined either by "macro" or "bind".
1364 static void km_unbind_all(struct Keymap **map, unsigned long mode)
1366 struct Keymap *next = NULL;
1367 struct Keymap *first = NULL;
1368 struct Keymap *last = NULL;
1369 struct Keymap *cur = *map;
1374 if (((mode & MUTT_UNBIND) && !cur->macro) || ((mode & MUTT_UNMACRO) && cur->macro))
1405 * mutt_parse_unbind - Parse the 'unbind' command - Implements ::command_t
1408 * - one binding in one menu-name
1409 * - one binding in all menu-names
1410 * - all bindings in all menu-names
1412 * unbind `<menu-name[,...]|*>` [`<key_sequence>`]
1414 enum CommandResult mutt_parse_unbind(struct Buffer *buf, struct Buffer *s,
1415 unsigned long data, struct Buffer *err)
1417 bool menu[MENU_MAX] = { 0 };
1418 bool all_keys = false;
1421 mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
1422 if (mutt_str_strcmp(buf->data, "*") == 0)
1424 for (enum MenuType i = 0; i < MENU_MAX; i++)
1428 parse_menu(menu, buf->data, err);
1432 mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
1440 const char *cmd = (data & MUTT_UNMACRO) ? "unmacro" : "unbind";
1442 mutt_buffer_printf(err, _("%s: too many arguments"), cmd);
1443 return MUTT_CMD_ERROR;
1446 for (enum MenuType i = 0; i < MENU_MAX; i++)
1452 km_unbind_all(&Keymaps[i], data);
1453 km_bindkey("<enter>", MENU_GENERIC, OP_GENERIC_SELECT_ENTRY);
1454 km_bindkey("<return>", MENU_GENERIC, OP_GENERIC_SELECT_ENTRY);
1455 km_bindkey("<enter>", MENU_MAIN, OP_DISPLAY_MESSAGE);
1456 km_bindkey("<return>", MENU_MAIN, OP_DISPLAY_MESSAGE);
1457 km_bindkey("<backspace>", MENU_EDITOR, OP_EDITOR_BACKSPACE);
1458 km_bindkey("\177", MENU_EDITOR, OP_EDITOR_BACKSPACE);
1459 km_bindkey(":", MENU_GENERIC, OP_ENTER_COMMAND);
1460 km_bindkey(":", MENU_PAGER, OP_ENTER_COMMAND);
1461 if (i != MENU_EDITOR)
1463 km_bindkey("?", i, OP_HELP);
1464 km_bindkey("q", i, OP_EXIT);
1468 km_bindkey(key, i, OP_NULL);
1471 return MUTT_CMD_SUCCESS;
1475 * mutt_parse_macro - Parse the 'macro' command - Implements ::command_t
1477 * macro `<menu>` `<key>` `<macro>` `<description>`
1479 enum CommandResult mutt_parse_macro(struct Buffer *buf, struct Buffer *s,
1480 unsigned long data, struct Buffer *err)
1482 enum MenuType menu[sizeof(Menus) / sizeof(struct Mapping) - 1];
1484 enum CommandResult rc = MUTT_CMD_ERROR;
1487 char *key = parse_keymap(menu, s, mutt_array_size(menu), &num_menus, err, false);
1489 return MUTT_CMD_ERROR;
1491 mutt_extract_token(buf, s, MUTT_TOKEN_CONDENSE);
1492 /* make sure the macro sequence is not an empty string */
1493 if (buf->data[0] == '\0')
1495 mutt_buffer_strcpy(err, _("macro: empty key sequence"));
1501 seq = mutt_str_strdup(buf->data);
1502 mutt_extract_token(buf, s, MUTT_TOKEN_CONDENSE);
1506 mutt_buffer_printf(err, _("%s: too many arguments"), "macro");
1510 for (int i = 0; i < num_menus; i++)
1512 rc = km_bind(key, menu[i], OP_MACRO, seq, buf->data);
1520 for (int i = 0; i < num_menus; i++)
1522 rc = km_bind(key, menu[i], OP_MACRO, buf->data, NULL);
1531 * mutt_parse_exec - Parse the 'exec' command - Implements ::command_t
1533 enum CommandResult mutt_parse_exec(struct Buffer *buf, struct Buffer *s,
1534 unsigned long data, struct Buffer *err)
1538 const struct Binding *bindings = NULL;
1539 char *function = NULL;
1543 mutt_buffer_strcpy(err, _("exec: no arguments"));
1544 return MUTT_CMD_ERROR;
1549 mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
1550 function = buf->data;
1552 bindings = km_get_table(CurrentMenu);
1553 if (!bindings && (CurrentMenu != MENU_PAGER))
1554 bindings = OpGeneric;
1556 ops[nops] = get_op(bindings, function, mutt_str_strlen(function));
1557 if ((ops[nops] == OP_NULL) && (CurrentMenu != MENU_PAGER))
1558 ops[nops] = get_op(OpGeneric, function, mutt_str_strlen(function));
1560 if (ops[nops] == OP_NULL)
1563 mutt_error(_("%s: no such function"), function);
1564 return MUTT_CMD_ERROR;
1567 } while (MoreArgs(s) && nops < mutt_array_size(ops));
1570 mutt_push_macro_event(0, ops[--nops]);
1572 return MUTT_CMD_SUCCESS;
1576 * mutt_what_key - Ask the user to press a key
1578 * Displays the octal value back to the user.
1580 void mutt_what_key(void)
1584 mutt_window_mvprintw(MuttMessageWindow, 0, 0, _("Enter keys (^G to abort): "));
1588 if ((ch != ERR) && (ch != ctrl('G')))
1590 mutt_message(_("Char = %s, Octal = %o, Decimal = %d"), km_keyname(ch), ch, ch);
1592 } while (ch != ERR && ch != ctrl('G'));
1599 * mutt_keys_free - Free the key maps
1601 void mutt_keys_free(void)
1603 struct Keymap *map = NULL;
1604 struct Keymap *next = NULL;
1606 for (int i = 0; i < MENU_MAX; i++)
1608 for (map = Keymaps[i]; map; map = next)