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