mutt stable branch with some hacks
at master 1149 lines 27 kB view raw
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}