]> granicus.if.org Git - neomutt/blob - keymap.c
merge: light refactoring
[neomutt] / keymap.c
1 /**
2  * @file
3  * Manage keymappings
4  *
5  * @authors
6  * Copyright (C) 1996-2000,2002,2010-2011 Michael R. Elkins <me@mutt.org>
7  *
8  * @copyright
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
12  * version.
13  *
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
17  * details.
18  *
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/>.
21  */
22
23 /**
24  * @page keymap Manage keymappings
25  *
26  * Manage keymappings
27  */
28
29 #include "config.h"
30 #include <ctype.h>
31 #include <limits.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include "mutt/mutt.h"
37 #include "mutt.h"
38 #include "keymap.h"
39 #include "curs_lib.h"
40 #include "functions.h"
41 #include "globals.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"
47 #include "opcodes.h"
48 #include "options.h"
49 #ifndef USE_SLANG_CURSES
50 #include <strings.h>
51 #endif
52 #ifdef USE_IMAP
53 #include "imap/imap.h"
54 #endif
55 #ifdef USE_INOTIFY
56 #include "monitor.h"
57 #endif
58
59 /**
60  * Menus - Menu name lookup table
61  */
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 },
71   { "pgp", MENU_PGP },
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 },
76 #endif
77 #ifdef MIXMASTER
78   { "mix", MENU_MIX },
79 #endif
80   { "query", MENU_QUERY },
81   { "generic", MENU_GENERIC },
82   { NULL, 0 },
83 };
84
85 /**
86  * KeyNames - Key name lookup table
87  */
88 static struct Mapping KeyNames[] = {
89   { "<PageUp>", KEY_PPAGE },
90   { "<PageDown>", KEY_NPAGE },
91   { "<Up>", KEY_UP },
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 },
99   { "<End>", KEY_END },
100   { "<Enter>", '\n' },
101   { "<Return>", '\r' },
102   { "<Esc>", '\033' }, // Escape
103   { "<Tab>", '\t' },
104   { "<Space>", ' ' },
105 #ifdef KEY_BTAB
106   { "<BackTab>", KEY_BTAB },
107 #endif
108 #ifdef KEY_NEXT
109   { "<Next>", KEY_NEXT },
110 #endif
111 #ifdef NCURSES_VERSION
112   /* extensions supported by ncurses.  values are filled in during initialization */
113
114   /* CTRL+key */
115   { "<C-Up>", -1 },
116   { "<C-Down>", -1 },
117   { "<C-Left>", -1 },
118   { "<C-Right>", -1 },
119   { "<C-Home>", -1 },
120   { "<C-End>", -1 },
121   { "<C-Next>", -1 },
122   { "<C-Prev>", -1 },
123
124   /* SHIFT+key */
125   { "<S-Up>", -1 },
126   { "<S-Down>", -1 },
127   { "<S-Left>", -1 },
128   { "<S-Right>", -1 },
129   { "<S-Home>", -1 },
130   { "<S-End>", -1 },
131   { "<S-Next>", -1 },
132   { "<S-Prev>", -1 },
133
134   /* ALT+key */
135   { "<A-Up>", -1 },
136   { "<A-Down>", -1 },
137   { "<A-Left>", -1 },
138   { "<A-Right>", -1 },
139   { "<A-Home>", -1 },
140   { "<A-End>", -1 },
141   { "<A-Next>", -1 },
142   { "<A-Prev>", -1 },
143 #endif /* NCURSES_VERSION */
144   { NULL, 0 },
145 };
146
147 int LastKey; /**< contains the last key the user pressed */
148
149 struct Keymap *Keymaps[MENU_MAX];
150
151 #ifdef NCURSES_VERSION
152 /**
153  * struct Extkey - Map key names from NeoMutt's style to Curses style
154  */
155 struct Extkey
156 {
157   const char *name;
158   const char *sym;
159 };
160
161 static const struct Extkey ExtKeys[] = {
162   { "<c-up>", "kUP5" },
163   { "<s-up>", "kUP" },
164   { "<a-up>", "kUP3" },
165
166   { "<s-down>", "kDN" },
167   { "<a-down>", "kDN3" },
168   { "<c-down>", "kDN5" },
169
170   { "<c-right>", "kRIT5" },
171   { "<s-right>", "kRIT" },
172   { "<a-right>", "kRIT3" },
173
174   { "<s-left>", "kLFT" },
175   { "<a-left>", "kLFT3" },
176   { "<c-left>", "kLFT5" },
177
178   { "<s-home>", "kHOM" },
179   { "<a-home>", "kHOM3" },
180   { "<c-home>", "kHOM5" },
181
182   { "<s-end>", "kEND" },
183   { "<a-end>", "kEND3" },
184   { "<c-end>", "kEND5" },
185
186   { "<s-next>", "kNXT" },
187   { "<a-next>", "kNXT3" },
188   { "<c-next>", "kNXT5" },
189
190   { "<s-prev>", "kPRV" },
191   { "<a-prev>", "kPRV3" },
192   { "<c-prev>", "kPRV5" },
193
194   { 0, 0 },
195 };
196 #endif
197
198 /**
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
203  */
204 static struct Keymap *alloc_keys(size_t len, keycode_t *keys)
205 {
206   struct Keymap *p = mutt_mem_calloc(1, sizeof(struct Keymap));
207   p->len = len;
208   p->keys = mutt_mem_malloc(len * sizeof(keycode_t));
209   memcpy(p->keys, keys, len * sizeof(keycode_t));
210   return p;
211 }
212
213 /**
214  * parse_fkey - Parse a function key string
215  * @param s String to parse
216  * @retval num Number of the key
217  *
218  * Given "<f8>", it will return 8.
219  */
220 static int parse_fkey(char *s)
221 {
222   char *t = NULL;
223   int n = 0;
224
225   if ((s[0] != '<') || (tolower(s[1]) != 'f'))
226     return -1;
227
228   for (t = s + 2; *t && isdigit((unsigned char) *t); t++)
229   {
230     n *= 10;
231     n += *t - '0';
232   }
233
234   if (*t != '>')
235     return -1;
236   return n;
237 }
238
239 /**
240  * parse_keycode - Parse a numeric keycode
241  * @param s String to parse
242  * @retval num Number of the key
243  *
244  * This function parses the string `<NNN>` and uses the octal value as the key
245  * to bind.
246  */
247 static int parse_keycode(const char *s)
248 {
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))
253     end_char++;
254   /* negative keycodes don't make sense, also detect overflow */
255   if ((*end_char != '>') || (result < 0) || (result == LONG_MAX))
256   {
257     return -1;
258   }
259
260   return result;
261 }
262
263 /**
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
269  */
270 static size_t parsekeys(const char *str, keycode_t *d, size_t max)
271 {
272   int n;
273   size_t len = max;
274   char buf[128];
275   char c;
276   char *t = NULL;
277
278   mutt_str_strfcpy(buf, str, sizeof(buf));
279   char *s = buf;
280
281   while (*s && len)
282   {
283     *d = '\0';
284     if ((*s == '<') && (t = strchr(s, '>')))
285     {
286       t++;
287       c = *t;
288       *t = '\0';
289
290       n = mutt_map_get_value(s, KeyNames);
291       if (n != -1)
292       {
293         s = t;
294         *d = n;
295       }
296       else if ((n = parse_fkey(s)) > 0)
297       {
298         s = t;
299         *d = KEY_F(n);
300       }
301       else if ((n = parse_keycode(s)) > 0)
302       {
303         s = t;
304         *d = n;
305       }
306
307       *t = c;
308     }
309
310     if (!*d)
311     {
312       *d = (unsigned char) *s;
313       s++;
314     }
315     d++;
316     len--;
317   }
318
319   return max - len;
320 }
321
322 /**
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
331  *
332  * Insert a key sequence into the specified map.
333  * The map is sorted by ASCII value (lowest to highest)
334  */
335 static enum CommandResult km_bind_err(const char *s, enum MenuType menu, int op,
336                                       char *macro, char *desc, struct Buffer *err)
337 {
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;
342
343   size_t len = parsekeys(s, buf, MAX_SEQ);
344
345   struct Keymap *map = alloc_keys(len, buf);
346   map->op = op;
347   map->macro = mutt_str_strdup(macro);
348   map->desc = mutt_str_strdup(desc);
349
350   struct Keymap *tmp = Keymaps[menu];
351
352   while (tmp)
353   {
354     if ((pos >= len) || (pos >= tmp->len))
355     {
356       /* map and tmp match so overwrite */
357       do
358       {
359         /* Don't warn on overwriting a 'noop' binding */
360         if ((tmp->len != len) && (tmp->op != OP_NULL))
361         {
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);
368           if (err)
369           {
370             /* err was passed, put the string there */
371             snprintf(
372                 err->data, err->dsize,
373                 _("Binding '%s' will alias '%s'  Before, try: 'bind %s %s "
374                   "noop'  "
375                   "https://neomutt.org/guide/configuration.html#bind-warnings"),
376                 old_binding, new_binding, mutt_map_get_name(menu, Menus), new_binding);
377           }
378           else
379           {
380             mutt_error(
381                 _("Binding '%s' will alias '%s'  Before, try: 'bind %s %s "
382                   "noop'  "
383                   "https://neomutt.org/guide/configuration.html#bind-warnings"),
384                 old_binding, new_binding, mutt_map_get_name(menu, Menus), new_binding);
385           }
386           rc = MUTT_CMD_WARNING;
387         }
388         len = tmp->eq;
389         next = tmp->next;
390         FREE(&tmp->macro);
391         FREE(&tmp->keys);
392         FREE(&tmp->desc);
393         FREE(&tmp);
394         tmp = next;
395       } while (tmp && len >= pos);
396       map->eq = len;
397       break;
398     }
399     else if (buf[pos] == tmp->keys[pos])
400       pos++;
401     else if (buf[pos] < tmp->keys[pos])
402     {
403       /* found location to insert between last and tmp */
404       map->eq = pos;
405       break;
406     }
407     else /* buf[pos] > tmp->keys[pos] */
408     {
409       last = tmp;
410       lastpos = pos;
411       if (pos > tmp->eq)
412         pos = tmp->eq;
413       tmp = tmp->next;
414     }
415   }
416
417   map->next = tmp;
418   if (last)
419   {
420     last->next = map;
421     last->eq = lastpos;
422   }
423   else
424   {
425     Keymaps[menu] = map;
426   }
427
428   return rc;
429 }
430
431 /**
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
439  */
440 enum CommandResult km_bind(char *s, enum MenuType menu, int op, char *macro, char *desc)
441 {
442   return km_bind_err(s, menu, op, macro, desc, NULL);
443 }
444
445 /**
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
452  */
453 static enum CommandResult km_bindkey_err(const char *s, enum MenuType menu,
454                                          int op, struct Buffer *err)
455 {
456   return km_bind_err(s, menu, op, NULL, NULL, err);
457 }
458
459 /**
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
465  */
466 static enum CommandResult km_bindkey(const char *s, enum MenuType menu, int op)
467 {
468   return km_bindkey_err(s, menu, op, NULL);
469 }
470
471 /**
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
477  */
478 static int get_op(const struct Binding *bindings, const char *start, size_t len)
479 {
480   for (int i = 0; bindings[i].name; i++)
481   {
482     if ((mutt_str_strncasecmp(start, bindings[i].name, len) == 0) &&
483         (mutt_str_strlen(bindings[i].name) == len))
484     {
485       return bindings[i].op;
486     }
487   }
488
489   return OP_NULL;
490 }
491
492 /**
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
498  *
499  * @note This returns a static string.
500  */
501 const char *mutt_get_func(const struct Binding *bindings, int op)
502 {
503   for (int i = 0; bindings[i].name; i++)
504   {
505     if (bindings[i].op == op)
506       return bindings[i].name;
507   }
508
509   return NULL;
510 }
511
512 /**
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
516  *
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
519  * below.
520  */
521 static void generic_tokenize_push_string(char *s, void (*generic_push)(int, int))
522 {
523   char *pp = NULL;
524   char *p = s + mutt_str_strlen(s) - 1;
525   size_t l;
526   int i, op = OP_NULL;
527
528   while (p >= s)
529   {
530     /* if we see something like "<PageUp>", look to see if it is a real
531      * function name and return the corresponding value */
532     if (*p == '>')
533     {
534       for (pp = p - 1; pp >= s && *pp != '<'; pp--)
535         ;
536       if (pp >= s)
537       {
538         i = parse_fkey(pp);
539         if (i > 0)
540         {
541           generic_push(KEY_F(i), 0);
542           p = pp - 1;
543           continue;
544         }
545
546         l = p - pp + 1;
547         for (i = 0; KeyNames[i].name; i++)
548         {
549           if (mutt_str_strncasecmp(pp, KeyNames[i].name, l) == 0)
550             break;
551         }
552         if (KeyNames[i].name)
553         {
554           /* found a match */
555           generic_push(KeyNames[i].value, 0);
556           p = pp - 1;
557           continue;
558         }
559
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++)
563         {
564           const struct Binding *binding = km_get_table(Menus[j].value);
565           if (binding)
566           {
567             op = get_op(binding, pp + 1, l - 2);
568             if (op != OP_NULL)
569               break;
570           }
571         }
572
573         if (op != OP_NULL)
574         {
575           generic_push(0, op);
576           p = pp - 1;
577           continue;
578         }
579       }
580     }
581     generic_push((unsigned char) *p--, 0); /* independent 8 bits chars */
582   }
583 }
584
585 /**
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
592  */
593 static int retry_generic(enum MenuType menu, keycode_t *keys, int keyslen, int lastkey)
594 {
595   if ((menu != MENU_EDITOR) && (menu != MENU_GENERIC) && (menu != MENU_PAGER))
596   {
597     if (lastkey)
598       mutt_unget_event(lastkey, 0);
599     for (; keyslen; keyslen--)
600       mutt_unget_event(keys[keyslen - 1], 0);
601     return km_dokey(MENU_GENERIC);
602   }
603   if (menu != MENU_EDITOR)
604   {
605     /* probably a good idea to flush input here so we can abort macros */
606     mutt_flushinp();
607   }
608   return OP_NULL;
609 }
610
611 /**
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
618  */
619 int km_dokey(enum MenuType menu)
620 {
621   struct KeyEvent tmp;
622   struct Keymap *map = Keymaps[menu];
623   int pos = 0;
624   int n = 0;
625
626   if (!map && (menu != MENU_EDITOR))
627     return retry_generic(menu, NULL, 0, 0);
628
629   while (true)
630   {
631     int i = (C_Timeout > 0) ? C_Timeout : 60;
632 #ifdef USE_IMAP
633     /* keepalive may need to run more frequently than C_Timeout allows */
634     if (C_ImapKeepalive)
635     {
636       if (C_ImapKeepalive >= i)
637         imap_keepalive();
638       else
639       {
640         while (C_ImapKeepalive && (C_ImapKeepalive < i))
641         {
642           mutt_getch_timeout(C_ImapKeepalive * 1000);
643           tmp = mutt_getch();
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)
649             goto gotkey;
650 #ifdef USE_INOTIFY
651           if (MonitorFilesChanged)
652             goto gotkey;
653 #endif
654           i -= C_ImapKeepalive;
655           imap_keepalive();
656         }
657       }
658     }
659 #endif
660
661     mutt_getch_timeout(i * 1000);
662     tmp = mutt_getch();
663     mutt_getch_timeout(-1);
664
665 #ifdef USE_IMAP
666   gotkey:
667 #endif
668     /* hide timeouts, but not window resizes, from the line editor. */
669     if ((menu == MENU_EDITOR) && (tmp.ch == -2) && !SigWinch)
670       continue;
671
672     LastKey = tmp.ch;
673     if (LastKey < 0)
674       return LastKey;
675
676     /* do we have an op already? */
677     if (tmp.op)
678     {
679       const char *func = NULL;
680       const struct Binding *bindings = NULL;
681
682       /* is this a valid op for this menu? */
683       if ((bindings = km_get_table(menu)) && (func = mutt_get_func(bindings, tmp.op)))
684         return tmp.op;
685
686       if ((menu == MENU_EDITOR) && mutt_get_func(OpEditor, tmp.op))
687         return tmp.op;
688
689       if ((menu != MENU_EDITOR) && (menu != MENU_PAGER))
690       {
691         /* check generic menu */
692         bindings = OpGeneric;
693         func = mutt_get_func(bindings, tmp.op);
694         if (func)
695           return tmp.op;
696       }
697
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++)
701       {
702         bindings = km_get_table(Menus[i].value);
703         if (bindings)
704         {
705           func = mutt_get_func(bindings, tmp.op);
706           if (func)
707           {
708             mutt_unget_event('>', 0);
709             mutt_unget_string(func);
710             mutt_unget_event('<', 0);
711             break;
712           }
713         }
714       }
715       /* continue to chew */
716       if (func)
717         continue;
718     }
719
720     if (!map)
721       return tmp.op;
722
723     /* Nope. Business as usual */
724     while (LastKey > map->keys[pos])
725     {
726       if ((pos > map->eq) || !map->next)
727         return retry_generic(menu, map->keys, pos, LastKey);
728       map = map->next;
729     }
730
731     if (LastKey != map->keys[pos])
732       return retry_generic(menu, map->keys, pos, LastKey);
733
734     if (++pos == map->len)
735     {
736       if (map->op != OP_MACRO)
737         return map->op;
738
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.
742        *
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.
746        *
747        * It may be unexpected for a macro's keybinding to be returned,
748        * but less so than aborting the prompt.  */
749       if (OptIgnoreMacroEvents)
750       {
751         return OP_NULL;
752       }
753
754       if (n++ == 10)
755       {
756         mutt_flushinp();
757         mutt_error(_("Macro loop detected"));
758         return -1;
759       }
760
761       generic_tokenize_push_string(map->macro, mutt_push_macro_event);
762       map = Keymaps[menu];
763       pos = 0;
764     }
765   }
766
767   /* not reached */
768 }
769
770 /**
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
774  */
775 static void create_bindings(const struct Binding *map, enum MenuType menu)
776 {
777   for (int i = 0; map[i].name; i++)
778     if (map[i].seq)
779       km_bindkey(map[i].seq, menu, map[i].op);
780 }
781
782 /**
783  * km_keyname - Get the human name for a key
784  * @param c Key code
785  * @retval ptr Name of the key
786  *
787  * @note This returns a pointer to a static buffer.
788  */
789 static const char *km_keyname(int c)
790 {
791   static char buf[35];
792
793   const char *p = mutt_map_get_name(c, KeyNames);
794   if (p)
795     return p;
796
797   if ((c < 256) && (c > -128) && iscntrl((unsigned char) c))
798   {
799     if (c < 0)
800       c += 256;
801
802     if (c < 128)
803     {
804       buf[0] = '^';
805       buf[1] = (c + '@') & 0x7f;
806       buf[2] = '\0';
807     }
808     else
809       snprintf(buf, sizeof(buf), "\\%d%d%d", c >> 6, (c >> 3) & 7, c & 7);
810   }
811   else if ((c >= KEY_F0) && (c < KEY_F(256))) /* this maximum is just a guess */
812     sprintf(buf, "<F%d>", c - KEY_F0);
813   else if (IsPrint(c))
814     snprintf(buf, sizeof(buf), "%c", (unsigned char) c);
815   else
816     snprintf(buf, sizeof(buf), "\\x%hx", (unsigned short) c);
817   return buf;
818 }
819
820 /**
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
825  * @retval 1 Success
826  * @retval 0 Error
827  */
828 int km_expand_key(char *s, size_t len, struct Keymap *map)
829 {
830   if (!map)
831     return 0;
832
833   int p = 0;
834
835   while (true)
836   {
837     mutt_str_strfcpy(s, km_keyname(map->keys[p]), len);
838     const size_t l = mutt_str_strlen(s);
839     len -= l;
840
841     if ((++p >= map->len) || !len)
842       return 1;
843
844     s += l;
845   }
846
847   /* not reached */
848 }
849
850 /**
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
855  */
856 struct Keymap *km_find_func(enum MenuType menu, int func)
857 {
858   struct Keymap *map = Keymaps[menu];
859
860   for (; map; map = map->next)
861     if (map->op == func)
862       break;
863   return map;
864 }
865
866 #ifdef NCURSES_VERSION
867 /**
868  * find_ext_name - Find the curses name for a key
869  * @param key Key name
870  * @retval ptr Curses name
871  *
872  * Look up NeoMutt's name for a key and find the ncurses extended name for it.
873  *
874  * @note This returns a static string.
875  */
876 static const char *find_ext_name(const char *key)
877 {
878   for (int j = 0; ExtKeys[j].name; j++)
879   {
880     if (strcasecmp(key, ExtKeys[j].name) == 0)
881       return ExtKeys[j].sym;
882   }
883   return 0;
884 }
885 #endif /* NCURSES_VERSION */
886
887 /**
888  * init_extended_keys - Initialise map of ncurses extended keys
889  *
890  * Determine the keycodes for ncurses extended keys and fill in the KeyNames array.
891  *
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.
896  */
897 void init_extended_keys(void)
898 {
899 #ifdef NCURSES_VERSION
900
901   use_extended_names(true);
902
903   for (int j = 0; KeyNames[j].name; j++)
904   {
905     if (KeyNames[j].value == -1)
906     {
907       const char *keyname = find_ext_name(KeyNames[j].name);
908
909       if (keyname)
910       {
911         char *s = tigetstr((char *) keyname);
912         if (s && ((long) (s) != -1))
913         {
914           int code = key_defined(s);
915           if (code > 0)
916             KeyNames[j].value = code;
917         }
918       }
919     }
920   }
921 #endif
922 }
923
924 /**
925  * km_init - Initialise all the menu keybindings
926  */
927 void km_init(void)
928 {
929   memset(Keymaps, 0, sizeof(struct Keymap *) * MENU_MAX);
930
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);
939
940   if (WithCrypto & APPLICATION_PGP)
941     create_bindings(OpPgp, MENU_PGP);
942
943   if (WithCrypto & APPLICATION_SMIME)
944     create_bindings(OpSmime, MENU_SMIME);
945
946 #ifdef CRYPT_BACKEND_GPGME
947   create_bindings(OpPgp, MENU_KEY_SELECT_PGP);
948   create_bindings(OpSmime, MENU_KEY_SELECT_SMIME);
949 #endif
950
951 #ifdef MIXMASTER
952   create_bindings(OpMix, MENU_MIX);
953
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);
957 #endif
958
959 #ifdef USE_AUTOCRYPT
960   create_bindings(OpAutocryptAcct, MENU_AUTOCRYPT_ACCT);
961 #endif
962
963   /* bindings for the line editor */
964   create_bindings(OpEditor, MENU_EDITOR);
965
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);
975
976   /* generic menu keymap */
977   create_bindings(OpGeneric, MENU_GENERIC);
978
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);
996
997   km_bindkey("<return>", MENU_GENERIC, OP_GENERIC_SELECT_ENTRY);
998   km_bindkey("<enter>", MENU_GENERIC, OP_GENERIC_SELECT_ENTRY);
999
1000   /* Miscellaneous extra bindings */
1001
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);
1008
1009   km_bindkey("<return>", MENU_MAIN, OP_DISPLAY_MESSAGE);
1010   km_bindkey("<enter>", MENU_MAIN, OP_DISPLAY_MESSAGE);
1011
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);
1032
1033   km_bindkey("<return>", MENU_PAGER, OP_NEXT_LINE);
1034   km_bindkey("<enter>", MENU_PAGER, OP_NEXT_LINE);
1035
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);
1039
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);
1044
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);
1048 }
1049
1050 /**
1051  * km_error_key - Handle an unbound key sequence
1052  * @param menu Menu id, e.g. #MENU_PAGER
1053  */
1054 void km_error_key(enum MenuType menu)
1055 {
1056   char buf[128];
1057   int p, op;
1058
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);
1062   if (!key)
1063   {
1064     mutt_error(_("Key is not bound"));
1065     return;
1066   }
1067
1068   /* Make sure the key is really the help key in this menu.
1069    *
1070    * OP_END_COND is used as a barrier to ensure nothing extra
1071    * is left in the unget buffer.
1072    *
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);
1077   p = key->len;
1078   while (p--)
1079     mutt_unget_event(key->keys[p], 0);
1080
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.
1087    *
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.
1093    */
1094   op = km_dokey(menu);
1095   if (op != OP_END_COND)
1096     mutt_flush_unget_to_endcond();
1097   if (op != OP_HELP)
1098   {
1099     mutt_error(_("Key is not bound"));
1100     return;
1101   }
1102
1103   km_expand_key(buf, sizeof(buf), key);
1104   mutt_error(_("Key is not bound.  Press '%s' for help."), buf);
1105 }
1106
1107 /**
1108  * mutt_parse_push - Parse the 'push' command - Implements ::command_t
1109  */
1110 enum CommandResult mutt_parse_push(struct Buffer *buf, struct Buffer *s,
1111                                    unsigned long data, struct Buffer *err)
1112 {
1113   mutt_extract_token(buf, s, MUTT_TOKEN_CONDENSE);
1114   if (MoreArgs(s))
1115   {
1116     mutt_buffer_printf(err, _("%s: too many arguments"), "push");
1117     return MUTT_CMD_ERROR;
1118   }
1119
1120   generic_tokenize_push_string(buf->data, mutt_push_macro_event);
1121   return MUTT_CMD_SUCCESS;
1122 }
1123
1124 /**
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
1133  *
1134  * Expects to see: <menu-string>,<menu-string>,... <key-string>
1135  *
1136  * @note Caller needs to free the returned string
1137  */
1138 static char *parse_keymap(enum MenuType *menu, struct Buffer *s, int max_menus,
1139                           int *num_menus, struct Buffer *err, bool bind)
1140 {
1141   struct Buffer buf;
1142   int i = 0;
1143   char *q = NULL;
1144
1145   mutt_buffer_init(&buf);
1146
1147   /* menu name */
1148   mutt_extract_token(&buf, s, MUTT_TOKEN_NO_FLAGS);
1149   char *p = buf.data;
1150   if (MoreArgs(s))
1151   {
1152     while (i < max_menus)
1153     {
1154       q = strchr(p, ',');
1155       if (q)
1156         *q = '\0';
1157
1158       int val = mutt_map_get_value(p, Menus);
1159       if (val == -1)
1160       {
1161         mutt_buffer_printf(err, _("%s: no such menu"), p);
1162         goto error;
1163       }
1164       menu[i] = val;
1165       i++;
1166       if (q)
1167         p = q + 1;
1168       else
1169         break;
1170     }
1171     *num_menus = i;
1172     /* key sequence */
1173     mutt_extract_token(&buf, s, MUTT_TOKEN_NO_FLAGS);
1174
1175     if (buf.data[0] == '\0')
1176     {
1177       mutt_buffer_printf(err, _("%s: null key sequence"), bind ? "bind" : "macro");
1178     }
1179     else if (MoreArgs(s))
1180       return buf.data;
1181   }
1182   else
1183   {
1184     mutt_buffer_printf(err, _("%s: too few arguments"), bind ? "bind" : "macro");
1185   }
1186 error:
1187   FREE(&buf.data);
1188   return NULL;
1189 }
1190
1191 /**
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
1199  */
1200 static enum CommandResult try_bind(char *key, enum MenuType menu, char *func,
1201                                    const struct Binding *bindings, struct Buffer *err)
1202 {
1203   for (int i = 0; bindings[i].name; i++)
1204   {
1205     if (mutt_str_strcmp(func, bindings[i].name) == 0)
1206     {
1207       return km_bindkey_err(key, menu, bindings[i].op, err);
1208     }
1209   }
1210   if (err)
1211   {
1212     mutt_buffer_printf(err, _("Function '%s' not available for menu '%s'"),
1213                        func, mutt_map_get_name(menu, Menus));
1214   }
1215   return MUTT_CMD_ERROR; /* Couldn't find an existing function with this name */
1216 }
1217
1218 /**
1219  * km_get_table - Lookup a menu's keybindings
1220  * @param menu Menu id, e.g. #MENU_EDITOR
1221  * @retval ptr Array of keybindings
1222  */
1223 const struct Binding *km_get_table(enum MenuType menu)
1224 {
1225   switch (menu)
1226   {
1227     case MENU_ALIAS:
1228       return OpAlias;
1229     case MENU_ATTACH:
1230       return OpAttach;
1231 #ifdef USE_AUTOCRYPT
1232     case MENU_AUTOCRYPT_ACCT:
1233       return OpAutocryptAcct;
1234 #endif
1235     case MENU_COMPOSE:
1236       return OpCompose;
1237     case MENU_EDITOR:
1238       return OpEditor;
1239     case MENU_FOLDER:
1240       return OpBrowser;
1241     case MENU_GENERIC:
1242       return OpGeneric;
1243 #ifdef CRYPT_BACKEND_GPGME
1244     case MENU_KEY_SELECT_PGP:
1245       return OpPgp;
1246     case MENU_KEY_SELECT_SMIME:
1247       return OpSmime;
1248 #endif
1249     case MENU_MAIN:
1250       return OpMain;
1251 #ifdef MIXMASTER
1252     case MENU_MIX:
1253       return OpMix;
1254 #endif
1255     case MENU_PAGER:
1256       return OpPager;
1257     case MENU_PGP:
1258       return (WithCrypto & APPLICATION_PGP) ? OpPgp : NULL;
1259     case MENU_POSTPONE:
1260       return OpPost;
1261     case MENU_QUERY:
1262       return OpQuery;
1263     default:
1264       return NULL;
1265   }
1266 }
1267
1268 /**
1269  * mutt_parse_bind - Parse the 'bind' command - Implements ::command_t
1270  *
1271  * bind menu-name `<key_sequence>` function-name
1272  */
1273 enum CommandResult mutt_parse_bind(struct Buffer *buf, struct Buffer *s,
1274                                    unsigned long data, struct Buffer *err)
1275 {
1276   const struct Binding *bindings = NULL;
1277   enum MenuType menu[sizeof(Menus) / sizeof(struct Mapping) - 1];
1278   int num_menus = 0;
1279   enum CommandResult rc = MUTT_CMD_SUCCESS;
1280
1281   char *key = parse_keymap(menu, s, mutt_array_size(menu), &num_menus, err, true);
1282   if (!key)
1283     return MUTT_CMD_ERROR;
1284
1285   /* function to execute */
1286   mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
1287   if (MoreArgs(s))
1288   {
1289     mutt_buffer_printf(err, _("%s: too many arguments"), "bind");
1290     rc = MUTT_CMD_ERROR;
1291   }
1292   else if (mutt_str_strcasecmp("noop", buf->data) == 0)
1293   {
1294     for (int i = 0; i < num_menus; i++)
1295     {
1296       km_bindkey(key, menu[i], OP_NULL); /* the 'unbind' command */
1297     }
1298   }
1299   else
1300   {
1301     for (int i = 0; i < num_menus; i++)
1302     {
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))
1306       {
1307         rc = try_bind(key, menu[i], buf->data, OpGeneric, err);
1308         if (rc == 0)
1309           continue;
1310         if (rc == -2)
1311           break;
1312       }
1313
1314       /* Clear any error message, we're going to try again */
1315       err->data[0] = '\0';
1316       bindings = km_get_table(menu[i]);
1317       if (bindings)
1318       {
1319         rc = try_bind(key, menu[i], buf->data, bindings, err);
1320       }
1321     }
1322   }
1323   FREE(&key);
1324   return rc;
1325 }
1326
1327 /**
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
1332  *
1333  * Expects to see: <menu-string>[,<menu-string>]
1334  */
1335 static void *parse_menu(bool *menu, char *s, struct Buffer *err)
1336 {
1337   char *menu_names_dup = mutt_str_strdup(s);
1338   char *marker = menu_names_dup;
1339   char *menu_name = NULL;
1340
1341   while ((menu_name = strsep(&marker, ",")))
1342   {
1343     int value = mutt_map_get_value(menu_name, Menus);
1344     if (value == -1)
1345     {
1346       mutt_buffer_printf(err, _("%s: no such menu"), menu_name);
1347       break;
1348     }
1349     else
1350       menu[value] = true;
1351   }
1352
1353   FREE(&menu_names_dup);
1354   return NULL;
1355 }
1356
1357 /**
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
1361  *
1362  * Iterate through Keymap and free keys defined either by "macro" or "bind".
1363  */
1364 static void km_unbind_all(struct Keymap **map, unsigned long mode)
1365 {
1366   struct Keymap *next = NULL;
1367   struct Keymap *first = NULL;
1368   struct Keymap *last = NULL;
1369   struct Keymap *cur = *map;
1370
1371   while (cur)
1372   {
1373     next = cur->next;
1374     if (((mode & MUTT_UNBIND) && !cur->macro) || ((mode & MUTT_UNMACRO) && cur->macro))
1375     {
1376       FREE(&cur->macro);
1377       FREE(&cur->keys);
1378       FREE(&cur->desc);
1379       FREE(&cur);
1380     }
1381     else if (!first)
1382     {
1383       first = cur;
1384       last = cur;
1385     }
1386     else if (last)
1387     {
1388       last->next = cur;
1389       last = cur;
1390     }
1391     else
1392     {
1393       last = cur;
1394     }
1395     cur = next;
1396   }
1397
1398   if (last)
1399     last->next = NULL;
1400
1401   *map = first;
1402 }
1403
1404 /**
1405  * mutt_parse_unbind - Parse the 'unbind' command - Implements ::command_t
1406  *
1407  * Command unbinds:
1408  * - one binding in one menu-name
1409  * - one binding in all menu-names
1410  * - all bindings in all menu-names
1411  *
1412  * unbind `<menu-name[,...]|*>` [`<key_sequence>`]
1413  */
1414 enum CommandResult mutt_parse_unbind(struct Buffer *buf, struct Buffer *s,
1415                                      unsigned long data, struct Buffer *err)
1416 {
1417   bool menu[MENU_MAX] = { 0 };
1418   bool all_keys = false;
1419   char *key = NULL;
1420
1421   mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
1422   if (mutt_str_strcmp(buf->data, "*") == 0)
1423   {
1424     for (enum MenuType i = 0; i < MENU_MAX; i++)
1425       menu[i] = true;
1426   }
1427   else
1428     parse_menu(menu, buf->data, err);
1429
1430   if (MoreArgs(s))
1431   {
1432     mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
1433     key = buf->data;
1434   }
1435   else
1436     all_keys = true;
1437
1438   if (MoreArgs(s))
1439   {
1440     const char *cmd = (data & MUTT_UNMACRO) ? "unmacro" : "unbind";
1441
1442     mutt_buffer_printf(err, _("%s: too many arguments"), cmd);
1443     return MUTT_CMD_ERROR;
1444   }
1445
1446   for (enum MenuType i = 0; i < MENU_MAX; i++)
1447   {
1448     if (!menu[i])
1449       continue;
1450     if (all_keys)
1451     {
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)
1462       {
1463         km_bindkey("?", i, OP_HELP);
1464         km_bindkey("q", i, OP_EXIT);
1465       }
1466     }
1467     else
1468       km_bindkey(key, i, OP_NULL);
1469   }
1470
1471   return MUTT_CMD_SUCCESS;
1472 }
1473
1474 /**
1475  * mutt_parse_macro - Parse the 'macro' command - Implements ::command_t
1476  *
1477  * macro `<menu>` `<key>` `<macro>` `<description>`
1478  */
1479 enum CommandResult mutt_parse_macro(struct Buffer *buf, struct Buffer *s,
1480                                     unsigned long data, struct Buffer *err)
1481 {
1482   enum MenuType menu[sizeof(Menus) / sizeof(struct Mapping) - 1];
1483   int num_menus = 0;
1484   enum CommandResult rc = MUTT_CMD_ERROR;
1485   char *seq = NULL;
1486
1487   char *key = parse_keymap(menu, s, mutt_array_size(menu), &num_menus, err, false);
1488   if (!key)
1489     return MUTT_CMD_ERROR;
1490
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')
1494   {
1495     mutt_buffer_strcpy(err, _("macro: empty key sequence"));
1496   }
1497   else
1498   {
1499     if (MoreArgs(s))
1500     {
1501       seq = mutt_str_strdup(buf->data);
1502       mutt_extract_token(buf, s, MUTT_TOKEN_CONDENSE);
1503
1504       if (MoreArgs(s))
1505       {
1506         mutt_buffer_printf(err, _("%s: too many arguments"), "macro");
1507       }
1508       else
1509       {
1510         for (int i = 0; i < num_menus; i++)
1511         {
1512           rc = km_bind(key, menu[i], OP_MACRO, seq, buf->data);
1513         }
1514       }
1515
1516       FREE(&seq);
1517     }
1518     else
1519     {
1520       for (int i = 0; i < num_menus; i++)
1521       {
1522         rc = km_bind(key, menu[i], OP_MACRO, buf->data, NULL);
1523       }
1524     }
1525   }
1526   FREE(&key);
1527   return rc;
1528 }
1529
1530 /**
1531  * mutt_parse_exec - Parse the 'exec' command - Implements ::command_t
1532  */
1533 enum CommandResult mutt_parse_exec(struct Buffer *buf, struct Buffer *s,
1534                                    unsigned long data, struct Buffer *err)
1535 {
1536   int ops[128];
1537   int nops = 0;
1538   const struct Binding *bindings = NULL;
1539   char *function = NULL;
1540
1541   if (!MoreArgs(s))
1542   {
1543     mutt_buffer_strcpy(err, _("exec: no arguments"));
1544     return MUTT_CMD_ERROR;
1545   }
1546
1547   do
1548   {
1549     mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
1550     function = buf->data;
1551
1552     bindings = km_get_table(CurrentMenu);
1553     if (!bindings && (CurrentMenu != MENU_PAGER))
1554       bindings = OpGeneric;
1555
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));
1559
1560     if (ops[nops] == OP_NULL)
1561     {
1562       mutt_flushinp();
1563       mutt_error(_("%s: no such function"), function);
1564       return MUTT_CMD_ERROR;
1565     }
1566     nops++;
1567   } while (MoreArgs(s) && nops < mutt_array_size(ops));
1568
1569   while (nops)
1570     mutt_push_macro_event(0, ops[--nops]);
1571
1572   return MUTT_CMD_SUCCESS;
1573 }
1574
1575 /**
1576  * mutt_what_key - Ask the user to press a key
1577  *
1578  * Displays the octal value back to the user.
1579  */
1580 void mutt_what_key(void)
1581 {
1582   int ch;
1583
1584   mutt_window_mvprintw(MuttMessageWindow, 0, 0, _("Enter keys (^G to abort): "));
1585   do
1586   {
1587     ch = getch();
1588     if ((ch != ERR) && (ch != ctrl('G')))
1589     {
1590       mutt_message(_("Char = %s, Octal = %o, Decimal = %d"), km_keyname(ch), ch, ch);
1591     }
1592   } while (ch != ERR && ch != ctrl('G'));
1593
1594   mutt_flushinp();
1595   mutt_clear_error();
1596 }
1597
1598 /**
1599  * mutt_keys_free - Free the key maps
1600  */
1601 void mutt_keys_free(void)
1602 {
1603   struct Keymap *map = NULL;
1604   struct Keymap *next = NULL;
1605
1606   for (int i = 0; i < MENU_MAX; i++)
1607   {
1608     for (map = Keymaps[i]; map; map = next)
1609     {
1610       next = map->next;
1611
1612       FREE(&map->macro);
1613       FREE(&map->desc);
1614       FREE(&map->keys);
1615       FREE(&map);
1616     }
1617
1618     Keymaps[i] = NULL;
1619   }
1620 }