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