default magic is useless: only _create_ mboxes and maildirs.
[apps/madmutt.git] / keymap.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000,2002 Michael R. Elkins <me@mutt.org>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 #include <lib-lib/lib-lib.h>
11
12 #include <lib-ui/lib-ui.h>
13 #include <lib-ui/menu.h>
14
15 #include "mutt.h"
16 #include "keymap.h"
17 #include "crypt.h"
18
19 #define MUTT_FUNCTIONS_VALUES
20 #include "functions.def"
21
22 struct mapping_t Menus[] = {
23   {"alias", MENU_ALIAS},
24   {"attach", MENU_ATTACH},
25   {"browser", MENU_FOLDER},
26   {"compose", MENU_COMPOSE},
27   {"editor", MENU_EDITOR},
28   {"index", MENU_MAIN},
29   {"pager", MENU_PAGER},
30   {"postpone", MENU_POST},
31   {"pgp", MENU_PGP},
32   {"smime", MENU_SMIME},
33   {"key_select_pgp", MENU_KEY_SELECT_PGP},
34   {"key_select_smime", MENU_KEY_SELECT_SMIME},
35   {"query", MENU_QUERY},
36   {"generic", MENU_GENERIC},
37   {NULL, 0}
38 };
39
40 static struct mapping_t KeyNames[] = {
41   {"<PageUp>", KEY_PPAGE},
42   {"<PageDown>", KEY_NPAGE},
43   {"<Up>", KEY_UP},
44   {"<Down>", KEY_DOWN},
45   {"<Right>", KEY_RIGHT},
46   {"<Left>", KEY_LEFT},
47   {"<Delete>", KEY_DC},
48   {"<BackSpace>", KEY_BACKSPACE},
49   {"<Insert>", KEY_IC},
50   {"<Home>", KEY_HOME},
51   {"<End>", KEY_END},
52 #ifdef KEY_ENTER
53   {"<Enter>", KEY_ENTER},
54 #endif
55   {"<Return>", '\n'},
56   {"<Esc>", '\033'},
57   {"<Tab>", '\t'},
58   {"<Space>", ' '},
59 #ifdef KEY_BTAB
60   {"<BackTab>", KEY_BTAB},
61 #endif
62 #ifdef KEY_NEXT
63   {"<Next>",    KEY_NEXT},
64 #endif
65   {NULL, 0}
66 };
67
68 /* contains the last key the user pressed */
69 int LastKey;
70
71 struct keymap_t *Keymaps[MENU_MAX];
72
73 static struct keymap_t *allocKeys(int len, keycode_t *keys)
74 {
75     struct keymap_t *p;
76
77     p = p_new(struct keymap_t, 1);
78     p->len  = len;
79     p->keys = p_dup(keys, len);
80     return p;
81 }
82
83 static int parse_fkey (const char *s)
84 {
85   int n = 0;
86
87   if (s[0] != '<' || ascii_tolower(s[1]) != 'f')
88     return -1;
89
90   n = strtol(s + 2, (char **)&s, 10);
91   return *s == '>' ? n : -1;
92 }
93
94 /*
95  * This function parses the string <NNN> and uses the octal value as the key
96  * to bind.
97  */
98 static int parse_keycode (const char *s)
99 {
100     int n;
101
102     if (*s != '<')
103         return -1;
104     n = strtol(s + 1, (char **)&s, 8);
105     return *s == '>' ? n : -1;
106 }
107
108 static int parsekeys (const char *str, keycode_t * d, int max)
109 {
110   int n, len = max;
111   char buff[STRING];
112   char c;
113   char *s, *t;
114
115   m_strcpy(buff, sizeof(buff), str);
116   s = buff;
117
118   while (*s && len) {
119     *d = '\0';
120     if (*s == '<' && (t = strchr (s, '>'))) {
121       t++;
122       c = *t;
123       *t = '\0';
124
125       if ((n = mutt_getvaluebyname (s, KeyNames)) != -1) {
126         s = t;
127         *d = n;
128       }
129       else if ((n = parse_fkey (s)) > 0) {
130         s = t;
131         *d = KEY_F (n);
132       }
133       else if ((n = parse_keycode (s)) > 0) {
134         s = t;
135         *d = n;
136       }
137
138       *t = c;
139     }
140
141     if (!*d) {
142       *d = (unsigned char) *s;
143       s++;
144     }
145     d++;
146     len--;
147   }
148
149   return (max - len);
150 }
151
152 /* insert a key sequence into the specified map.  the map is sorted by ASCII
153  * value (lowest to highest)
154  */
155 void km_bind (const char *s, int menu, int op, char *macro, char *descr)
156 {
157   struct keymap_t *map, *tmp, *last = NULL, *next;
158   keycode_t buf[MAX_SEQ];
159   int len, pos = 0, lastpos = 0;
160
161   len = parsekeys (s, buf, MAX_SEQ);
162
163   map = allocKeys (len, buf);
164   map->op = op;
165   map->macro = m_strdup(macro);
166   map->descr = m_strdup(descr);
167
168   tmp = Keymaps[menu];
169
170   while (tmp) {
171     if (pos >= len || pos >= tmp->len) {
172       /* map and tmp match, but have different lengths, so overwrite */
173       do {
174         len = tmp->eq;
175         next = tmp->next;
176         p_delete(&tmp->macro);
177         p_delete(&tmp->keys);
178         p_delete(&tmp->descr);
179         p_delete(&tmp);
180         tmp = next;
181       }
182       while (tmp && len >= pos);
183       map->eq = len;
184       break;
185     }
186     else if (buf[pos] == tmp->keys[pos])
187       pos++;
188     else if (buf[pos] < tmp->keys[pos]) {
189       /* found location to insert between last and tmp */
190       map->eq = pos;
191       break;
192     }
193     else {                      /* buf[pos] > tmp->keys[pos] */
194
195       last = tmp;
196       lastpos = pos;
197       if (pos > tmp->eq)
198         pos = tmp->eq;
199       tmp = tmp->next;
200     }
201   }
202
203   map->next = tmp;
204   if (last) {
205     last->next = map;
206     last->eq = lastpos;
207   }
208   else
209     Keymaps[menu] = map;
210 }
211
212 void km_bindkey (const char *s, int menu, int op)
213 {
214   km_bind (s, menu, op, NULL, NULL);
215 }
216
217 static int get_op (struct binding_t *bindings, const char *start, ssize_t len)
218 {
219   int i;
220
221   for (i = 0; bindings[i].name; i++) {
222     if (!ascii_strncasecmp (start, bindings[i].name, len) &&
223         m_strlen(bindings[i].name) == len)
224       return bindings[i].op;
225   }
226
227   return OP_NULL;
228 }
229
230 static const char *get_func (struct binding_t *bindings, int op)
231 {
232   int i;
233
234   for (i = 0; bindings[i].name; i++) {
235     if (bindings[i].op == op) {
236       return bindings[i].name;
237     }
238   }
239
240   return NULL;
241 }
242
243 static void push_string(const char *s)
244 {
245   const char *pp, *p = s + m_strlen(s) - 1;
246   ssize_t l;
247   int i, op = OP_NULL;
248
249   while (p >= s) {
250     /* if we see something like "<PageUp>", look to see if it is a real
251        function name and return the corresponding value */
252     if (*p == '>') {
253       for (pp = p - 1; pp >= s && *pp != '<'; pp--);
254       if (pp >= s) {
255         if ((i = parse_fkey (pp)) > 0) {
256           mutt_ungetch (KEY_F (i), 0);
257           p = pp - 1;
258           continue;
259         }
260
261         l = p - pp + 1;
262         for (i = 0; KeyNames[i].name; i++) {
263           if (!ascii_strncasecmp (pp, KeyNames[i].name, l))
264             break;
265         }
266         if (KeyNames[i].name) {
267           /* found a match */
268           mutt_ungetch (KeyNames[i].value, 0);
269           p = pp - 1;
270           continue;
271         }
272
273         /* See if it is a valid command
274          * skip the '<' and the '>' when comparing */
275         for (i = 0; Menus[i].name; i++) {
276           struct binding_t *binding = km_get_table (Menus[i].value);
277
278           if (binding) {
279             op = get_op (binding, pp + 1, l - 2);
280             if (op != OP_NULL)
281               break;
282           }
283         }
284
285         if (op != OP_NULL) {
286           mutt_ungetch (0, op);
287           p = pp - 1;
288           continue;
289         }
290       }
291     }
292     mutt_ungetch ((unsigned char) *p--, 0);
293   }
294 }
295
296 static int retry_generic (int menu, keycode_t * keys, int keyslen,
297                           int lastkey)
298 {
299   if (menu != MENU_EDITOR && menu != MENU_GENERIC && menu != MENU_PAGER) {
300     if (lastkey)
301       mutt_ungetch (lastkey, 0);
302     for (; keyslen; keyslen--)
303       mutt_ungetch (keys[keyslen - 1], 0);
304     return (km_dokey (MENU_GENERIC));
305   }
306   if (menu != MENU_EDITOR) {
307     /* probably a good idea to flush input here so we can abort macros */
308     mutt_flushinp ();
309   }
310   return OP_NULL;
311 }
312
313 /* return values:
314  *        >0                function to execute
315  *        OP_NULL                no function bound to key sequence
316  *        -1                error occured while reading input
317  */
318 int km_dokey (int menu)
319 {
320   event_t tmp;
321   struct keymap_t *map = Keymaps[menu];
322   int pos = 0;
323   int n = 0;
324   int i;
325
326
327   if (!map)
328     return (retry_generic (menu, NULL, 0, 0));
329
330   for (;;) {
331     /* ncurses doesn't return on resized screen when timeout is set to zero */
332     if (menu != MENU_EDITOR)
333       wtimeout (main_w, (Timeout > 0 ? Timeout : 60) * 1000);
334
335     tmp = mutt_getch ();
336
337     if (menu != MENU_EDITOR)
338       wtimeout (main_w, -1);             /* restore blocking operation */
339
340     LastKey = tmp.ch;
341     if (LastKey == -1)
342       return -1;
343
344     /* do we have an op already? */
345     if (tmp.op) {
346       const char *func = NULL;
347       struct binding_t *bindings;
348
349       /* is this a valid op for this menu? */
350       if ((bindings = km_get_table (menu)) &&
351           (func = get_func (bindings, tmp.op)))
352         return tmp.op;
353
354       if (menu == MENU_EDITOR && get_func (OpEditor, tmp.op))
355         return tmp.op;
356
357       if (menu != MENU_EDITOR && menu != MENU_PAGER) {
358         /* check generic menu */
359         bindings = OpGeneric;
360         if ((func = get_func (bindings, tmp.op)))
361           return tmp.op;
362       }
363
364       /* Sigh. Valid function but not in this context.
365        * Find the literal string and push it back */
366       for (i = 0; Menus[i].name; i++) {
367         bindings = km_get_table (Menus[i].value);
368         if (bindings) {
369           func = get_func (bindings, tmp.op);
370           if (func) {
371             /* careful not to feed the <..> as one token. otherwise 
372              * push_string() will push the bogus op right back! */
373             mutt_ungetch ('>', 0);
374             push_string(func);
375             mutt_ungetch ('<', 0);
376             break;
377           }
378         }
379       }
380       /* continue to chew */
381       if (func)
382         continue;
383     }
384
385     /* Nope. Business as usual */
386     while (LastKey > map->keys[pos]) {
387       if (pos > map->eq || !map->next)
388         return (retry_generic (menu, map->keys, pos, LastKey));
389       map = map->next;
390     }
391
392     if (LastKey != map->keys[pos])
393       return (retry_generic (menu, map->keys, pos, LastKey));
394
395     if (++pos == map->len) {
396
397       if (map->op != OP_MACRO)
398         return map->op;
399
400       if (n++ == 10) {
401         mutt_flushinp ();
402         mutt_error _("Macro loop detected.");
403
404         return -1;
405       }
406
407       push_string (map->macro);
408       map = Keymaps[menu];
409       pos = 0;
410     }
411   }
412
413   /* not reached */
414 }
415
416 static void create_bindings (struct binding_t *map, int menu)
417 {
418   int i;
419
420   for (i = 0; map[i].name; i++)
421     if (map[i].seq)
422       km_bindkey (map[i].seq, menu, map[i].op);
423 }
424
425 const char *km_keyname(int c)
426 {
427   static char buf[10];
428   const char *p;
429
430   if ((p = mutt_getnamebyvalue (c, KeyNames)))
431     return p;
432
433   if (c < 256 && c > -128 && iscntrl ((unsigned char) c)) {
434     if (c < 0)
435       c += 256;
436
437     if (c < 128) {
438       buf[0] = '^';
439       buf[1] = (c + '@') & 0x7f;
440       buf[2] = 0;
441     }
442     else
443       snprintf (buf, sizeof (buf), "\\%d%d%d", c >> 6, (c >> 3) & 7, c & 7);
444   }
445   else if (c >= KEY_F0 && c < KEY_F (256))      /* this maximum is just a guess */
446     sprintf (buf, "<F%d>", c - KEY_F0);
447   else if (isprint((unsigned char)c))
448     snprintf (buf, sizeof (buf), "%c", (unsigned char) c);
449   else
450     snprintf (buf, sizeof (buf), "\\x%hx", (unsigned short) c);
451   return (buf);
452 }
453
454 int km_expand_key (char *s, size_t len, struct keymap_t *map)
455 {
456   size_t l;
457   int p = 0;
458
459   if (!map)
460     return (0);
461
462   for (;;) {
463     m_strcpy(s, len, km_keyname(map->keys[p]));
464     len -= (l = m_strlen(s));
465
466     if (++p >= map->len || !len)
467       return (1);
468
469     s += l;
470   }
471
472   /* not reached */
473 }
474
475 struct keymap_t *km_find_func (int menu, int func)
476 {
477   struct keymap_t *map = Keymaps[menu];
478
479   for (; map; map = map->next)
480     if (map->op == func)
481       break;
482   return (map);
483 }
484
485 void km_init (void)
486 {
487   p_clear(Keymaps, MENU_MAX);
488
489   create_bindings (OpAttach, MENU_ATTACH);
490   create_bindings (OpBrowser, MENU_FOLDER);
491   create_bindings (OpCompose, MENU_COMPOSE);
492   create_bindings (OpMain, MENU_MAIN);
493   create_bindings (OpPager, MENU_PAGER);
494   create_bindings (OpPost, MENU_POST);
495   create_bindings (OpQuery, MENU_QUERY);
496   create_bindings (OpAlias, MENU_ALIAS);
497   create_bindings (OpPgp, MENU_PGP);
498   create_bindings (OpSmime, MENU_SMIME);
499   create_bindings (OpPgp, MENU_KEY_SELECT_PGP);
500   create_bindings (OpSmime, MENU_KEY_SELECT_SMIME);
501
502   /* bindings for the line editor */
503   create_bindings (OpEditor, MENU_EDITOR);
504
505   km_bindkey ("<up>", MENU_EDITOR, OP_EDITOR_HISTORY_UP);
506   km_bindkey ("<down>", MENU_EDITOR, OP_EDITOR_HISTORY_DOWN);
507   km_bindkey ("<left>", MENU_EDITOR, OP_EDITOR_BACKWARD_CHAR);
508   km_bindkey ("<right>", MENU_EDITOR, OP_EDITOR_FORWARD_CHAR);
509   km_bindkey ("<home>", MENU_EDITOR, OP_EDITOR_BOL);
510   km_bindkey ("<end>", MENU_EDITOR, OP_EDITOR_EOL);
511   km_bindkey ("<backspace>", MENU_EDITOR, OP_EDITOR_BACKSPACE);
512   km_bindkey ("<delete>", MENU_EDITOR, OP_EDITOR_BACKSPACE);
513   km_bindkey ("\177", MENU_EDITOR, OP_EDITOR_BACKSPACE);
514
515   /* generic menu keymap */
516   create_bindings (OpGeneric, MENU_GENERIC);
517
518   km_bindkey ("<home>", MENU_GENERIC, OP_FIRST_ENTRY);
519   km_bindkey ("<end>", MENU_GENERIC, OP_LAST_ENTRY);
520   km_bindkey ("<pagedown>", MENU_GENERIC, OP_NEXT_PAGE);
521   km_bindkey ("<pageup>", MENU_GENERIC, OP_PREV_PAGE);
522   km_bindkey ("<right>", MENU_GENERIC, OP_NEXT_PAGE);
523   km_bindkey ("<left>", MENU_GENERIC, OP_PREV_PAGE);
524   km_bindkey ("<up>", MENU_GENERIC, OP_PREV_ENTRY);
525   km_bindkey ("<down>", MENU_GENERIC, OP_NEXT_ENTRY);
526   km_bindkey ("1", MENU_GENERIC, OP_JUMP);
527   km_bindkey ("2", MENU_GENERIC, OP_JUMP);
528   km_bindkey ("3", MENU_GENERIC, OP_JUMP);
529   km_bindkey ("4", MENU_GENERIC, OP_JUMP);
530   km_bindkey ("5", MENU_GENERIC, OP_JUMP);
531   km_bindkey ("6", MENU_GENERIC, OP_JUMP);
532   km_bindkey ("7", MENU_GENERIC, OP_JUMP);
533   km_bindkey ("8", MENU_GENERIC, OP_JUMP);
534   km_bindkey ("9", MENU_GENERIC, OP_JUMP);
535
536   km_bindkey ("<enter>", MENU_GENERIC, OP_GENERIC_SELECT_ENTRY);
537
538   /* Miscellaneous extra bindings */
539
540   km_bindkey (" ", MENU_MAIN, OP_DISPLAY_MESSAGE);
541   km_bindkey ("<up>", MENU_MAIN, OP_MAIN_PREV_UNDELETED);
542   km_bindkey ("<down>", MENU_MAIN, OP_MAIN_NEXT_UNDELETED);
543   km_bindkey ("J", MENU_MAIN, OP_NEXT_ENTRY);
544   km_bindkey ("K", MENU_MAIN, OP_PREV_ENTRY);
545   km_bindkey ("x", MENU_MAIN, OP_EXIT);
546
547   km_bindkey ("<enter>", MENU_MAIN, OP_DISPLAY_MESSAGE);
548
549   km_bindkey ("x", MENU_PAGER, OP_EXIT);
550   km_bindkey ("<backspace>", MENU_PAGER, OP_PREV_LINE);
551   km_bindkey ("<pagedown>", MENU_PAGER, OP_NEXT_PAGE);
552   km_bindkey ("<pageup>", MENU_PAGER, OP_PREV_PAGE);
553   km_bindkey ("<up>", MENU_PAGER, OP_MAIN_PREV_UNDELETED);
554   km_bindkey ("<right>", MENU_PAGER, OP_MAIN_NEXT_UNDELETED);
555   km_bindkey ("<down>", MENU_PAGER, OP_MAIN_NEXT_UNDELETED);
556   km_bindkey ("<left>", MENU_PAGER, OP_MAIN_PREV_UNDELETED);
557   km_bindkey ("<home>", MENU_PAGER, OP_PAGER_TOP);
558   km_bindkey ("<end>", MENU_PAGER, OP_PAGER_BOTTOM);
559   km_bindkey ("1", MENU_PAGER, OP_JUMP);
560   km_bindkey ("2", MENU_PAGER, OP_JUMP);
561   km_bindkey ("3", MENU_PAGER, OP_JUMP);
562   km_bindkey ("4", MENU_PAGER, OP_JUMP);
563   km_bindkey ("5", MENU_PAGER, OP_JUMP);
564   km_bindkey ("6", MENU_PAGER, OP_JUMP);
565   km_bindkey ("7", MENU_PAGER, OP_JUMP);
566   km_bindkey ("8", MENU_PAGER, OP_JUMP);
567   km_bindkey ("9", MENU_PAGER, OP_JUMP);
568
569   km_bindkey ("<enter>", MENU_PAGER, OP_NEXT_LINE);
570
571   km_bindkey ("<return>", MENU_ALIAS, OP_GENERIC_SELECT_ENTRY);
572   km_bindkey ("<enter>", MENU_ALIAS, OP_GENERIC_SELECT_ENTRY);
573   km_bindkey ("<space>", MENU_ALIAS, OP_TAG);
574
575   km_bindkey ("<enter>", MENU_ATTACH, OP_VIEW_ATTACH);
576   km_bindkey ("<enter>", MENU_COMPOSE, OP_VIEW_ATTACH);
577
578   /* edit-to (default "t") hides generic tag-entry in Compose menu
579      This will bind tag-entry to  "T" in the Compose menu */
580   km_bindkey ("T", MENU_COMPOSE, OP_TAG);
581 }
582
583 void km_error_key (int menu)
584 {
585   char buf[STRING];
586   struct keymap_t *key;
587
588   if (!(key = km_find_func (menu, OP_HELP)))
589     key = km_find_func (MENU_GENERIC, OP_HELP);
590
591   if (!(km_expand_key (buf, sizeof (buf), key))) {
592     mutt_error _("Key is not bound.");
593
594     return;
595   }
596
597   /* make sure the key is really the help key in this menu */
598   push_string (buf);
599   if (km_dokey (menu) != OP_HELP) {
600     mutt_error _("Key is not bound.");
601
602     return;
603   }
604
605   mutt_error (_("Key is not bound.  Press '%s' for help."), buf);
606   return;
607 }
608
609 int mutt_parse_push (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
610                      BUFFER * err)
611 {
612   int r = 0;
613
614   mutt_extract_token (buf, s, M_TOKEN_CONDENSE);
615   if (MoreArgs (s)) {
616     m_strcpy(err->data, err->dsize, _("push: too many arguments"));
617     r = -1;
618   }
619   else
620     push_string (buf->data);
621   return (r);
622 }
623
624 /* expects to see: <menu-string>,<menu-string>,... <key-string> */
625 static char *parse_keymap (int *menu, BUFFER * s, int maxmenus, int *nummenus,
626                            BUFFER * err)
627 {
628   BUFFER buf;
629   int i = 0;
630   char *p, *q;
631
632   p_clear(&buf, 1);
633
634   /* menu name */
635   mutt_extract_token (&buf, s, 0);
636   p = buf.data;
637   if (MoreArgs (s)) {
638     while (i < maxmenus) {
639       q = strchr (p, ',');
640       if (q)
641         *q = '\0';
642
643       if ((menu[i] = mutt_getvaluebyname(p, Menus)) == -1) {
644         snprintf (err->data, err->dsize, _("%s: no such menu"), p);
645         goto error;
646       }
647       ++i;
648       if (q)
649         p = q + 1;
650       else
651         break;
652     }
653     *nummenus = i;
654     /* key sequence */
655     mutt_extract_token (&buf, s, 0);
656
657     if (!*buf.data) {
658       m_strcpy(err->data, err->dsize, _("null key sequence"));
659     }
660     else if (MoreArgs (s))
661       return (buf.data);
662   }
663   else {
664     m_strcpy(err->data, err->dsize, _("too few arguments"));
665   }
666 error:
667   p_delete(&buf.data);
668   return (NULL);
669 }
670
671 static int
672 try_bind (char *key, int menu, char *func, struct binding_t *bindings)
673 {
674   int i;
675
676   for (i = 0; bindings[i].name; i++)
677     if (m_strcmp(func, bindings[i].name) == 0) {
678       km_bindkey (key, menu, bindings[i].op);
679       return (0);
680     }
681   return (-1);
682 }
683
684 struct binding_t *km_get_table (int menu)
685 {
686   switch (menu) {
687   case MENU_MAIN:
688     return OpMain;
689   case MENU_GENERIC:
690     return OpGeneric;
691   case MENU_COMPOSE:
692     return OpCompose;
693   case MENU_PAGER:
694     return OpPager;
695   case MENU_POST:
696     return OpPost;
697   case MENU_FOLDER:
698     return OpBrowser;
699   case MENU_ALIAS:
700     return OpAlias;
701   case MENU_ATTACH:
702     return OpAttach;
703   case MENU_EDITOR:
704     return OpEditor;
705   case MENU_QUERY:
706     return OpQuery;
707   case MENU_PGP:
708     return OpPgp;
709   case MENU_KEY_SELECT_PGP:
710     return OpPgp;
711   case MENU_KEY_SELECT_SMIME:
712     return OpSmime;
713   }
714   return NULL;
715 }
716
717 /* bind menu-name '<key_sequence>' function-name */
718 int mutt_parse_bind (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
719                      BUFFER * err)
720 {
721   struct binding_t *bindings = NULL;
722   char *key;
723   int menu[countof(Menus) - 1], r =
724     0, nummenus, i;
725
726   if (!(key = parse_keymap(menu, s, countof(menu), &nummenus, err)))
727     return -1;
728
729   /* function to execute */
730   mutt_extract_token (buf, s, 0);
731   if (MoreArgs (s)) {
732     m_strcpy(err->data, err->dsize, _("bind: too many arguments"));
733     r = -1;
734   }
735   else if (ascii_strcasecmp ("noop", buf->data) == 0) {
736     for (i = 0; i < nummenus; ++i) {
737       km_bindkey (key, menu[i], OP_NULL);       /* the `unbind' command */
738     }
739   }
740   else {
741     for (i = 0; i < nummenus; ++i) {
742       /* First check the "generic" list of commands */
743       if (menu[i] == MENU_PAGER || menu[i] == MENU_EDITOR ||
744           menu[i] == MENU_GENERIC ||
745           try_bind (key, menu[i], buf->data, OpGeneric) != 0) {
746         /* Now check the menu-specific list of commands (if they exist) */
747         bindings = km_get_table (menu[i]);
748         if (bindings && try_bind (key, menu[i], buf->data, bindings) != 0) {
749           snprintf (err->data, err->dsize, _("%s: no such function in map"),
750                     buf->data);
751           r = -1;
752         }
753       }
754     }
755   }
756   p_delete(&key);
757   return (r);
758 }
759
760 /* macro <menu> <key> <macro> <description> */
761 int mutt_parse_macro (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
762                       BUFFER * err)
763 {
764   int menu[sizeof (Menus) / sizeof (struct mapping_t) - 1], r =
765     -1, nummenus, i;
766   char *seq = NULL;
767   char *key;
768
769   if ((key =
770        parse_keymap (menu, s, sizeof (menu) / sizeof (menu[0]), &nummenus,
771                      err)) == NULL)
772     return (-1);
773
774   mutt_extract_token (buf, s, M_TOKEN_CONDENSE);
775   /* make sure the macro sequence is not an empty string */
776   if (!*buf->data) {
777     m_strcpy(err->data, err->dsize, _("macro: empty key sequence"));
778   }
779   else {
780     if (MoreArgs (s)) {
781       seq = m_strdup(buf->data);
782       mutt_extract_token (buf, s, M_TOKEN_CONDENSE);
783
784       if (MoreArgs (s)) {
785         m_strcpy(err->data, err->dsize, _("macro: too many arguments"));
786       }
787       else {
788         for (i = 0; i < nummenus; ++i) {
789           km_bind (key, menu[i], OP_MACRO, seq, buf->data);
790           r = 0;
791         }
792       }
793
794       p_delete(&seq);
795     }
796     else {
797       for (i = 0; i < nummenus; ++i) {
798         km_bind (key, menu[i], OP_MACRO, buf->data, NULL);
799         r = 0;
800       }
801     }
802   }
803   p_delete(&key);
804   return (r);
805 }
806
807 /* exec function-name */
808 int mutt_parse_exec (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
809                      BUFFER * err)
810 {
811   int ops[128];
812   int nops = 0;
813   struct binding_t *bindings = NULL;
814   char *function;
815
816   if (!MoreArgs (s)) {
817     m_strcpy(err->data, err->dsize, _("exec: no arguments"));
818     return (-1);
819   }
820
821   do {
822     mutt_extract_token (buf, s, 0);
823     function = buf->data;
824
825     if ((bindings = km_get_table (CurrentMenu)) == NULL
826         && CurrentMenu != MENU_PAGER)
827       bindings = OpGeneric;
828
829     ops[nops] = get_op (bindings, function, m_strlen(function));
830     if (ops[nops] == OP_NULL && CurrentMenu != MENU_PAGER)
831       ops[nops] = get_op (OpGeneric, function, m_strlen(function));
832
833     if (ops[nops] == OP_NULL) {
834       mutt_flushinp ();
835       mutt_error (_("%s: no such function"), function);
836       return (-1);
837     }
838     nops++;
839   }
840   while (MoreArgs (s) && nops < ssizeof (ops) / ssizeof (ops[0]));
841
842   while (nops)
843     mutt_ungetch (0, ops[--nops]);
844
845   return 0;
846 }