mutt stable branch with some hacks
at master 2913 lines 72 kB view raw
1/* 2 * Copyright (C) 1996-2002,2007,2010,2012-2013 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_curses.h" 25#include "mutt_regex.h" 26#include "keymap.h" 27#include "mutt_menu.h" 28#include "mapping.h" 29#include "pager.h" 30#include "attach.h" 31#include "mbyte.h" 32#ifdef USE_SIDEBAR 33#include "sidebar.h" 34#endif 35 36#include "mutt_crypt.h" 37 38#include <sys/stat.h> 39#include <ctype.h> 40#include <unistd.h> 41#include <stdlib.h> 42#include <string.h> 43#include <errno.h> 44 45#define ISHEADER(x) ((x) == MT_COLOR_HEADER || (x) == MT_COLOR_HDEFAULT) 46 47#define IsAttach(x) (x && (x)->bdy) 48#define IsRecvAttach(x) (x && (x)->bdy && (x)->fp) 49#define IsSendAttach(x) (x && (x)->bdy && !(x)->fp) 50#define IsMsgAttach(x) (x && (x)->fp && (x)->bdy && (x)->bdy->hdr) 51#define IsHeader(x) (x && (x)->hdr && !(x)->bdy) 52 53static const char *Not_available_in_this_menu = N_("Not available in this menu."); 54static const char *Mailbox_is_read_only = N_("Mailbox is read-only."); 55static const char *Function_not_permitted_in_attach_message_mode = N_("Function not permitted in attach-message mode."); 56 57/* hack to return to position when returning from index to same message */ 58static int TopLine = 0; 59static HEADER *OldHdr = NULL; 60 61#define CHECK_MODE(x) if (!(x)) \ 62 { \ 63 mutt_flushinp (); \ 64 mutt_error _(Not_available_in_this_menu); \ 65 break; \ 66 } 67 68#define CHECK_READONLY if (Context->readonly) \ 69 { \ 70 mutt_flushinp (); \ 71 mutt_error _(Mailbox_is_read_only); \ 72 break; \ 73 } 74 75#define CHECK_ATTACH if(option(OPTATTACHMSG)) \ 76 {\ 77 mutt_flushinp (); \ 78 mutt_error _(Function_not_permitted_in_attach_message_mode); \ 79 break; \ 80 } 81 82#define CHECK_ACL(aclbit,action) \ 83 if (!mutt_bit_isset(Context->rights,aclbit)) { \ 84 mutt_flushinp(); \ 85 /* L10N: %s is one of the CHECK_ACL entries below. */ \ 86 mutt_error (_("%s: Operation not permitted by ACL"), action); \ 87 break; \ 88 } 89 90struct q_class_t 91{ 92 int length; 93 int index; 94 int color; 95 char *prefix; 96 struct q_class_t *next, *prev; 97 struct q_class_t *down, *up; 98}; 99 100struct syntax_t 101{ 102 int color; 103 int first; 104 int last; 105}; 106 107struct line_t 108{ 109 LOFF_T offset; 110 short type; 111 short continuation; 112 short chunks; 113 short search_cnt; 114 struct syntax_t *syntax; 115 struct syntax_t *search; 116 struct q_class_t *quote; 117 unsigned int is_cont_hdr; /* this line is a continuation of the previous header line */ 118}; 119 120#define ANSI_OFF (1<<0) 121#define ANSI_BLINK (1<<1) 122#define ANSI_BOLD (1<<2) 123#define ANSI_UNDERLINE (1<<3) 124#define ANSI_REVERSE (1<<4) 125#define ANSI_COLOR (1<<5) 126 127typedef struct _ansi_attr { 128 int attr; 129 int fg; 130 int bg; 131 int pair; 132} ansi_attr; 133 134static short InHelp = 0; 135 136#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM) 137static struct resize { 138 int line; 139 int SearchCompiled; 140 int SearchBack; 141} *Resize = NULL; 142#endif 143 144#define NumSigLines 4 145 146static int check_sig (const char *s, struct line_t *info, int n) 147{ 148 int count = 0; 149 150 while (n > 0 && count <= NumSigLines) 151 { 152 if (info[n].type != MT_COLOR_SIGNATURE) 153 break; 154 count++; 155 n--; 156 } 157 158 if (count == 0) 159 return (-1); 160 161 if (count > NumSigLines) 162 { 163 /* check for a blank line */ 164 while (*s) 165 { 166 if (!ISSPACE (*s)) 167 return 0; 168 s++; 169 } 170 171 return (-1); 172 } 173 174 return (0); 175} 176 177static void 178resolve_color (struct line_t *lineInfo, int n, int cnt, int flags, int special, 179 ansi_attr *a) 180{ 181 int def_color; /* color without syntax hilight */ 182 int color; /* final color */ 183 static int last_color; /* last color set */ 184 int search = 0, i, m; 185 186 if (!cnt) 187 last_color = -1; /* force attrset() */ 188 189 if (lineInfo[n].continuation) 190 { 191 if (!cnt && option (OPTMARKERS)) 192 { 193 SETCOLOR (MT_COLOR_MARKERS); 194 addch ('+'); 195 last_color = ColorDefs[MT_COLOR_MARKERS]; 196 } 197 m = (lineInfo[n].syntax)[0].first; 198 cnt += (lineInfo[n].syntax)[0].last; 199 } 200 else 201 m = n; 202 if (!(flags & MUTT_SHOWCOLOR)) 203 def_color = ColorDefs[MT_COLOR_NORMAL]; 204 else if (lineInfo[m].type == MT_COLOR_HEADER) 205 def_color = (lineInfo[m].syntax)[0].color; 206 else 207 def_color = ColorDefs[lineInfo[m].type]; 208 209 if ((flags & MUTT_SHOWCOLOR) && lineInfo[m].type == MT_COLOR_QUOTED) 210 { 211 struct q_class_t *class = lineInfo[m].quote; 212 213 if (class) 214 { 215 def_color = class->color; 216 217 while (class && class->length > cnt) 218 { 219 def_color = class->color; 220 class = class->up; 221 } 222 } 223 } 224 225 color = def_color; 226 if (flags & MUTT_SHOWCOLOR) 227 { 228 for (i = 0; i < lineInfo[m].chunks; i++) 229 { 230 /* we assume the chunks are sorted */ 231 if (cnt > (lineInfo[m].syntax)[i].last) 232 continue; 233 if (cnt < (lineInfo[m].syntax)[i].first) 234 break; 235 if (cnt != (lineInfo[m].syntax)[i].last) 236 { 237 color = (lineInfo[m].syntax)[i].color; 238 break; 239 } 240 /* don't break here, as cnt might be 241 * in the next chunk as well */ 242 } 243 } 244 245 if (flags & MUTT_SEARCH) 246 { 247 for (i = 0; i < lineInfo[m].search_cnt; i++) 248 { 249 if (cnt > (lineInfo[m].search)[i].last) 250 continue; 251 if (cnt < (lineInfo[m].search)[i].first) 252 break; 253 if (cnt != (lineInfo[m].search)[i].last) 254 { 255 color = ColorDefs[MT_COLOR_SEARCH]; 256 search = 1; 257 break; 258 } 259 } 260 } 261 262 /* handle "special" bold & underlined characters */ 263 if (special || a->attr) 264 { 265#ifdef HAVE_COLOR 266 if ((a->attr & ANSI_COLOR)) 267 { 268 if (a->pair == -1) 269 a->pair = mutt_alloc_color (a->fg, a->bg); 270 color = a->pair; 271 if (a->attr & ANSI_BOLD) 272 color |= A_BOLD; 273 } 274 else 275#endif 276 if ((special & A_BOLD) || (a->attr & ANSI_BOLD)) 277 { 278 if (ColorDefs[MT_COLOR_BOLD] && !search) 279 color = ColorDefs[MT_COLOR_BOLD]; 280 else 281 color ^= A_BOLD; 282 } 283 if ((special & A_UNDERLINE) || (a->attr & ANSI_UNDERLINE)) 284 { 285 if (ColorDefs[MT_COLOR_UNDERLINE] && !search) 286 color = ColorDefs[MT_COLOR_UNDERLINE]; 287 else 288 color ^= A_UNDERLINE; 289 } 290 else if (a->attr & ANSI_REVERSE) 291 { 292 color ^= A_REVERSE; 293 } 294 else if (a->attr & ANSI_BLINK) 295 { 296 color ^= A_BLINK; 297 } 298 else if (a->attr & ANSI_OFF) 299 { 300 a->attr = 0; 301 } 302 } 303 304 if (color != last_color) 305 { 306 ATTRSET (color); 307 last_color = color; 308 } 309} 310 311static void 312append_line (struct line_t *lineInfo, int n, int cnt) 313{ 314 int m; 315 316 lineInfo[n+1].type = lineInfo[n].type; 317 (lineInfo[n+1].syntax)[0].color = (lineInfo[n].syntax)[0].color; 318 lineInfo[n+1].continuation = 1; 319 320 /* find the real start of the line */ 321 for (m = n; m >= 0; m--) 322 if (lineInfo[m].continuation == 0) break; 323 324 (lineInfo[n+1].syntax)[0].first = m; 325 (lineInfo[n+1].syntax)[0].last = (lineInfo[n].continuation) ? 326 cnt + (lineInfo[n].syntax)[0].last : cnt; 327} 328 329static void 330new_class_color (struct q_class_t *class, int *q_level) 331{ 332 class->index = (*q_level)++; 333 class->color = ColorQuote[class->index % ColorQuoteUsed]; 334} 335 336static void 337shift_class_colors (struct q_class_t *QuoteList, struct q_class_t *new_class, 338 int index, int *q_level) 339{ 340 struct q_class_t * q_list; 341 342 q_list = QuoteList; 343 new_class->index = -1; 344 345 while (q_list) 346 { 347 if (q_list->index >= index) 348 { 349 q_list->index++; 350 q_list->color = ColorQuote[q_list->index % ColorQuoteUsed]; 351 } 352 if (q_list->down) 353 q_list = q_list->down; 354 else if (q_list->next) 355 q_list = q_list->next; 356 else 357 { 358 while (!q_list->next) 359 { 360 q_list = q_list->up; 361 if (q_list == NULL) 362 break; 363 } 364 if (q_list) 365 q_list = q_list->next; 366 } 367 } 368 369 new_class->index = index; 370 new_class->color = ColorQuote[index % ColorQuoteUsed]; 371 (*q_level)++; 372} 373 374static void 375cleanup_quote (struct q_class_t **QuoteList) 376{ 377 struct q_class_t *ptr; 378 379 while (*QuoteList) 380 { 381 if ((*QuoteList)->down) 382 cleanup_quote (&((*QuoteList)->down)); 383 ptr = (*QuoteList)->next; 384 if ((*QuoteList)->prefix) 385 FREE (&(*QuoteList)->prefix); 386 FREE (QuoteList); /* __FREE_CHECKED__ */ 387 *QuoteList = ptr; 388 } 389 390 return; 391} 392 393static struct q_class_t * 394classify_quote (struct q_class_t **QuoteList, const char *qptr, 395 int length, int *force_redraw, int *q_level) 396{ 397 struct q_class_t *q_list = *QuoteList; 398 struct q_class_t *class = NULL, *tmp = NULL, *ptr, *save; 399 char *tail_qptr; 400 int offset, tail_lng; 401 int index = -1; 402 403 if (ColorQuoteUsed <= 1) 404 { 405 /* not much point in classifying quotes... */ 406 407 if (*QuoteList == NULL) 408 { 409 class = (struct q_class_t *) safe_calloc (1, sizeof (struct q_class_t)); 410 class->color = ColorQuote[0]; 411 *QuoteList = class; 412 } 413 return (*QuoteList); 414 } 415 416 /* Did I mention how much I like emulating Lisp in C? */ 417 418 /* classify quoting prefix */ 419 while (q_list) 420 { 421 if (length <= q_list->length) 422 { 423 /* case 1: check the top level nodes */ 424 425 if (mutt_strncmp (qptr, q_list->prefix, length) == 0) 426 { 427 if (length == q_list->length) 428 return q_list; /* same prefix: return the current class */ 429 430 /* found shorter prefix */ 431 if (tmp == NULL) 432 { 433 /* add a node above q_list */ 434 tmp = (struct q_class_t *) safe_calloc (1, sizeof (struct q_class_t)); 435 tmp->prefix = (char *) safe_calloc (1, length + 1); 436 strncpy (tmp->prefix, qptr, length); 437 tmp->length = length; 438 439 /* replace q_list by tmp in the top level list */ 440 if (q_list->next) 441 { 442 tmp->next = q_list->next; 443 q_list->next->prev = tmp; 444 } 445 if (q_list->prev) 446 { 447 tmp->prev = q_list->prev; 448 q_list->prev->next = tmp; 449 } 450 451 /* make q_list a child of tmp */ 452 tmp->down = q_list; 453 q_list->up = tmp; 454 455 /* q_list has no siblings for now */ 456 q_list->next = NULL; 457 q_list->prev = NULL; 458 459 /* update the root if necessary */ 460 if (q_list == *QuoteList) 461 *QuoteList = tmp; 462 463 index = q_list->index; 464 465 /* tmp should be the return class too */ 466 class = tmp; 467 468 /* next class to test; if tmp is a shorter prefix for another 469 * node, that node can only be in the top level list, so don't 470 * go down after this point 471 */ 472 q_list = tmp->next; 473 } 474 else 475 { 476 /* found another branch for which tmp is a shorter prefix */ 477 478 /* save the next sibling for later */ 479 save = q_list->next; 480 481 /* unlink q_list from the top level list */ 482 if (q_list->next) 483 q_list->next->prev = q_list->prev; 484 if (q_list->prev) 485 q_list->prev->next = q_list->next; 486 487 /* at this point, we have a tmp->down; link q_list to it */ 488 ptr = tmp->down; 489 /* sibling order is important here, q_list should be linked last */ 490 while (ptr->next) 491 ptr = ptr->next; 492 ptr->next = q_list; 493 q_list->next = NULL; 494 q_list->prev = ptr; 495 q_list->up = tmp; 496 497 index = q_list->index; 498 499 /* next class to test; as above, we shouldn't go down */ 500 q_list = save; 501 } 502 503 /* we found a shorter prefix, so certain quotes have changed classes */ 504 *force_redraw = 1; 505 continue; 506 } 507 else 508 { 509 /* shorter, but not a substring of the current class: try next */ 510 q_list = q_list->next; 511 continue; 512 } 513 } 514 else 515 { 516 /* case 2: try subclassing the current top level node */ 517 518 /* tmp != NULL means we already found a shorter prefix at case 1 */ 519 if (tmp == NULL && mutt_strncmp (qptr, q_list->prefix, q_list->length) == 0) 520 { 521 /* ok, it's a subclass somewhere on this branch */ 522 523 ptr = q_list; 524 offset = q_list->length; 525 526 q_list = q_list->down; 527 tail_lng = length - offset; 528 tail_qptr = (char *) qptr + offset; 529 530 while (q_list) 531 { 532 if (length <= q_list->length) 533 { 534 if (mutt_strncmp (tail_qptr, (q_list->prefix) + offset, tail_lng) == 0) 535 { 536 /* same prefix: return the current class */ 537 if (length == q_list->length) 538 return q_list; 539 540 /* found shorter common prefix */ 541 if (tmp == NULL) 542 { 543 /* add a node above q_list */ 544 tmp = (struct q_class_t *) safe_calloc (1, 545 sizeof (struct q_class_t)); 546 tmp->prefix = (char *) safe_calloc (1, length + 1); 547 strncpy (tmp->prefix, qptr, length); 548 tmp->length = length; 549 550 /* replace q_list by tmp */ 551 if (q_list->next) 552 { 553 tmp->next = q_list->next; 554 q_list->next->prev = tmp; 555 } 556 if (q_list->prev) 557 { 558 tmp->prev = q_list->prev; 559 q_list->prev->next = tmp; 560 } 561 562 /* make q_list a child of tmp */ 563 tmp->down = q_list; 564 tmp->up = q_list->up; 565 q_list->up = tmp; 566 if (tmp->up->down == q_list) 567 tmp->up->down = tmp; 568 569 /* q_list has no siblings */ 570 q_list->next = NULL; 571 q_list->prev = NULL; 572 573 index = q_list->index; 574 575 /* tmp should be the return class too */ 576 class = tmp; 577 578 /* next class to test */ 579 q_list = tmp->next; 580 } 581 else 582 { 583 /* found another branch for which tmp is a shorter prefix */ 584 585 /* save the next sibling for later */ 586 save = q_list->next; 587 588 /* unlink q_list from the top level list */ 589 if (q_list->next) 590 q_list->next->prev = q_list->prev; 591 if (q_list->prev) 592 q_list->prev->next = q_list->next; 593 594 /* at this point, we have a tmp->down; link q_list to it */ 595 ptr = tmp->down; 596 while (ptr->next) 597 ptr = ptr->next; 598 ptr->next = q_list; 599 q_list->next = NULL; 600 q_list->prev = ptr; 601 q_list->up = tmp; 602 603 index = q_list->index; 604 605 /* next class to test */ 606 q_list = save; 607 } 608 609 /* we found a shorter prefix, so we need a redraw */ 610 *force_redraw = 1; 611 continue; 612 } 613 else 614 { 615 q_list = q_list->next; 616 continue; 617 } 618 } 619 else 620 { 621 /* longer than the current prefix: try subclassing it */ 622 if (tmp == NULL && mutt_strncmp (tail_qptr, (q_list->prefix) + offset, 623 q_list->length - offset) == 0) 624 { 625 /* still a subclass: go down one level */ 626 ptr = q_list; 627 offset = q_list->length; 628 629 q_list = q_list->down; 630 tail_lng = length - offset; 631 tail_qptr = (char *) qptr + offset; 632 633 continue; 634 } 635 else 636 { 637 /* nope, try the next prefix */ 638 q_list = q_list->next; 639 continue; 640 } 641 } 642 } 643 644 /* still not found so far: add it as a sibling to the current node */ 645 if (class == NULL) 646 { 647 tmp = (struct q_class_t *) safe_calloc (1, sizeof (struct q_class_t)); 648 tmp->prefix = (char *) safe_calloc (1, length + 1); 649 strncpy (tmp->prefix, qptr, length); 650 tmp->length = length; 651 652 if (ptr->down) 653 { 654 tmp->next = ptr->down; 655 ptr->down->prev = tmp; 656 } 657 ptr->down = tmp; 658 tmp->up = ptr; 659 660 new_class_color (tmp, q_level); 661 662 return tmp; 663 } 664 else 665 { 666 if (index != -1) 667 shift_class_colors (*QuoteList, tmp, index, q_level); 668 669 return class; 670 } 671 } 672 else 673 { 674 /* nope, try the next prefix */ 675 q_list = q_list->next; 676 continue; 677 } 678 } 679 } 680 681 if (class == NULL) 682 { 683 /* not found so far: add it as a top level class */ 684 class = (struct q_class_t *) safe_calloc (1, sizeof (struct q_class_t)); 685 class->prefix = (char *) safe_calloc (1, length + 1); 686 strncpy (class->prefix, qptr, length); 687 class->length = length; 688 new_class_color (class, q_level); 689 690 if (*QuoteList) 691 { 692 class->next = *QuoteList; 693 (*QuoteList)->prev = class; 694 } 695 *QuoteList = class; 696 } 697 698 if (index != -1) 699 shift_class_colors (*QuoteList, tmp, index, q_level); 700 701 return class; 702} 703 704static int brailleLine = -1; 705static int brailleCol = -1; 706 707static int check_attachment_marker (char *); 708 709static void 710resolve_types (char *buf, char *raw, struct line_t *lineInfo, int n, int last, 711 struct q_class_t **QuoteList, int *q_level, int *force_redraw, 712 int q_classify) 713{ 714 COLOR_LINE *color_line; 715 regmatch_t pmatch[1], smatch[1]; 716 int found, offset, null_rx, i; 717 718 if (n == 0 || ISHEADER (lineInfo[n-1].type)) 719 { 720 if (buf[0] == '\n') /* end of header */ 721 { 722 lineInfo[n].type = MT_COLOR_NORMAL; 723 getyx(stdscr, brailleLine, brailleCol); 724 } 725 else 726 { 727 /* if this is a continuation of the previous line, use the previous 728 * line's color as default. */ 729 if (n > 0 && (buf[0] == ' ' || buf[0] == '\t')) 730 { 731 lineInfo[n].type = lineInfo[n-1].type; /* wrapped line */ 732 (lineInfo[n].syntax)[0].color = (lineInfo[n-1].syntax)[0].color; 733 lineInfo[n].is_cont_hdr = 1; 734 } 735 else 736 { 737 lineInfo[n].type = MT_COLOR_HDEFAULT; 738 } 739 740 for (color_line = ColorHdrList; color_line; color_line = color_line->next) 741 { 742 if (REGEXEC (color_line->rx, buf) == 0) 743 { 744 lineInfo[n].type = MT_COLOR_HEADER; 745 lineInfo[n].syntax[0].color = color_line->pair; 746 if (lineInfo[n].is_cont_hdr) 747 { 748 /* adjust the previous continuation lines to reflect the color of this continuation line */ 749 int j; 750 for (j = n - 1; j >= 0 && lineInfo[j].is_cont_hdr; --j) 751 { 752 lineInfo[j].type = lineInfo[n].type; 753 lineInfo[j].syntax[0].color = lineInfo[n].syntax[0].color; 754 } 755 /* now adjust the first line of this header field */ 756 if (j >= 0) 757 { 758 lineInfo[j].type = lineInfo[n].type; 759 lineInfo[j].syntax[0].color = lineInfo[n].syntax[0].color; 760 } 761 *force_redraw = 1; /* the previous lines have already been drawn on the screen */ 762 } 763 break; 764 } 765 } 766 } 767 } 768 else if (mutt_strncmp ("\033[0m", raw, 4) == 0) /* a little hack... */ 769 lineInfo[n].type = MT_COLOR_NORMAL; 770#if 0 771 else if (mutt_strncmp ("[-- ", buf, 4) == 0) 772 lineInfo[n].type = MT_COLOR_ATTACHMENT; 773#else 774 else if (check_attachment_marker ((char *) raw) == 0) 775 lineInfo[n].type = MT_COLOR_ATTACHMENT; 776#endif 777 else if (mutt_strcmp ("-- \n", buf) == 0 || mutt_strcmp ("-- \r\n", buf) == 0) 778 { 779 i = n + 1; 780 781 lineInfo[n].type = MT_COLOR_SIGNATURE; 782 while (i < last && check_sig (buf, lineInfo, i - 1) == 0 && 783 (lineInfo[i].type == MT_COLOR_NORMAL || 784 lineInfo[i].type == MT_COLOR_QUOTED || 785 lineInfo[i].type == MT_COLOR_HEADER)) 786 { 787 /* oops... */ 788 if (lineInfo[i].chunks) 789 { 790 lineInfo[i].chunks = 0; 791 safe_realloc (&(lineInfo[n].syntax), 792 sizeof (struct syntax_t)); 793 } 794 lineInfo[i++].type = MT_COLOR_SIGNATURE; 795 } 796 } 797 else if (check_sig (buf, lineInfo, n - 1) == 0) 798 lineInfo[n].type = MT_COLOR_SIGNATURE; 799 else if (regexec ((regex_t *) QuoteRegexp.rx, buf, 1, pmatch, 0) == 0) 800 { 801 if (regexec ((regex_t *) Smileys.rx, buf, 1, smatch, 0) == 0) 802 { 803 if (smatch[0].rm_so > 0) 804 { 805 char c; 806 807 /* hack to avoid making an extra copy of buf */ 808 c = buf[smatch[0].rm_so]; 809 buf[smatch[0].rm_so] = 0; 810 811 if (regexec ((regex_t *) QuoteRegexp.rx, buf, 1, pmatch, 0) == 0) 812 { 813 if (q_classify && lineInfo[n].quote == NULL) 814 lineInfo[n].quote = classify_quote (QuoteList, 815 buf + pmatch[0].rm_so, 816 pmatch[0].rm_eo - pmatch[0].rm_so, 817 force_redraw, q_level); 818 lineInfo[n].type = MT_COLOR_QUOTED; 819 } 820 else 821 lineInfo[n].type = MT_COLOR_NORMAL; 822 823 buf[smatch[0].rm_so] = c; 824 } 825 else 826 lineInfo[n].type = MT_COLOR_NORMAL; 827 } 828 else 829 { 830 if (q_classify && lineInfo[n].quote == NULL) 831 lineInfo[n].quote = classify_quote (QuoteList, buf + pmatch[0].rm_so, 832 pmatch[0].rm_eo - pmatch[0].rm_so, 833 force_redraw, q_level); 834 lineInfo[n].type = MT_COLOR_QUOTED; 835 } 836 } 837 else 838 lineInfo[n].type = MT_COLOR_NORMAL; 839 840 /* body patterns */ 841 if (lineInfo[n].type == MT_COLOR_NORMAL || 842 lineInfo[n].type == MT_COLOR_QUOTED) 843 { 844 size_t nl; 845 846 /* don't consider line endings part of the buffer 847 * for regex matching */ 848 if ((nl = mutt_strlen (buf)) > 0 && buf[nl-1] == '\n') 849 buf[nl-1] = 0; 850 851 i = 0; 852 offset = 0; 853 lineInfo[n].chunks = 0; 854 do 855 { 856 if (!buf[offset]) 857 break; 858 859 found = 0; 860 null_rx = 0; 861 color_line = ColorBodyList; 862 while (color_line) 863 { 864 if (regexec (&color_line->rx, buf + offset, 1, pmatch, 865 (offset ? REG_NOTBOL : 0)) == 0) 866 { 867 if (pmatch[0].rm_eo != pmatch[0].rm_so) 868 { 869 if (!found) 870 { 871 /* Abort if we fill up chunks. 872 * Yes, this really happened. See #3888 */ 873 if (lineInfo[n].chunks == SHRT_MAX) 874 { 875 null_rx = 0; 876 break; 877 } 878 if (++(lineInfo[n].chunks) > 1) 879 safe_realloc (&(lineInfo[n].syntax), 880 (lineInfo[n].chunks) * sizeof (struct syntax_t)); 881 } 882 i = lineInfo[n].chunks - 1; 883 pmatch[0].rm_so += offset; 884 pmatch[0].rm_eo += offset; 885 if (!found || 886 pmatch[0].rm_so < (lineInfo[n].syntax)[i].first || 887 (pmatch[0].rm_so == (lineInfo[n].syntax)[i].first && 888 pmatch[0].rm_eo > (lineInfo[n].syntax)[i].last)) 889 { 890 (lineInfo[n].syntax)[i].color = color_line->pair; 891 (lineInfo[n].syntax)[i].first = pmatch[0].rm_so; 892 (lineInfo[n].syntax)[i].last = pmatch[0].rm_eo; 893 } 894 found = 1; 895 null_rx = 0; 896 } 897 else 898 null_rx = 1; /* empty regexp; don't add it, but keep looking */ 899 } 900 color_line = color_line->next; 901 } 902 903 if (null_rx) 904 offset++; /* avoid degenerate cases */ 905 else 906 offset = (lineInfo[n].syntax)[i].last; 907 } while (found || null_rx); 908 if (nl > 0) 909 buf[nl] = '\n'; 910 } 911} 912 913static int is_ansi (unsigned char *buf) 914{ 915 while (*buf && (isdigit(*buf) || *buf == ';')) 916 buf++; 917 return (*buf == 'm'); 918} 919 920static int check_attachment_marker (char *p) 921{ 922 char *q = AttachmentMarker; 923 924 for (;*p == *q && *q && *p && *q != '\a' && *p != '\a'; p++, q++) 925 ; 926 return (int) (*p - *q); 927} 928 929static int grok_ansi(unsigned char *buf, int pos, ansi_attr *a) 930{ 931 int x = pos; 932 933 while (isdigit(buf[x]) || buf[x] == ';') 934 x++; 935 936 /* Character Attributes */ 937 if (option (OPTALLOWANSI) && a != NULL && buf[x] == 'm') 938 { 939 if (pos == x) 940 { 941#ifdef HAVE_COLOR 942 if (a->pair != -1) 943 mutt_free_color (a->fg, a->bg); 944#endif 945 a->attr = ANSI_OFF; 946 a->pair = -1; 947 } 948 while (pos < x) 949 { 950 if (buf[pos] == '1' && (pos+1 == x || buf[pos+1] == ';')) 951 { 952 a->attr |= ANSI_BOLD; 953 pos += 2; 954 } 955 else if (buf[pos] == '4' && (pos+1 == x || buf[pos+1] == ';')) 956 { 957 a->attr |= ANSI_UNDERLINE; 958 pos += 2; 959 } 960 else if (buf[pos] == '5' && (pos+1 == x || buf[pos+1] == ';')) 961 { 962 a->attr |= ANSI_BLINK; 963 pos += 2; 964 } 965 else if (buf[pos] == '7' && (pos+1 == x || buf[pos+1] == ';')) 966 { 967 a->attr |= ANSI_REVERSE; 968 pos += 2; 969 } 970 else if (buf[pos] == '0' && (pos+1 == x || buf[pos+1] == ';')) 971 { 972#ifdef HAVE_COLOR 973 if (a->pair != -1) 974 mutt_free_color(a->fg,a->bg); 975#endif 976 a->attr = ANSI_OFF; 977 a->pair = -1; 978 pos += 2; 979 } 980 else if (buf[pos] == '3' && isdigit(buf[pos+1])) 981 { 982#ifdef HAVE_COLOR 983 if (a->pair != -1) 984 mutt_free_color(a->fg,a->bg); 985#endif 986 a->pair = -1; 987 a->attr |= ANSI_COLOR; 988 a->fg = buf[pos+1] - '0'; 989 pos += 3; 990 } 991 else if (buf[pos] == '4' && isdigit(buf[pos+1])) 992 { 993#ifdef HAVE_COLOR 994 if (a->pair != -1) 995 mutt_free_color(a->fg,a->bg); 996#endif 997 a->pair = -1; 998 a->attr |= ANSI_COLOR; 999 a->bg = buf[pos+1] - '0'; 1000 pos += 3; 1001 } 1002 else 1003 { 1004 while (pos < x && buf[pos] != ';') pos++; 1005 pos++; 1006 } 1007 } 1008 } 1009 pos = x; 1010 return pos; 1011} 1012 1013/* trim tail of buf so that it contains complete multibyte characters */ 1014static int 1015trim_incomplete_mbyte(unsigned char *buf, size_t len) 1016{ 1017 mbstate_t mbstate; 1018 size_t k; 1019 1020 memset (&mbstate, 0, sizeof (mbstate)); 1021 for (; len > 0; buf += k, len -= k) 1022 { 1023 k = mbrtowc (NULL, (char *) buf, len, &mbstate); 1024 if (k == (size_t)(-2)) 1025 break; 1026 else if (k == (size_t)(-1) || k == 0) 1027 { 1028 if (k == (size_t)(-1)) 1029 memset (&mbstate, 0, sizeof (mbstate)); 1030 k = 1; 1031 } 1032 } 1033 *buf = '\0'; 1034 1035 return len; 1036} 1037 1038static int 1039fill_buffer (FILE *f, LOFF_T *last_pos, LOFF_T offset, unsigned char **buf, 1040 unsigned char **fmt, size_t *blen, int *buf_ready) 1041{ 1042 unsigned char *p, *q; 1043 static int b_read; 1044 int l; 1045 1046 if (*buf_ready == 0) 1047 { 1048 if (offset != *last_pos) 1049 fseeko (f, offset, 0); 1050 if ((*buf = (unsigned char *) mutt_read_line ((char *) *buf, blen, f, &l, MUTT_EOL)) == NULL) 1051 { 1052 fmt[0] = 0; 1053 return (-1); 1054 } 1055 *last_pos = ftello (f); 1056 b_read = (int) (*last_pos - offset); 1057 *buf_ready = 1; 1058 1059 safe_realloc (fmt, *blen); 1060 1061 /* incomplete mbyte characters trigger a segfault in regex processing for 1062 * certain versions of glibc. Trim them if necessary. */ 1063 if (b_read == *blen - 2) 1064 b_read -= trim_incomplete_mbyte(*buf, b_read); 1065 1066 /* copy "buf" to "fmt", but without bold and underline controls */ 1067 p = *buf; 1068 q = *fmt; 1069 while (*p) 1070 { 1071 if (*p == '\010' && (p > *buf)) 1072 { 1073 if (*(p+1) == '_') /* underline */ 1074 p += 2; 1075 else if (*(p+1) && q > *fmt) /* bold or overstrike */ 1076 { 1077 *(q-1) = *(p+1); 1078 p += 2; 1079 } 1080 else /* ^H */ 1081 *q++ = *p++; 1082 } 1083 else if (*p == '\033' && *(p+1) == '[' && is_ansi (p + 2)) 1084 { 1085 while (*p++ != 'm') /* skip ANSI sequence */ 1086 ; 1087 } 1088 else if (*p == '\033' && *(p+1) == ']' && check_attachment_marker ((char *) p) == 0) 1089 { 1090 dprint (2, (debugfile, "fill_buffer: Seen attachment marker.\n")); 1091 while (*p++ != '\a') /* skip pseudo-ANSI sequence */ 1092 ; 1093 } 1094 else 1095 *q++ = *p++; 1096 } 1097 *q = 0; 1098 } 1099 return b_read; 1100} 1101 1102 1103static int format_line (struct line_t **lineInfo, int n, unsigned char *buf, 1104 int flags, ansi_attr *pa, int cnt, 1105 int *pspace, int *pvch, int *pcol, int *pspecial, 1106 mutt_window_t *pager_window) 1107{ 1108 int space = -1; /* index of the last space or TAB */ 1109 int col = option (OPTMARKERS) ? (*lineInfo)[n].continuation : 0; 1110 size_t k; 1111 int ch, vch, last_special = -1, special = 0, t; 1112 wchar_t wc; 1113 mbstate_t mbstate; 1114 int wrap_cols = mutt_window_wrap_cols (pager_window, (flags & MUTT_PAGER_NOWRAP) ? 0 : Wrap); 1115 1116 if (check_attachment_marker ((char *)buf) == 0) 1117 wrap_cols = pager_window->cols; 1118 1119 /* FIXME: this should come from lineInfo */ 1120 memset(&mbstate, 0, sizeof(mbstate)); 1121 1122 for (ch = 0, vch = 0; ch < cnt; ch += k, vch += k) 1123 { 1124 /* Handle ANSI sequences */ 1125 while (cnt-ch >= 2 && buf[ch] == '\033' && buf[ch+1] == '[' && 1126 is_ansi (buf+ch+2)) 1127 ch = grok_ansi (buf, ch+2, pa) + 1; 1128 1129 while (cnt-ch >= 2 && buf[ch] == '\033' && buf[ch+1] == ']' && 1130 check_attachment_marker ((char *) buf+ch) == 0) 1131 { 1132 while (buf[ch++] != '\a') 1133 if (ch >= cnt) 1134 break; 1135 } 1136 1137 /* is anything left to do? */ 1138 if (ch >= cnt) 1139 break; 1140 1141 k = mbrtowc (&wc, (char *)buf+ch, cnt-ch, &mbstate); 1142 if (k == (size_t)(-2) || k == (size_t)(-1)) 1143 { 1144 if (k == (size_t)(-1)) 1145 memset(&mbstate, 0, sizeof(mbstate)); 1146 dprint (1, (debugfile, "%s:%d: mbrtowc returned %d; errno = %d.\n", 1147 __FILE__, __LINE__, k, errno)); 1148 if (col + 4 > wrap_cols) 1149 break; 1150 col += 4; 1151 if (pa) 1152 printw ("\\%03o", buf[ch]); 1153 k = 1; 1154 continue; 1155 } 1156 if (k == 0) 1157 k = 1; 1158 1159 if (Charset_is_utf8) 1160 { 1161 if (wc == 0x200B || wc == 0xFEFF) 1162 { 1163 dprint (3, (debugfile, "skip zero-width character U+%04X\n", (unsigned short)wc)); 1164 continue; 1165 } 1166 if (is_display_corrupting_utf8 (wc)) 1167 { 1168 dprint (3, (debugfile, "filtered U+%04X\n", (unsigned short)wc)); 1169 continue; 1170 } 1171 } 1172 1173 /* Handle backspace */ 1174 special = 0; 1175 if (IsWPrint (wc)) 1176 { 1177 wchar_t wc1; 1178 mbstate_t mbstate1; 1179 size_t k1, k2; 1180 1181 mbstate1 = mbstate; 1182 k1 = mbrtowc (&wc1, (char *)buf+ch+k, cnt-ch-k, &mbstate1); 1183 while ((k1 != (size_t)(-2)) && (k1 != (size_t)(-1)) && 1184 (k1 > 0) && (wc1 == '\b')) 1185 { 1186 k2 = mbrtowc (&wc1, (char *)buf+ch+k+k1, cnt-ch-k-k1, &mbstate1); 1187 if ((k2 == (size_t)(-2)) || (k2 == (size_t)(-1)) || 1188 (k2 == 0) || (!IsWPrint (wc1))) 1189 break; 1190 1191 if (wc == wc1) 1192 { 1193 special |= (wc == '_' && special & A_UNDERLINE) 1194 ? A_UNDERLINE : A_BOLD; 1195 } 1196 else if (wc == '_' || wc1 == '_') 1197 { 1198 special |= A_UNDERLINE; 1199 wc = (wc1 == '_') ? wc : wc1; 1200 } 1201 else 1202 { 1203 /* special = 0; / * overstrike: nothing to do! */ 1204 wc = wc1; 1205 } 1206 1207 ch += k + k1; 1208 k = k2; 1209 mbstate = mbstate1; 1210 k1 = mbrtowc (&wc1, (char *)buf+ch+k, cnt-ch-k, &mbstate1); 1211 } 1212 } 1213 1214 if (pa && 1215 ((flags & (MUTT_SHOWCOLOR | MUTT_SEARCH | MUTT_PAGER_MARKER)) || 1216 special || last_special || pa->attr)) 1217 { 1218 resolve_color (*lineInfo, n, vch, flags, special, pa); 1219 last_special = special; 1220 } 1221 1222 if (IsWPrint (wc) || (Charset_is_utf8 && wc == 0x00A0)) 1223 { 1224 if (wc == ' ') 1225 space = ch; 1226 else if (Charset_is_utf8 && wc == 0x00A0) 1227 { 1228 /* Convert non-breaking space to normal space. The local variable 1229 * `space' is not set here so that the caller of this function won't 1230 * attempt to wrap at this character. */ 1231 wc = ' '; 1232 } 1233 t = wcwidth (wc); 1234 if (col + t > wrap_cols) 1235 break; 1236 col += t; 1237 if (pa) 1238 mutt_addwch (wc); 1239 } 1240 else if (wc == '\n') 1241 break; 1242 else if (wc == '\t') 1243 { 1244 space = ch; 1245 t = (col & ~7) + 8; 1246 if (t > wrap_cols) 1247 break; 1248 if (pa) 1249 for (; col < t; col++) 1250 addch (' '); 1251 else 1252 col = t; 1253 } 1254 else if (wc < 0x20 || wc == 0x7f) 1255 { 1256 if (col + 2 > wrap_cols) 1257 break; 1258 col += 2; 1259 if (pa) 1260 printw ("^%c", ('@' + wc) & 0x7f); 1261 } 1262 else if (wc < 0x100) 1263 { 1264 if (col + 4 > wrap_cols) 1265 break; 1266 col += 4; 1267 if (pa) 1268 printw ("\\%03o", wc); 1269 } 1270 else 1271 { 1272 if (col + 1 > wrap_cols) 1273 break; 1274 ++col; 1275 if (pa) 1276 addch (replacement_char ()); 1277 } 1278 } 1279 *pspace = space; 1280 *pcol = col; 1281 *pvch = vch; 1282 *pspecial = special; 1283 return ch; 1284} 1285 1286/* 1287 * Args: 1288 * flags MUTT_SHOWFLAT, show characters (used for displaying help) 1289 * MUTT_SHOWCOLOR, show characters in color 1290 * otherwise don't show characters 1291 * MUTT_HIDE, don't show quoted text 1292 * MUTT_SEARCH, resolve search patterns 1293 * MUTT_TYPES, compute line's type 1294 * MUTT_PAGER_NSKIP, keeps leading whitespace 1295 * MUTT_PAGER_MARKER, eventually show markers 1296 * 1297 * Return values: 1298 * -1 EOF was reached 1299 * 0 normal exit, line was not displayed 1300 * >0 normal exit, line was displayed 1301 */ 1302 1303static int 1304display_line (FILE *f, LOFF_T *last_pos, struct line_t **lineInfo, int n, 1305 int *last, int *max, int flags, struct q_class_t **QuoteList, 1306 int *q_level, int *force_redraw, regex_t *SearchRE, 1307 mutt_window_t *pager_window) 1308{ 1309 unsigned char *buf = NULL, *fmt = NULL; 1310 size_t buflen = 0; 1311 unsigned char *buf_ptr = buf; 1312 int ch, vch, col, cnt, b_read; 1313 int buf_ready = 0, change_last = 0; 1314 int special; 1315 int offset; 1316 int def_color; 1317 int m; 1318 int rc = -1; 1319 ansi_attr a = {0,0,0,-1}; 1320 regmatch_t pmatch[1]; 1321 1322 if (n == *last) 1323 { 1324 (*last)++; 1325 change_last = 1; 1326 } 1327 1328 if (*last == *max) 1329 { 1330 safe_realloc (lineInfo, sizeof (struct line_t) * (*max += LINES)); 1331 for (ch = *last; ch < *max ; ch++) 1332 { 1333 memset (&((*lineInfo)[ch]), 0, sizeof (struct line_t)); 1334 (*lineInfo)[ch].type = -1; 1335 (*lineInfo)[ch].search_cnt = -1; 1336 (*lineInfo)[ch].syntax = safe_malloc (sizeof (struct syntax_t)); 1337 ((*lineInfo)[ch].syntax)[0].first = ((*lineInfo)[ch].syntax)[0].last = -1; 1338 } 1339 } 1340 1341 /* only do color hiliting if we are viewing a message */ 1342 if (flags & (MUTT_SHOWCOLOR | MUTT_TYPES)) 1343 { 1344 if ((*lineInfo)[n].type == -1) 1345 { 1346 /* determine the line class */ 1347 if (fill_buffer (f, last_pos, (*lineInfo)[n].offset, &buf, &fmt, &buflen, &buf_ready) < 0) 1348 { 1349 if (change_last) 1350 (*last)--; 1351 goto out; 1352 } 1353 1354 resolve_types ((char *) fmt, (char *) buf, *lineInfo, n, *last, 1355 QuoteList, q_level, force_redraw, flags & MUTT_SHOWCOLOR); 1356 1357 /* avoid race condition for continuation lines when scrolling up */ 1358 for (m = n + 1; m < *last && (*lineInfo)[m].offset && (*lineInfo)[m].continuation; m++) 1359 (*lineInfo)[m].type = (*lineInfo)[n].type; 1360 } 1361 1362 /* this also prevents searching through the hidden lines */ 1363 if ((flags & MUTT_HIDE) && (*lineInfo)[n].type == MT_COLOR_QUOTED) 1364 flags = 0; /* MUTT_NOSHOW */ 1365 } 1366 1367 /* At this point, (*lineInfo[n]).quote may still be undefined. We 1368 * don't want to compute it every time MUTT_TYPES is set, since this 1369 * would slow down the "bottom" function unacceptably. A compromise 1370 * solution is hence to call regexec() again, just to find out the 1371 * length of the quote prefix. 1372 */ 1373 if ((flags & MUTT_SHOWCOLOR) && !(*lineInfo)[n].continuation && 1374 (*lineInfo)[n].type == MT_COLOR_QUOTED && (*lineInfo)[n].quote == NULL) 1375 { 1376 if (fill_buffer (f, last_pos, (*lineInfo)[n].offset, &buf, &fmt, &buflen, &buf_ready) < 0) 1377 { 1378 if (change_last) 1379 (*last)--; 1380 goto out; 1381 } 1382 regexec ((regex_t *) QuoteRegexp.rx, (char *) fmt, 1, pmatch, 0); 1383 (*lineInfo)[n].quote = classify_quote (QuoteList, 1384 (char *) fmt + pmatch[0].rm_so, 1385 pmatch[0].rm_eo - pmatch[0].rm_so, 1386 force_redraw, q_level); 1387 } 1388 1389 if ((flags & MUTT_SEARCH) && !(*lineInfo)[n].continuation && (*lineInfo)[n].search_cnt == -1) 1390 { 1391 if (fill_buffer (f, last_pos, (*lineInfo)[n].offset, &buf, &fmt, &buflen, &buf_ready) < 0) 1392 { 1393 if (change_last) 1394 (*last)--; 1395 goto out; 1396 } 1397 1398 offset = 0; 1399 (*lineInfo)[n].search_cnt = 0; 1400 while (regexec (SearchRE, (char *) fmt + offset, 1, pmatch, (offset ? REG_NOTBOL : 0)) == 0) 1401 { 1402 if (++((*lineInfo)[n].search_cnt) > 1) 1403 safe_realloc (&((*lineInfo)[n].search), 1404 ((*lineInfo)[n].search_cnt) * sizeof (struct syntax_t)); 1405 else 1406 (*lineInfo)[n].search = safe_malloc (sizeof (struct syntax_t)); 1407 pmatch[0].rm_so += offset; 1408 pmatch[0].rm_eo += offset; 1409 ((*lineInfo)[n].search)[(*lineInfo)[n].search_cnt - 1].first = pmatch[0].rm_so; 1410 ((*lineInfo)[n].search)[(*lineInfo)[n].search_cnt - 1].last = pmatch[0].rm_eo; 1411 1412 if (pmatch[0].rm_eo == pmatch[0].rm_so) 1413 offset++; /* avoid degenerate cases */ 1414 else 1415 offset = pmatch[0].rm_eo; 1416 if (!fmt[offset]) 1417 break; 1418 } 1419 } 1420 1421 if (!(flags & MUTT_SHOW) && (*lineInfo)[n+1].offset > 0) 1422 { 1423 /* we've already scanned this line, so just exit */ 1424 rc = 0; 1425 goto out; 1426 } 1427 if ((flags & MUTT_SHOWCOLOR) && *force_redraw && (*lineInfo)[n+1].offset > 0) 1428 { 1429 /* no need to try to display this line... */ 1430 rc = 1; 1431 goto out; /* fake display */ 1432 } 1433 1434 if ((b_read = fill_buffer (f, last_pos, (*lineInfo)[n].offset, &buf, &fmt, 1435 &buflen, &buf_ready)) < 0) 1436 { 1437 if (change_last) 1438 (*last)--; 1439 goto out; 1440 } 1441 1442 /* now chose a good place to break the line */ 1443 cnt = format_line (lineInfo, n, buf, flags, 0, b_read, &ch, &vch, &col, &special, 1444 pager_window); 1445 buf_ptr = buf + cnt; 1446 1447 /* move the break point only if smart_wrap is set */ 1448 if (option (OPTWRAP)) 1449 { 1450 if (cnt < b_read) 1451 { 1452 if (ch != -1 && buf[0] != ' ' && buf[0] != '\t' && 1453 buf[cnt] != ' ' && buf[cnt] != '\t' && buf[cnt] != '\n' && buf[cnt] != '\r') 1454 { 1455 buf_ptr = buf + ch; 1456 /* skip trailing blanks */ 1457 while (ch && (buf[ch] == ' ' || buf[ch] == '\t' || buf[ch] == '\r')) 1458 ch--; 1459 /* a very long word with leading spaces causes infinite wrapping */ 1460 if ((!ch) && (flags & MUTT_PAGER_NSKIP)) 1461 buf_ptr = buf + cnt; 1462 else 1463 cnt = ch + 1; 1464 } 1465 else 1466 buf_ptr = buf + cnt; /* a very long word... */ 1467 } 1468 if (!(flags & MUTT_PAGER_NSKIP)) 1469 /* skip leading blanks on the next line too */ 1470 while (*buf_ptr == ' ' || *buf_ptr == '\t') 1471 buf_ptr++; 1472 } 1473 1474 if (*buf_ptr == '\r') 1475 buf_ptr++; 1476 if (*buf_ptr == '\n') 1477 buf_ptr++; 1478 1479 if ((int) (buf_ptr - buf) < b_read && !(*lineInfo)[n+1].continuation) 1480 append_line (*lineInfo, n, (int) (buf_ptr - buf)); 1481 (*lineInfo)[n+1].offset = (*lineInfo)[n].offset + (long) (buf_ptr - buf); 1482 1483 /* if we don't need to display the line we are done */ 1484 if (!(flags & MUTT_SHOW)) 1485 { 1486 rc = 0; 1487 goto out; 1488 } 1489 1490 /* display the line */ 1491 format_line (lineInfo, n, buf, flags, &a, cnt, &ch, &vch, &col, &special, 1492 pager_window); 1493 1494 /* avoid a bug in ncurses... */ 1495#ifndef USE_SLANG_CURSES 1496 if (col == 0) 1497 { 1498 NORMAL_COLOR; 1499 addch (' '); 1500 } 1501#endif 1502 1503 /* end the last color pattern (needed by S-Lang) */ 1504 if (special || (col != pager_window->cols && (flags & (MUTT_SHOWCOLOR | MUTT_SEARCH)))) 1505 resolve_color (*lineInfo, n, vch, flags, 0, &a); 1506 1507 /* 1508 * Fill the blank space at the end of the line with the prevailing color. 1509 * ncurses does an implicit clrtoeol() when you do addch('\n') so we have 1510 * to make sure to reset the color *after* that 1511 */ 1512 if (flags & MUTT_SHOWCOLOR) 1513 { 1514 m = ((*lineInfo)[n].continuation) ? ((*lineInfo)[n].syntax)[0].first : n; 1515 if ((*lineInfo)[m].type == MT_COLOR_HEADER) 1516 def_color = ((*lineInfo)[m].syntax)[0].color; 1517 else 1518 def_color = ColorDefs[ (*lineInfo)[m].type ]; 1519 1520 ATTRSET(def_color); 1521 } 1522 1523 if (col < pager_window->cols) 1524 mutt_window_clrtoeol (pager_window); 1525 1526 /* 1527 * reset the color back to normal. This *must* come after the 1528 * clrtoeol, otherwise the color for this line will not be 1529 * filled to the right margin. 1530 */ 1531 if (flags & MUTT_SHOWCOLOR) 1532 NORMAL_COLOR; 1533 1534 /* build a return code */ 1535 if (!(flags & MUTT_SHOW)) 1536 flags = 0; 1537 1538 rc = flags; 1539 1540out: 1541 FREE(&buf); 1542 FREE(&fmt); 1543 return rc; 1544} 1545 1546static int 1547upNLines (int nlines, struct line_t *info, int cur, int hiding) 1548{ 1549 while (cur > 0 && nlines > 0) 1550 { 1551 cur--; 1552 if (!hiding || info[cur].type != MT_COLOR_QUOTED) 1553 nlines--; 1554 } 1555 1556 return cur; 1557} 1558 1559static const struct mapping_t PagerHelp[] = { 1560 { N_("Exit"), OP_EXIT }, 1561 { N_("PrevPg"), OP_PREV_PAGE }, 1562 { N_("NextPg"), OP_NEXT_PAGE }, 1563 { NULL, 0 } 1564}; 1565static const struct mapping_t PagerHelpExtra[] = { 1566 { N_("View Attachm."), OP_VIEW_ATTACHMENTS }, 1567 { N_("Del"), OP_DELETE }, 1568 { N_("Reply"), OP_REPLY }, 1569 { N_("Next"), OP_MAIN_NEXT_UNDELETED }, 1570 { NULL, 0 } 1571}; 1572 1573void mutt_clear_pager_position (void) 1574{ 1575 TopLine = 0; 1576 OldHdr = NULL; 1577} 1578 1579 1580/* This pager is actually not so simple as it once was. It now operates in 1581 two modes: one for viewing messages and the other for viewing help. These 1582 can be distinguished by whether or not ``hdr'' is NULL. The ``hdr'' arg 1583 is there so that we can do operations on the current message without the 1584 need to pop back out to the main-menu. */ 1585int 1586mutt_pager (const char *banner, const char *fname, int flags, pager_t *extra) 1587{ 1588 static char searchbuf[STRING] = ""; 1589 char buffer[LONG_STRING]; 1590 char helpstr[SHORT_STRING*2]; 1591 char tmphelp[SHORT_STRING*2]; 1592 int maxLine, lastLine = 0; 1593 struct line_t *lineInfo; 1594 struct q_class_t *QuoteList = NULL; 1595 int i, j, ch = 0, rc = -1, hideQuoted = 0, q_level = 0, force_redraw = 0; 1596 int lines = 0, curline = 0, topline = 0, oldtopline = 0, err, first = 1; 1597 int r = -1, wrapped = 0, searchctx = 0; 1598 int redraw = REDRAW_FULL; 1599 FILE *fp = NULL; 1600 LOFF_T last_pos = 0, last_offset = 0; 1601 int old_smart_wrap, old_markers; 1602 struct stat sb; 1603 regex_t SearchRE; 1604 int SearchCompiled = 0, SearchFlag = 0, SearchBack = 0; 1605 int has_types = (IsHeader(extra) || (flags & MUTT_SHOWCOLOR)) ? MUTT_TYPES : 0; /* main message or rfc822 attachment */ 1606 1607 mutt_window_t *index_status_window = NULL; 1608 mutt_window_t *index_window = NULL; 1609 mutt_window_t *pager_status_window = NULL; 1610 mutt_window_t *pager_window = NULL; 1611 1612 MUTTMENU *index = NULL; /* the Pager Index (PI) */ 1613 int indexlen = PagerIndexLines; /* indexlen not always == PIL */ 1614 int indicator = indexlen / 3; /* the indicator line of the PI */ 1615 int old_PagerIndexLines; /* some people want to resize it 1616 * while inside the pager... */ 1617 1618 if (!(flags & MUTT_SHOWCOLOR)) 1619 flags |= MUTT_SHOWFLAT; 1620 1621 if ((fp = fopen (fname, "r")) == NULL) 1622 { 1623 mutt_perror (fname); 1624 return (-1); 1625 } 1626 1627 if (stat (fname, &sb) != 0) 1628 { 1629 mutt_perror (fname); 1630 safe_fclose (&fp); 1631 return (-1); 1632 } 1633 unlink (fname); 1634 1635 /* Initialize variables */ 1636 1637 if (IsHeader (extra) && !extra->hdr->read) 1638 { 1639 Context->msgnotreadyet = extra->hdr->msgno; 1640 mutt_set_flag (Context, extra->hdr, MUTT_READ, 1); 1641 } 1642 1643 lineInfo = safe_malloc (sizeof (struct line_t) * (maxLine = LINES)); 1644 for (i = 0 ; i < maxLine ; i++) 1645 { 1646 memset (&lineInfo[i], 0, sizeof (struct line_t)); 1647 lineInfo[i].type = -1; 1648 lineInfo[i].search_cnt = -1; 1649 lineInfo[i].syntax = safe_malloc (sizeof (struct syntax_t)); 1650 (lineInfo[i].syntax)[0].first = (lineInfo[i].syntax)[0].last = -1; 1651 } 1652 1653 mutt_compile_help (helpstr, sizeof (helpstr), MENU_PAGER, PagerHelp); 1654 if (IsHeader (extra)) 1655 { 1656 strfcpy (tmphelp, helpstr, sizeof (tmphelp)); 1657 mutt_compile_help (buffer, sizeof (buffer), MENU_PAGER, PagerHelpExtra); 1658 snprintf (helpstr, sizeof (helpstr), "%s %s", tmphelp, buffer); 1659 } 1660 if (!InHelp) 1661 { 1662 strfcpy (tmphelp, helpstr, sizeof (tmphelp)); 1663 mutt_make_help (buffer, sizeof (buffer), _("Help"), MENU_PAGER, OP_HELP); 1664 snprintf (helpstr, sizeof (helpstr), "%s %s", tmphelp, buffer); 1665 } 1666 1667 index_status_window = safe_calloc (sizeof (mutt_window_t), 1); 1668 index_window = safe_calloc (sizeof (mutt_window_t), 1); 1669 pager_status_window = safe_calloc (sizeof (mutt_window_t), 1); 1670 pager_window = safe_calloc (sizeof (mutt_window_t), 1); 1671 1672 while (ch != -1) 1673 { 1674 mutt_curs_set (0); 1675 1676 if (redraw & REDRAW_FULL) 1677 { 1678#if ! (defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)) 1679 mutt_reflow_windows (); 1680#endif 1681 NORMAL_COLOR; 1682 /* clear() doesn't optimize screen redraws */ 1683 move (0, 0); 1684 clrtobot (); 1685 1686 if (IsHeader (extra) && Context->vcount + 1 < PagerIndexLines) 1687 indexlen = Context->vcount + 1; 1688 else 1689 indexlen = PagerIndexLines; 1690 1691 indicator = indexlen / 3; 1692 1693 memcpy (pager_window, MuttIndexWindow, sizeof(mutt_window_t)); 1694 memcpy (pager_status_window, MuttStatusWindow, sizeof(mutt_window_t)); 1695 index_status_window->rows = index_window->rows = 0; 1696 1697 if (IsHeader (extra) && PagerIndexLines) 1698 { 1699 memcpy (index_window, MuttIndexWindow, sizeof(mutt_window_t)); 1700 index_window->rows = indexlen > 0 ? indexlen - 1 : 0; 1701 1702 if (option (OPTSTATUSONTOP)) 1703 { 1704 memcpy (index_status_window, MuttStatusWindow, sizeof(mutt_window_t)); 1705 1706 memcpy (pager_status_window, MuttIndexWindow, sizeof(mutt_window_t)); 1707 pager_status_window->rows = 1; 1708 pager_status_window->row_offset += index_window->rows; 1709 1710 pager_window->rows -= index_window->rows + pager_status_window->rows; 1711 pager_window->row_offset += index_window->rows + pager_status_window->rows; 1712 } 1713 else 1714 { 1715 memcpy (index_status_window, MuttIndexWindow, sizeof(mutt_window_t)); 1716 index_status_window->rows = 1; 1717 index_status_window->row_offset += index_window->rows; 1718 1719 pager_window->rows -= index_window->rows + index_status_window->rows; 1720 pager_window->row_offset += index_window->rows + index_status_window->rows; 1721 } 1722 } 1723 1724 if (option (OPTHELP)) 1725 { 1726 SETCOLOR (MT_COLOR_STATUS); 1727 mutt_window_move (MuttHelpWindow, 0, 0); 1728 mutt_paddstr (MuttHelpWindow->cols, helpstr); 1729 NORMAL_COLOR; 1730 } 1731 1732#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM) 1733 if (Resize != NULL) 1734 { 1735 if ((SearchCompiled = Resize->SearchCompiled)) 1736 { 1737 REGCOMP 1738 (&SearchRE, searchbuf, REG_NEWLINE | mutt_which_case (searchbuf)); 1739 SearchFlag = MUTT_SEARCH; 1740 SearchBack = Resize->SearchBack; 1741 } 1742 lines = Resize->line; 1743 redraw |= REDRAW_SIGWINCH; 1744 1745 FREE (&Resize); 1746 } 1747#endif 1748 1749 if (IsHeader (extra) && PagerIndexLines) 1750 { 1751 if (index == NULL) 1752 { 1753 /* only allocate the space if/when we need the index. 1754 Initialise the menu as per the main index */ 1755 index = mutt_new_menu(MENU_MAIN); 1756 index->make_entry = index_make_entry; 1757 index->color = index_color; 1758 index->max = Context->vcount; 1759 index->current = extra->hdr->virtual; 1760 index->indexwin = index_window; 1761 index->statuswin = index_status_window; 1762 } 1763 1764 NORMAL_COLOR; 1765 index->pagelen = index_window->rows;; 1766 1767 /* some fudge to work out where abouts the indicator should go */ 1768 if (index->current - indicator < 0) 1769 index->top = 0; 1770 else if (index->max - index->current < index->pagelen - indicator) 1771 index->top = index->max - index->pagelen; 1772 else 1773 index->top = index->current - indicator; 1774 1775 menu_redraw_index(index); 1776 } 1777 1778 redraw |= REDRAW_BODY | REDRAW_INDEX | REDRAW_STATUS; 1779#ifdef USE_SIDEBAR 1780 redraw |= REDRAW_SIDEBAR; 1781#endif 1782 mutt_show_error (); 1783 } 1784 1785 if (redraw & REDRAW_SIGWINCH) 1786 { 1787 i = -1; 1788 j = -1; 1789 while (display_line (fp, &last_pos, &lineInfo, ++i, &lastLine, &maxLine, 1790 has_types | SearchFlag | (flags & MUTT_PAGER_NOWRAP), &QuoteList, &q_level, &force_redraw, 1791 &SearchRE, pager_window) == 0) 1792 if (!lineInfo[i].continuation && ++j == lines) 1793 { 1794 topline = i; 1795 if (!SearchFlag) 1796 break; 1797 } 1798 } 1799 1800#ifdef USE_SIDEBAR 1801 if ((redraw & REDRAW_SIDEBAR) || SidebarNeedsRedraw) 1802 { 1803 SidebarNeedsRedraw = 0; 1804 mutt_sb_draw (); 1805 } 1806#endif 1807 1808 if ((redraw & REDRAW_BODY) || topline != oldtopline) 1809 { 1810 do { 1811 mutt_window_move (pager_window, 0, 0); 1812 curline = oldtopline = topline; 1813 lines = 0; 1814 force_redraw = 0; 1815 1816 while (lines < pager_window->rows && lineInfo[curline].offset <= sb.st_size - 1) 1817 { 1818 if (display_line (fp, &last_pos, &lineInfo, curline, &lastLine, 1819 &maxLine, 1820 (flags & MUTT_DISPLAYFLAGS) | hideQuoted | SearchFlag | (flags & MUTT_PAGER_NOWRAP), 1821 &QuoteList, &q_level, &force_redraw, &SearchRE, 1822 pager_window) > 0) 1823 lines++; 1824 curline++; 1825 mutt_window_move (pager_window, lines, 0); 1826 } 1827 last_offset = lineInfo[curline].offset; 1828 } while (force_redraw); 1829 1830 SETCOLOR (MT_COLOR_TILDE); 1831 while (lines < pager_window->rows) 1832 { 1833 mutt_window_clrtoeol (pager_window); 1834 if (option (OPTTILDE)) 1835 addch ('~'); 1836 lines++; 1837 mutt_window_move (pager_window, lines, 0); 1838 } 1839 NORMAL_COLOR; 1840 1841 /* We are going to update the pager status bar, so it isn't 1842 * necessary to reset to normal color now. */ 1843 1844 redraw |= REDRAW_STATUS; /* need to update the % seen */ 1845 } 1846 1847 if (redraw & REDRAW_STATUS) 1848 { 1849 struct hdr_format_info hfi; 1850 char pager_progress_str[4]; 1851 1852 hfi.ctx = Context; 1853 hfi.pager_progress = pager_progress_str; 1854 1855 if (last_pos < sb.st_size - 1) 1856 snprintf(pager_progress_str, sizeof(pager_progress_str), OFF_T_FMT "%%", (100 * last_offset / sb.st_size)); 1857 else 1858 strfcpy(pager_progress_str, (topline == 0) ? "all" : "end", sizeof(pager_progress_str)); 1859 1860 /* print out the pager status bar */ 1861 mutt_window_move (pager_status_window, 0, 0); 1862 SETCOLOR (MT_COLOR_STATUS); 1863 1864 if (IsHeader (extra) || IsMsgAttach (extra)) 1865 { 1866 size_t l1 = pager_status_window->cols * MB_LEN_MAX; 1867 size_t l2 = sizeof (buffer); 1868 hfi.hdr = (IsHeader (extra)) ? extra->hdr : extra->bdy->hdr; 1869 mutt_make_string_info (buffer, l1 < l2 ? l1 : l2, pager_status_window->cols, NONULL (PagerFmt), &hfi, MUTT_FORMAT_MAKEPRINT); 1870 mutt_paddstr (pager_status_window->cols, buffer); 1871 } 1872 else 1873 { 1874 char bn[STRING]; 1875 snprintf (bn, sizeof (bn), "%s (%s)", banner, pager_progress_str); 1876 mutt_paddstr (pager_status_window->cols, bn); 1877 } 1878 NORMAL_COLOR; 1879 if (option(OPTTSENABLED) && TSSupported) 1880 { 1881 menu_status_line (buffer, sizeof (buffer), index, NONULL (TSStatusFormat)); 1882 mutt_ts_status(buffer); 1883 menu_status_line (buffer, sizeof (buffer), index, NONULL (TSIconFormat)); 1884 mutt_ts_icon(buffer); 1885 } 1886 } 1887 1888 if ((redraw & REDRAW_INDEX) && index) 1889 { 1890 /* redraw the pager_index indicator, because the 1891 * flags for this message might have changed. */ 1892 if (index_window->rows > 0) 1893 menu_redraw_current (index); 1894 1895 /* print out the index status bar */ 1896 menu_status_line (buffer, sizeof (buffer), index, NONULL(Status)); 1897 1898 mutt_window_move (index_status_window, 0, 0); 1899 SETCOLOR (MT_COLOR_STATUS); 1900 mutt_paddstr (index_status_window->cols, buffer); 1901 NORMAL_COLOR; 1902 } 1903 1904 redraw = 0; 1905 1906 if (option(OPTBRAILLEFRIENDLY)) { 1907 if (brailleLine!=-1) { 1908 move(brailleLine+1, 0); 1909 brailleLine = -1; 1910 } 1911 } else 1912 mutt_window_move (pager_status_window, 0, pager_status_window->cols-1); 1913 mutt_refresh (); 1914 1915 if (IsHeader (extra) && OldHdr == extra->hdr && TopLine != topline 1916 && lineInfo[curline].offset < sb.st_size-1) 1917 { 1918 if (TopLine - topline > lines) 1919 topline += lines; 1920 else 1921 topline = TopLine; 1922 continue; 1923 } 1924 else 1925 OldHdr = NULL; 1926 1927 ch = km_dokey (MENU_PAGER); 1928 if (ch != -1) 1929 mutt_clear_error (); 1930 mutt_curs_set (1); 1931 1932 if (SigInt) 1933 { 1934 mutt_query_exit (); 1935 continue; 1936 } 1937#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM) 1938 else if (SigWinch) 1939 { 1940 mutt_resize_screen (); 1941 1942 /* Store current position. */ 1943 lines = -1; 1944 for (i = 0; i <= topline; i++) 1945 if (!lineInfo[i].continuation) 1946 lines++; 1947 1948 if (flags & MUTT_PAGER_RETWINCH) 1949 { 1950 Resize = safe_malloc (sizeof (struct resize)); 1951 1952 Resize->line = lines; 1953 Resize->SearchCompiled = SearchCompiled; 1954 Resize->SearchBack = SearchBack; 1955 1956 ch = -1; 1957 rc = OP_REFORMAT_WINCH; 1958 } 1959 else 1960 { 1961 for (i = 0; i < maxLine; i++) 1962 { 1963 lineInfo[i].offset = 0; 1964 lineInfo[i].type = -1; 1965 lineInfo[i].continuation = 0; 1966 lineInfo[i].chunks = 0; 1967 lineInfo[i].search_cnt = -1; 1968 lineInfo[i].quote = NULL; 1969 1970 safe_realloc (&(lineInfo[i].syntax), 1971 sizeof (struct syntax_t)); 1972 if (SearchCompiled && lineInfo[i].search) 1973 FREE (&(lineInfo[i].search)); 1974 } 1975 1976 lastLine = 0; 1977 topline = 0; 1978 1979 redraw = REDRAW_FULL | REDRAW_SIGWINCH; 1980 ch = 0; 1981 } 1982 1983 SigWinch = 0; 1984 clearok(stdscr,TRUE);/*force complete redraw*/ 1985 continue; 1986 } 1987#endif 1988 else if (ch == -1) 1989 { 1990 ch = 0; 1991 continue; 1992 } 1993 1994 rc = ch; 1995 1996 switch (ch) 1997 { 1998 case OP_EXIT: 1999 rc = -1; 2000 ch = -1; 2001 break; 2002 2003 case OP_QUIT: 2004 if (query_quadoption (OPT_QUIT, _("Quit Mutt?")) == MUTT_YES) 2005 { 2006 /* avoid prompting again in the index menu */ 2007 set_quadoption (OPT_QUIT, MUTT_YES); 2008 ch = -1; 2009 } 2010 break; 2011 2012 case OP_NEXT_PAGE: 2013 if (lineInfo[curline].offset < sb.st_size-1) 2014 { 2015 topline = upNLines (PagerContext, lineInfo, curline, hideQuoted); 2016 } 2017 else if (option (OPTPAGERSTOP)) 2018 { 2019 /* emulate "less -q" and don't go on to the next message. */ 2020 mutt_error _("Bottom of message is shown."); 2021 } 2022 else 2023 { 2024 /* end of the current message, so display the next message. */ 2025 rc = OP_MAIN_NEXT_UNDELETED; 2026 ch = -1; 2027 } 2028 break; 2029 2030 case OP_PREV_PAGE: 2031 if (topline != 0) 2032 { 2033 topline = upNLines (pager_window->rows-PagerContext, lineInfo, topline, hideQuoted); 2034 } 2035 else 2036 mutt_error _("Top of message is shown."); 2037 break; 2038 2039 case OP_NEXT_LINE: 2040 if (lineInfo[curline].offset < sb.st_size-1) 2041 { 2042 topline++; 2043 if (hideQuoted) 2044 { 2045 while (lineInfo[topline].type == MT_COLOR_QUOTED && 2046 topline < lastLine) 2047 topline++; 2048 } 2049 } 2050 else 2051 mutt_error _("Bottom of message is shown."); 2052 break; 2053 2054 case OP_PREV_LINE: 2055 if (topline) 2056 topline = upNLines (1, lineInfo, topline, hideQuoted); 2057 else 2058 mutt_error _("Top of message is shown."); 2059 break; 2060 2061 case OP_PAGER_TOP: 2062 if (topline) 2063 topline = 0; 2064 else 2065 mutt_error _("Top of message is shown."); 2066 break; 2067 2068 case OP_HALF_UP: 2069 if (topline) 2070 topline = upNLines (pager_window->rows/2, lineInfo, topline, hideQuoted); 2071 else 2072 mutt_error _("Top of message is shown."); 2073 break; 2074 2075 case OP_HALF_DOWN: 2076 if (lineInfo[curline].offset < sb.st_size-1) 2077 { 2078 topline = upNLines (pager_window->rows/2, lineInfo, curline, hideQuoted); 2079 } 2080 else if (option (OPTPAGERSTOP)) 2081 { 2082 /* emulate "less -q" and don't go on to the next message. */ 2083 mutt_error _("Bottom of message is shown."); 2084 } 2085 else 2086 { 2087 /* end of the current message, so display the next message. */ 2088 rc = OP_MAIN_NEXT_UNDELETED; 2089 ch = -1; 2090 } 2091 break; 2092 2093 case OP_SEARCH_NEXT: 2094 case OP_SEARCH_OPPOSITE: 2095 if (SearchCompiled) 2096 { 2097 wrapped = 0; 2098 2099 if (SearchContext > 0 && SearchContext < pager_window->rows) 2100 searchctx = SearchContext; 2101 else 2102 searchctx = 0; 2103 2104search_next: 2105 if ((!SearchBack && ch==OP_SEARCH_NEXT) || 2106 (SearchBack &&ch==OP_SEARCH_OPPOSITE)) 2107 { 2108 /* searching forward */ 2109 for (i = wrapped ? 0 : topline + searchctx + 1; i < lastLine; i++) 2110 { 2111 if ((!hideQuoted || lineInfo[i].type != MT_COLOR_QUOTED) && 2112 !lineInfo[i].continuation && lineInfo[i].search_cnt > 0) 2113 break; 2114 } 2115 2116 if (i < lastLine) 2117 topline = i; 2118 else if (wrapped || !option (OPTWRAPSEARCH)) 2119 mutt_error _("Not found."); 2120 else 2121 { 2122 mutt_message _("Search wrapped to top."); 2123 wrapped = 1; 2124 goto search_next; 2125 } 2126 } 2127 else 2128 { 2129 /* searching backward */ 2130 for (i = wrapped ? lastLine : topline + searchctx - 1; i >= 0; i--) 2131 { 2132 if ((!hideQuoted || (has_types && 2133 lineInfo[i].type != MT_COLOR_QUOTED)) && 2134 !lineInfo[i].continuation && lineInfo[i].search_cnt > 0) 2135 break; 2136 } 2137 2138 if (i >= 0) 2139 topline = i; 2140 else if (wrapped || !option (OPTWRAPSEARCH)) 2141 mutt_error _("Not found."); 2142 else 2143 { 2144 mutt_message _("Search wrapped to bottom."); 2145 wrapped = 1; 2146 goto search_next; 2147 } 2148 } 2149 2150 if (lineInfo[topline].search_cnt > 0) 2151 { 2152 SearchFlag = MUTT_SEARCH; 2153 /* give some context for search results */ 2154 if (topline - searchctx > 0) 2155 topline -= searchctx; 2156 } 2157 2158 break; 2159 } 2160 /* no previous search pattern, so fall through to search */ 2161 2162 case OP_SEARCH: 2163 case OP_SEARCH_REVERSE: 2164 strfcpy (buffer, searchbuf, sizeof (buffer)); 2165 if (mutt_get_field ((ch == OP_SEARCH || ch == OP_SEARCH_NEXT) ? 2166 _("Search for: ") : _("Reverse search for: "), 2167 buffer, sizeof (buffer), 2168 MUTT_CLEAR) != 0) 2169 break; 2170 2171 if (!strcmp (buffer, searchbuf)) 2172 { 2173 if (SearchCompiled) 2174 { 2175 /* do an implicit search-next */ 2176 if (ch == OP_SEARCH) 2177 ch = OP_SEARCH_NEXT; 2178 else 2179 ch = OP_SEARCH_OPPOSITE; 2180 2181 wrapped = 0; 2182 goto search_next; 2183 } 2184 } 2185 2186 if (!buffer[0]) 2187 break; 2188 2189 strfcpy (searchbuf, buffer, sizeof (searchbuf)); 2190 2191 /* leave SearchBack alone if ch == OP_SEARCH_NEXT */ 2192 if (ch == OP_SEARCH) 2193 SearchBack = 0; 2194 else if (ch == OP_SEARCH_REVERSE) 2195 SearchBack = 1; 2196 2197 if (SearchCompiled) 2198 { 2199 regfree (&SearchRE); 2200 for (i = 0; i < lastLine; i++) 2201 { 2202 if (lineInfo[i].search) 2203 FREE (&(lineInfo[i].search)); 2204 lineInfo[i].search_cnt = -1; 2205 } 2206 } 2207 2208 if ((err = REGCOMP (&SearchRE, searchbuf, REG_NEWLINE | mutt_which_case (searchbuf))) != 0) 2209 { 2210 regerror (err, &SearchRE, buffer, sizeof (buffer)); 2211 mutt_error ("%s", buffer); 2212 for (i = 0; i < maxLine ; i++) 2213 { 2214 /* cleanup */ 2215 if (lineInfo[i].search) 2216 FREE (&(lineInfo[i].search)); 2217 lineInfo[i].search_cnt = -1; 2218 } 2219 SearchFlag = 0; 2220 SearchCompiled = 0; 2221 } 2222 else 2223 { 2224 SearchCompiled = 1; 2225 /* update the search pointers */ 2226 i = 0; 2227 while (display_line (fp, &last_pos, &lineInfo, i, &lastLine, 2228 &maxLine, MUTT_SEARCH | (flags & MUTT_PAGER_NSKIP) | (flags & MUTT_PAGER_NOWRAP), 2229 &QuoteList, &q_level, 2230 &force_redraw, &SearchRE, pager_window) == 0) 2231 i++; 2232 2233 if (!SearchBack) 2234 { 2235 /* searching forward */ 2236 for (i = topline; i < lastLine; i++) 2237 { 2238 if ((!hideQuoted || lineInfo[i].type != MT_COLOR_QUOTED) && 2239 !lineInfo[i].continuation && lineInfo[i].search_cnt > 0) 2240 break; 2241 } 2242 2243 if (i < lastLine) topline = i; 2244 } 2245 else 2246 { 2247 /* searching backward */ 2248 for (i = topline; i >= 0; i--) 2249 { 2250 if ((!hideQuoted || lineInfo[i].type != MT_COLOR_QUOTED) && 2251 !lineInfo[i].continuation && lineInfo[i].search_cnt > 0) 2252 break; 2253 } 2254 2255 if (i >= 0) topline = i; 2256 } 2257 2258 if (lineInfo[topline].search_cnt == 0) 2259 { 2260 SearchFlag = 0; 2261 mutt_error _("Not found."); 2262 } 2263 else 2264 { 2265 SearchFlag = MUTT_SEARCH; 2266 /* give some context for search results */ 2267 if (SearchContext > 0 && SearchContext < pager_window->rows) 2268 searchctx = SearchContext; 2269 else 2270 searchctx = 0; 2271 if (topline - searchctx > 0) 2272 topline -= searchctx; 2273 } 2274 2275 } 2276 redraw = REDRAW_BODY; 2277 break; 2278 2279 case OP_SEARCH_TOGGLE: 2280 if (SearchCompiled) 2281 { 2282 SearchFlag ^= MUTT_SEARCH; 2283 redraw = REDRAW_BODY; 2284 } 2285 break; 2286 2287 case OP_HELP: 2288 /* don't let the user enter the help-menu from the help screen! */ 2289 if (! InHelp) 2290 { 2291 InHelp = 1; 2292 mutt_help (MENU_PAGER); 2293 redraw = REDRAW_FULL; 2294 InHelp = 0; 2295 } 2296 else 2297 mutt_error _("Help is currently being shown."); 2298 break; 2299 2300 case OP_PAGER_HIDE_QUOTED: 2301 if (has_types) 2302 { 2303 hideQuoted ^= MUTT_HIDE; 2304 if (hideQuoted && lineInfo[topline].type == MT_COLOR_QUOTED) 2305 topline = upNLines (1, lineInfo, topline, hideQuoted); 2306 else 2307 redraw = REDRAW_BODY; 2308 } 2309 break; 2310 2311 case OP_PAGER_SKIP_QUOTED: 2312 if (has_types) 2313 { 2314 int dretval = 0; 2315 int new_topline = topline; 2316 2317 while ((new_topline < lastLine || 2318 (0 == (dretval = display_line (fp, &last_pos, &lineInfo, 2319 new_topline, &lastLine, &maxLine, MUTT_TYPES | (flags & MUTT_PAGER_NOWRAP), 2320 &QuoteList, &q_level, &force_redraw, &SearchRE, pager_window)))) 2321 && lineInfo[new_topline].type != MT_COLOR_QUOTED) 2322 new_topline++; 2323 2324 if (dretval < 0) 2325 { 2326 mutt_error _("No more quoted text."); 2327 break; 2328 } 2329 2330 while ((new_topline < lastLine || 2331 (0 == (dretval = display_line (fp, &last_pos, &lineInfo, 2332 new_topline, &lastLine, &maxLine, MUTT_TYPES | (flags & MUTT_PAGER_NOWRAP), 2333 &QuoteList, &q_level, &force_redraw, &SearchRE, pager_window)))) 2334 && lineInfo[new_topline].type == MT_COLOR_QUOTED) 2335 new_topline++; 2336 2337 if (dretval < 0) 2338 { 2339 mutt_error _("No more unquoted text after quoted text."); 2340 break; 2341 } 2342 topline = new_topline; 2343 } 2344 break; 2345 2346 case OP_PAGER_BOTTOM: /* move to the end of the file */ 2347 if (lineInfo[curline].offset < sb.st_size - 1) 2348 { 2349 i = curline; 2350 /* make sure the types are defined to the end of file */ 2351 while (display_line (fp, &last_pos, &lineInfo, i, &lastLine, 2352 &maxLine, has_types | (flags & MUTT_PAGER_NOWRAP), 2353 &QuoteList, &q_level, &force_redraw, 2354 &SearchRE, pager_window) == 0) 2355 i++; 2356 topline = upNLines (pager_window->rows, lineInfo, lastLine, hideQuoted); 2357 } 2358 else 2359 mutt_error _("Bottom of message is shown."); 2360 break; 2361 2362 case OP_REDRAW: 2363 clearok (stdscr, TRUE); 2364 redraw = REDRAW_FULL; 2365 break; 2366 2367 case OP_NULL: 2368 km_error_key (MENU_PAGER); 2369 break; 2370 2371 /* -------------------------------------------------------------------- 2372 * The following are operations on the current message rather than 2373 * adjusting the view of the message. 2374 */ 2375 2376 case OP_BOUNCE_MESSAGE: 2377 CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra)) 2378 CHECK_ATTACH; 2379 if (IsMsgAttach (extra)) 2380 mutt_attach_bounce (extra->fp, extra->hdr, 2381 extra->idx, extra->idxlen, 2382 extra->bdy); 2383 else 2384 ci_bounce_message (extra->hdr, &redraw); 2385 break; 2386 2387 case OP_RESEND: 2388 CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra)) 2389 CHECK_ATTACH; 2390 if (IsMsgAttach (extra)) 2391 mutt_attach_resend (extra->fp, extra->hdr, 2392 extra->idx, extra->idxlen, 2393 extra->bdy); 2394 else 2395 mutt_resend_message (NULL, extra->ctx, extra->hdr); 2396 redraw = REDRAW_FULL; 2397 break; 2398 2399 case OP_CHECK_TRADITIONAL: 2400 CHECK_MODE (IsHeader (extra)); 2401 if (!(WithCrypto & APPLICATION_PGP)) 2402 break; 2403 if (!(extra->hdr->security & PGP_TRADITIONAL_CHECKED)) 2404 { 2405 ch = -1; 2406 rc = OP_CHECK_TRADITIONAL; 2407 } 2408 break; 2409 2410 case OP_CREATE_ALIAS: 2411 CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra)); 2412 if (IsMsgAttach (extra)) 2413 mutt_create_alias (extra->bdy->hdr->env, NULL); 2414 else 2415 mutt_create_alias (extra->hdr->env, NULL); 2416 MAYBE_REDRAW (redraw); 2417 break; 2418 2419 case OP_PURGE_MESSAGE: 2420 case OP_DELETE: 2421 CHECK_MODE(IsHeader (extra)); 2422 CHECK_READONLY; 2423 /* L10N: CHECK_ACL */ 2424 CHECK_ACL(MUTT_ACL_DELETE, _("Cannot delete message")); 2425 2426 mutt_set_flag (Context, extra->hdr, MUTT_DELETE, 1); 2427 mutt_set_flag (Context, extra->hdr, MUTT_PURGE, (ch == OP_PURGE_MESSAGE)); 2428 if (option (OPTDELETEUNTAG)) 2429 mutt_set_flag (Context, extra->hdr, MUTT_TAG, 0); 2430 redraw = REDRAW_STATUS | REDRAW_INDEX; 2431 if (option (OPTRESOLVE)) 2432 { 2433 ch = -1; 2434 rc = OP_MAIN_NEXT_UNDELETED; 2435 } 2436 break; 2437 2438 case OP_MAIN_SET_FLAG: 2439 case OP_MAIN_CLEAR_FLAG: 2440 CHECK_MODE(IsHeader (extra)); 2441 CHECK_READONLY; 2442 2443 if (mutt_change_flag (extra->hdr, (ch == OP_MAIN_SET_FLAG)) == 0) 2444 redraw |= REDRAW_STATUS | REDRAW_INDEX; 2445 if (extra->hdr->deleted && option (OPTRESOLVE)) 2446 { 2447 ch = -1; 2448 rc = OP_MAIN_NEXT_UNDELETED; 2449 } 2450 break; 2451 2452 case OP_DELETE_THREAD: 2453 case OP_DELETE_SUBTHREAD: 2454 CHECK_MODE(IsHeader (extra)); 2455 CHECK_READONLY; 2456 /* L10N: CHECK_ACL */ 2457 CHECK_ACL(MUTT_ACL_DELETE, _("Cannot delete message(s)")); 2458 2459 r = mutt_thread_set_flag (extra->hdr, MUTT_DELETE, 1, 2460 ch == OP_DELETE_THREAD ? 0 : 1); 2461 2462 if (r != -1) 2463 { 2464 if (option (OPTDELETEUNTAG)) 2465 mutt_thread_set_flag (extra->hdr, MUTT_TAG, 0, 2466 ch == OP_DELETE_THREAD ? 0 : 1); 2467 if (option (OPTRESOLVE)) 2468 { 2469 rc = OP_MAIN_NEXT_UNDELETED; 2470 ch = -1; 2471 } 2472 2473 if (!option (OPTRESOLVE) && PagerIndexLines) 2474 redraw = REDRAW_FULL; 2475 else 2476 redraw = REDRAW_STATUS | REDRAW_INDEX; 2477 } 2478 break; 2479 2480 case OP_DISPLAY_ADDRESS: 2481 CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra)); 2482 if (IsMsgAttach (extra)) 2483 mutt_display_address (extra->bdy->hdr->env); 2484 else 2485 mutt_display_address (extra->hdr->env); 2486 break; 2487 2488 case OP_ENTER_COMMAND: 2489 old_smart_wrap = option (OPTWRAP); 2490 old_markers = option (OPTMARKERS); 2491 old_PagerIndexLines = PagerIndexLines; 2492 2493 CurrentMenu = MENU_PAGER; 2494 mutt_enter_command (); 2495 2496 if (option (OPTNEEDRESORT)) 2497 { 2498 unset_option (OPTNEEDRESORT); 2499 CHECK_MODE(IsHeader (extra)); 2500 set_option (OPTNEEDRESORT); 2501 } 2502 2503 if (old_PagerIndexLines != PagerIndexLines) 2504 { 2505 if (index) 2506 mutt_menuDestroy (&index); 2507 index = NULL; 2508 } 2509 2510 if (option (OPTWRAP) != old_smart_wrap || 2511 option (OPTMARKERS) != old_markers) 2512 { 2513 if (flags & MUTT_PAGER_RETWINCH) 2514 { 2515 ch = -1; 2516 rc = OP_REFORMAT_WINCH; 2517 continue; 2518 } 2519 2520 /* count the real lines above */ 2521 j = 0; 2522 for (i = 0; i <= topline; i++) 2523 { 2524 if (!lineInfo[i].continuation) 2525 j++; 2526 } 2527 2528 /* we need to restart the whole thing */ 2529 for (i = 0; i < maxLine; i++) 2530 { 2531 lineInfo[i].offset = 0; 2532 lineInfo[i].type = -1; 2533 lineInfo[i].continuation = 0; 2534 lineInfo[i].chunks = 0; 2535 lineInfo[i].search_cnt = -1; 2536 lineInfo[i].quote = NULL; 2537 2538 safe_realloc (&(lineInfo[i].syntax), sizeof (struct syntax_t)); 2539 if (SearchCompiled && lineInfo[i].search) 2540 FREE (&(lineInfo[i].search)); 2541 } 2542 2543 if (SearchCompiled) 2544 { 2545 regfree (&SearchRE); 2546 SearchCompiled = 0; 2547 } 2548 SearchFlag = 0; 2549 2550 /* try to keep the old position */ 2551 topline = 0; 2552 lastLine = 0; 2553 while (j > 0 && display_line (fp, &last_pos, &lineInfo, topline, 2554 &lastLine, &maxLine, 2555 (has_types ? MUTT_TYPES : 0) | (flags & MUTT_PAGER_NOWRAP), 2556 &QuoteList, &q_level, &force_redraw, 2557 &SearchRE, pager_window) == 0) 2558 { 2559 if (! lineInfo[topline].continuation) 2560 j--; 2561 if (j > 0) 2562 topline++; 2563 } 2564 2565 ch = 0; 2566 } 2567 2568 if (option (OPTFORCEREDRAWPAGER)) 2569 redraw = REDRAW_FULL; 2570 unset_option (OPTFORCEREDRAWINDEX); 2571 unset_option (OPTFORCEREDRAWPAGER); 2572 break; 2573 2574 case OP_FLAG_MESSAGE: 2575 CHECK_MODE(IsHeader (extra)); 2576 CHECK_READONLY; 2577 /* L10N: CHECK_ACL */ 2578 CHECK_ACL(MUTT_ACL_WRITE, "Cannot flag message"); 2579 2580 mutt_set_flag (Context, extra->hdr, MUTT_FLAG, !extra->hdr->flagged); 2581 redraw = REDRAW_STATUS | REDRAW_INDEX; 2582 if (option (OPTRESOLVE)) 2583 { 2584 ch = -1; 2585 rc = OP_MAIN_NEXT_UNDELETED; 2586 } 2587 break; 2588 2589 case OP_PIPE: 2590 CHECK_MODE(IsHeader (extra) || IsAttach (extra)); 2591 if (IsAttach (extra)) 2592 mutt_pipe_attachment_list (extra->fp, 0, extra->bdy, 0); 2593 else 2594 mutt_pipe_message (extra->hdr); 2595 MAYBE_REDRAW (redraw); 2596 break; 2597 2598 case OP_PRINT: 2599 CHECK_MODE(IsHeader (extra) || IsAttach (extra)); 2600 if (IsAttach (extra)) 2601 mutt_print_attachment_list (extra->fp, 0, extra->bdy); 2602 else 2603 mutt_print_message (extra->hdr); 2604 break; 2605 2606 case OP_MAIL: 2607 CHECK_MODE(IsHeader (extra) && !IsAttach (extra)); 2608 CHECK_ATTACH; 2609 ci_send_message (0, NULL, NULL, extra->ctx, NULL); 2610 redraw = REDRAW_FULL; 2611 break; 2612 2613 case OP_REPLY: 2614 CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra)); 2615 CHECK_ATTACH; 2616 if (IsMsgAttach (extra)) 2617 mutt_attach_reply (extra->fp, extra->hdr, extra->idx, 2618 extra->idxlen, extra->bdy, 2619 SENDREPLY); 2620 else 2621 ci_send_message (SENDREPLY, NULL, NULL, extra->ctx, extra->hdr); 2622 redraw = REDRAW_FULL; 2623 break; 2624 2625 case OP_RECALL_MESSAGE: 2626 CHECK_MODE(IsHeader (extra) && !IsAttach(extra)); 2627 CHECK_ATTACH; 2628 ci_send_message (SENDPOSTPONED, NULL, NULL, extra->ctx, extra->hdr); 2629 redraw = REDRAW_FULL; 2630 break; 2631 2632 case OP_GROUP_REPLY: 2633 CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra)); 2634 CHECK_ATTACH; 2635 if (IsMsgAttach (extra)) 2636 mutt_attach_reply (extra->fp, extra->hdr, extra->idx, 2637 extra->idxlen, extra->bdy, SENDREPLY|SENDGROUPREPLY); 2638 else 2639 ci_send_message (SENDREPLY | SENDGROUPREPLY, NULL, NULL, extra->ctx, extra->hdr); 2640 redraw = REDRAW_FULL; 2641 break; 2642 2643 case OP_LIST_REPLY: 2644 CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra)); 2645 CHECK_ATTACH; 2646 if (IsMsgAttach (extra)) 2647 mutt_attach_reply (extra->fp, extra->hdr, extra->idx, 2648 extra->idxlen, extra->bdy, SENDREPLY|SENDLISTREPLY); 2649 else 2650 ci_send_message (SENDREPLY | SENDLISTREPLY, NULL, NULL, extra->ctx, extra->hdr); 2651 redraw = REDRAW_FULL; 2652 break; 2653 2654 case OP_FORWARD_MESSAGE: 2655 CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra)); 2656 CHECK_ATTACH; 2657 if (IsMsgAttach (extra)) 2658 mutt_attach_forward (extra->fp, extra->hdr, extra->idx, 2659 extra->idxlen, extra->bdy); 2660 else 2661 ci_send_message (SENDFORWARD, NULL, NULL, extra->ctx, extra->hdr); 2662 redraw = REDRAW_FULL; 2663 break; 2664 2665 case OP_DECRYPT_SAVE: 2666 if (!WithCrypto) 2667 { 2668 ch = -1; 2669 break; 2670 } 2671 /* fall through */ 2672 case OP_SAVE: 2673 if (IsAttach (extra)) 2674 { 2675 mutt_save_attachment_list (extra->fp, 0, extra->bdy, extra->hdr, NULL); 2676 break; 2677 } 2678 /* fall through */ 2679 case OP_COPY_MESSAGE: 2680 case OP_DECODE_SAVE: 2681 case OP_DECODE_COPY: 2682 case OP_DECRYPT_COPY: 2683 if (!WithCrypto && ch == OP_DECRYPT_COPY) 2684 { 2685 ch = -1; 2686 break; 2687 } 2688 CHECK_MODE(IsHeader (extra)); 2689 if (mutt_save_message (extra->hdr, 2690 (ch == OP_DECRYPT_SAVE) || 2691 (ch == OP_SAVE) || (ch == OP_DECODE_SAVE), 2692 (ch == OP_DECODE_SAVE) || (ch == OP_DECODE_COPY), 2693 (ch == OP_DECRYPT_SAVE) || (ch == OP_DECRYPT_COPY) || 2694 0, 2695 &redraw) == 0 && (ch == OP_SAVE || ch == OP_DECODE_SAVE 2696 || ch == OP_DECRYPT_SAVE 2697 )) 2698 { 2699 if (option (OPTRESOLVE)) 2700 { 2701 ch = -1; 2702 rc = OP_MAIN_NEXT_UNDELETED; 2703 } 2704 else 2705 redraw |= REDRAW_STATUS | REDRAW_INDEX; 2706 } 2707 MAYBE_REDRAW (redraw); 2708 break; 2709 2710 case OP_SHELL_ESCAPE: 2711 mutt_shell_escape (); 2712 MAYBE_REDRAW (redraw); 2713 break; 2714 2715 case OP_TAG: 2716 CHECK_MODE(IsHeader (extra)); 2717 mutt_set_flag (Context, extra->hdr, MUTT_TAG, !extra->hdr->tagged); 2718 2719 Context->last_tag = extra->hdr->tagged ? extra->hdr : 2720 ((Context->last_tag == extra->hdr && !extra->hdr->tagged) 2721 ? NULL : Context->last_tag); 2722 2723 redraw = REDRAW_STATUS | REDRAW_INDEX; 2724 if (option (OPTRESOLVE)) 2725 { 2726 ch = -1; 2727 rc = OP_NEXT_ENTRY; 2728 } 2729 break; 2730 2731 case OP_TOGGLE_NEW: 2732 CHECK_MODE(IsHeader (extra)); 2733 CHECK_READONLY; 2734 /* L10N: CHECK_ACL */ 2735 CHECK_ACL(MUTT_ACL_SEEN, _("Cannot toggle new")); 2736 2737 if (extra->hdr->read || extra->hdr->old) 2738 mutt_set_flag (Context, extra->hdr, MUTT_NEW, 1); 2739 else if (!first) 2740 mutt_set_flag (Context, extra->hdr, MUTT_READ, 1); 2741 first = 0; 2742 Context->msgnotreadyet = -1; 2743 redraw = REDRAW_STATUS | REDRAW_INDEX; 2744 if (option (OPTRESOLVE)) 2745 { 2746 ch = -1; 2747 rc = OP_MAIN_NEXT_UNDELETED; 2748 } 2749 break; 2750 2751 case OP_UNDELETE: 2752 CHECK_MODE(IsHeader (extra)); 2753 CHECK_READONLY; 2754 /* L10N: CHECK_ACL */ 2755 CHECK_ACL(MUTT_ACL_DELETE, _("Cannot undelete message")); 2756 2757 mutt_set_flag (Context, extra->hdr, MUTT_DELETE, 0); 2758 mutt_set_flag (Context, extra->hdr, MUTT_PURGE, 0); 2759 redraw = REDRAW_STATUS | REDRAW_INDEX; 2760 if (option (OPTRESOLVE)) 2761 { 2762 ch = -1; 2763 rc = OP_NEXT_ENTRY; 2764 } 2765 break; 2766 2767 case OP_UNDELETE_THREAD: 2768 case OP_UNDELETE_SUBTHREAD: 2769 CHECK_MODE(IsHeader (extra)); 2770 CHECK_READONLY; 2771 /* L10N: CHECK_ACL */ 2772 CHECK_ACL(MUTT_ACL_DELETE, _("Cannot undelete message(s)")); 2773 2774 r = mutt_thread_set_flag (extra->hdr, MUTT_DELETE, 0, 2775 ch == OP_UNDELETE_THREAD ? 0 : 1); 2776 if (r != -1) 2777 r = mutt_thread_set_flag (extra->hdr, MUTT_PURGE, 0, 2778 ch == OP_UNDELETE_THREAD ? 0 : 1); 2779 if (r != -1) 2780 { 2781 if (option (OPTRESOLVE)) 2782 { 2783 rc = (ch == OP_DELETE_THREAD) ? 2784 OP_MAIN_NEXT_THREAD : OP_MAIN_NEXT_SUBTHREAD; 2785 ch = -1; 2786 } 2787 2788 if (!option (OPTRESOLVE) && PagerIndexLines) 2789 redraw = REDRAW_FULL; 2790 else 2791 redraw = REDRAW_STATUS | REDRAW_INDEX; 2792 } 2793 break; 2794 2795 case OP_VERSION: 2796 mutt_version (); 2797 break; 2798 2799 case OP_BUFFY_LIST: 2800 mutt_buffy_list (); 2801 break; 2802 2803 case OP_VIEW_ATTACHMENTS: 2804 if (flags & MUTT_PAGER_ATTACHMENT) 2805 { 2806 ch = -1; 2807 rc = OP_ATTACH_COLLAPSE; 2808 break; 2809 } 2810 CHECK_MODE(IsHeader (extra)); 2811 mutt_view_attachments (extra->hdr); 2812 if (extra->hdr->attach_del) 2813 Context->changed = 1; 2814 redraw = REDRAW_FULL; 2815 break; 2816 2817 2818 case OP_MAIL_KEY: 2819 if (!(WithCrypto & APPLICATION_PGP)) 2820 { 2821 ch = -1; 2822 break; 2823 } 2824 CHECK_MODE(IsHeader(extra)); 2825 CHECK_ATTACH; 2826 ci_send_message (SENDKEY, NULL, NULL, extra->ctx, extra->hdr); 2827 redraw = REDRAW_FULL; 2828 break; 2829 2830 2831 case OP_FORGET_PASSPHRASE: 2832 crypt_forget_passphrase (); 2833 break; 2834 2835 case OP_EXTRACT_KEYS: 2836 if (!WithCrypto) 2837 { 2838 ch = -1; 2839 break; 2840 } 2841 CHECK_MODE(IsHeader(extra)); 2842 crypt_extract_keys_from_messages(extra->hdr); 2843 redraw = REDRAW_FULL; 2844 break; 2845 2846 case OP_WHAT_KEY: 2847 mutt_what_key (); 2848 break; 2849 2850#ifdef USE_SIDEBAR 2851 case OP_SIDEBAR_NEXT: 2852 case OP_SIDEBAR_NEXT_NEW: 2853 case OP_SIDEBAR_PAGE_DOWN: 2854 case OP_SIDEBAR_PAGE_UP: 2855 case OP_SIDEBAR_PREV: 2856 case OP_SIDEBAR_PREV_NEW: 2857 mutt_sb_change_mailbox (ch); 2858 break; 2859 2860 case OP_SIDEBAR_TOGGLE_VISIBLE: 2861 toggle_option (OPTSIDEBAR); 2862 mutt_reflow_windows(); 2863 redraw = REDRAW_FULL; 2864 break; 2865#endif 2866 2867 default: 2868 ch = -1; 2869 break; 2870 } 2871 } 2872 2873 safe_fclose (&fp); 2874 if (IsHeader (extra)) 2875 { 2876 Context->msgnotreadyet = -1; 2877 switch (rc) 2878 { 2879 case -1: 2880 case OP_DISPLAY_HEADERS: 2881 mutt_clear_pager_position (); 2882 break; 2883 default: 2884 TopLine = topline; 2885 OldHdr = extra->hdr; 2886 break; 2887 } 2888 } 2889 2890 cleanup_quote (&QuoteList); 2891 2892 for (i = 0; i < maxLine ; i++) 2893 { 2894 FREE (&(lineInfo[i].syntax)); 2895 if (SearchCompiled && lineInfo[i].search) 2896 FREE (&(lineInfo[i].search)); 2897 } 2898 if (SearchCompiled) 2899 { 2900 regfree (&SearchRE); 2901 SearchCompiled = 0; 2902 } 2903 FREE (&lineInfo); 2904 if (index) 2905 mutt_menuDestroy(&index); 2906 2907 FREE (&index_status_window); 2908 FREE (&index_window); 2909 FREE (&pager_status_window); 2910 FREE (&pager_window); 2911 2912 return (rc != -1 ? rc : 0); 2913}