mutt stable branch with some hacks
1/*
2 * Copyright (C) 1996-2000,2002,2010-2011 Michael R. Elkins <me@mutt.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19#if HAVE_CONFIG_H
20# include "config.h"
21#endif
22
23#include "mutt.h"
24#include "mutt_menu.h"
25#include "mutt_curses.h"
26#include "keymap.h"
27#include "mapping.h"
28#include "mutt_crypt.h"
29#ifdef USE_IMAP
30#include "imap/imap.h"
31#endif
32#ifdef USE_INOTIFY
33#include "monitor.h"
34#endif
35
36#include <stdlib.h>
37#include <string.h>
38#include <ctype.h>
39
40#include "functions.h"
41
42const struct mapping_t Menus[] = {
43 { "alias", MENU_ALIAS },
44 { "attach", MENU_ATTACH },
45 { "browser", MENU_FOLDER },
46 { "compose", MENU_COMPOSE },
47 { "editor", MENU_EDITOR },
48 { "index", MENU_MAIN },
49 { "pager", MENU_PAGER },
50 { "postpone", MENU_POST },
51 { "pgp", MENU_PGP },
52 { "smime", MENU_SMIME },
53#ifdef CRYPT_BACKEND_GPGME
54 { "key_select_pgp", MENU_KEY_SELECT_PGP },
55 { "key_select_smime", MENU_KEY_SELECT_SMIME },
56#endif
57
58#ifdef MIXMASTER
59 { "mix", MENU_MIX },
60#endif
61
62
63 { "query", MENU_QUERY },
64 { "generic", MENU_GENERIC },
65 { NULL, 0 }
66};
67
68#define mutt_check_menu(s) mutt_getvaluebyname(s, Menus)
69
70static struct mapping_t KeyNames[] = {
71 { "<PageUp>", KEY_PPAGE },
72 { "<PageDown>", KEY_NPAGE },
73 { "<Up>", KEY_UP },
74 { "<Down>", KEY_DOWN },
75 { "<Right>", KEY_RIGHT },
76 { "<Left>", KEY_LEFT },
77 { "<Delete>", KEY_DC },
78 { "<BackSpace>",KEY_BACKSPACE },
79 { "<Insert>", KEY_IC },
80 { "<Home>", KEY_HOME },
81 { "<End>", KEY_END },
82#ifdef KEY_ENTER
83 { "<Enter>", KEY_ENTER },
84#endif
85 { "<Return>", MUTT_ENTER_C },
86 { "<Esc>", '\033' },
87 { "<Tab>", '\t' },
88 { "<Space>", ' ' },
89#ifdef KEY_BTAB
90 { "<BackTab>", KEY_BTAB },
91#endif
92#ifdef KEY_NEXT
93 { "<Next>", KEY_NEXT },
94#endif
95#ifdef NCURSES_VERSION
96 /* extensions supported by ncurses. values are filled in during initialization */
97
98 /* CTRL+key */
99 { "<C-Up>", -1 },
100 { "<C-Down>", -1 },
101 { "<C-Left>", -1 },
102 { "<C-Right>", -1 },
103 { "<C-Home>", -1 },
104 { "<C-End>", -1 },
105 { "<C-Next>", -1 },
106 { "<C-Prev>", -1 },
107
108 /* SHIFT+key */
109 { "<S-Up>", -1 },
110 { "<S-Down>", -1 },
111 { "<S-Left>", -1 },
112 { "<S-Right>", -1 },
113 { "<S-Home>", -1 },
114 { "<S-End>", -1 },
115 { "<S-Next>", -1 },
116 { "<S-Prev>", -1 },
117
118 /* ALT+key */
119 { "<A-Up>", -1 },
120 { "<A-Down>", -1 },
121 { "<A-Left>", -1 },
122 { "<A-Right>", -1 },
123 { "<A-Home>", -1 },
124 { "<A-End>", -1 },
125 { "<A-Next>", -1 },
126 { "<A-Prev>", -1 },
127#endif /* NCURSES_VERSION */
128 { NULL, 0 }
129};
130
131/* contains the last key the user pressed */
132int LastKey;
133
134struct keymap_t *Keymaps[MENU_MAX];
135
136static struct keymap_t *allocKeys (int len, keycode_t *keys)
137{
138 struct keymap_t *p;
139
140 p = safe_calloc (1, sizeof (struct keymap_t));
141 p->len = len;
142 p->keys = safe_malloc (len * sizeof (keycode_t));
143 memcpy (p->keys, keys, len * sizeof (keycode_t));
144 return (p);
145}
146
147static int parse_fkey(char *s)
148{
149 char *t;
150 int n = 0;
151
152 if (s[0] != '<' || ascii_tolower(s[1]) != 'f')
153 return -1;
154
155 for (t = s + 2; *t && isdigit((unsigned char) *t); t++)
156 {
157 n *= 10;
158 n += *t - '0';
159 }
160
161 if (*t != '>')
162 return -1;
163 else
164 return n;
165}
166
167/*
168 * This function parses the string <NNN> and uses the octal value as the key
169 * to bind.
170 */
171static int parse_keycode (const char *s)
172{
173 char *endChar;
174 long int result = strtol(s+1, &endChar, 8);
175 /* allow trailing whitespace, eg. < 1001 > */
176 while (ISSPACE(*endChar))
177 ++endChar;
178 /* negative keycodes don't make sense, also detect overflow */
179 if (*endChar != '>' || result < 0 || result == LONG_MAX)
180 {
181 return -1;
182 }
183
184 return result;
185}
186
187static int parsekeys (const char *str, keycode_t *d, int max)
188{
189 int n, len = max;
190 char buff[SHORT_STRING];
191 char c;
192 char *s, *t;
193
194 strfcpy(buff, str, sizeof(buff));
195 s = buff;
196
197 while (*s && len)
198 {
199 *d = '\0';
200 if (*s == '<' && (t = strchr(s, '>')))
201 {
202 t++; c = *t; *t = '\0';
203
204 if ((n = mutt_getvaluebyname (s, KeyNames)) != -1)
205 {
206 s = t;
207 *d = n;
208 }
209 else if ((n = parse_fkey(s)) > 0)
210 {
211 s = t;
212 *d = KEY_F (n);
213 }
214 else if ((n = parse_keycode(s)) > 0)
215 {
216 s = t;
217 *d = n;
218 }
219
220 *t = c;
221 }
222
223 if (!*d)
224 {
225 *d = (unsigned char)*s;
226 s++;
227 }
228 d++;
229 len--;
230 }
231
232 return (max - len);
233}
234
235/* insert a key sequence into the specified map. the map is sorted by ASCII
236 * value (lowest to highest)
237 */
238void km_bind (char *s, int menu, int op, char *macro, char *descr)
239{
240 struct keymap_t *map, *tmp, *last = NULL, *next;
241 keycode_t buf[MAX_SEQ];
242 int len, pos = 0, lastpos = 0;
243
244 len = parsekeys (s, buf, MAX_SEQ);
245
246 map = allocKeys (len, buf);
247 map->op = op;
248 map->macro = safe_strdup (macro);
249 map->descr = safe_strdup (descr);
250
251 tmp = Keymaps[menu];
252
253 while (tmp)
254 {
255 if (pos >= len || pos >= tmp->len)
256 {
257 /* map and tmp match, but have different lengths, so overwrite */
258 do
259 {
260 len = tmp->eq;
261 next = tmp->next;
262 FREE (&tmp->macro);
263 FREE (&tmp->keys);
264 FREE (&tmp->descr);
265 FREE (&tmp);
266 tmp = next;
267 }
268 while (tmp && len >= pos);
269 map->eq = len;
270 break;
271 }
272 else if (buf[pos] == tmp->keys[pos])
273 pos++;
274 else if (buf[pos] < tmp->keys[pos])
275 {
276 /* found location to insert between last and tmp */
277 map->eq = pos;
278 break;
279 }
280 else /* buf[pos] > tmp->keys[pos] */
281 {
282 last = tmp;
283 lastpos = pos;
284 if (pos > tmp->eq)
285 pos = tmp->eq;
286 tmp = tmp->next;
287 }
288 }
289
290 map->next = tmp;
291 if (last)
292 {
293 last->next = map;
294 last->eq = lastpos;
295 }
296 else
297 Keymaps[menu] = map;
298}
299
300void km_bindkey (char *s, int menu, int op)
301{
302 km_bind (s, menu, op, NULL, NULL);
303}
304
305static int get_op (const struct binding_t *bindings, const char *start, size_t len)
306{
307 int i;
308
309 for (i = 0; bindings[i].name; i++)
310 {
311 if (!ascii_strncasecmp (start, bindings[i].name, len) &&
312 mutt_strlen (bindings[i].name) == len)
313 return bindings[i].op;
314 }
315
316 return OP_NULL;
317}
318
319static char *get_func (const struct binding_t *bindings, int op)
320{
321 int i;
322
323 for (i = 0; bindings[i].name; i++)
324 {
325 if (bindings[i].op == op)
326 return bindings[i].name;
327 }
328
329 return NULL;
330}
331
332/* Parses s for <function> syntax and adds the whole sequence to
333 * either the macro or unget buffer. This function is invoked by the next
334 * two defines below.
335 */
336static void generic_tokenize_push_string (char *s, void (*generic_push) (int, int))
337{
338 char *pp, *p = s + mutt_strlen (s) - 1;
339 size_t l;
340 int i, op = OP_NULL;
341
342 while (p >= s)
343 {
344 /* if we see something like "<PageUp>", look to see if it is a real
345 function name and return the corresponding value */
346 if (*p == '>')
347 {
348 for (pp = p - 1; pp >= s && *pp != '<'; pp--)
349 ;
350 if (pp >= s)
351 {
352 if ((i = parse_fkey (pp)) > 0)
353 {
354 generic_push (KEY_F (i), 0);
355 p = pp - 1;
356 continue;
357 }
358
359 l = p - pp + 1;
360 for (i = 0; KeyNames[i].name; i++)
361 {
362 if (!ascii_strncasecmp (pp, KeyNames[i].name, l))
363 break;
364 }
365 if (KeyNames[i].name)
366 {
367 /* found a match */
368 generic_push (KeyNames[i].value, 0);
369 p = pp - 1;
370 continue;
371 }
372
373 /* See if it is a valid command
374 * skip the '<' and the '>' when comparing */
375 for (i = 0; Menus[i].name; i++)
376 {
377 const struct binding_t *binding = km_get_table (Menus[i].value);
378 if (binding)
379 {
380 op = get_op (binding, pp + 1, l - 2);
381 if (op != OP_NULL)
382 break;
383 }
384 }
385
386 if (op != OP_NULL)
387 {
388 generic_push (0, op);
389 p = pp - 1;
390 continue;
391 }
392 }
393 }
394 generic_push ((unsigned char)*p--, 0); /* independent 8 bits chars */
395 }
396}
397
398/* This should be used for macros, push, and exec commands only. */
399#define tokenize_push_macro_string(s) generic_tokenize_push_string (s, mutt_push_macro_event)
400/* This should be used for other unget operations. */
401#define tokenize_unget_string(s) generic_tokenize_push_string (s, mutt_unget_event)
402
403static int retry_generic (int menu, keycode_t *keys, int keyslen, int lastkey)
404{
405 if (menu != MENU_EDITOR && menu != MENU_GENERIC && menu != MENU_PAGER)
406 {
407 if (lastkey)
408 mutt_unget_event (lastkey, 0);
409 for (; keyslen; keyslen--)
410 mutt_unget_event (keys[keyslen - 1], 0);
411 return (km_dokey (MENU_GENERIC));
412 }
413 if (menu != MENU_EDITOR)
414 {
415 /* probably a good idea to flush input here so we can abort macros */
416 mutt_flushinp ();
417 }
418 return OP_NULL;
419}
420
421/* return values:
422 * >0 function to execute
423 * OP_NULL no function bound to key sequence
424 * -1 error occurred while reading input
425 * -2 a timeout or sigwinch occurred
426 */
427int km_dokey (int menu)
428{
429 event_t tmp;
430 struct keymap_t *map = Keymaps[menu];
431 int pos = 0;
432 int n = 0;
433 int i;
434
435 if (!map)
436 return (retry_generic (menu, NULL, 0, 0));
437
438 FOREVER
439 {
440 i = Timeout > 0 ? Timeout : 60;
441#ifdef USE_IMAP
442 /* keepalive may need to run more frequently than Timeout allows */
443 if (ImapKeepalive)
444 {
445 if (ImapKeepalive >= i)
446 imap_keepalive ();
447 else
448 while (ImapKeepalive && ImapKeepalive < i)
449 {
450 mutt_getch_timeout (ImapKeepalive * 1000);
451 tmp = mutt_getch ();
452 mutt_getch_timeout (-1);
453 /* If a timeout was not received, or the window was resized, exit the
454 * loop now. Otherwise, continue to loop until reaching a total of
455 * $timeout seconds.
456 */
457#ifdef USE_INOTIFY
458 if (tmp.ch != -2 || SigWinch || MonitorFilesChanged)
459#else
460 if (tmp.ch != -2 || SigWinch)
461#endif
462 goto gotkey;
463 i -= ImapKeepalive;
464 imap_keepalive ();
465 }
466 }
467#endif
468
469 mutt_getch_timeout (i * 1000);
470 tmp = mutt_getch();
471 mutt_getch_timeout (-1);
472
473#ifdef USE_IMAP
474 gotkey:
475#endif
476 /* hide timeouts, but not window resizes, from the line editor. */
477 if (menu == MENU_EDITOR && tmp.ch == -2 && !SigWinch)
478 continue;
479
480 LastKey = tmp.ch;
481 if (LastKey < 0)
482 return LastKey;
483
484 /* do we have an op already? */
485 if (tmp.op)
486 {
487 char *func = NULL;
488 const struct binding_t *bindings;
489
490 /* is this a valid op for this menu? */
491 if ((bindings = km_get_table (menu)) &&
492 (func = get_func (bindings, tmp.op)))
493 return tmp.op;
494
495 if (menu == MENU_EDITOR && get_func (OpEditor, tmp.op))
496 return tmp.op;
497
498 if (menu != MENU_EDITOR && menu != MENU_PAGER)
499 {
500 /* check generic menu */
501 bindings = OpGeneric;
502 if ((func = get_func (bindings, tmp.op)))
503 return tmp.op;
504 }
505
506 /* Sigh. Valid function but not in this context.
507 * Find the literal string and push it back */
508 for (i = 0; Menus[i].name; i++)
509 {
510 bindings = km_get_table (Menus[i].value);
511 if (bindings)
512 {
513 func = get_func (bindings, tmp.op);
514 if (func)
515 {
516 mutt_unget_event ('>', 0);
517 mutt_unget_string (func);
518 mutt_unget_event ('<', 0);
519 break;
520 }
521 }
522 }
523 /* continue to chew */
524 if (func)
525 continue;
526 }
527
528 /* Nope. Business as usual */
529 while (LastKey > map->keys[pos])
530 {
531 if (pos > map->eq || !map->next)
532 return (retry_generic (menu, map->keys, pos, LastKey));
533 map = map->next;
534 }
535
536 if (LastKey != map->keys[pos])
537 return (retry_generic (menu, map->keys, pos, LastKey));
538
539 if (++pos == map->len)
540 {
541
542 if (map->op != OP_MACRO)
543 return map->op;
544
545 /* OPTIGNOREMACROEVENTS turns off processing the MacroEvents buffer
546 * in mutt_getch(). Generating new macro events during that time would
547 * result in undesired behavior once the option is turned off.
548 *
549 * Originally this returned -1, however that results in an unbuffered
550 * username or password prompt being aborted. Returning OP_NULL allows
551 * _mutt_enter_string() to display the keybinding pressed instead.
552 *
553 * It may be unexpected for a macro's keybinding to be returned,
554 * but less so than aborting the prompt.
555 */
556 if (option (OPTIGNOREMACROEVENTS))
557 {
558 return OP_NULL;
559 }
560
561 if (n++ == 10)
562 {
563 mutt_flushinp ();
564 mutt_error _("Macro loop detected.");
565 return -1;
566 }
567
568 tokenize_push_macro_string (map->macro);
569 map = Keymaps[menu];
570 pos = 0;
571 }
572 }
573
574 /* not reached */
575}
576
577static void create_bindings (const struct binding_t *map, int menu)
578{
579 int i;
580
581 for (i = 0 ; map[i].name ; i++)
582 if (map[i].seq)
583 km_bindkey (map[i].seq, menu, map[i].op);
584}
585
586static const char *km_keyname (int c)
587{
588 static char buf[10];
589 const char *p;
590
591 if ((p = mutt_getnamebyvalue (c, KeyNames)))
592 return p;
593
594 if (c < 256 && c > -128 && iscntrl ((unsigned char) c))
595 {
596 if (c < 0)
597 c += 256;
598
599 if (c < 128)
600 {
601 buf[0] = '^';
602 buf[1] = (c + '@') & 0x7f;
603 buf[2] = 0;
604 }
605 else
606 snprintf (buf, sizeof (buf), "\\%d%d%d", c >> 6, (c >> 3) & 7, c & 7);
607 }
608 else if (c >= KEY_F0 && c < KEY_F(256)) /* this maximum is just a guess */
609 sprintf (buf, "<F%d>", c - KEY_F0);
610 else if (IsPrint (c))
611 snprintf (buf, sizeof (buf), "%c", (unsigned char) c);
612 else
613 snprintf (buf, sizeof (buf), "\\x%hx", (unsigned short) c);
614 return (buf);
615}
616
617int km_expand_key (char *s, size_t len, struct keymap_t *map)
618{
619 size_t l;
620 int p = 0;
621
622 if (!map)
623 return (0);
624
625 FOREVER
626 {
627 strfcpy (s, km_keyname (map->keys[p]), len);
628 len -= (l = mutt_strlen (s));
629
630 if (++p >= map->len || !len)
631 return (1);
632
633 s += l;
634 }
635
636 /* not reached */
637}
638
639struct keymap_t *km_find_func (int menu, int func)
640{
641 struct keymap_t *map = Keymaps[menu];
642
643 for (; map; map = map->next)
644 if (map->op == func)
645 break;
646 return (map);
647}
648
649#ifdef NCURSES_VERSION
650struct extkey {
651 const char *name;
652 const char *sym;
653};
654
655static const struct extkey ExtKeys[] = {
656 { "<c-up>", "kUP5" },
657 { "<s-up>", "kUP" },
658 { "<a-up>", "kUP3" },
659
660 { "<s-down>", "kDN" },
661 { "<a-down>", "kDN3" },
662 { "<c-down>", "kDN5" },
663
664 { "<c-right>", "kRIT5" },
665 { "<s-right>", "kRIT" },
666 { "<a-right>", "kRIT3" },
667
668 { "<s-left>", "kLFT" },
669 { "<a-left>", "kLFT3" },
670 { "<c-left>", "kLFT5" },
671
672 { "<s-home>", "kHOM" },
673 { "<a-home>", "kHOM3" },
674 { "<c-home>", "kHOM5" },
675
676 { "<s-end>", "kEND" },
677 { "<a-end>", "kEND3" },
678 { "<c-end>", "kEND5" },
679
680 { "<s-next>", "kNXT" },
681 { "<a-next>", "kNXT3" },
682 { "<c-next>", "kNXT5" },
683
684 { "<s-prev>", "kPRV" },
685 { "<a-prev>", "kPRV3" },
686 { "<c-prev>", "kPRV5" },
687
688 { 0, 0 }
689};
690
691/* Look up Mutt's name for a key and find the ncurses extended name for it */
692static const char *find_ext_name(const char *key)
693{
694 int j;
695
696 for (j = 0; ExtKeys[j].name; ++j)
697 {
698 if (strcasecmp(key, ExtKeys[j].name) == 0)
699 return ExtKeys[j].sym;
700 }
701 return 0;
702}
703#endif /* NCURSES_VERSION */
704
705/* Determine the keycodes for ncurses extended keys and fill in the KeyNames array.
706 *
707 * This function must be called *after* initscr(), or tigetstr() returns -1. This
708 * creates a bit of a chicken-and-egg problem because km_init() is called prior to
709 * start_curses(). This means that the default keybindings can't include any of the
710 * extended keys because they won't be defined until later.
711 */
712void init_extended_keys(void)
713{
714#ifdef NCURSES_VERSION
715 int j;
716
717 use_extended_names(TRUE);
718
719 for (j = 0; KeyNames[j].name; ++j)
720 {
721 if (KeyNames[j].value == -1)
722 {
723 const char *keyname = find_ext_name(KeyNames[j].name);
724
725 if (keyname)
726 {
727 char *s = tigetstr((char *)keyname);
728 if (s && (long)(s) != -1)
729 {
730 int code = key_defined(s);
731 if (code > 0)
732 KeyNames[j].value = code;
733 }
734 }
735 }
736 }
737#endif
738}
739
740void km_init (void)
741{
742 memset (Keymaps, 0, sizeof (struct keymap_t *) * MENU_MAX);
743
744 create_bindings (OpAttach, MENU_ATTACH);
745 create_bindings (OpBrowser, MENU_FOLDER);
746 create_bindings (OpCompose, MENU_COMPOSE);
747 create_bindings (OpMain, MENU_MAIN);
748 create_bindings (OpPager, MENU_PAGER);
749 create_bindings (OpPost, MENU_POST);
750 create_bindings (OpQuery, MENU_QUERY);
751 create_bindings (OpAlias, MENU_ALIAS);
752
753
754 if ((WithCrypto & APPLICATION_PGP))
755 create_bindings (OpPgp, MENU_PGP);
756
757 if ((WithCrypto & APPLICATION_SMIME))
758 create_bindings (OpSmime, MENU_SMIME);
759
760#ifdef CRYPT_BACKEND_GPGME
761 create_bindings (OpPgp, MENU_KEY_SELECT_PGP);
762 create_bindings (OpSmime, MENU_KEY_SELECT_SMIME);
763#endif
764
765#ifdef MIXMASTER
766 create_bindings (OpMix, MENU_MIX);
767
768 km_bindkey ("<space>", MENU_MIX, OP_GENERIC_SELECT_ENTRY);
769 km_bindkey ("h", MENU_MIX, OP_MIX_CHAIN_PREV);
770 km_bindkey ("l", MENU_MIX, OP_MIX_CHAIN_NEXT);
771#endif
772
773#ifdef USE_AUTOCRYPT
774 create_bindings (OpAutocryptAcct, MENU_AUTOCRYPT_ACCT);
775#endif
776
777 /* bindings for the line editor */
778 create_bindings (OpEditor, MENU_EDITOR);
779
780 km_bindkey ("<up>", MENU_EDITOR, OP_EDITOR_HISTORY_UP);
781 km_bindkey ("<down>", MENU_EDITOR, OP_EDITOR_HISTORY_DOWN);
782 km_bindkey ("<left>", MENU_EDITOR, OP_EDITOR_BACKWARD_CHAR);
783 km_bindkey ("<right>", MENU_EDITOR, OP_EDITOR_FORWARD_CHAR);
784 km_bindkey ("<home>", MENU_EDITOR, OP_EDITOR_BOL);
785 km_bindkey ("<end>", MENU_EDITOR, OP_EDITOR_EOL);
786 km_bindkey ("<backspace>", MENU_EDITOR, OP_EDITOR_BACKSPACE);
787 km_bindkey ("<delete>", MENU_EDITOR, OP_EDITOR_BACKSPACE);
788 km_bindkey ("\177", MENU_EDITOR, OP_EDITOR_BACKSPACE);
789
790 /* generic menu keymap */
791 create_bindings (OpGeneric, MENU_GENERIC);
792
793 km_bindkey ("<home>", MENU_GENERIC, OP_FIRST_ENTRY);
794 km_bindkey ("<end>", MENU_GENERIC, OP_LAST_ENTRY);
795 km_bindkey ("<pagedown>", MENU_GENERIC, OP_NEXT_PAGE);
796 km_bindkey ("<pageup>", MENU_GENERIC, OP_PREV_PAGE);
797 km_bindkey ("<right>", MENU_GENERIC, OP_NEXT_PAGE);
798 km_bindkey ("<left>", MENU_GENERIC, OP_PREV_PAGE);
799 km_bindkey ("<up>", MENU_GENERIC, OP_PREV_ENTRY);
800 km_bindkey ("<down>", MENU_GENERIC, OP_NEXT_ENTRY);
801 km_bindkey ("1", MENU_GENERIC, OP_JUMP);
802 km_bindkey ("2", MENU_GENERIC, OP_JUMP);
803 km_bindkey ("3", MENU_GENERIC, OP_JUMP);
804 km_bindkey ("4", MENU_GENERIC, OP_JUMP);
805 km_bindkey ("5", MENU_GENERIC, OP_JUMP);
806 km_bindkey ("6", MENU_GENERIC, OP_JUMP);
807 km_bindkey ("7", MENU_GENERIC, OP_JUMP);
808 km_bindkey ("8", MENU_GENERIC, OP_JUMP);
809 km_bindkey ("9", MENU_GENERIC, OP_JUMP);
810
811 km_bindkey ("<enter>", MENU_GENERIC, OP_GENERIC_SELECT_ENTRY);
812
813 /* Miscellaneous extra bindings */
814
815 km_bindkey (" ", MENU_MAIN, OP_DISPLAY_MESSAGE);
816 km_bindkey ("<up>", MENU_MAIN, OP_MAIN_PREV_UNDELETED);
817 km_bindkey ("<down>", MENU_MAIN, OP_MAIN_NEXT_UNDELETED);
818 km_bindkey ("J", MENU_MAIN, OP_NEXT_ENTRY);
819 km_bindkey ("K", MENU_MAIN, OP_PREV_ENTRY);
820 km_bindkey ("x", MENU_MAIN, OP_EXIT);
821
822 km_bindkey ("<enter>", MENU_MAIN, OP_DISPLAY_MESSAGE);
823
824 km_bindkey ("x", MENU_PAGER, OP_EXIT);
825 km_bindkey ("i", MENU_PAGER, OP_EXIT);
826 km_bindkey ("<backspace>", MENU_PAGER, OP_PREV_LINE);
827 km_bindkey ("<pagedown>", MENU_PAGER, OP_NEXT_PAGE);
828 km_bindkey ("<pageup>", MENU_PAGER, OP_PREV_PAGE);
829 km_bindkey ("<up>", MENU_PAGER, OP_MAIN_PREV_UNDELETED);
830 km_bindkey ("<right>", MENU_PAGER, OP_MAIN_NEXT_UNDELETED);
831 km_bindkey ("<down>", MENU_PAGER, OP_MAIN_NEXT_UNDELETED);
832 km_bindkey ("<left>", MENU_PAGER, OP_MAIN_PREV_UNDELETED);
833 km_bindkey ("<home>", MENU_PAGER, OP_PAGER_TOP);
834 km_bindkey ("<end>", MENU_PAGER, OP_PAGER_BOTTOM);
835 km_bindkey ("1", MENU_PAGER, OP_JUMP);
836 km_bindkey ("2", MENU_PAGER, OP_JUMP);
837 km_bindkey ("3", MENU_PAGER, OP_JUMP);
838 km_bindkey ("4", MENU_PAGER, OP_JUMP);
839 km_bindkey ("5", MENU_PAGER, OP_JUMP);
840 km_bindkey ("6", MENU_PAGER, OP_JUMP);
841 km_bindkey ("7", MENU_PAGER, OP_JUMP);
842 km_bindkey ("8", MENU_PAGER, OP_JUMP);
843 km_bindkey ("9", MENU_PAGER, OP_JUMP);
844
845 km_bindkey ("<enter>", MENU_PAGER, OP_NEXT_LINE);
846
847 km_bindkey ("<return>", MENU_ALIAS, OP_GENERIC_SELECT_ENTRY);
848 km_bindkey ("<enter>", MENU_ALIAS, OP_GENERIC_SELECT_ENTRY);
849 km_bindkey ("<space>", MENU_ALIAS, OP_TAG);
850
851 km_bindkey ("<enter>", MENU_ATTACH, OP_VIEW_ATTACH);
852 km_bindkey ("<enter>", MENU_COMPOSE, OP_VIEW_ATTACH);
853
854 /* edit-to (default "t") hides generic tag-entry in Compose menu
855 This will bind tag-entry to "T" in the Compose menu */
856 km_bindkey ("T", MENU_COMPOSE, OP_TAG);
857}
858
859void km_error_key (int menu)
860{
861 char buf[SHORT_STRING];
862 struct keymap_t *key;
863 int p, op;
864
865 key = km_find_func (menu, OP_HELP);
866 if (!key && (menu != MENU_EDITOR) && (menu != MENU_PAGER))
867 key = km_find_func (MENU_GENERIC, OP_HELP);
868 if (!key)
869 {
870 mutt_error _("Key is not bound.");
871 return;
872 }
873
874 /* Make sure the key is really the help key in this menu.
875 *
876 * OP_END_COND is used as a barrier to ensure nothing extra
877 * is left in the unget buffer.
878 *
879 * Note that km_expand_key() + tokenize_unget_string() should
880 * not be used here: control sequences are expanded to a form
881 * (e.g. "^H") not recognized by km_dokey(). */
882 mutt_unget_event (0, OP_END_COND);
883 p = key->len;
884 while (p--)
885 mutt_unget_event (key->keys[p], 0);
886
887 /* Note, e.g. for the index menu:
888 * bind generic ? noop
889 * bind generic ,a help
890 * bind index ,ab quit
891 * The index keybinding shadows the generic binding.
892 * OP_END_COND will be read and returned as the op.
893 *
894 * bind generic ? noop
895 * bind generic dq help
896 * bind index d delete-message
897 * OP_DELETE will be returned as the op, leaving "q" + OP_END_COND
898 * in the unget buffer.
899 */
900 op = km_dokey (menu);
901 if (op != OP_END_COND)
902 mutt_flush_unget_to_endcond ();
903 if (op != OP_HELP)
904 {
905 mutt_error _("Key is not bound.");
906 return;
907 }
908
909 km_expand_key (buf, sizeof(buf), key);
910 mutt_error (_("Key is not bound. Press '%s' for help."), buf);
911 return;
912}
913
914int mutt_parse_push (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err)
915{
916 int r = 0;
917
918 mutt_extract_token (buf, s, MUTT_TOKEN_CONDENSE);
919 if (MoreArgs (s))
920 {
921 strfcpy (err->data, _("push: too many arguments"), err->dsize);
922 r = -1;
923 }
924 else
925 tokenize_push_macro_string (buf->data);
926 return (r);
927}
928
929/* expects to see: <menu-string>,<menu-string>,... <key-string> */
930static char *parse_keymap (int *menu, BUFFER *s, int maxmenus, int *nummenus, BUFFER *err)
931{
932 BUFFER buf;
933 int i=0;
934 char *p, *q;
935
936 mutt_buffer_init (&buf);
937
938 /* menu name */
939 mutt_extract_token (&buf, s, 0);
940 p = buf.data;
941 if (MoreArgs (s))
942 {
943 while (i < maxmenus)
944 {
945 q = strchr(p,',');
946 if (q)
947 *q = '\0';
948
949 if ((menu[i] = mutt_check_menu (p)) == -1)
950 {
951 snprintf (err->data, err->dsize, _("%s: no such menu"), p);
952 goto error;
953 }
954 ++i;
955 if (q)
956 p = q+1;
957 else
958 break;
959 }
960 *nummenus=i;
961 /* key sequence */
962 mutt_extract_token (&buf, s, 0);
963
964 if (!*buf.data)
965 {
966 strfcpy (err->data, _("null key sequence"), err->dsize);
967 }
968 else if (MoreArgs (s))
969 return (buf.data);
970 }
971 else
972 {
973 strfcpy (err->data, _("too few arguments"), err->dsize);
974 }
975error:
976 FREE (&buf.data);
977 return (NULL);
978}
979
980static int
981try_bind (char *key, int menu, char *func, const struct binding_t *bindings)
982{
983 int i;
984
985 for (i = 0; bindings[i].name; i++)
986 if (mutt_strcmp (func, bindings[i].name) == 0)
987 {
988 km_bindkey (key, menu, bindings[i].op);
989 return (0);
990 }
991 return (-1);
992}
993
994const struct binding_t *km_get_table (int menu)
995{
996 switch (menu)
997 {
998 case MENU_MAIN:
999 return OpMain;
1000 case MENU_GENERIC:
1001 return OpGeneric;
1002 case MENU_COMPOSE:
1003 return OpCompose;
1004 case MENU_PAGER:
1005 return OpPager;
1006 case MENU_POST:
1007 return OpPost;
1008 case MENU_FOLDER:
1009 return OpBrowser;
1010 case MENU_ALIAS:
1011 return OpAlias;
1012 case MENU_ATTACH:
1013 return OpAttach;
1014 case MENU_EDITOR:
1015 return OpEditor;
1016 case MENU_QUERY:
1017 return OpQuery;
1018
1019 case MENU_PGP:
1020 return (WithCrypto & APPLICATION_PGP)? OpPgp:NULL;
1021
1022#ifdef CRYPT_BACKEND_GPGME
1023 case MENU_KEY_SELECT_PGP:
1024 return OpPgp;
1025 case MENU_KEY_SELECT_SMIME:
1026 return OpSmime;
1027#endif
1028
1029#ifdef MIXMASTER
1030 case MENU_MIX:
1031 return OpMix;
1032#endif
1033
1034#ifdef USE_AUTOCRYPT
1035 case MENU_AUTOCRYPT_ACCT:
1036 return OpAutocryptAcct;
1037#endif
1038
1039 }
1040 return NULL;
1041}
1042
1043/* bind menu-name '<key_sequence>' function-name */
1044int mutt_parse_bind (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err)
1045{
1046 const struct binding_t *bindings = NULL;
1047 char *key;
1048 int menu[sizeof(Menus)/sizeof(struct mapping_t)-1], r = 0, nummenus, i;
1049
1050 if ((key = parse_keymap (menu, s, sizeof (menu)/sizeof (menu[0]),
1051 &nummenus, err)) == NULL)
1052 return (-1);
1053
1054 /* function to execute */
1055 mutt_extract_token (buf, s, 0);
1056 if (MoreArgs (s))
1057 {
1058 strfcpy (err->data, _("bind: too many arguments"), err->dsize);
1059 r = -1;
1060 }
1061 else if (ascii_strcasecmp ("noop", buf->data) == 0)
1062 {
1063 for (i = 0; i < nummenus; ++i)
1064 {
1065 km_bindkey (key, menu[i], OP_NULL); /* the `unbind' command */
1066 }
1067 }
1068 else
1069 {
1070 for (i = 0; i < nummenus; ++i)
1071 {
1072 /* First check the "generic" list of commands */
1073 if (menu[i] == MENU_PAGER || menu[i] == MENU_EDITOR ||
1074 menu[i] == MENU_GENERIC ||
1075 try_bind (key, menu[i], buf->data, OpGeneric) != 0)
1076 {
1077 /* Now check the menu-specific list of commands (if they exist) */
1078 bindings = km_get_table (menu[i]);
1079 if (bindings && try_bind (key, menu[i], buf->data, bindings) != 0)
1080 {
1081 snprintf (err->data, err->dsize, _("%s: no such function in map"), buf->data);
1082 r = -1;
1083 }
1084 }
1085 }
1086 }
1087 FREE (&key);
1088 return (r);
1089}
1090
1091/* macro <menu> <key> <macro> <description> */
1092int mutt_parse_macro (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err)
1093{
1094 int menu[sizeof(Menus)/sizeof(struct mapping_t)-1], r = -1, nummenus, i;
1095 char *seq = NULL;
1096 char *key;
1097
1098 if ((key = parse_keymap (menu, s, sizeof (menu) / sizeof (menu[0]), &nummenus, err)) == NULL)
1099 return (-1);
1100
1101 mutt_extract_token (buf, s, MUTT_TOKEN_CONDENSE);
1102 /* make sure the macro sequence is not an empty string */
1103 if (!*buf->data)
1104 {
1105 strfcpy (err->data, _("macro: empty key sequence"), err->dsize);
1106 }
1107 else
1108 {
1109 if (MoreArgs (s))
1110 {
1111 seq = safe_strdup (buf->data);
1112 mutt_extract_token (buf, s, MUTT_TOKEN_CONDENSE);
1113
1114 if (MoreArgs (s))
1115 {
1116 strfcpy (err->data, _("macro: too many arguments"), err->dsize);
1117 }
1118 else
1119 {
1120 for (i = 0; i < nummenus; ++i)
1121 {
1122 km_bind (key, menu[i], OP_MACRO, seq, buf->data);
1123 r = 0;
1124 }
1125 }
1126
1127 FREE (&seq);
1128 }
1129 else
1130 {
1131 for (i = 0; i < nummenus; ++i)
1132 {
1133 km_bind (key, menu[i], OP_MACRO, buf->data, NULL);
1134 r = 0;
1135 }
1136 }
1137 }
1138 FREE (&key);
1139 return (r);
1140}
1141
1142/* exec function-name */
1143int mutt_parse_exec (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err)
1144{
1145 int ops[128];
1146 int nops = 0;
1147 const struct binding_t *bindings = NULL;
1148 char *function;
1149
1150 if (!MoreArgs (s))
1151 {
1152 strfcpy (err->data, _("exec: no arguments"), err->dsize);
1153 return (-1);
1154 }
1155
1156 do
1157 {
1158 mutt_extract_token (buf, s, 0);
1159 function = buf->data;
1160
1161 if ((bindings = km_get_table (CurrentMenu)) == NULL
1162 && CurrentMenu != MENU_PAGER)
1163 bindings = OpGeneric;
1164
1165 ops[nops] = get_op (bindings, function, mutt_strlen(function));
1166 if (ops[nops] == OP_NULL && CurrentMenu != MENU_PAGER)
1167 ops[nops] = get_op (OpGeneric, function, mutt_strlen(function));
1168
1169 if (ops[nops] == OP_NULL)
1170 {
1171 mutt_flushinp ();
1172 mutt_error (_("%s: no such function"), function);
1173 return (-1);
1174 }
1175 nops++;
1176 }
1177 while (MoreArgs(s) && nops < sizeof(ops)/sizeof(ops[0]));
1178
1179 while (nops)
1180 mutt_push_macro_event (0, ops[--nops]);
1181
1182 return 0;
1183}
1184
1185/*
1186 * prompts the user to enter a keystroke, and displays the octal value back
1187 * to the user.
1188 */
1189void mutt_what_key (void)
1190{
1191 int ch;
1192
1193 mutt_window_mvprintw (MuttMessageWindow, 0, 0, _("Enter keys (^G to abort): "));
1194 do
1195 {
1196 ch = getch();
1197 if (ch != ERR && ch != ctrl ('G'))
1198 {
1199 mutt_message(_("Char = %s, Octal = %o, Decimal = %d"),
1200 km_keyname(ch), ch, ch);
1201 }
1202 }
1203 while (ch != ERR && ch != ctrl ('G'));
1204
1205 mutt_flushinp();
1206 mutt_clear_error();
1207}