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