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 neo_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/lib.h"
37 #include "config/lib.h"
38 #include "core/lib.h"
39 #include "gui/lib.h"
40 #include "mutt.h"
41 #include "keymap.h"
42 #include "menu/lib.h"
43 #include "ncrypt/lib.h"
44 #include "functions.h"
45 #include "init.h"
46 #include "mutt_globals.h"
47 #include "mutt_logging.h"
48 #include "opcodes.h"
49 #include "options.h"
50 #ifdef USE_IMAP
51 #include "imap/lib.h"
52 #endif
53 #ifdef USE_INOTIFY
54 #include "monitor.h"
55 #endif
56 
57 /**
58  * KeyNames - Key name lookup table
59  */
60 static struct Mapping KeyNames[] = {
61   { "<PageUp>", KEY_PPAGE },
62   { "<PageDown>", KEY_NPAGE },
63   { "<Up>", KEY_UP },
64   { "<Down>", KEY_DOWN },
65   { "<Right>", KEY_RIGHT },
66   { "<Left>", KEY_LEFT },
67   { "<Delete>", KEY_DC },
68   { "<BackSpace>", KEY_BACKSPACE },
69   { "<Insert>", KEY_IC },
70   { "<Home>", KEY_HOME },
71   { "<End>", KEY_END },
72   { "<Enter>", '\n' },
73   { "<Return>", '\r' },
74   { "<Esc>", '\033' }, // Escape
75   { "<Tab>", '\t' },
76   { "<Space>", ' ' },
77 #ifdef KEY_BTAB
78   { "<BackTab>", KEY_BTAB },
79 #endif
80 #ifdef KEY_NEXT
81   { "<Next>", KEY_NEXT },
82 #endif
83   /* extensions supported by ncurses.  values are filled in during initialization */
84 
85   /* CTRL+key */
86   { "<C-Up>", -1 },
87   { "<C-Down>", -1 },
88   { "<C-Left>", -1 },
89   { "<C-Right>", -1 },
90   { "<C-Home>", -1 },
91   { "<C-End>", -1 },
92   { "<C-Next>", -1 },
93   { "<C-Prev>", -1 },
94 
95   /* SHIFT+key */
96   { "<S-Up>", -1 },
97   { "<S-Down>", -1 },
98   { "<S-Left>", -1 },
99   { "<S-Right>", -1 },
100   { "<S-Home>", -1 },
101   { "<S-End>", -1 },
102   { "<S-Next>", -1 },
103   { "<S-Prev>", -1 },
104 
105   /* ALT+key */
106   { "<A-Up>", -1 },
107   { "<A-Down>", -1 },
108   { "<A-Left>", -1 },
109   { "<A-Right>", -1 },
110   { "<A-Home>", -1 },
111   { "<A-End>", -1 },
112   { "<A-Next>", -1 },
113   { "<A-Prev>", -1 },
114   { NULL, 0 },
115 };
116 
117 int LastKey;        ///< contains the last key the user pressed
118 keycode_t AbortKey; ///< code of key to abort prompts, normally Ctrl-G
119 
120 struct KeymapList Keymaps[MENU_MAX];
121 
122 /**
123  * struct Extkey - Map key names from NeoMutt's style to Curses style
124  */
125 struct Extkey
126 {
127   const char *name; ///< NeoMutt key name
128   const char *sym;  ///< Curses key name
129 };
130 
131 static const struct Extkey ExtKeys[] = {
132   { "<c-up>", "kUP5" },
133   { "<s-up>", "kUP" },
134   { "<a-up>", "kUP3" },
135 
136   { "<s-down>", "kDN" },
137   { "<a-down>", "kDN3" },
138   { "<c-down>", "kDN5" },
139 
140   { "<c-right>", "kRIT5" },
141   { "<s-right>", "kRIT" },
142   { "<a-right>", "kRIT3" },
143 
144   { "<s-left>", "kLFT" },
145   { "<a-left>", "kLFT3" },
146   { "<c-left>", "kLFT5" },
147 
148   { "<s-home>", "kHOM" },
149   { "<a-home>", "kHOM3" },
150   { "<c-home>", "kHOM5" },
151 
152   { "<s-end>", "kEND" },
153   { "<a-end>", "kEND3" },
154   { "<c-end>", "kEND5" },
155 
156   { "<s-next>", "kNXT" },
157   { "<a-next>", "kNXT3" },
158   { "<c-next>", "kNXT5" },
159 
160   { "<s-prev>", "kPRV" },
161   { "<a-prev>", "kPRV3" },
162   { "<c-prev>", "kPRV5" },
163 
164   { 0, 0 },
165 };
166 
167 /**
168  * mutt_keymap_free - Free a Keymap
169  * @param km Keymap to free
170  */
mutt_keymap_free(struct Keymap ** km)171 static void mutt_keymap_free(struct Keymap **km)
172 {
173   if (!km || !*km)
174     return;
175 
176   FREE(&(*km)->macro);
177   FREE(&(*km)->desc);
178   FREE(&(*km)->keys);
179   FREE(km);
180 }
181 
182 /**
183  * mutt_keymaplist_free - Free a List of Keymaps
184  * @param km_list List of Keymaps to free
185  */
mutt_keymaplist_free(struct KeymapList * km_list)186 static void mutt_keymaplist_free(struct KeymapList *km_list)
187 {
188   struct Keymap *np = NULL, *tmp = NULL;
189   STAILQ_FOREACH_SAFE(np, km_list, entries, tmp)
190   {
191     STAILQ_REMOVE(km_list, np, Keymap, entries);
192     mutt_keymap_free(&np);
193   }
194 }
195 
196 /**
197  * alloc_keys - Allocate space for a sequence of keys
198  * @param len  Number of keys
199  * @param keys Array of keys
200  * @retval ptr Sequence of keys
201  */
alloc_keys(size_t len,keycode_t * keys)202 static struct Keymap *alloc_keys(size_t len, keycode_t *keys)
203 {
204   struct Keymap *p = mutt_mem_calloc(1, sizeof(struct Keymap));
205   p->len = len;
206   p->keys = mutt_mem_calloc(len, sizeof(keycode_t));
207   memcpy(p->keys, keys, len * sizeof(keycode_t));
208   return p;
209 }
210 
211 /**
212  * parse_fkey - Parse a function key string
213  * @param s String to parse
214  * @retval num Number of the key
215  *
216  * Given "<f8>", it will return 8.
217  */
parse_fkey(char * s)218 static int parse_fkey(char *s)
219 {
220   char *t = NULL;
221   int n = 0;
222 
223   if ((s[0] != '<') || (tolower(s[1]) != 'f'))
224     return -1;
225 
226   for (t = s + 2; *t && isdigit((unsigned char) *t); t++)
227   {
228     n *= 10;
229     n += *t - '0';
230   }
231 
232   if (*t != '>')
233     return -1;
234   return n;
235 }
236 
237 /**
238  * parse_keycode - Parse a numeric keycode
239  * @param s String to parse
240  * @retval num Number of the key
241  *
242  * This function parses the string `<NNN>` and uses the octal value as the key
243  * to bind.
244  */
parse_keycode(const char * s)245 static int parse_keycode(const char *s)
246 {
247   char *end_char = NULL;
248   long int result = strtol(s + 1, &end_char, 8);
249   /* allow trailing whitespace, eg.  < 1001 > */
250   while (IS_SPACE(*end_char))
251     end_char++;
252   /* negative keycodes don't make sense, also detect overflow */
253   if ((*end_char != '>') || (result < 0) || (result == LONG_MAX))
254   {
255     return -1;
256   }
257 
258   return result;
259 }
260 
261 /**
262  * parsekeys - Parse a key string into key codes
263  * @param str Key string
264  * @param d   Array for key codes
265  * @param max Maximum length of key sequence
266  * @retval num Length of key sequence
267  */
parsekeys(const char * str,keycode_t * d,size_t max)268 static size_t parsekeys(const char *str, keycode_t *d, size_t max)
269 {
270   int n;
271   size_t len = max;
272   char buf[128];
273   char c;
274   char *t = NULL;
275 
276   mutt_str_copy(buf, str, sizeof(buf));
277   char *s = buf;
278 
279   while (*s && len)
280   {
281     *d = '\0';
282     if ((*s == '<') && (t = strchr(s, '>')))
283     {
284       t++;
285       c = *t;
286       *t = '\0';
287 
288       n = mutt_map_get_value(s, KeyNames);
289       if (n != -1)
290       {
291         s = t;
292         *d = n;
293       }
294       else if ((n = parse_fkey(s)) > 0)
295       {
296         s = t;
297         *d = KEY_F(n);
298       }
299       else if ((n = parse_keycode(s)) > 0)
300       {
301         s = t;
302         *d = n;
303       }
304 
305       *t = c;
306     }
307 
308     if (!*d)
309     {
310       *d = (unsigned char) *s;
311       s++;
312     }
313     d++;
314     len--;
315   }
316 
317   return max - len;
318 }
319 
320 /**
321  * km_compare_keys - Compare two keymaps' keyscodes and return the bigger one
322  * @param k1    first keymap to compare
323  * @param k2    second keymap to compare
324  * @param pos   position where the two keycodes differ
325  * @retval ptr Keymap with a bigger ASCII keycode
326  */
km_compare_keys(struct Keymap * k1,struct Keymap * k2,size_t * pos)327 static struct Keymap *km_compare_keys(struct Keymap *k1, struct Keymap *k2, size_t *pos)
328 {
329   while (*pos < k1->len && *pos < k2->len)
330   {
331     if (k1->keys[*pos] < k2->keys[*pos])
332       return k2;
333     else if (k1->keys[*pos] > k2->keys[*pos])
334       return k1;
335     else
336       *pos = *pos + 1;
337   }
338 
339   return NULL;
340 }
341 
342 /**
343  * km_bind_err - Set up a key binding
344  * @param s     Key string
345  * @param mtype Menu type, e.g. #MENU_EDITOR
346  * @param op    Operation, e.g. OP_DELETE
347  * @param macro Macro string
348  * @param desc Description of macro (OPTIONAL)
349  * @param err   Buffer for error message
350  * @retval #CommandResult Result e.g. #MUTT_CMD_SUCCESS
351  *
352  * Insert a key sequence into the specified map.
353  * The map is sorted by ASCII value (lowest to highest)
354  */
km_bind_err(const char * s,enum MenuType mtype,int op,char * macro,char * desc,struct Buffer * err)355 static enum CommandResult km_bind_err(const char *s, enum MenuType mtype, int op,
356                                       char *macro, char *desc, struct Buffer *err)
357 {
358   enum CommandResult rc = MUTT_CMD_SUCCESS;
359   struct Keymap *last = NULL, *np = NULL, *compare = NULL;
360   keycode_t buf[MAX_SEQ];
361   size_t pos = 0, lastpos = 0;
362 
363   size_t len = parsekeys(s, buf, MAX_SEQ);
364 
365   struct Keymap *map = alloc_keys(len, buf);
366   map->op = op;
367   map->macro = mutt_str_dup(macro);
368   map->desc = mutt_str_dup(desc);
369 
370   /* find position to place new keymap */
371   STAILQ_FOREACH(np, &Keymaps[mtype], entries)
372   {
373     compare = km_compare_keys(map, np, &pos);
374 
375     if (compare == map) /* map's keycode is bigger */
376     {
377       last = np;
378       lastpos = pos;
379       if (pos > np->eq)
380         pos = np->eq;
381     }
382     else if (compare == np) /* np's keycode is bigger, found insert location */
383     {
384       map->eq = pos;
385       break;
386     }
387     else /* equal keycodes */
388     {
389       /* Don't warn on overwriting a 'noop' binding */
390       if ((np->len != len) && (np->op != OP_NULL))
391       {
392         /* Overwrite with the different lengths, warn */
393         /* TODO: MAX_SEQ here is wrong */
394         char old_binding[MAX_SEQ];
395         char new_binding[MAX_SEQ];
396         km_expand_key(old_binding, MAX_SEQ, map);
397         km_expand_key(new_binding, MAX_SEQ, np);
398         char *err_msg =
399             _("Binding '%s' will alias '%s'  Before, try: 'bind %s %s noop'  "
400               "https://neomutt.org/guide/configuration.html#bind-warnings");
401         if (err)
402         {
403           /* err was passed, put the string there */
404           snprintf(err->data, err->dsize, err_msg, old_binding, new_binding,
405                    mutt_map_get_name(mtype, MenuNames), new_binding);
406         }
407         else
408         {
409           mutt_error(err_msg, old_binding, new_binding,
410                      mutt_map_get_name(mtype, MenuNames), new_binding);
411         }
412         rc = MUTT_CMD_WARNING;
413       }
414 
415       map->eq = np->eq;
416       STAILQ_REMOVE(&Keymaps[mtype], np, Keymap, entries);
417       mutt_keymap_free(&np);
418       break;
419     }
420   }
421 
422   if (last) /* if queue has at least one entry */
423   {
424     if (STAILQ_NEXT(last, entries))
425       STAILQ_INSERT_AFTER(&Keymaps[mtype], last, map, entries);
426     else /* last entry in the queue */
427       STAILQ_INSERT_TAIL(&Keymaps[mtype], map, entries);
428     last->eq = lastpos;
429   }
430   else /* queue is empty, so insert from head */
431   {
432     STAILQ_INSERT_HEAD(&Keymaps[mtype], map, entries);
433   }
434 
435   return rc;
436 }
437 
438 /**
439  * km_bind - Bind a key to a macro
440  * @param s     Key string
441  * @param mtype Menu type, e.g. #MENU_EDITOR
442  * @param op    Operation, e.g. OP_DELETE
443  * @param macro Macro string
444  * @param desc Description of macro (OPTIONAL)
445  * @retval #CommandResult Result e.g. #MUTT_CMD_SUCCESS
446  */
km_bind(char * s,enum MenuType mtype,int op,char * macro,char * desc)447 enum CommandResult km_bind(char *s, enum MenuType mtype, int op, char *macro, char *desc)
448 {
449   return km_bind_err(s, mtype, op, macro, desc, NULL);
450 }
451 
452 /**
453  * km_bindkey_err - Bind a key in a Menu to an operation (with error message)
454  * @param s     Key string
455  * @param mtype Menu type, e.g. #MENU_PAGER
456  * @param op    Operation, e.g. OP_DELETE
457  * @param err   Buffer for error message
458  * @retval #CommandResult Result e.g. #MUTT_CMD_SUCCESS
459  */
km_bindkey_err(const char * s,enum MenuType mtype,int op,struct Buffer * err)460 static enum CommandResult km_bindkey_err(const char *s, enum MenuType mtype,
461                                          int op, struct Buffer *err)
462 {
463   return km_bind_err(s, mtype, op, NULL, NULL, err);
464 }
465 
466 /**
467  * km_bindkey - Bind a key in a Menu to an operation
468  * @param s     Key string
469  * @param mtype Menu type, e.g. #MENU_PAGER
470  * @param op    Operation, e.g. OP_DELETE
471  * @retval #CommandResult Result e.g. #MUTT_CMD_SUCCESS
472  */
km_bindkey(const char * s,enum MenuType mtype,int op)473 static enum CommandResult km_bindkey(const char *s, enum MenuType mtype, int op)
474 {
475   return km_bindkey_err(s, mtype, op, NULL);
476 }
477 
478 /**
479  * get_op - Get the function by its name
480  * @param bindings Key bindings table
481  * @param start    Name of function to find
482  * @param len      Length of string to match
483  * @retval num Operation, e.g. OP_DELETE
484  */
get_op(const struct Binding * bindings,const char * start,size_t len)485 static int get_op(const struct Binding *bindings, const char *start, size_t len)
486 {
487   for (int i = 0; bindings[i].name; i++)
488   {
489     if (mutt_istrn_equal(start, bindings[i].name, len) &&
490         (mutt_str_len(bindings[i].name) == len))
491     {
492       return bindings[i].op;
493     }
494   }
495 
496   return OP_NULL;
497 }
498 
499 /**
500  * mutt_get_func - Get the name of a function
501  * @param bindings Key bindings table
502  * @param op       Operation, e.g. OP_DELETE
503  * @retval ptr  Name of function
504  * @retval NULL Operation not found
505  *
506  * @note This returns a static string.
507  */
mutt_get_func(const struct Binding * bindings,int op)508 const char *mutt_get_func(const struct Binding *bindings, int op)
509 {
510   for (int i = 0; bindings[i].name; i++)
511   {
512     if (bindings[i].op == op)
513       return bindings[i].name;
514   }
515 
516   return NULL;
517 }
518 
519 /**
520  * generic_tokenize_push_string - Parse and queue a 'push' command
521  * @param s            String to push into the key queue
522  * @param generic_push Callback function to add events to macro queue
523  *
524  * Parses s for `<function>` syntax and adds the whole sequence to either the
525  * macro or unget buffer.  This function is invoked by the next two defines
526  * below.
527  */
generic_tokenize_push_string(char * s,void (* generic_push)(int,int))528 static void generic_tokenize_push_string(char *s, void (*generic_push)(int, int))
529 {
530   char *pp = NULL;
531   char *p = s + mutt_str_len(s) - 1;
532   size_t l;
533   int i, op = OP_NULL;
534 
535   while (p >= s)
536   {
537     /* if we see something like "<PageUp>", look to see if it is a real
538      * function name and return the corresponding value */
539     if (*p == '>')
540     {
541       for (pp = p - 1; pp >= s && *pp != '<'; pp--)
542         ; // do nothing
543 
544       if (pp >= s)
545       {
546         i = parse_fkey(pp);
547         if (i > 0)
548         {
549           generic_push(KEY_F(i), 0);
550           p = pp - 1;
551           continue;
552         }
553 
554         l = p - pp + 1;
555         for (i = 0; KeyNames[i].name; i++)
556         {
557           if (mutt_istrn_equal(pp, KeyNames[i].name, l))
558             break;
559         }
560         if (KeyNames[i].name)
561         {
562           /* found a match */
563           generic_push(KeyNames[i].value, 0);
564           p = pp - 1;
565           continue;
566         }
567 
568         /* See if it is a valid command
569          * skip the '<' and the '>' when comparing */
570         for (enum MenuType j = 0; MenuNames[j].name; j++)
571         {
572           const struct Binding *binding = km_get_table(MenuNames[j].value);
573           if (binding)
574           {
575             op = get_op(binding, pp + 1, l - 2);
576             if (op != OP_NULL)
577               break;
578           }
579         }
580 
581         if (op != OP_NULL)
582         {
583           generic_push(0, op);
584           p = pp - 1;
585           continue;
586         }
587       }
588     }
589     generic_push((unsigned char) *p--, 0); /* independent 8 bits chars */
590   }
591 }
592 
593 /**
594  * retry_generic - Try to find the key in the generic menu bindings
595  * @param mtype   Menu type, e.g. #MENU_PAGER
596  * @param keys    Array of keys to return to the input queue
597  * @param keyslen Number of keys in the array
598  * @param lastkey Last key pressed (to return to input queue)
599  * @retval num Operation, e.g. OP_DELETE
600  */
retry_generic(enum MenuType mtype,keycode_t * keys,int keyslen,int lastkey)601 static int retry_generic(enum MenuType mtype, keycode_t *keys, int keyslen, int lastkey)
602 {
603   if ((mtype != MENU_EDITOR) && (mtype != MENU_GENERIC) && (mtype != MENU_PAGER))
604   {
605     if (lastkey)
606       mutt_unget_event(lastkey, 0);
607     for (; keyslen; keyslen--)
608       mutt_unget_event(keys[keyslen - 1], 0);
609     return km_dokey(MENU_GENERIC);
610   }
611   if (mtype != MENU_EDITOR)
612   {
613     /* probably a good idea to flush input here so we can abort macros */
614     mutt_flushinp();
615   }
616   return OP_NULL;
617 }
618 
619 /**
620  * km_dokey - Determine what a keypress should do
621  * @param mtype Menu type, e.g. #MENU_EDITOR
622  * @retval >0      Function to execute
623  * @retval OP_NULL No function bound to key sequence
624  * @retval -1      Error occurred while reading input
625  * @retval -2      A timeout or sigwinch occurred
626  */
km_dokey(enum MenuType mtype)627 int km_dokey(enum MenuType mtype)
628 {
629   struct KeyEvent tmp;
630   struct Keymap *map = STAILQ_FIRST(&Keymaps[mtype]);
631   int pos = 0;
632   int n = 0;
633 
634   if (!map && (mtype != MENU_EDITOR))
635     return retry_generic(mtype, NULL, 0, 0);
636 
637 #ifdef USE_IMAP
638   const short c_imap_keepalive =
639       cs_subset_number(NeoMutt->sub, "imap_keepalive");
640 #endif
641 
642   while (true)
643   {
644     const short c_timeout = cs_subset_number(NeoMutt->sub, "timeout");
645     int i = (c_timeout > 0) ? c_timeout : 60;
646 #ifdef USE_IMAP
647     /* keepalive may need to run more frequently than `$timeout` allows */
648     if (c_imap_keepalive)
649     {
650       if (c_imap_keepalive >= i)
651         imap_keepalive();
652       else
653       {
654         while (c_imap_keepalive && (c_imap_keepalive < i))
655         {
656           mutt_getch_timeout(c_imap_keepalive * 1000);
657           tmp = mutt_getch();
658           mutt_getch_timeout(-1);
659           /* If a timeout was not received, or the window was resized, exit the
660            * loop now.  Otherwise, continue to loop until reaching a total of
661            * $timeout seconds.  */
662           if ((tmp.ch != -2) || SigWinch)
663             goto gotkey;
664 #ifdef USE_INOTIFY
665           if (MonitorFilesChanged)
666             goto gotkey;
667 #endif
668           i -= c_imap_keepalive;
669           imap_keepalive();
670         }
671       }
672     }
673 #endif
674 
675     mutt_getch_timeout(i * 1000);
676     tmp = mutt_getch();
677     mutt_getch_timeout(-1);
678 
679 #ifdef USE_IMAP
680   gotkey:
681 #endif
682     /* hide timeouts, but not window resizes, from the line editor. */
683     if ((mtype == MENU_EDITOR) && (tmp.ch == -2) && !SigWinch)
684       continue;
685 
686     LastKey = tmp.ch;
687     if (LastKey < 0)
688       return LastKey;
689 
690     /* do we have an op already? */
691     if (tmp.op)
692     {
693       const char *func = NULL;
694       const struct Binding *bindings = NULL;
695 
696       /* is this a valid op for this menu type? */
697       if ((bindings = km_get_table(mtype)) && (func = mutt_get_func(bindings, tmp.op)))
698         return tmp.op;
699 
700       if ((mtype == MENU_EDITOR) && mutt_get_func(OpEditor, tmp.op))
701         return tmp.op;
702 
703       if ((mtype != MENU_EDITOR) && (mtype != MENU_PAGER))
704       {
705         /* check generic menu type */
706         bindings = OpGeneric;
707         func = mutt_get_func(bindings, tmp.op);
708         if (func)
709           return tmp.op;
710       }
711 
712       /* Sigh. Valid function but not in this context.
713        * Find the literal string and push it back */
714       for (i = 0; MenuNames[i].name; i++)
715       {
716         bindings = km_get_table(MenuNames[i].value);
717         if (bindings)
718         {
719           func = mutt_get_func(bindings, tmp.op);
720           if (func)
721           {
722             mutt_unget_event('>', 0);
723             mutt_unget_string(func);
724             mutt_unget_event('<', 0);
725             break;
726           }
727         }
728       }
729       /* continue to chew */
730       if (func)
731         continue;
732     }
733 
734     if (!map)
735       return tmp.op;
736 
737     /* Nope. Business as usual */
738     while (LastKey > map->keys[pos])
739     {
740       if ((pos > map->eq) || !STAILQ_NEXT(map, entries))
741         return retry_generic(mtype, map->keys, pos, LastKey);
742       map = STAILQ_NEXT(map, entries);
743     }
744 
745     if (LastKey != map->keys[pos])
746       return retry_generic(mtype, map->keys, pos, LastKey);
747 
748     if (++pos == map->len)
749     {
750       if (map->op != OP_MACRO)
751         return map->op;
752 
753       /* OptIgnoreMacroEvents turns off processing the MacroEvents buffer
754        * in mutt_getch().  Generating new macro events during that time would
755        * result in undesired behavior once the option is turned off.
756        *
757        * Originally this returned -1, however that results in an unbuffered
758        * username or password prompt being aborted.  Returning OP_NULL allows
759        * mutt_enter_string_full() to display the keybinding pressed instead.
760        *
761        * It may be unexpected for a macro's keybinding to be returned,
762        * but less so than aborting the prompt.  */
763       if (OptIgnoreMacroEvents)
764       {
765         return OP_NULL;
766       }
767 
768       if (n++ == 10)
769       {
770         mutt_flushinp();
771         mutt_error(_("Macro loop detected"));
772         return -1;
773       }
774 
775       generic_tokenize_push_string(map->macro, mutt_push_macro_event);
776       map = STAILQ_FIRST(&Keymaps[mtype]);
777       pos = 0;
778     }
779   }
780 
781   /* not reached */
782 }
783 
784 /**
785  * create_bindings - Attach a set of keybindings to a Menu
786  * @param map   Key bindings
787  * @param mtype Menu type, e.g. #MENU_PAGER
788  */
create_bindings(const struct Binding * map,enum MenuType mtype)789 static void create_bindings(const struct Binding *map, enum MenuType mtype)
790 {
791   STAILQ_INIT(&Keymaps[mtype]);
792 
793   for (int i = 0; map[i].name; i++)
794     if (map[i].seq)
795       km_bindkey(map[i].seq, mtype, map[i].op);
796 }
797 
798 /**
799  * km_keyname - Get the human name for a key
800  * @param c Key code
801  * @retval ptr Name of the key
802  *
803  * @note This returns a pointer to a static buffer.
804  */
km_keyname(int c)805 static const char *km_keyname(int c)
806 {
807   static char buf[35];
808 
809   const char *p = mutt_map_get_name(c, KeyNames);
810   if (p)
811     return p;
812 
813   if ((c < 256) && (c > -128) && iscntrl((unsigned char) c))
814   {
815     if (c < 0)
816       c += 256;
817 
818     if (c < 128)
819     {
820       buf[0] = '^';
821       buf[1] = (c + '@') & 0x7f;
822       buf[2] = '\0';
823     }
824     else
825       snprintf(buf, sizeof(buf), "\\%d%d%d", c >> 6, (c >> 3) & 7, c & 7);
826   }
827   else if ((c >= KEY_F0) && (c < KEY_F(256))) /* this maximum is just a guess */
828     sprintf(buf, "<F%d>", c - KEY_F0);
829   else if (IsPrint(c))
830     snprintf(buf, sizeof(buf), "%c", (unsigned char) c);
831   else
832     snprintf(buf, sizeof(buf), "\\x%hx", (unsigned short) c);
833   return buf;
834 }
835 
836 /**
837  * mutt_init_abort_key - Parse the abort_key config string
838  *
839  * Parse the string into `$abort_key` and put the keycode into AbortKey.
840  */
mutt_init_abort_key(void)841 void mutt_init_abort_key(void)
842 {
843   keycode_t buf[2];
844   const char *const c_abort_key = cs_subset_string(NeoMutt->sub, "abort_key");
845   size_t len = parsekeys(c_abort_key, buf, mutt_array_size(buf));
846   if (len == 0)
847   {
848     mutt_error(_("Abort key is not set, defaulting to Ctrl-G"));
849     AbortKey = ctrl('G');
850     return;
851   }
852   if (len > 1)
853   {
854     mutt_warning(
855         _("Specified abort key sequence (%s) will be truncated to first key"), c_abort_key);
856   }
857   AbortKey = buf[0];
858 }
859 
860 /**
861  * main_config_observer - Notification that a Config Variable has changed - Implements ::observer_t - @ingroup observer_api
862  */
main_config_observer(struct NotifyCallback * nc)863 int main_config_observer(struct NotifyCallback *nc)
864 {
865   if ((nc->event_type != NT_CONFIG) || !nc->event_data)
866     return -1;
867 
868   struct EventConfig *ev_c = nc->event_data;
869 
870   if (!mutt_str_equal(ev_c->name, "abort_key"))
871     return 0;
872 
873   mutt_init_abort_key();
874   mutt_debug(LL_DEBUG5, "config done\n");
875   return 0;
876 }
877 
878 /**
879  * km_expand_key_string - Get a human-readable key string
880  * @param str    Raw key string
881  * @param buf    Buffer for the key string
882  * @param buflen Length of buffer
883  * @retval num Length of string
884  */
km_expand_key_string(char * str,char * buf,size_t buflen)885 static int km_expand_key_string(char *str, char *buf, size_t buflen)
886 {
887   size_t len = 0;
888   for (; *str; str++)
889   {
890     const char *key = km_keyname(*str);
891     size_t keylen = mutt_str_len(key);
892 
893     mutt_str_copy(buf, key, buflen);
894     buf += keylen;
895     buflen -= keylen;
896     len += keylen;
897   }
898 
899   return len;
900 }
901 
902 /**
903  * km_expand_key - Get the key string bound to a Keymap
904  * @param s   Buffer for the key string
905  * @param len Length of buffer
906  * @param map Keybinding map
907  * @retval 1 Success
908  * @retval 0 Error
909  */
km_expand_key(char * s,size_t len,struct Keymap * map)910 int km_expand_key(char *s, size_t len, struct Keymap *map)
911 {
912   if (!map)
913     return 0;
914 
915   int p = 0;
916 
917   while (true)
918   {
919     mutt_str_copy(s, km_keyname(map->keys[p]), len);
920     const size_t l = mutt_str_len(s);
921     len -= l;
922 
923     if ((++p >= map->len) || !len)
924       return 1;
925 
926     s += l;
927   }
928 
929   /* not reached */
930 }
931 
932 /**
933  * km_find_func - Find a function's mapping in a Menu
934  * @param mtype Menu type, e.g. #MENU_PAGER
935  * @param func  Function, e.g. OP_DELETE
936  * @retval ptr Keymap for the function
937  */
km_find_func(enum MenuType mtype,int func)938 struct Keymap *km_find_func(enum MenuType mtype, int func)
939 {
940   struct Keymap *np = NULL;
941   STAILQ_FOREACH(np, &Keymaps[mtype], entries)
942   {
943     if (np->op == func)
944       break;
945   }
946   return np;
947 }
948 
949 /**
950  * find_ext_name - Find the curses name for a key
951  * @param key Key name
952  * @retval ptr Curses name
953  *
954  * Look up NeoMutt's name for a key and find the ncurses extended name for it.
955  *
956  * @note This returns a static string.
957  */
find_ext_name(const char * key)958 static const char *find_ext_name(const char *key)
959 {
960   for (int j = 0; ExtKeys[j].name; j++)
961   {
962     if (strcasecmp(key, ExtKeys[j].name) == 0)
963       return ExtKeys[j].sym;
964   }
965   return 0;
966 }
967 
968 /**
969  * init_extended_keys - Initialise map of ncurses extended keys
970  *
971  * Determine the keycodes for ncurses extended keys and fill in the KeyNames array.
972  *
973  * This function must be called *after* initscr(), or tigetstr() returns -1.
974  * This creates a bit of a chicken-and-egg problem because km_init() is called
975  * prior to start_curses().  This means that the default keybindings can't
976  * include any of the extended keys because they won't be defined until later.
977  */
init_extended_keys(void)978 void init_extended_keys(void)
979 {
980   use_extended_names(true);
981 
982   for (int j = 0; KeyNames[j].name; j++)
983   {
984     if (KeyNames[j].value == -1)
985     {
986       const char *keyname = find_ext_name(KeyNames[j].name);
987 
988       if (keyname)
989       {
990         char *s = tigetstr((char *) keyname);
991         if (s && ((long) (s) != -1))
992         {
993           int code = key_defined(s);
994           if (code > 0)
995             KeyNames[j].value = code;
996         }
997       }
998     }
999   }
1000 }
1001 
1002 /**
1003  * km_init - Initialise all the menu keybindings
1004  */
km_init(void)1005 void km_init(void)
1006 {
1007   memset(Keymaps, 0, sizeof(struct KeymapList) * MENU_MAX);
1008 
1009   create_bindings(OpAttach, MENU_ATTACH);
1010   create_bindings(OpBrowser, MENU_FOLDER);
1011   create_bindings(OpCompose, MENU_COMPOSE);
1012   create_bindings(OpMain, MENU_MAIN);
1013   create_bindings(OpPager, MENU_PAGER);
1014   create_bindings(OpPost, MENU_POSTPONE);
1015   create_bindings(OpQuery, MENU_QUERY);
1016   create_bindings(OpAlias, MENU_ALIAS);
1017 
1018   if (WithCrypto & APPLICATION_PGP)
1019     create_bindings(OpPgp, MENU_PGP);
1020 
1021   if (WithCrypto & APPLICATION_SMIME)
1022     create_bindings(OpSmime, MENU_SMIME);
1023 
1024 #ifdef CRYPT_BACKEND_GPGME
1025   create_bindings(OpPgp, MENU_KEY_SELECT_PGP);
1026   create_bindings(OpSmime, MENU_KEY_SELECT_SMIME);
1027 #endif
1028 
1029 #ifdef MIXMASTER
1030   create_bindings(OpMix, MENU_MIX);
1031 
1032   km_bindkey("<space>", MENU_MIX, OP_GENERIC_SELECT_ENTRY);
1033   km_bindkey("h", MENU_MIX, OP_MIX_CHAIN_PREV);
1034   km_bindkey("l", MENU_MIX, OP_MIX_CHAIN_NEXT);
1035 #endif
1036 
1037 #ifdef USE_AUTOCRYPT
1038   create_bindings(OpAutocryptAcct, MENU_AUTOCRYPT_ACCT);
1039 #endif
1040 
1041   /* bindings for the line editor */
1042   create_bindings(OpEditor, MENU_EDITOR);
1043 
1044   km_bindkey("<up>", MENU_EDITOR, OP_EDITOR_HISTORY_UP);
1045   km_bindkey("<down>", MENU_EDITOR, OP_EDITOR_HISTORY_DOWN);
1046   km_bindkey("<left>", MENU_EDITOR, OP_EDITOR_BACKWARD_CHAR);
1047   km_bindkey("<right>", MENU_EDITOR, OP_EDITOR_FORWARD_CHAR);
1048   km_bindkey("<home>", MENU_EDITOR, OP_EDITOR_BOL);
1049   km_bindkey("<end>", MENU_EDITOR, OP_EDITOR_EOL);
1050   km_bindkey("<backspace>", MENU_EDITOR, OP_EDITOR_BACKSPACE);
1051   km_bindkey("<delete>", MENU_EDITOR, OP_EDITOR_DELETE_CHAR);
1052   km_bindkey("\177", MENU_EDITOR, OP_EDITOR_BACKSPACE);
1053 
1054   /* generic menu keymap */
1055   create_bindings(OpGeneric, MENU_GENERIC);
1056 
1057   km_bindkey("<home>", MENU_GENERIC, OP_FIRST_ENTRY);
1058   km_bindkey("<end>", MENU_GENERIC, OP_LAST_ENTRY);
1059   km_bindkey("<pagedown>", MENU_GENERIC, OP_NEXT_PAGE);
1060   km_bindkey("<pageup>", MENU_GENERIC, OP_PREV_PAGE);
1061   km_bindkey("<right>", MENU_GENERIC, OP_NEXT_PAGE);
1062   km_bindkey("<left>", MENU_GENERIC, OP_PREV_PAGE);
1063   km_bindkey("<up>", MENU_GENERIC, OP_PREV_ENTRY);
1064   km_bindkey("<down>", MENU_GENERIC, OP_NEXT_ENTRY);
1065   km_bindkey("1", MENU_GENERIC, OP_JUMP);
1066   km_bindkey("2", MENU_GENERIC, OP_JUMP);
1067   km_bindkey("3", MENU_GENERIC, OP_JUMP);
1068   km_bindkey("4", MENU_GENERIC, OP_JUMP);
1069   km_bindkey("5", MENU_GENERIC, OP_JUMP);
1070   km_bindkey("6", MENU_GENERIC, OP_JUMP);
1071   km_bindkey("7", MENU_GENERIC, OP_JUMP);
1072   km_bindkey("8", MENU_GENERIC, OP_JUMP);
1073   km_bindkey("9", MENU_GENERIC, OP_JUMP);
1074 
1075   km_bindkey("<return>", MENU_GENERIC, OP_GENERIC_SELECT_ENTRY);
1076   km_bindkey("<enter>", MENU_GENERIC, OP_GENERIC_SELECT_ENTRY);
1077 
1078   /* Miscellaneous extra bindings */
1079 
1080   km_bindkey(" ", MENU_MAIN, OP_DISPLAY_MESSAGE);
1081   km_bindkey("<up>", MENU_MAIN, OP_MAIN_PREV_UNDELETED);
1082   km_bindkey("<down>", MENU_MAIN, OP_MAIN_NEXT_UNDELETED);
1083   km_bindkey("J", MENU_MAIN, OP_NEXT_ENTRY);
1084   km_bindkey("K", MENU_MAIN, OP_PREV_ENTRY);
1085   km_bindkey("x", MENU_MAIN, OP_EXIT);
1086 
1087   km_bindkey("<return>", MENU_MAIN, OP_DISPLAY_MESSAGE);
1088   km_bindkey("<enter>", MENU_MAIN, OP_DISPLAY_MESSAGE);
1089 
1090   km_bindkey("x", MENU_PAGER, OP_EXIT);
1091   km_bindkey("i", MENU_PAGER, OP_EXIT);
1092   km_bindkey("<backspace>", MENU_PAGER, OP_PREV_LINE);
1093   km_bindkey("<pagedown>", MENU_PAGER, OP_NEXT_PAGE);
1094   km_bindkey("<pageup>", MENU_PAGER, OP_PREV_PAGE);
1095   km_bindkey("<up>", MENU_PAGER, OP_MAIN_PREV_UNDELETED);
1096   km_bindkey("<right>", MENU_PAGER, OP_MAIN_NEXT_UNDELETED);
1097   km_bindkey("<down>", MENU_PAGER, OP_MAIN_NEXT_UNDELETED);
1098   km_bindkey("<left>", MENU_PAGER, OP_MAIN_PREV_UNDELETED);
1099   km_bindkey("<home>", MENU_PAGER, OP_PAGER_TOP);
1100   km_bindkey("<end>", MENU_PAGER, OP_PAGER_BOTTOM);
1101   km_bindkey("1", MENU_PAGER, OP_JUMP);
1102   km_bindkey("2", MENU_PAGER, OP_JUMP);
1103   km_bindkey("3", MENU_PAGER, OP_JUMP);
1104   km_bindkey("4", MENU_PAGER, OP_JUMP);
1105   km_bindkey("5", MENU_PAGER, OP_JUMP);
1106   km_bindkey("6", MENU_PAGER, OP_JUMP);
1107   km_bindkey("7", MENU_PAGER, OP_JUMP);
1108   km_bindkey("8", MENU_PAGER, OP_JUMP);
1109   km_bindkey("9", MENU_PAGER, OP_JUMP);
1110 
1111   km_bindkey("<return>", MENU_PAGER, OP_NEXT_LINE);
1112   km_bindkey("<enter>", MENU_PAGER, OP_NEXT_LINE);
1113 
1114   km_bindkey("<return>", MENU_ALIAS, OP_GENERIC_SELECT_ENTRY);
1115   km_bindkey("<enter>", MENU_ALIAS, OP_GENERIC_SELECT_ENTRY);
1116   km_bindkey("<space>", MENU_ALIAS, OP_TAG);
1117 
1118   km_bindkey("<return>", MENU_ATTACH, OP_VIEW_ATTACH);
1119   km_bindkey("<enter>", MENU_ATTACH, OP_VIEW_ATTACH);
1120   km_bindkey("<return>", MENU_COMPOSE, OP_VIEW_ATTACH);
1121   km_bindkey("<enter>", MENU_COMPOSE, OP_VIEW_ATTACH);
1122 
1123   /* edit-to (default "t") hides generic tag-entry in Compose menu
1124    * This will bind tag-entry to  "T" in the Compose menu */
1125   km_bindkey("T", MENU_COMPOSE, OP_TAG);
1126 }
1127 
1128 /**
1129  * km_error_key - Handle an unbound key sequence
1130  * @param mtype Menu type, e.g. #MENU_PAGER
1131  */
km_error_key(enum MenuType mtype)1132 void km_error_key(enum MenuType mtype)
1133 {
1134   char buf[128];
1135   int p, op;
1136 
1137   struct Keymap *key = km_find_func(mtype, OP_HELP);
1138   if (!key && (mtype != MENU_EDITOR) && (mtype != MENU_PAGER))
1139     key = km_find_func(MENU_GENERIC, OP_HELP);
1140   if (!key)
1141   {
1142     mutt_error(_("Key is not bound"));
1143     return;
1144   }
1145 
1146   /* Make sure the key is really the help key in this menu.
1147    *
1148    * OP_END_COND is used as a barrier to ensure nothing extra
1149    * is left in the unget buffer.
1150    *
1151    * Note that km_expand_key() + tokenize_unget_string() should
1152    * not be used here: control sequences are expanded to a form
1153    * (e.g. "^H") not recognized by km_dokey(). */
1154   mutt_unget_event(0, OP_END_COND);
1155   p = key->len;
1156   while (p--)
1157     mutt_unget_event(key->keys[p], 0);
1158 
1159   /* Note, e.g. for the index menu:
1160    *   bind generic ?   noop
1161    *   bind generic ,a  help
1162    *   bind index   ,ab quit
1163    * The index keybinding shadows the generic binding.
1164    * OP_END_COND will be read and returned as the op.
1165    *
1166    *   bind generic ?   noop
1167    *   bind generic dq  help
1168    *   bind index   d   delete-message
1169    * OP_DELETE will be returned as the op, leaving "q" + OP_END_COND
1170    * in the unget buffer.
1171    */
1172   op = km_dokey(mtype);
1173   if (op != OP_END_COND)
1174     mutt_flush_unget_to_endcond();
1175   if (op != OP_HELP)
1176   {
1177     mutt_error(_("Key is not bound"));
1178     return;
1179   }
1180 
1181   km_expand_key(buf, sizeof(buf), key);
1182   mutt_error(_("Key is not bound.  Press '%s' for help."), buf);
1183 }
1184 
1185 /**
1186  * mutt_parse_push - Parse the 'push' command - Implements Command::parse() - @ingroup command_parse
1187  */
mutt_parse_push(struct Buffer * buf,struct Buffer * s,intptr_t data,struct Buffer * err)1188 enum CommandResult mutt_parse_push(struct Buffer *buf, struct Buffer *s,
1189                                    intptr_t data, struct Buffer *err)
1190 {
1191   mutt_extract_token(buf, s, MUTT_TOKEN_CONDENSE);
1192   if (MoreArgs(s))
1193   {
1194     mutt_buffer_printf(err, _("%s: too many arguments"), "push");
1195     return MUTT_CMD_ERROR;
1196   }
1197 
1198   generic_tokenize_push_string(buf->data, mutt_push_macro_event);
1199   return MUTT_CMD_SUCCESS;
1200 }
1201 
1202 /**
1203  * parse_keymap - Parse a user-config key binding
1204  * @param mtypes    Array for results
1205  * @param s         Buffer containing config string
1206  * @param max_menus Total number of menus
1207  * @param num_menus Number of menus this config applies to
1208  * @param err       Buffer for error messages
1209  * @param bind      If true 'bind', otherwise 'macro'
1210  * @retval ptr Key string for the binding
1211  *
1212  * Expects to see: <menu-string>,<menu-string>,... <key-string>
1213  *
1214  * @note Caller needs to free the returned string
1215  */
parse_keymap(enum MenuType * mtypes,struct Buffer * s,int max_menus,int * num_menus,struct Buffer * err,bool bind)1216 static char *parse_keymap(enum MenuType *mtypes, struct Buffer *s, int max_menus,
1217                           int *num_menus, struct Buffer *err, bool bind)
1218 {
1219   struct Buffer buf;
1220   int i = 0;
1221   char *q = NULL;
1222 
1223   mutt_buffer_init(&buf);
1224 
1225   /* menu name */
1226   mutt_extract_token(&buf, s, MUTT_TOKEN_NO_FLAGS);
1227   char *p = buf.data;
1228   if (MoreArgs(s))
1229   {
1230     while (i < max_menus)
1231     {
1232       q = strchr(p, ',');
1233       if (q)
1234         *q = '\0';
1235 
1236       int val = mutt_map_get_value(p, MenuNames);
1237       if (val == -1)
1238       {
1239         mutt_buffer_printf(err, _("%s: no such menu"), p);
1240         goto error;
1241       }
1242       mtypes[i] = val;
1243       i++;
1244       if (q)
1245         p = q + 1;
1246       else
1247         break;
1248     }
1249     *num_menus = i;
1250     /* key sequence */
1251     mutt_extract_token(&buf, s, MUTT_TOKEN_NO_FLAGS);
1252 
1253     if (buf.data[0] == '\0')
1254     {
1255       mutt_buffer_printf(err, _("%s: null key sequence"), bind ? "bind" : "macro");
1256     }
1257     else if (MoreArgs(s))
1258       return buf.data;
1259   }
1260   else
1261   {
1262     mutt_buffer_printf(err, _("%s: too few arguments"), bind ? "bind" : "macro");
1263   }
1264 error:
1265   FREE(&buf.data);
1266   return NULL;
1267 }
1268 
1269 /**
1270  * try_bind - Try to make a key binding
1271  * @param key      Key name
1272  * @param mtype    Menu type, e.g. #MENU_PAGER
1273  * @param func     Function name
1274  * @param bindings Key bindings table
1275  * @param err      Buffer for error message
1276  * @retval #CommandResult Result e.g. #MUTT_CMD_SUCCESS
1277  */
try_bind(char * key,enum MenuType mtype,char * func,const struct Binding * bindings,struct Buffer * err)1278 static enum CommandResult try_bind(char *key, enum MenuType mtype, char *func,
1279                                    const struct Binding *bindings, struct Buffer *err)
1280 {
1281   for (int i = 0; bindings[i].name; i++)
1282   {
1283     if (mutt_str_equal(func, bindings[i].name))
1284     {
1285       return km_bindkey_err(key, mtype, bindings[i].op, err);
1286     }
1287   }
1288   if (err)
1289   {
1290     mutt_buffer_printf(err, _("Function '%s' not available for menu '%s'"),
1291                        func, mutt_map_get_name(mtype, MenuNames));
1292   }
1293   return MUTT_CMD_ERROR; /* Couldn't find an existing function with this name */
1294 }
1295 
1296 /**
1297  * km_get_table - Lookup a menu's keybindings
1298  * @param mtype Menu type, e.g. #MENU_EDITOR
1299  * @retval ptr Array of keybindings
1300  */
km_get_table(enum MenuType mtype)1301 const struct Binding *km_get_table(enum MenuType mtype)
1302 {
1303   switch (mtype)
1304   {
1305     case MENU_ALIAS:
1306       return OpAlias;
1307     case MENU_ATTACH:
1308       return OpAttach;
1309 #ifdef USE_AUTOCRYPT
1310     case MENU_AUTOCRYPT_ACCT:
1311       return OpAutocryptAcct;
1312 #endif
1313     case MENU_COMPOSE:
1314       return OpCompose;
1315     case MENU_EDITOR:
1316       return OpEditor;
1317     case MENU_FOLDER:
1318       return OpBrowser;
1319     case MENU_GENERIC:
1320       return OpGeneric;
1321 #ifdef CRYPT_BACKEND_GPGME
1322     case MENU_KEY_SELECT_PGP:
1323       return OpPgp;
1324     case MENU_KEY_SELECT_SMIME:
1325       return OpSmime;
1326 #endif
1327     case MENU_MAIN:
1328       return OpMain;
1329 #ifdef MIXMASTER
1330     case MENU_MIX:
1331       return OpMix;
1332 #endif
1333     case MENU_PAGER:
1334       return OpPager;
1335     case MENU_PGP:
1336       return (WithCrypto & APPLICATION_PGP) ? OpPgp : NULL;
1337     case MENU_POSTPONE:
1338       return OpPost;
1339     case MENU_QUERY:
1340       return OpQuery;
1341     default:
1342       return NULL;
1343   }
1344 }
1345 
1346 /**
1347  * mutt_parse_bind - Parse the 'bind' command - Implements Command::parse() - @ingroup command_parse
1348  *
1349  * bind menu-name `<key_sequence>` function-name
1350  */
mutt_parse_bind(struct Buffer * buf,struct Buffer * s,intptr_t data,struct Buffer * err)1351 enum CommandResult mutt_parse_bind(struct Buffer *buf, struct Buffer *s,
1352                                    intptr_t data, struct Buffer *err)
1353 {
1354   const struct Binding *bindings = NULL;
1355   enum MenuType mtypes[MenuNamesLen];
1356   int num_menus = 0;
1357   enum CommandResult rc = MUTT_CMD_SUCCESS;
1358 
1359   char *key = parse_keymap(mtypes, s, mutt_array_size(mtypes), &num_menus, err, true);
1360   if (!key)
1361     return MUTT_CMD_ERROR;
1362 
1363   /* function to execute */
1364   mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
1365   if (MoreArgs(s))
1366   {
1367     mutt_buffer_printf(err, _("%s: too many arguments"), "bind");
1368     rc = MUTT_CMD_ERROR;
1369   }
1370   else if (mutt_istr_equal("noop", buf->data))
1371   {
1372     for (int i = 0; i < num_menus; i++)
1373     {
1374       km_bindkey(key, mtypes[i], OP_NULL); /* the 'unbind' command */
1375       bindings = km_get_table(mtypes[i]);
1376       if (bindings)
1377       {
1378         char keystr[32] = { 0 };
1379         km_expand_key_string(key, keystr, sizeof(keystr));
1380         const char *mname = mutt_map_get_name(mtypes[i], MenuNames);
1381         mutt_debug(LL_NOTIFY, "NT_BINDING_DELETE: %s %s\n", mname, keystr);
1382 
1383         int op = get_op(OpGeneric, buf->data, mutt_str_len(buf->data));
1384         struct EventBinding ev_b = { mtypes[i], key, op };
1385         notify_send(NeoMutt->notify, NT_BINDING, NT_BINDING_DELETE, &ev_b);
1386       }
1387     }
1388   }
1389   else
1390   {
1391     for (int i = 0; i < num_menus; i++)
1392     {
1393       /* The pager and editor menus don't use the generic map,
1394        * however for other menus try generic first. */
1395       if ((mtypes[i] != MENU_PAGER) && (mtypes[i] != MENU_EDITOR) && (mtypes[i] != MENU_GENERIC))
1396       {
1397         rc = try_bind(key, mtypes[i], buf->data, OpGeneric, err);
1398         if (rc == MUTT_CMD_SUCCESS)
1399         {
1400           char keystr[32] = { 0 };
1401           km_expand_key_string(key, keystr, sizeof(keystr));
1402           const char *mname = mutt_map_get_name(mtypes[i], MenuNames);
1403           mutt_debug(LL_NOTIFY, "NT_BINDING_NEW: %s %s\n", mname, keystr);
1404 
1405           int op = get_op(OpGeneric, buf->data, mutt_str_len(buf->data));
1406           struct EventBinding ev_b = { mtypes[i], key, op };
1407           notify_send(NeoMutt->notify, NT_BINDING, NT_BINDING_ADD, &ev_b);
1408           continue;
1409         }
1410         if (rc == MUTT_CMD_WARNING)
1411           break;
1412       }
1413 
1414       /* Clear any error message, we're going to try again */
1415       err->data[0] = '\0';
1416       bindings = km_get_table(mtypes[i]);
1417       if (bindings)
1418       {
1419         rc = try_bind(key, mtypes[i], buf->data, bindings, err);
1420         if (rc == MUTT_CMD_SUCCESS)
1421         {
1422           char keystr[32] = { 0 };
1423           km_expand_key_string(key, keystr, sizeof(keystr));
1424           const char *mname = mutt_map_get_name(mtypes[i], MenuNames);
1425           mutt_debug(LL_NOTIFY, "NT_BINDING_NEW: %s %s\n", mname, keystr);
1426 
1427           int op = get_op(bindings, buf->data, mutt_str_len(buf->data));
1428           struct EventBinding ev_b = { mtypes[i], key, op };
1429           notify_send(NeoMutt->notify, NT_BINDING, NT_BINDING_ADD, &ev_b);
1430           continue;
1431         }
1432       }
1433     }
1434   }
1435   FREE(&key);
1436   return rc;
1437 }
1438 
1439 /**
1440  * parse_menu - Parse menu-names into an array
1441  * @param menus    Array for results
1442  * @param s        String containing menu-names
1443  * @param err      Buffer for error messages
1444  *
1445  * Expects to see: <menu-string>[,<menu-string>]
1446  */
parse_menu(bool * menus,char * s,struct Buffer * err)1447 static void *parse_menu(bool *menus, char *s, struct Buffer *err)
1448 {
1449   char *menu_names_dup = mutt_str_dup(s);
1450   char *marker = menu_names_dup;
1451   char *menu_name = NULL;
1452 
1453   while ((menu_name = strsep(&marker, ",")))
1454   {
1455     int value = mutt_map_get_value(menu_name, MenuNames);
1456     if (value == -1)
1457     {
1458       mutt_buffer_printf(err, _("%s: no such menu"), menu_name);
1459       break;
1460     }
1461     else
1462       menus[value] = true;
1463   }
1464 
1465   FREE(&menu_names_dup);
1466   return NULL;
1467 }
1468 
1469 /**
1470  * km_unbind_all - Free all the keys in the supplied Keymap
1471  * @param km_list Keymap mapping
1472  * @param mode    Undo bind or macro, e.g. #MUTT_UNBIND, #MUTT_UNMACRO
1473  *
1474  * Iterate through Keymap and free keys defined either by "macro" or "bind".
1475  */
km_unbind_all(struct KeymapList * km_list,unsigned long mode)1476 static void km_unbind_all(struct KeymapList *km_list, unsigned long mode)
1477 {
1478   struct Keymap *np = NULL, *tmp = NULL;
1479 
1480   STAILQ_FOREACH_SAFE(np, km_list, entries, tmp)
1481   {
1482     if (((mode & MUTT_UNBIND) && !np->macro) || ((mode & MUTT_UNMACRO) && np->macro))
1483     {
1484       STAILQ_REMOVE(km_list, np, Keymap, entries);
1485       mutt_keymap_free(&np);
1486     }
1487   }
1488 }
1489 
1490 /**
1491  * mutt_parse_unbind - Parse the 'unbind' command - Implements Command::parse() - @ingroup command_parse
1492  *
1493  * Command unbinds:
1494  * - one binding in one menu-name
1495  * - one binding in all menu-names
1496  * - all bindings in all menu-names
1497  *
1498  * unbind `<menu-name[,...]|*>` [`<key_sequence>`]
1499  */
mutt_parse_unbind(struct Buffer * buf,struct Buffer * s,intptr_t data,struct Buffer * err)1500 enum CommandResult mutt_parse_unbind(struct Buffer *buf, struct Buffer *s,
1501                                      intptr_t data, struct Buffer *err)
1502 {
1503   bool menu_matches[MENU_MAX] = { 0 };
1504   bool all_keys = false;
1505   char *key = NULL;
1506 
1507   mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
1508   if (mutt_str_equal(buf->data, "*"))
1509   {
1510     for (enum MenuType i = 0; i < MENU_MAX; i++)
1511       menu_matches[i] = true;
1512   }
1513   else
1514     parse_menu(menu_matches, buf->data, err);
1515 
1516   if (MoreArgs(s))
1517   {
1518     mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
1519     key = buf->data;
1520   }
1521   else
1522     all_keys = true;
1523 
1524   if (MoreArgs(s))
1525   {
1526     const char *cmd = (data & MUTT_UNMACRO) ? "unmacro" : "unbind";
1527 
1528     mutt_buffer_printf(err, _("%s: too many arguments"), cmd);
1529     return MUTT_CMD_ERROR;
1530   }
1531 
1532   for (enum MenuType i = 0; i < MENU_MAX; i++)
1533   {
1534     if (!menu_matches[i])
1535       continue;
1536     if (all_keys)
1537     {
1538       km_unbind_all(&Keymaps[i], data);
1539       km_bindkey("<enter>", MENU_GENERIC, OP_GENERIC_SELECT_ENTRY);
1540       km_bindkey("<return>", MENU_GENERIC, OP_GENERIC_SELECT_ENTRY);
1541       km_bindkey("<enter>", MENU_MAIN, OP_DISPLAY_MESSAGE);
1542       km_bindkey("<return>", MENU_MAIN, OP_DISPLAY_MESSAGE);
1543       km_bindkey("<backspace>", MENU_EDITOR, OP_EDITOR_BACKSPACE);
1544       km_bindkey("\177", MENU_EDITOR, OP_EDITOR_BACKSPACE);
1545       km_bindkey(":", MENU_GENERIC, OP_ENTER_COMMAND);
1546       km_bindkey(":", MENU_PAGER, OP_ENTER_COMMAND);
1547       if (i != MENU_EDITOR)
1548       {
1549         km_bindkey("?", i, OP_HELP);
1550         km_bindkey("q", i, OP_EXIT);
1551       }
1552 
1553       const char *mname = mutt_map_get_name(i, MenuNames);
1554       mutt_debug(LL_NOTIFY, "NT_MACRO_DELETE_ALL: %s\n", mname);
1555 
1556       struct EventBinding ev_b = { i, NULL, OP_NULL };
1557       notify_send(NeoMutt->notify, NT_BINDING,
1558                   (data & MUTT_UNMACRO) ? NT_MACRO_DELETE_ALL : NT_BINDING_DELETE_ALL,
1559                   &ev_b);
1560     }
1561     else
1562     {
1563       char keystr[32] = { 0 };
1564       km_expand_key_string(key, keystr, sizeof(keystr));
1565       const char *mname = mutt_map_get_name(i, MenuNames);
1566       mutt_debug(LL_NOTIFY, "NT_MACRO_DELETE: %s %s\n", mname, keystr);
1567 
1568       km_bindkey(key, i, OP_NULL);
1569       struct EventBinding ev_b = { i, key, OP_NULL };
1570       notify_send(NeoMutt->notify, NT_BINDING,
1571                   (data & MUTT_UNMACRO) ? NT_MACRO_DELETE : NT_BINDING_DELETE, &ev_b);
1572     }
1573   }
1574 
1575   return MUTT_CMD_SUCCESS;
1576 }
1577 
1578 /**
1579  * mutt_parse_macro - Parse the 'macro' command - Implements Command::parse() - @ingroup command_parse
1580  *
1581  * macro `<menu>` `<key>` `<macro>` `<description>`
1582  */
mutt_parse_macro(struct Buffer * buf,struct Buffer * s,intptr_t data,struct Buffer * err)1583 enum CommandResult mutt_parse_macro(struct Buffer *buf, struct Buffer *s,
1584                                     intptr_t data, struct Buffer *err)
1585 {
1586   enum MenuType mtypes[MenuNamesLen];
1587   int num_menus = 0;
1588   enum CommandResult rc = MUTT_CMD_ERROR;
1589 
1590   char *key = parse_keymap(mtypes, s, mutt_array_size(mtypes), &num_menus, err, false);
1591   if (!key)
1592     return MUTT_CMD_ERROR;
1593 
1594   mutt_extract_token(buf, s, MUTT_TOKEN_CONDENSE);
1595   /* make sure the macro sequence is not an empty string */
1596   if (buf->data[0] == '\0')
1597   {
1598     mutt_buffer_strcpy(err, _("macro: empty key sequence"));
1599   }
1600   else
1601   {
1602     if (MoreArgs(s))
1603     {
1604       char *seq = mutt_str_dup(buf->data);
1605       mutt_extract_token(buf, s, MUTT_TOKEN_CONDENSE);
1606 
1607       if (MoreArgs(s))
1608       {
1609         mutt_buffer_printf(err, _("%s: too many arguments"), "macro");
1610       }
1611       else
1612       {
1613         for (int i = 0; i < num_menus; i++)
1614         {
1615           rc = km_bind(key, mtypes[i], OP_MACRO, seq, buf->data);
1616           if (rc == MUTT_CMD_SUCCESS)
1617           {
1618             char keystr[32] = { 0 };
1619             km_expand_key_string(key, keystr, sizeof(keystr));
1620             const char *mname = mutt_map_get_name(mtypes[i], MenuNames);
1621             mutt_debug(LL_NOTIFY, "NT_MACRO_NEW: %s %s\n", mname, keystr);
1622 
1623             struct EventBinding ev_b = { mtypes[i], key, OP_MACRO };
1624             notify_send(NeoMutt->notify, NT_BINDING, NT_MACRO_ADD, &ev_b);
1625             continue;
1626           }
1627         }
1628       }
1629 
1630       FREE(&seq);
1631     }
1632     else
1633     {
1634       for (int i = 0; i < num_menus; i++)
1635       {
1636         rc = km_bind(key, mtypes[i], OP_MACRO, buf->data, NULL);
1637         if (rc == MUTT_CMD_SUCCESS)
1638         {
1639           char keystr[32] = { 0 };
1640           km_expand_key_string(key, keystr, sizeof(keystr));
1641           const char *mname = mutt_map_get_name(mtypes[i], MenuNames);
1642           mutt_debug(LL_NOTIFY, "NT_MACRO_NEW: %s %s\n", mname, keystr);
1643 
1644           struct EventBinding ev_b = { mtypes[i], key, OP_MACRO };
1645           notify_send(NeoMutt->notify, NT_BINDING, NT_MACRO_ADD, &ev_b);
1646           continue;
1647         }
1648       }
1649     }
1650   }
1651   FREE(&key);
1652   return rc;
1653 }
1654 
1655 /**
1656  * mutt_parse_exec - Parse the 'exec' command - Implements Command::parse() - @ingroup command_parse
1657  */
mutt_parse_exec(struct Buffer * buf,struct Buffer * s,intptr_t data,struct Buffer * err)1658 enum CommandResult mutt_parse_exec(struct Buffer *buf, struct Buffer *s,
1659                                    intptr_t data, struct Buffer *err)
1660 {
1661   int ops[128];
1662   int nops = 0;
1663   const struct Binding *bindings = NULL;
1664   char *function = NULL;
1665 
1666   if (!MoreArgs(s))
1667   {
1668     mutt_buffer_strcpy(err, _("exec: no arguments"));
1669     return MUTT_CMD_ERROR;
1670   }
1671 
1672   do
1673   {
1674     mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
1675     function = buf->data;
1676 
1677     const enum MenuType mtype = menu_get_current_type();
1678     bindings = km_get_table(mtype);
1679     if (!bindings && (mtype != MENU_PAGER))
1680       bindings = OpGeneric;
1681 
1682     ops[nops] = get_op(bindings, function, mutt_str_len(function));
1683     if ((ops[nops] == OP_NULL) && (mtype != MENU_PAGER))
1684       ops[nops] = get_op(OpGeneric, function, mutt_str_len(function));
1685 
1686     if (ops[nops] == OP_NULL)
1687     {
1688       mutt_flushinp();
1689       mutt_error(_("%s: no such function"), function);
1690       return MUTT_CMD_ERROR;
1691     }
1692     nops++;
1693   } while (MoreArgs(s) && nops < mutt_array_size(ops));
1694 
1695   while (nops)
1696     mutt_push_macro_event(0, ops[--nops]);
1697 
1698   return MUTT_CMD_SUCCESS;
1699 }
1700 
1701 /**
1702  * mutt_what_key - Ask the user to press a key
1703  *
1704  * Displays the octal value back to the user.
1705  */
mutt_what_key(void)1706 void mutt_what_key(void)
1707 {
1708   int ch;
1709 
1710   struct MuttWindow *win = msgwin_get_window();
1711   if (!win)
1712     return;
1713 
1714   mutt_window_mvprintw(win, 0, 0, _("Enter keys (%s to abort): "), km_keyname(AbortKey));
1715   do
1716   {
1717     ch = getch();
1718     if ((ch != ERR) && (ch != AbortKey))
1719     {
1720       mutt_message(_("Char = %s, Octal = %o, Decimal = %d"), km_keyname(ch), ch, ch);
1721     }
1722   } while (ch != ERR && ch != AbortKey);
1723 
1724   mutt_flushinp();
1725   mutt_clear_error();
1726 }
1727 
1728 /**
1729  * mutt_keys_free - Free the key maps
1730  */
mutt_keys_free(void)1731 void mutt_keys_free(void)
1732 {
1733   for (int i = 0; i < MENU_MAX; i++)
1734   {
1735     mutt_keymaplist_free(&Keymaps[i]);
1736   }
1737 }
1738