]> git.llucax.com Git - software/mutt-debian.git/blob - keymap.c
patch to fix 533459
[software/mutt-debian.git] / keymap.c
1 /*
2  * Copyright (C) 1996-2000,2002 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
39 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 HAVE_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
67 static 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>", M_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   { NULL,       0 }
93 };
94
95 /* contains the last key the user pressed */
96 int LastKey;
97
98 struct keymap_t *Keymaps[MENU_MAX];
99
100 static struct keymap_t *allocKeys (int len, keycode_t *keys)
101 {
102   struct keymap_t *p;
103
104   p = safe_calloc (1, sizeof (struct keymap_t));
105   p->len = len;
106   p->keys = safe_malloc (len * sizeof (keycode_t));
107   memcpy (p->keys, keys, len * sizeof (keycode_t));
108   return (p);
109 }
110
111 static int parse_fkey(char *s)
112 {
113   char *t;
114   int n = 0;
115
116   if(s[0] != '<' || ascii_tolower(s[1]) != 'f')
117     return -1;
118
119   for(t = s + 2; *t && isdigit((unsigned char) *t); t++)
120   {
121     n *= 10;
122     n += *t - '0';
123   }
124
125   if(*t != '>')
126     return -1;
127   else
128     return n;
129 }
130
131 /*
132  * This function parses the string <NNN> and uses the octal value as the key
133  * to bind.
134  */
135 static int parse_keycode (const char *s)
136 {
137   if (isdigit ((unsigned char) s[1]) &&
138       isdigit ((unsigned char) s[2]) &&
139       isdigit ((unsigned char) s[3]) &&
140       s[4] == '>')
141   {
142     return (s[3] - '0') + (s[2] - '0') * 8 + (s[1] - '0') * 64;
143   }
144   return -1;
145 }
146
147 static int parsekeys (char *str, keycode_t *d, int max)
148 {
149   int n, len = max;
150   char buff[SHORT_STRING];
151   char c;
152   char *s, *t;
153
154   strfcpy(buff, str, sizeof(buff));
155   s = buff;
156   
157   while (*s && len)
158   {
159     *d = '\0';
160     if(*s == '<' && (t = strchr(s, '>')))
161     {
162       t++; c = *t; *t = '\0';
163       
164       if ((n = mutt_getvaluebyname (s, KeyNames)) != -1)
165       {
166         s = t;
167         *d = n;
168       }
169       else if ((n = parse_fkey(s)) > 0)
170       {
171         s = t;
172         *d = KEY_F (n);
173       }
174       else if ((n = parse_keycode(s)) > 0)
175       {
176         s = t;
177         *d = n;
178       }
179       
180       *t = c;
181     }
182
183     if(!*d)
184     {
185       *d = (unsigned char)*s;
186       s++;
187     }
188     d++;
189     len--;
190   }
191
192   return (max - len);
193 }
194
195 /* insert a key sequence into the specified map.  the map is sorted by ASCII
196  * value (lowest to highest)
197  */
198 void km_bind (char *s, int menu, int op, char *macro, char *descr)
199 {
200   struct keymap_t *map, *tmp, *last = NULL, *next;
201   keycode_t buf[MAX_SEQ];
202   int len, pos = 0, lastpos = 0;
203
204   len = parsekeys (s, buf, MAX_SEQ);
205
206   map = allocKeys (len, buf);
207   map->op = op;
208   map->macro = safe_strdup (macro);
209   map->descr = safe_strdup (descr);
210
211   tmp = Keymaps[menu];
212
213   while (tmp)
214   {
215     if (pos >= len || pos >= tmp->len)
216     {
217       /* map and tmp match, but have different lengths, so overwrite */
218       do
219       {
220         len = tmp->eq;
221         next = tmp->next;
222         FREE (&tmp->macro);
223         FREE (&tmp->keys);
224         FREE (&tmp->descr);
225         FREE (&tmp);
226         tmp = next;
227       }
228       while (tmp && len >= pos);
229       map->eq = len;
230       break;
231     }
232     else if (buf[pos] == tmp->keys[pos])
233       pos++;
234     else if (buf[pos] < tmp->keys[pos])
235     {
236       /* found location to insert between last and tmp */
237       map->eq = pos;
238       break;
239     }
240     else /* buf[pos] > tmp->keys[pos] */
241     {
242       last = tmp;
243       lastpos = pos;
244       if (pos > tmp->eq)
245         pos = tmp->eq;
246       tmp = tmp->next;
247     }
248   }
249
250   map->next = tmp;
251   if (last)
252   {
253     last->next = map;
254     last->eq = lastpos;
255   }
256   else
257     Keymaps[menu] = map;
258 }
259
260 void km_bindkey (char *s, int menu, int op)
261 {
262   km_bind (s, menu, op, NULL, NULL);
263 }
264
265 static int get_op (struct binding_t *bindings, const char *start, size_t len)
266 {
267   int i;
268
269   for (i = 0; bindings[i].name; i++)
270   {
271     if (!ascii_strncasecmp (start, bindings[i].name, len) &&   
272         mutt_strlen (bindings[i].name) == len)
273       return bindings[i].op;
274   }
275
276   return OP_NULL;
277 }
278
279 static char *get_func (struct binding_t *bindings, int op)
280 {
281   int i;
282
283   for (i = 0; bindings[i].name; i++)
284   {
285     if (bindings[i].op == op)
286       return bindings[i].name;
287   }
288
289   return NULL;
290 }
291
292 static void push_string (char *s)
293 {
294   char *pp, *p = s + mutt_strlen (s) - 1;
295   size_t l;
296   int i, op = OP_NULL;
297
298   while (p >= s)
299   {
300     /* if we see something like "<PageUp>", look to see if it is a real
301        function name and return the corresponding value */
302     if (*p == '>')
303     {
304       for (pp = p - 1; pp >= s && *pp != '<'; pp--)
305         ;
306       if (pp >= s)
307       {
308         if ((i = parse_fkey (pp)) > 0)
309         {
310           mutt_ungetch (KEY_F (i), 0);
311           p = pp - 1;
312           continue;
313         }
314
315         l = p - pp + 1;
316         for (i = 0; KeyNames[i].name; i++)
317         {
318           if (!ascii_strncasecmp (pp, KeyNames[i].name, l))
319             break;
320         }
321         if (KeyNames[i].name)
322         {
323           /* found a match */
324           mutt_ungetch (KeyNames[i].value, 0);
325           p = pp - 1;
326           continue;
327         }
328
329         /* See if it is a valid command
330          * skip the '<' and the '>' when comparing */
331         for (i = 0; Menus[i].name; i++)
332         {
333           struct binding_t *binding = km_get_table (Menus[i].value);
334           if (binding)
335           {
336             op = get_op (binding, pp + 1, l - 2);
337             if (op != OP_NULL)
338               break;
339           }
340         }
341
342         if (op != OP_NULL)
343         {
344           mutt_ungetch (0, op);
345           p = pp - 1;
346           continue;
347         }
348       }
349     }
350     mutt_ungetch ((unsigned char)*p--, 0);      /* independent 8 bits chars */
351   }
352 }
353
354 static int retry_generic (int menu, keycode_t *keys, int keyslen, int lastkey)
355 {
356   if (menu != MENU_EDITOR && menu != MENU_GENERIC && menu != MENU_PAGER)
357   {
358     if (lastkey)
359       mutt_ungetch (lastkey, 0);
360     for (; keyslen; keyslen--)
361       mutt_ungetch (keys[keyslen - 1], 0);
362     return (km_dokey (MENU_GENERIC));
363   }
364   if (menu != MENU_EDITOR)
365   {
366     /* probably a good idea to flush input here so we can abort macros */
367     mutt_flushinp ();
368   }
369   return OP_NULL;
370 }
371
372 /* return values:
373  *      >0              function to execute
374  *      OP_NULL         no function bound to key sequence
375  *      -1              error occured while reading input
376  */
377 int km_dokey (int menu)
378 {
379   event_t tmp;
380   struct keymap_t *map = Keymaps[menu];
381   int pos = 0;
382   int n = 0;
383   int i;
384
385   if (!map)
386     return (retry_generic (menu, NULL, 0, 0));
387
388   FOREVER
389   {
390     i = Timeout > 0 ? Timeout : 60;
391 #ifdef USE_IMAP
392     /* keepalive may need to run more frequently than Timeout allows */
393     if (ImapKeepalive)
394     {
395       if (ImapKeepalive >= i)
396         imap_keepalive ();
397       else
398         while (ImapKeepalive && ImapKeepalive < i)
399         {
400           timeout (ImapKeepalive * 1000);
401           tmp = mutt_getch ();
402           timeout (-1);
403           if (tmp.ch != -2)
404             /* something other than timeout */
405             goto gotkey;
406           i -= ImapKeepalive;
407           imap_keepalive ();
408         }
409     }
410 #endif
411
412     timeout (i * 1000);
413     tmp = mutt_getch();
414     timeout (-1);
415
416     /* hide timeouts from line editor */
417     if (menu == MENU_EDITOR && tmp.ch == -2)
418       continue;
419
420   gotkey:
421     LastKey = tmp.ch;
422     if (LastKey < 0)
423       return -1;
424
425     /* do we have an op already? */
426     if (tmp.op)
427     {
428       char *func = NULL;
429       struct binding_t *bindings;
430
431       /* is this a valid op for this menu? */
432       if ((bindings = km_get_table (menu)) &&
433           (func = get_func (bindings, tmp.op)))
434         return tmp.op;
435
436       if (menu == MENU_EDITOR && get_func (OpEditor, tmp.op))
437         return tmp.op;
438
439       if (menu != MENU_EDITOR && menu != MENU_PAGER)
440       {
441         /* check generic menu */
442         bindings = OpGeneric; 
443         if ((func = get_func (bindings, tmp.op)))
444           return tmp.op;
445       }
446
447       /* Sigh. Valid function but not in this context.
448        * Find the literal string and push it back */
449       for (i = 0; Menus[i].name; i++)
450       {
451         bindings = km_get_table (Menus[i].value);
452         if (bindings)
453         {
454           func = get_func (bindings, tmp.op);
455           if (func)
456           {
457             /* careful not to feed the <..> as one token. otherwise 
458             * push_string() will push the bogus op right back! */
459             mutt_ungetch ('>', 0);
460             push_string (func);
461             mutt_ungetch ('<', 0);
462             break;
463           }
464         }
465       }
466       /* continue to chew */
467       if (func)
468         continue;
469     }
470
471     /* Nope. Business as usual */
472     while (LastKey > map->keys[pos])
473     {
474       if (pos > map->eq || !map->next)
475         return (retry_generic (menu, map->keys, pos, LastKey));
476       map = map->next;
477     }
478
479     if (LastKey != map->keys[pos])
480       return (retry_generic (menu, map->keys, pos, LastKey));
481
482     if (++pos == map->len)
483     {
484
485       if (map->op != OP_MACRO)
486         return map->op;
487
488       if (n++ == 10)
489       {
490         mutt_flushinp ();
491         mutt_error _("Macro loop detected.");
492         return -1;
493       }
494
495       push_string (map->macro);
496       map = Keymaps[menu];
497       pos = 0;
498     }
499   }
500
501   /* not reached */
502 }
503
504 static void create_bindings (struct binding_t *map, int menu)
505 {
506   int i;
507
508   for (i = 0 ; map[i].name ; i++)
509     if (map[i].seq)
510       km_bindkey (map[i].seq, menu, map[i].op);
511 }
512
513 char *km_keyname (int c)
514 {
515   static char buf[10];
516   char *p;
517
518   if ((p = mutt_getnamebyvalue (c, KeyNames)))
519     return p;
520
521   if (c < 256 && c > -128 && iscntrl ((unsigned char) c))
522   {
523     if (c < 0)
524       c += 256;
525
526     if (c < 128)
527     {
528       buf[0] = '^';
529       buf[1] = (c + '@') & 0x7f;
530       buf[2] = 0;
531     }
532     else
533       snprintf (buf, sizeof (buf), "\\%d%d%d", c >> 6, (c >> 3) & 7, c & 7);
534   }
535   else if (c >= KEY_F0 && c < KEY_F(256)) /* this maximum is just a guess */
536     sprintf (buf, "<F%d>", c - KEY_F0);
537   else if (IsPrint (c))
538     snprintf (buf, sizeof (buf), "%c", (unsigned char) c);
539   else
540     snprintf (buf, sizeof (buf), "\\x%hx", (unsigned short) c);
541   return (buf);
542 }
543
544 int km_expand_key (char *s, size_t len, struct keymap_t *map)
545 {
546   size_t l;
547   int p = 0;
548
549   if (!map)
550     return (0);
551
552   FOREVER
553   {
554     strfcpy (s, km_keyname (map->keys[p]), len);
555     len -= (l = mutt_strlen (s));
556
557     if (++p >= map->len || !len)
558       return (1);
559
560     s += l;
561   }
562
563   /* not reached */
564 }
565
566 struct keymap_t *km_find_func (int menu, int func)
567 {
568   struct keymap_t *map = Keymaps[menu];
569
570   for (; map; map = map->next)
571     if (map->op == func)
572       break;
573   return (map);
574 }
575
576 void km_init (void)
577 {
578   memset (Keymaps, 0, sizeof (struct keymap_t *) * MENU_MAX);
579
580   create_bindings (OpAttach, MENU_ATTACH);
581   create_bindings (OpBrowser, MENU_FOLDER);
582   create_bindings (OpCompose, MENU_COMPOSE);
583   create_bindings (OpMain, MENU_MAIN);
584   create_bindings (OpPager, MENU_PAGER);
585   create_bindings (OpPost, MENU_POST);
586   create_bindings (OpQuery, MENU_QUERY);
587   create_bindings (OpAlias, MENU_ALIAS);
588
589
590   if ((WithCrypto & APPLICATION_PGP))
591     create_bindings (OpPgp, MENU_PGP);
592
593   if ((WithCrypto & APPLICATION_SMIME))
594     create_bindings (OpSmime, MENU_SMIME);
595
596 #ifdef CRYPT_BACKEND_GPGME
597   create_bindings (OpPgp, MENU_KEY_SELECT_PGP);
598   create_bindings (OpSmime, MENU_KEY_SELECT_SMIME);
599 #endif
600
601 #ifdef MIXMASTER
602   create_bindings (OpMix, MENU_MIX);
603   
604   km_bindkey ("<space>", MENU_MIX, OP_GENERIC_SELECT_ENTRY);
605   km_bindkey ("h", MENU_MIX, OP_MIX_CHAIN_PREV);
606   km_bindkey ("l", MENU_MIX, OP_MIX_CHAIN_NEXT);
607 #endif
608   
609   /* bindings for the line editor */
610   create_bindings (OpEditor, MENU_EDITOR);
611   
612   km_bindkey ("<up>", MENU_EDITOR, OP_EDITOR_HISTORY_UP);
613   km_bindkey ("<down>", MENU_EDITOR, OP_EDITOR_HISTORY_DOWN);
614   km_bindkey ("<left>", MENU_EDITOR, OP_EDITOR_BACKWARD_CHAR);
615   km_bindkey ("<right>", MENU_EDITOR, OP_EDITOR_FORWARD_CHAR);
616   km_bindkey ("<home>", MENU_EDITOR, OP_EDITOR_BOL);
617   km_bindkey ("<end>", MENU_EDITOR, OP_EDITOR_EOL);
618   km_bindkey ("<backspace>", MENU_EDITOR, OP_EDITOR_BACKSPACE);
619   km_bindkey ("<delete>", MENU_EDITOR, OP_EDITOR_BACKSPACE);
620   km_bindkey ("\177", MENU_EDITOR, OP_EDITOR_BACKSPACE);
621   
622   /* generic menu keymap */
623   create_bindings (OpGeneric, MENU_GENERIC);
624   
625   km_bindkey ("<home>", MENU_GENERIC, OP_FIRST_ENTRY);
626   km_bindkey ("<end>", MENU_GENERIC, OP_LAST_ENTRY);
627   km_bindkey ("<pagedown>", MENU_GENERIC, OP_NEXT_PAGE);
628   km_bindkey ("<pageup>", MENU_GENERIC, OP_PREV_PAGE);
629   km_bindkey ("<right>", MENU_GENERIC, OP_NEXT_PAGE);
630   km_bindkey ("<left>", MENU_GENERIC, OP_PREV_PAGE);
631   km_bindkey ("<up>", MENU_GENERIC, OP_PREV_ENTRY);
632   km_bindkey ("<down>", MENU_GENERIC, OP_NEXT_ENTRY);
633   km_bindkey ("1", MENU_GENERIC, OP_JUMP);
634   km_bindkey ("2", MENU_GENERIC, OP_JUMP);
635   km_bindkey ("3", MENU_GENERIC, OP_JUMP);
636   km_bindkey ("4", MENU_GENERIC, OP_JUMP);
637   km_bindkey ("5", MENU_GENERIC, OP_JUMP);
638   km_bindkey ("6", MENU_GENERIC, OP_JUMP);
639   km_bindkey ("7", MENU_GENERIC, OP_JUMP);
640   km_bindkey ("8", MENU_GENERIC, OP_JUMP);
641   km_bindkey ("9", MENU_GENERIC, OP_JUMP);
642
643   km_bindkey ("<enter>", MENU_GENERIC, OP_GENERIC_SELECT_ENTRY);
644
645   /* Miscellaneous extra bindings */
646   
647   km_bindkey (" ", MENU_MAIN, OP_DISPLAY_MESSAGE);
648   km_bindkey ("<up>", MENU_MAIN, OP_MAIN_PREV_UNDELETED);
649   km_bindkey ("<down>", MENU_MAIN, OP_MAIN_NEXT_UNDELETED);
650   km_bindkey ("J", MENU_MAIN, OP_NEXT_ENTRY);
651   km_bindkey ("K", MENU_MAIN, OP_PREV_ENTRY);
652   km_bindkey ("x", MENU_MAIN, OP_EXIT);
653
654   km_bindkey ("<enter>", MENU_MAIN, OP_DISPLAY_MESSAGE);
655
656   km_bindkey ("x", MENU_PAGER, OP_EXIT);
657   km_bindkey ("i", MENU_PAGER, OP_EXIT);
658   km_bindkey ("<backspace>", MENU_PAGER, OP_PREV_LINE);
659   km_bindkey ("<pagedown>", MENU_PAGER, OP_NEXT_PAGE);
660   km_bindkey ("<pageup>", MENU_PAGER, OP_PREV_PAGE);
661   km_bindkey ("<up>", MENU_PAGER, OP_MAIN_PREV_UNDELETED);
662   km_bindkey ("<right>", MENU_PAGER, OP_MAIN_NEXT_UNDELETED);
663   km_bindkey ("<down>", MENU_PAGER, OP_MAIN_NEXT_UNDELETED);
664   km_bindkey ("<left>", MENU_PAGER, OP_MAIN_PREV_UNDELETED);
665   km_bindkey ("<home>", MENU_PAGER, OP_PAGER_TOP);
666   km_bindkey ("<end>", MENU_PAGER, OP_PAGER_BOTTOM);
667   km_bindkey ("1", MENU_PAGER, OP_JUMP);
668   km_bindkey ("2", MENU_PAGER, OP_JUMP);
669   km_bindkey ("3", MENU_PAGER, OP_JUMP);
670   km_bindkey ("4", MENU_PAGER, OP_JUMP);
671   km_bindkey ("5", MENU_PAGER, OP_JUMP);
672   km_bindkey ("6", MENU_PAGER, OP_JUMP);
673   km_bindkey ("7", MENU_PAGER, OP_JUMP);
674   km_bindkey ("8", MENU_PAGER, OP_JUMP);
675   km_bindkey ("9", MENU_PAGER, OP_JUMP);
676
677   km_bindkey ("<enter>", MENU_PAGER, OP_NEXT_LINE);
678   
679   km_bindkey ("<return>", MENU_ALIAS, OP_GENERIC_SELECT_ENTRY);
680   km_bindkey ("<enter>",  MENU_ALIAS, OP_GENERIC_SELECT_ENTRY);
681   km_bindkey ("<space>", MENU_ALIAS, OP_TAG);
682
683   km_bindkey ("<enter>", MENU_ATTACH, OP_VIEW_ATTACH);
684   km_bindkey ("<enter>", MENU_COMPOSE, OP_VIEW_ATTACH);
685
686   /* edit-to (default "t") hides generic tag-entry in Compose menu
687      This will bind tag-entry to  "T" in the Compose menu */
688   km_bindkey ("T", MENU_COMPOSE, OP_TAG);
689 }
690
691 void km_error_key (int menu)
692 {
693   char buf[SHORT_STRING];
694   struct keymap_t *key;
695
696   if(!(key = km_find_func(menu, OP_HELP)))
697     key = km_find_func(MENU_GENERIC, OP_HELP);
698   
699   if(!(km_expand_key(buf, sizeof(buf), key)))
700   {
701     mutt_error _("Key is not bound.");
702     return;
703   }
704
705   /* make sure the key is really the help key in this menu */
706   push_string (buf);
707   if (km_dokey (menu) != OP_HELP)
708   {
709     mutt_error _("Key is not bound.");
710     return;
711   }
712
713   mutt_error (_("Key is not bound.  Press '%s' for help."), buf);
714   return;
715 }
716
717 int mutt_parse_push (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
718 {
719   int r = 0;
720
721   mutt_extract_token (buf, s, M_TOKEN_CONDENSE);
722   if (MoreArgs (s))
723   {
724     strfcpy (err->data, _("push: too many arguments"), err->dsize);
725     r = -1;
726   }
727   else
728     push_string (buf->data);
729   return (r);
730 }
731
732 /* expects to see: <menu-string>,<menu-string>,... <key-string> */
733 static char *parse_keymap (int *menu, BUFFER *s, int maxmenus, int *nummenus, BUFFER *err)
734 {
735   BUFFER buf;
736   int i=0;
737   char *p, *q;
738
739   memset (&buf, 0, sizeof (buf));
740
741   /* menu name */
742   mutt_extract_token (&buf, s, 0);
743   p = buf.data;
744   if (MoreArgs (s))
745   {
746     while (i < maxmenus)
747     {
748       q = strchr(p,',');
749       if (q)
750         *q = '\0';
751
752       if ((menu[i] = mutt_check_menu (p)) == -1)
753       {
754          snprintf (err->data, err->dsize, _("%s: no such menu"), p);
755          goto error;
756       }
757       ++i;
758       if (q)
759         p = q+1;
760       else
761         break;
762     }
763     *nummenus=i;
764     /* key sequence */
765     mutt_extract_token (&buf, s, 0);
766
767     if (!*buf.data)
768     {
769       strfcpy (err->data, _("null key sequence"), err->dsize);
770     }
771     else if (MoreArgs (s))
772       return (buf.data);
773   }
774   else
775   {
776     strfcpy (err->data, _("too few arguments"), err->dsize);
777   }
778 error:
779   FREE (&buf.data);
780   return (NULL);
781 }
782
783 static int
784 try_bind (char *key, int menu, char *func, struct binding_t *bindings)
785 {
786   int i;
787   
788   for (i = 0; bindings[i].name; i++)
789     if (mutt_strcmp (func, bindings[i].name) == 0)
790     {
791       km_bindkey (key, menu, bindings[i].op);
792       return (0);
793     }
794   return (-1);
795 }
796
797 struct binding_t *km_get_table (int menu)
798 {
799   switch (menu)
800   {
801     case MENU_MAIN:
802       return OpMain;
803     case MENU_GENERIC:
804       return OpGeneric;
805     case MENU_COMPOSE:
806       return OpCompose;
807     case MENU_PAGER:
808       return OpPager;
809     case MENU_POST:
810       return OpPost;
811     case MENU_FOLDER:
812       return OpBrowser;
813     case MENU_ALIAS:
814       return OpAlias;
815     case MENU_ATTACH:
816       return OpAttach;
817     case MENU_EDITOR:
818       return OpEditor;
819     case MENU_QUERY:
820       return OpQuery;
821
822     case MENU_PGP:
823       return (WithCrypto & APPLICATION_PGP)? OpPgp:NULL;
824
825 #ifdef CRYPT_BACKEND_GPGME
826     case MENU_KEY_SELECT_PGP:
827       return OpPgp;
828     case MENU_KEY_SELECT_SMIME:
829       return OpSmime;
830 #endif
831
832 #ifdef MIXMASTER
833     case MENU_MIX:
834       return OpMix;
835 #endif
836
837   }
838   return NULL;
839 }
840
841 /* bind menu-name '<key_sequence>' function-name */
842 int mutt_parse_bind (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
843 {
844   struct binding_t *bindings = NULL;
845   char *key;
846   int menu[sizeof(Menus)/sizeof(struct mapping_t)-1], r = 0, nummenus, i;
847
848   if ((key = parse_keymap (menu, s, sizeof (menu)/sizeof (menu[0]),
849                            &nummenus, err)) == NULL)
850     return (-1);
851
852   /* function to execute */
853   mutt_extract_token (buf, s, 0);
854   if (MoreArgs (s))
855   {
856     strfcpy (err->data, _("bind: too many arguments"), err->dsize);
857     r = -1;
858   }
859   else if (ascii_strcasecmp ("noop", buf->data) == 0)
860   {
861     for (i = 0; i < nummenus; ++i)
862     {
863       km_bindkey (key, menu[i], OP_NULL); /* the `unbind' command */
864     }
865   }
866   else
867   {
868     for (i = 0; i < nummenus; ++i)
869     {
870       /* First check the "generic" list of commands */
871       if (menu[i] == MENU_PAGER || menu[i] == MENU_EDITOR ||
872       menu[i] == MENU_GENERIC ||
873           try_bind (key, menu[i], buf->data, OpGeneric) != 0)
874       {
875         /* Now check the menu-specific list of commands (if they exist) */
876         bindings = km_get_table (menu[i]);
877         if (bindings && try_bind (key, menu[i], buf->data, bindings) != 0)
878         {
879           snprintf (err->data, err->dsize, _("%s: no such function in map"), buf->data);
880           r = -1;
881         }
882       }
883     }
884   }
885   FREE (&key);
886   return (r);
887 }
888
889 /* macro <menu> <key> <macro> <description> */
890 int mutt_parse_macro (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
891 {
892   int menu[sizeof(Menus)/sizeof(struct mapping_t)-1], r = -1, nummenus, i;
893   char *seq = NULL;
894   char *key;
895
896   if ((key = parse_keymap (menu, s, sizeof (menu) / sizeof (menu[0]), &nummenus, err)) == NULL)
897     return (-1);
898
899   mutt_extract_token (buf, s, M_TOKEN_CONDENSE);
900   /* make sure the macro sequence is not an empty string */
901   if (!*buf->data)
902   {
903     strfcpy (err->data, _("macro: empty key sequence"), err->dsize);
904   }
905   else
906   {
907     if (MoreArgs (s))
908     {
909       seq = safe_strdup (buf->data);
910       mutt_extract_token (buf, s, M_TOKEN_CONDENSE);
911
912       if (MoreArgs (s))
913       {
914         strfcpy (err->data, _("macro: too many arguments"), err->dsize);
915       }
916       else
917       {
918         for (i = 0; i < nummenus; ++i)
919         {
920           km_bind (key, menu[i], OP_MACRO, seq, buf->data);
921           r = 0;
922         }
923       }
924
925       FREE (&seq);
926     }
927     else
928     {
929       for (i = 0; i < nummenus; ++i)
930       {
931         km_bind (key, menu[i], OP_MACRO, buf->data, NULL);
932         r = 0;
933       }
934     }
935   }
936   FREE (&key);
937   return (r);
938 }
939
940 /* exec function-name */
941 int mutt_parse_exec (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
942 {
943   int ops[128]; 
944   int nops = 0;
945   struct binding_t *bindings = NULL;
946   char *function;
947
948   if (!MoreArgs (s))
949   {
950     strfcpy (err->data, _("exec: no arguments"), err->dsize);
951     return (-1);
952   }
953
954   do
955   {
956     mutt_extract_token (buf, s, 0);
957     function = buf->data;
958
959     if ((bindings = km_get_table (CurrentMenu)) == NULL 
960         && CurrentMenu != MENU_PAGER)
961       bindings = OpGeneric;
962
963     ops[nops] = get_op (bindings, function, mutt_strlen(function));
964     if (ops[nops] == OP_NULL && CurrentMenu != MENU_PAGER)
965       ops[nops] = get_op (OpGeneric, function, mutt_strlen(function));
966
967     if (ops[nops] == OP_NULL)
968     {
969       mutt_flushinp ();
970       mutt_error (_("%s: no such function"), function);
971       return (-1);
972     }
973     nops++;
974   }
975   while(MoreArgs(s) && nops < sizeof(ops)/sizeof(ops[0]));
976
977   while(nops)
978     mutt_ungetch(0, ops[--nops]);
979
980   return 0;
981 }
982
983 /*
984  * prompts the user to enter a keystroke, and displays the octal value back
985  * to the user.
986  */
987 void mutt_what_key (void)
988 {
989   int ch;
990
991   mvprintw(LINES-1,0, _("Enter keys (^G to abort): "));
992   do {
993     ch = getch();
994     if (ch != ERR && ch != ctrl ('G'))
995     {
996       mutt_message(_("Char = %s, Octal = %o, Decimal = %d"),
997                km_keyname(ch), ch, ch);
998     }
999   }
1000   while (ch != ERR && ch != ctrl ('G'));
1001
1002   mutt_flushinp();
1003   mutt_clear_error();
1004 }