Rocco Rutte:
[apps/madmutt.git] / curs_main.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4  *
5  * Parts were written/modified by:
6  * Nico Golde <nico@ngolde.de>
7  *
8  * This file is part of mutt-ng, see http://www.muttng.org/.
9  * It's licensed under the GNU General Public License,
10  * please see the file GPL in the top level source directory.
11  */
12
13 #if HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include "mutt.h"
18 #include "mutt_curses.h"
19 #include "mx.h"
20 #include "mutt_menu.h"
21 #include "mapping.h"
22 #include "sort.h"
23 #include "buffy.h"
24 #include "mx.h"
25 #include "sidebar.h"
26
27 #ifdef USE_POP
28 #include "pop.h"
29 #endif
30
31 #ifdef USE_IMAP
32 #include "imap_private.h"
33 #endif
34
35 #include "mutt_crypt.h"
36
37 #ifdef USE_NNTP
38 #include "nntp.h"
39 #endif
40
41 #include "lib/mem.h"
42 #include "lib/intl.h"
43 #include "lib/str.h"
44 #include "lib/debug.h"
45
46 #include <ctype.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 #include <sys/wait.h>
50 #include <string.h>
51 #include <sys/stat.h>
52 #include <errno.h>
53
54 static const char *No_mailbox_is_open = N_("No mailbox is open.");
55 static const char *There_are_no_messages = N_("There are no messages.");
56 static const char *Mailbox_is_read_only = N_("Mailbox is read-only.");
57 static const char *Function_not_permitted_in_attach_message_mode =
58 N_("Function not permitted in attach-message mode.");
59 static const char *No_visible = N_("No visible messages.");
60
61 #define CHECK_MSGCOUNT if (!Context) \
62         { \
63                   mutt_flushinp (); \
64                 mutt_error(_(No_mailbox_is_open)); \
65                 break; \
66         } \
67         else if (!Context->msgcount) \
68         { \
69                   mutt_flushinp (); \
70                 mutt_error(_(There_are_no_messages)); \
71                 break; \
72         }
73
74 #define CHECK_VISIBLE if (Context && menu->current >= Context->vcount) \
75           {\
76                   mutt_flushinp (); \
77                   mutt_error(_(No_visible)); \
78                   break; \
79         }
80
81
82 #define CHECK_READONLY if (Context->readonly) \
83                         { \
84                                   mutt_flushinp (); \
85                                 mutt_error(_(Mailbox_is_read_only)); \
86                                 break; \
87                         }
88
89 #define CHECK_ATTACH if(option(OPTATTACHMSG)) \
90                      {\
91                         mutt_flushinp (); \
92                         mutt_error(_(Function_not_permitted_in_attach_message_mode)); \
93                         break; \
94                      }
95
96 #define CURHDR Context->hdrs[Context->v2r[menu->current]]
97 #define OLDHDR Context->hdrs[Context->v2r[menu->oldcurrent]]
98 #define UNREAD(h) mutt_thread_contains_unread (Context, h)
99
100 extern const char *ReleaseDate;
101 extern size_t UngetCount;
102
103 static void set_xterm_title_bar (char *title)
104 {
105   fputs ("\033]2;", stdout);
106   fputs (title, stdout);
107   fputs ("\007", stdout);
108   fflush (stdout);
109 }
110
111 static void set_xterm_icon_name (char *name)
112 {
113   fputs ("\033]1;", stdout);
114   fputs (name, stdout);
115   fputs ("\007", stdout);
116   fflush (stdout);
117 }
118
119 void index_make_entry (char *s, size_t l, MUTTMENU * menu, int num)
120 {
121   format_flag flag =
122     M_FORMAT_MAKEPRINT | M_FORMAT_ARROWCURSOR | M_FORMAT_INDEX;
123   int edgemsgno, reverse = Sort & SORT_REVERSE;
124   HEADER *h = Context->hdrs[Context->v2r[num]];
125   THREAD *tmp;
126
127   if ((Sort & SORT_MASK) == SORT_THREADS && h->tree) {
128     flag |= M_FORMAT_TREE;      /* display the thread tree */
129     if (h->display_subject)
130       flag |= M_FORMAT_FORCESUBJ;
131     else {
132       if (reverse) {
133         if (menu->top + menu->pagelen > menu->max)
134           edgemsgno = Context->v2r[menu->max - 1];
135         else
136           edgemsgno = Context->v2r[menu->top + menu->pagelen - 1];
137       }
138       else
139         edgemsgno = Context->v2r[menu->top];
140
141       for (tmp = h->thread->parent; tmp; tmp = tmp->parent) {
142         if (!tmp->message)
143           continue;
144
145         /* if no ancestor is visible on current screen, provisionally force
146          * subject... */
147         if (reverse ? tmp->message->msgno > edgemsgno : tmp->message->msgno <
148             edgemsgno) {
149           flag |= M_FORMAT_FORCESUBJ;
150           break;
151         }
152         else if (tmp->message->virtual >= 0)
153           break;
154       }
155       if (flag & M_FORMAT_FORCESUBJ) {
156         for (tmp = h->thread->prev; tmp; tmp = tmp->prev) {
157           if (!tmp->message)
158             continue;
159
160           /* ...but if a previous sibling is available, don't force it */
161           if (reverse ? tmp->message->msgno >
162               edgemsgno : tmp->message->msgno < edgemsgno)
163             break;
164           else if (tmp->message->virtual >= 0) {
165             flag &= ~M_FORMAT_FORCESUBJ;
166             break;
167           }
168         }
169       }
170     }
171   }
172
173   _mutt_make_string (s, l, NONULL (HdrFmt), Context, h, flag);
174 }
175
176 int index_color (int index_no)
177 {
178   HEADER *h = Context->hdrs[Context->v2r[index_no]];
179
180   if (h && h->pair)
181     return h->pair;
182
183   mutt_set_header_color (Context, h);
184   return h->pair;
185 }
186
187 static int ci_next_undeleted (int msgno)
188 {
189   int i;
190
191   for (i = msgno + 1; i < Context->vcount; i++)
192     if (!Context->hdrs[Context->v2r[i]]->deleted)
193       return (i);
194   return (-1);
195 }
196
197 static int ci_previous_undeleted (int msgno)
198 {
199   int i;
200
201   for (i = msgno - 1; i >= 0; i--)
202     if (!Context->hdrs[Context->v2r[i]]->deleted)
203       return (i);
204   return (-1);
205 }
206
207 /* Return the index of the first new message, or failing that, the first
208  * unread message.
209  */
210 static int ci_first_message (void)
211 {
212   int old = -1, i;
213
214   if (Context && Context->msgcount) {
215     for (i = 0; i < Context->vcount; i++) {
216       if (!Context->hdrs[Context->v2r[i]]->read &&
217           !Context->hdrs[Context->v2r[i]]->deleted) {
218         if (!Context->hdrs[Context->v2r[i]]->old)
219           return (i);
220         else if (old == -1)
221           old = i;
222       }
223     }
224     if (old != -1)
225       return (old);
226
227     /* If Sort is reverse and not threaded, the latest message is first.
228      * If Sort is threaded, the latest message is first iff exactly one
229      * of Sort and SortAux are reverse.
230      */
231     if (((Sort & SORT_REVERSE) && (Sort & SORT_MASK) != SORT_THREADS) ||
232         ((Sort & SORT_MASK) == SORT_THREADS &&
233          ((Sort ^ SortAux) & SORT_REVERSE)))
234       return 0;
235     else
236       return (Context->vcount ? Context->vcount - 1 : 0);
237   }
238   return 0;
239 }
240
241 /* This should be in mx.c, but it only gets used here. */
242 static int mx_toggle_write (CONTEXT * ctx)
243 {
244   if (!ctx)
245     return -1;
246
247   if (ctx->readonly) {
248     mutt_error (_("Cannot toggle write on a readonly mailbox!"));
249
250     return -1;
251   }
252
253   if (ctx->dontwrite) {
254     ctx->dontwrite = 0;
255     mutt_message (_("Changes to folder will be written on folder exit."));
256   }
257   else {
258     ctx->dontwrite = 1;
259     mutt_message (_("Changes to folder will not be written."));
260   }
261
262   return 0;
263 }
264
265 static void update_index (MUTTMENU * menu, CONTEXT * ctx, int check,
266                           int oldcount, int index_hint)
267 {
268   /* store pointers to the newly added messages */
269   HEADER **save_new = NULL;
270   int j;
271
272   /* take note of the current message */
273   if (oldcount) {
274     if (menu->current < Context->vcount)
275       menu->oldcurrent = index_hint;
276     else
277       oldcount = 0;             /* invalid message number! */
278   }
279
280   /* We are in a limited view. Check if the new message(s) satisfy
281    * the limit criteria. If they do, set their virtual msgno so that
282    * they will be visible in the limited view */
283   if (Context->pattern) {
284 #define THIS_BODY Context->hdrs[j]->content
285     if (oldcount || check == M_REOPENED) {
286       for (j = (check == M_REOPENED) ? 0 : oldcount; j < Context->msgcount;
287            j++) {
288         if (mutt_pattern_exec
289             (Context->limit_pattern, M_MATCH_FULL_ADDRESS, Context,
290              Context->hdrs[j])) {
291           Context->hdrs[j]->virtual = Context->vcount;
292           Context->v2r[Context->vcount] = j;
293           Context->hdrs[j]->limited = 1;
294           Context->vcount++;
295           Context->vsize +=
296             THIS_BODY->length + THIS_BODY->offset - THIS_BODY->hdr_offset;
297         }
298       }
299     }
300 #undef THIS_BODY
301   }
302
303   /* save the list of new messages */
304   if (oldcount && check != M_REOPENED && ((Sort & SORT_MASK) == SORT_THREADS)) {
305     save_new =
306       (HEADER **) safe_malloc (sizeof (HEADER *) *
307                                (Context->msgcount - oldcount));
308     for (j = oldcount; j < Context->msgcount; j++)
309       save_new[j - oldcount] = Context->hdrs[j];
310   }
311
312   /* if the mailbox was reopened, need to rethread from scratch */
313   mutt_sort_headers (Context, (check == M_REOPENED));
314
315   /* uncollapse threads with new mail */
316   if ((Sort & SORT_MASK) == SORT_THREADS) {
317     if (check == M_REOPENED) {
318       THREAD *h, *j;
319
320       Context->collapsed = 0;
321
322       for (h = Context->tree; h; h = h->next) {
323         for (j = h; !j->message; j = j->child);
324         mutt_uncollapse_thread (Context, j->message);
325       }
326       mutt_set_virtual (Context);
327     }
328     else if (oldcount) {
329       for (j = 0; j < Context->msgcount - oldcount; j++) {
330         int k;
331
332         for (k = 0; k < Context->msgcount; k++) {
333           HEADER *h = Context->hdrs[k];
334
335           if (h == save_new[j] && (!Context->pattern || h->limited))
336             mutt_uncollapse_thread (Context, h);
337         }
338       }
339       FREE (&save_new);
340       mutt_set_virtual (Context);
341     }
342   }
343
344   menu->current = -1;
345   if (oldcount) {
346     /* restore the current message to the message it was pointing to */
347     for (j = 0; j < Context->vcount; j++) {
348       if (Context->hdrs[Context->v2r[j]]->index == menu->oldcurrent) {
349         menu->current = j;
350         break;
351       }
352     }
353   }
354
355   if (menu->current < 0)
356     menu->current = ci_first_message ();
357
358 }
359
360 static void resort_index (MUTTMENU * menu)
361 {
362   int i;
363   HEADER *current = CURHDR;
364
365   menu->current = -1;
366   mutt_sort_headers (Context, 0);
367   /* Restore the current message */
368
369   for (i = 0; i < Context->vcount; i++) {
370     if (Context->hdrs[Context->v2r[i]] == current) {
371       menu->current = i;
372       break;
373     }
374   }
375
376   if ((Sort & SORT_MASK) == SORT_THREADS && menu->current < 0)
377     menu->current = mutt_parent_message (Context, current);
378
379   if (menu->current < 0)
380     menu->current = ci_first_message ();
381
382   menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
383 }
384
385 struct mapping_t IndexHelp[] = {
386   {N_("Quit"), OP_QUIT},
387   {N_("Del"), OP_DELETE},
388   {N_("Undel"), OP_UNDELETE},
389   {N_("Save"), OP_SAVE},
390   {N_("Mail"), OP_MAIL},
391   {N_("Reply"), OP_REPLY},
392   {N_("Group"), OP_GROUP_REPLY},
393   {N_("Help"), OP_HELP},
394   {NULL}
395 };
396
397 #ifdef USE_NNTP
398 struct mapping_t IndexNewsHelp[] = {
399   {N_("Quit"), OP_QUIT},
400   {N_("Del"), OP_DELETE},
401   {N_("Undel"), OP_UNDELETE},
402   {N_("Save"), OP_SAVE},
403   {N_("Post"), OP_POST},
404   {N_("Followup"), OP_FOLLOWUP},
405   {N_("Catchup"), OP_CATCHUP},
406   {N_("Help"), OP_HELP},
407   {NULL}
408 };
409 #endif
410
411 /* This function handles the message index window as well as commands returned
412  * from the pager (MENU_PAGER).
413  */
414 int mutt_index_menu (void)
415 {
416   char buf[LONG_STRING], helpstr[SHORT_STRING];
417   int flags;
418   int op = OP_NULL;
419   int done = 0;                 /* controls when to exit the "event" loop */
420   int i = 0, j;
421   int tag = 0;                  /* has the tag-prefix command been pressed? */
422   int newcount = -1;
423   int oldcount = -1;
424   int rc = -1;
425   MUTTMENU *menu;
426   char *cp;                     /* temporary variable. */
427   int index_hint;               /* used to restore cursor position */
428   int do_buffy_notify = 1;
429   int close = 0;                /* did we OP_QUIT or OP_EXIT out of this menu? */
430   int attach_msg = option (OPTATTACHMSG);
431
432   menu = mutt_new_menu ();
433   menu->menu = MENU_MAIN;
434   menu->offset = 1;
435   menu->pagelen = LINES - 3;
436   menu->make_entry = index_make_entry;
437   menu->color = index_color;
438   menu->current = ci_first_message ();
439   menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN,
440 #ifdef USE_NNTP
441                                   (Context
442                                    && (Context->magic ==
443                                        M_NNTP)) ? IndexNewsHelp :
444 #endif
445                                   IndexHelp);
446
447   if (!attach_msg) {
448     buffy_check (1);       /* force the buffy check after we enter the folder */
449     /* record folder we open to place sidebar indicator properly */
450     if (Context && Context->path)
451       sidebar_set_current (Context->path);
452   }
453
454   FOREVER {
455     tag = 0;                    /* clear the tag-prefix */
456
457     menu->max = Context ? Context->vcount : 0;
458     oldcount = Context ? Context->msgcount : 0;
459
460     /* check if we need to resort the index because just about
461      * any 'op' below could do mutt_enter_command(), either here or
462      * from any new menu launched, and change $sort/$sort_aux
463      */
464     if (option (OPTNEEDRESORT) && Context && Context->msgcount)
465       resort_index (menu);
466
467     if (option (OPTREDRAWTREE) && Context && Context->msgcount
468         && (Sort & SORT_MASK) == SORT_THREADS) {
469       mutt_draw_tree (Context);
470       menu->redraw |= REDRAW_STATUS;
471       unset_option (OPTREDRAWTREE);
472     }
473
474     if (Context && !attach_msg) {
475       int check;
476
477       /* check for new mail in the mailbox.  If nonzero, then something has
478        * changed about the file (either we got new mail or the file was
479        * modified underneath us.)
480        */
481
482 #ifdef USE_IMAP
483       imap_allow_reopen (Context);
484 #endif
485
486       index_hint = (Context->vcount && menu->current >= 0
487                     && menu->current < Context->vcount) ? CURHDR->index : 0;
488
489       if ((check = mx_check_mailbox (Context, &index_hint, 0)) < 0) {
490         if (!Context->path) {
491           /* fatal error occurred */
492           FREE (&Context);
493           menu->redraw = REDRAW_FULL;
494         }
495         set_option (OPTSEARCHINVALID);
496       }
497       else if (check == M_NEW_MAIL || check == M_REOPENED || check == M_FLAGS) {
498         update_index (menu, Context, check, oldcount, index_hint);
499
500         /* notify the user of new mail */
501         if (check == M_REOPENED)
502           mutt_error (_
503                       ("Mailbox was externally modified.  Flags may be wrong."));
504         else if (check == M_NEW_MAIL) {
505           /* on new mail: redraw sidebar */
506           sidebar_draw (CurrentMenu);
507           mutt_message (_("New mail in this mailbox."));
508
509           if (option (OPTBEEPNEW))
510             beep ();
511         }
512         else if (check == M_FLAGS)
513           mutt_message (_("Mailbox was externally modified."));
514
515         /* avoid the message being overwritten by buffy */
516         do_buffy_notify = 0;
517
518         menu->redraw = REDRAW_FULL;
519         menu->max = Context->vcount;
520
521         set_option (OPTSEARCHINVALID);
522       }
523     }
524
525 #ifdef USE_IMAP
526     imap_keepalive ();
527     imap_disallow_reopen (Context);
528 #endif
529
530     if (!attach_msg) {
531       /* check for new mail in the incoming folders */
532       oldcount = newcount;
533       if ((newcount = buffy_check (0)) != oldcount) {
534         menu->redraw |= REDRAW_STATUS;
535         menu->redraw |= REDRAW_SIDEBAR;
536       }
537       if (do_buffy_notify) {
538         if (buffy_notify () && option (OPTBEEPNEW))
539           beep ();
540       }
541       else
542         do_buffy_notify = 1;
543     }
544
545     if (op != -1)
546       mutt_curs_set (0);
547     if (menu->redraw & REDRAW_SIDEBAR)
548       sidebar_draw (menu->menu);
549     if (menu->redraw & REDRAW_FULL) {
550       menu_redraw_full (menu);
551       sidebar_draw (menu->menu);
552       mutt_show_error ();
553     }
554
555     if (menu->menu == MENU_MAIN) {
556       if (Context && Context->hdrs && !(menu->current >= Context->vcount)) {
557         menu_check_recenter (menu);
558
559         if (menu->redraw & REDRAW_INDEX) {
560           menu_redraw_index (menu);
561           menu->redraw |= REDRAW_STATUS;
562         }
563         else if (menu->redraw & (REDRAW_MOTION_RESYNCH | REDRAW_MOTION))
564           menu_redraw_motion (menu);
565         else if (menu->redraw & REDRAW_CURRENT)
566           menu_redraw_current (menu);
567       }
568
569       if (menu->redraw & REDRAW_STATUS) {
570         DrawFullLine = 1;
571         menu_status_line (buf, sizeof (buf), menu, NONULL (Status));
572         DrawFullLine = 0;
573         CLEARLINE (option (OPTSTATUSONTOP) ? 0 : LINES - 2);
574         SETCOLOR (MT_COLOR_STATUS);
575         mutt_paddstr (COLS, buf);
576         SETCOLOR (MT_COLOR_NORMAL);
577         sidebar_set_buffystats (Context);
578         menu->redraw &= ~REDRAW_STATUS;
579         if (option (OPTXTERMSETTITLES)) {
580           menu_status_line (buf, sizeof (buf), menu, NONULL (XtermTitle));
581           set_xterm_title_bar (buf);
582           menu_status_line (buf, sizeof (buf), menu, NONULL (XtermIcon));
583           set_xterm_icon_name (buf);
584         }
585       }
586
587       menu->redraw = 0;
588       if (menu->current < menu->max)
589         menu->oldcurrent = menu->current;
590       else
591         menu->oldcurrent = -1;
592
593       if (option (OPTARROWCURSOR))
594         move (menu->current - menu->top + menu->offset, 2);
595        else if (option (OPTBRAILLEFRIENDLY))
596          move (menu->current - menu->top + menu->offset, 0);
597       else
598         move (menu->current - menu->top + menu->offset, COLS - 1);
599       mutt_refresh ();
600
601       op = km_dokey (MENU_MAIN);
602
603       debug_print (4, ("Got op %d\n", op));
604
605 #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
606       if (SigWinch) {
607         mutt_flushinp ();
608         mutt_resize_screen ();
609         menu->redraw = REDRAW_FULL;
610         menu->menu = MENU_MAIN;
611         SigWinch = 0;
612         menu->top = 0;          /* so we scroll the right amount */
613         /*
614          * force a real complete redraw.  clrtobot() doesn't seem to be able
615          * to handle every case without this.
616          */
617         clearok (stdscr, TRUE);
618         continue;
619       }
620 #endif
621
622       if (op == -1)
623         continue;               /* either user abort or timeout */
624
625       mutt_curs_set (1);
626
627       /* special handling for the tag-prefix function */
628       if (op == OP_TAG_PREFIX) {
629         if (!Context) {
630           mutt_error (_("No mailbox is open."));
631
632           continue;
633         }
634
635         if (!Context->tagged) {
636           mutt_error (_("No tagged messages."));
637
638           continue;
639         }
640         tag = 1;
641
642         /* give visual indication that the next command is a tag- command */
643         mvaddstr (LINES - 1, 0, "tag-");
644         clrtoeol ();
645
646         /* get the real command */
647         if ((op = km_dokey (MENU_MAIN)) == OP_TAG_PREFIX) {
648           /* abort tag sequence */
649           CLEARLINE (LINES - 1);
650           continue;
651         }
652       }
653       else if (option (OPTAUTOTAG) && Context && Context->tagged)
654         tag = 1;
655
656       if (op == OP_TAG_PREFIX_COND) {
657         if (!Context) {
658           mutt_error (_("No mailbox is open."));
659
660           continue;
661         }
662
663         if (!Context->tagged) {
664           event_t tmp;
665
666           while (UngetCount > 0) {
667             tmp = mutt_getch ();
668             if (tmp.op == OP_END_COND)
669               break;
670           }
671           mutt_message (_("Nothing to do."));
672
673           continue;
674         }
675         tag = 1;
676
677         /* give visual indication that the next command is a tag- command */
678         mvaddstr (LINES - 1, 0, "tag-");
679         clrtoeol ();
680
681         /* get the real command */
682         if ((op = km_dokey (MENU_MAIN)) == OP_TAG_PREFIX) {
683           /* abort tag sequence */
684           CLEARLINE (LINES - 1);
685           continue;
686         }
687       }
688
689       mutt_clear_error ();
690     }
691     else {
692       if (menu->current < menu->max)
693         menu->oldcurrent = menu->current;
694       else
695         menu->oldcurrent = -1;
696
697       mutt_curs_set (1);        /* fallback from the pager */
698     }
699
700 #ifdef USE_NNTP
701     unset_option (OPTNEWS);     /* for any case */
702 #endif
703
704     switch (op) {
705
706       /* ----------------------------------------------------------------------
707        * movement commands
708        */
709
710     case OP_BOTTOM_PAGE:
711       menu_bottom_page (menu);
712       break;
713     case OP_FIRST_ENTRY:
714       menu_first_entry (menu);
715       break;
716     case OP_MIDDLE_PAGE:
717       menu_middle_page (menu);
718       break;
719     case OP_HALF_UP:
720       menu_half_up (menu);
721       break;
722     case OP_HALF_DOWN:
723       menu_half_down (menu);
724       break;
725     case OP_NEXT_LINE:
726       menu_next_line (menu);
727       break;
728     case OP_PREV_LINE:
729       menu_prev_line (menu);
730       break;
731     case OP_NEXT_PAGE:
732       menu_next_page (menu);
733       break;
734     case OP_PREV_PAGE:
735       menu_prev_page (menu);
736       break;
737     case OP_LAST_ENTRY:
738       menu_last_entry (menu);
739       break;
740     case OP_TOP_PAGE:
741       menu_top_page (menu);
742       break;
743     case OP_CURRENT_TOP:
744       menu_current_top (menu);
745       break;
746     case OP_CURRENT_MIDDLE:
747       menu_current_middle (menu);
748       break;
749     case OP_CURRENT_BOTTOM:
750       menu_current_bottom (menu);
751       break;
752
753 #ifdef USE_NNTP
754     case OP_GET_MESSAGE:
755     case OP_GET_PARENT:
756       CHECK_MSGCOUNT;
757       if (Context->magic == M_NNTP) {
758         HEADER *h;
759
760         if (op == OP_GET_MESSAGE) {
761           buf[0] = 0;
762           if (mutt_get_field (_("Enter Message-Id: "), buf, sizeof (buf), 0)
763               != 0 || !buf[0])
764             break;
765         }
766         else {
767           LIST *ref = CURHDR->env->references;
768
769           if (!ref) {
770             mutt_error (_("Article has no parent reference!"));
771
772             break;
773           }
774           strfcpy (buf, ref->data, sizeof (buf));
775         }
776         if (!Context->id_hash)
777           Context->id_hash = mutt_make_id_hash (Context);
778         if ((h = hash_find (Context->id_hash, buf))) {
779           if (h->virtual != -1) {
780             menu->current = h->virtual;
781             menu->redraw = REDRAW_MOTION_RESYNCH;
782           }
783           else if (h->collapsed) {
784             mutt_uncollapse_thread (Context, h);
785             mutt_set_virtual (Context);
786             menu->current = h->virtual;
787             menu->redraw = REDRAW_MOTION_RESYNCH;
788           }
789           else
790             mutt_error (_("Message not visible in limited view."));
791         }
792         else {
793           if (nntp_check_msgid (Context, buf) == 0) {
794             h = Context->hdrs[Context->msgcount - 1];
795             mutt_sort_headers (Context, 0);
796             menu->current = h->virtual;
797             menu->redraw = REDRAW_FULL;
798           }
799           else
800             mutt_error (_("Article %s not found on server"), buf);
801         }
802       }
803       break;
804
805     case OP_GET_CHILDREN:
806     case OP_RECONSTRUCT_THREAD:
807       CHECK_MSGCOUNT;
808       if (Context->magic == M_NNTP) {
809         HEADER *h;
810         int old = CURHDR->index, i;
811
812         if (!CURHDR->env->message_id) {
813           mutt_error (_("No Message-Id. Unable to perform operation"));
814
815           break;
816         }
817
818         if (!Context->id_hash)
819           Context->id_hash = mutt_make_id_hash (Context);
820         strfcpy (buf, CURHDR->env->message_id, sizeof (buf));
821
822         if (op == OP_RECONSTRUCT_THREAD) {
823           LIST *ref = CURHDR->env->references;
824
825           while (ref) {
826             nntp_check_msgid (Context, ref->data);
827             /* the last msgid in References is the root message */
828             if (!ref->next)
829               strfcpy (buf, ref->data, sizeof (buf));
830             ref = ref->next;
831           }
832         }
833         mutt_message (_("Check for children of message..."));
834
835         if (nntp_check_children (Context, buf) == 0) {
836           mutt_sort_headers (Context, (op == OP_RECONSTRUCT_THREAD));
837           h = hash_find (Context->id_hash, buf);
838           /* if the root message was retrieved, move to it */
839           if (h)
840             menu->current = h->virtual;
841           else                  /* try to restore old position */
842             for (i = 0; i < Context->msgcount; i++)
843               if (Context->hdrs[i]->index == old) {
844                 menu->current = Context->hdrs[i]->virtual;
845                 /* As an added courtesy, recenter the menu
846                  * with the current entry at the middle of the screen */
847                 menu_check_recenter (menu);
848                 menu_current_middle (menu);
849               }
850         }
851         menu->redraw = REDRAW_FULL;
852         mutt_clear_error ();
853       }
854       break;
855 #endif
856
857     case OP_JUMP:
858
859       CHECK_MSGCOUNT;
860       CHECK_VISIBLE;
861       if (isdigit (LastKey))
862         mutt_ungetch (LastKey, 0);
863       buf[0] = 0;
864       if (mutt_get_field (_("Jump to message: "), buf, sizeof (buf), 0) != 0
865           || !buf[0])
866         break;
867
868       if (!isdigit ((unsigned char) buf[0])) {
869         mutt_error (_("Argument must be a message number."));
870
871         break;
872       }
873
874       i = atoi (buf);
875       if (i > 0 && i <= Context->msgcount) {
876         for (j = i - 1; j < Context->msgcount; j++) {
877           if (Context->hdrs[j]->virtual != -1)
878             break;
879         }
880         if (j >= Context->msgcount) {
881           for (j = i - 2; j >= 0; j--) {
882             if (Context->hdrs[j]->virtual != -1)
883               break;
884           }
885         }
886
887         if (j >= 0) {
888           menu->current = Context->hdrs[j]->virtual;
889           if (menu->menu == MENU_PAGER) {
890             op = OP_DISPLAY_MESSAGE;
891             continue;
892           }
893           else
894             menu->redraw = REDRAW_MOTION;
895         }
896         else
897           mutt_error (_("That message is not visible."));
898       }
899       else
900         mutt_error (_("Invalid message number."));
901
902       break;
903
904       /* --------------------------------------------------------------------
905        * `index' specific commands
906        */
907
908     case OP_MAIN_DELETE_PATTERN:
909
910       CHECK_MSGCOUNT;
911       CHECK_VISIBLE;
912       CHECK_READONLY;
913
914       CHECK_MX_ACL (Context, ACL_DELETE, _("Deletion"));
915
916       CHECK_ATTACH;
917       mutt_pattern_func (M_DELETE, _("Delete messages matching: "));
918       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
919       break;
920
921 #ifdef USE_POP
922     case OP_MAIN_FETCH_MAIL:
923
924       CHECK_ATTACH;
925       pop_fetch_mail ();
926       menu->redraw = REDRAW_FULL;
927       break;
928 #endif /* USE_POP */
929
930     case OP_HELP:
931
932       mutt_help (MENU_MAIN);
933       menu->redraw = REDRAW_FULL;
934       break;
935
936     case OP_MAIN_SHOW_LIMIT:
937       CHECK_MSGCOUNT;
938       if (!Context->pattern)
939         mutt_message (_("No limit pattern is in effect."));
940
941       else {
942         char buf[STRING];
943
944         /* i18n: ask for a limit to apply */
945         snprintf (buf, sizeof (buf), _("Limit: %s"), Context->pattern);
946         mutt_message ("%s", buf);
947       }
948       break;
949
950     case OP_MAIN_LIMIT:
951     case OP_TOGGLE_READ:
952
953       CHECK_MSGCOUNT;
954       menu->oldcurrent = (Context->vcount && menu->current >= 0
955                           && menu->current <
956                           Context->vcount) ? CURHDR->index : -1;
957       if (op == OP_TOGGLE_READ) {
958         char buf[LONG_STRING];
959
960         if (!Context->pattern
961             || strncmp (Context->pattern, "!~R!~D~s", 8) != 0) {
962           snprintf (buf, sizeof (buf), "!~R!~D~s%s",
963                     Context->pattern ? Context->pattern : ".*");
964           set_option (OPTHIDEREAD);
965         }
966         else {
967           strfcpy (buf, Context->pattern + 8, sizeof (buf));
968           if (!*buf || strncmp (buf, ".*", 2) == 0)
969             snprintf (buf, sizeof (buf), "~A");
970           unset_option (OPTHIDEREAD);
971         }
972         FREE (&Context->pattern);
973         Context->pattern = safe_strdup (buf);
974       }
975       if ((op == OP_TOGGLE_READ && mutt_pattern_func (M_LIMIT, NULL) == 0) ||
976           mutt_pattern_func (M_LIMIT, _("Limit to messages matching: ")) == 0)
977       {
978         if (menu->oldcurrent >= 0) {
979           /* try to find what used to be the current message */
980           menu->current = -1;
981           for (i = 0; i < Context->vcount; i++)
982             if (Context->hdrs[Context->v2r[i]]->index == menu->oldcurrent) {
983               menu->current = i;
984               break;
985             }
986           if (menu->current < 0)
987             menu->current = 0;
988         }
989         else
990           menu->current = 0;
991         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
992         if ((Sort & SORT_MASK) == SORT_THREADS)
993           mutt_draw_tree (Context);
994         menu->redraw = REDRAW_FULL;
995       }
996       break;
997
998     case OP_QUIT:
999
1000       close = op;
1001       if (attach_msg) {
1002         done = 1;
1003         break;
1004       }
1005
1006       if (query_quadoption (OPT_QUIT, _("Quit Mutt-ng?")) == M_YES) {
1007         int check;
1008
1009         oldcount = Context ? Context->msgcount : 0;
1010
1011         if (!Context
1012             || (check = mx_close_mailbox (Context, &index_hint)) == 0)
1013           done = 1;
1014         else {
1015           if (check == M_NEW_MAIL || check == M_REOPENED)
1016             update_index (menu, Context, check, oldcount, index_hint);
1017
1018           menu->redraw = REDRAW_FULL;   /* new mail arrived? */
1019           set_option (OPTSEARCHINVALID);
1020         }
1021       }
1022       break;
1023
1024     case OP_REDRAW:
1025
1026       clearok (stdscr, TRUE);
1027       menu->redraw = REDRAW_FULL;
1028       break;
1029
1030     case OP_SEARCH:
1031     case OP_SEARCH_REVERSE:
1032     case OP_SEARCH_NEXT:
1033     case OP_SEARCH_OPPOSITE:
1034
1035       CHECK_MSGCOUNT;
1036       CHECK_VISIBLE;
1037       if ((menu->current = mutt_search_command (menu->current, op)) == -1)
1038         menu->current = menu->oldcurrent;
1039       else
1040         menu->redraw = REDRAW_MOTION;
1041       break;
1042
1043     case OP_SORT:
1044     case OP_SORT_REVERSE:
1045
1046       if (mutt_select_sort ((op == OP_SORT_REVERSE)) == 0) {
1047         if (Context && Context->msgcount) {
1048           resort_index (menu);
1049           set_option (OPTSEARCHINVALID);
1050         }
1051       }
1052       break;
1053
1054     case OP_TAG:
1055
1056       CHECK_MSGCOUNT;
1057       CHECK_VISIBLE;
1058       if (tag && !option (OPTAUTOTAG)) {
1059         for (j = 0; j < Context->vcount; j++)
1060           mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_TAG, 0);
1061         menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
1062       }
1063       else {
1064         mutt_set_flag (Context, CURHDR, M_TAG, !CURHDR->tagged);
1065         Context->last_tag = CURHDR->tagged ? CURHDR :
1066           ((Context->last_tag == CURHDR && !CURHDR->tagged)
1067            ? NULL : Context->last_tag);
1068         menu->redraw = REDRAW_STATUS;
1069         if (option (OPTRESOLVE) && menu->current < Context->vcount - 1) {
1070           menu->current++;
1071           menu->redraw |= REDRAW_MOTION_RESYNCH;
1072         }
1073         else
1074           menu->redraw |= REDRAW_CURRENT;
1075       }
1076       break;
1077
1078     case OP_MAIN_TAG_PATTERN:
1079
1080       CHECK_MSGCOUNT;
1081       CHECK_VISIBLE;
1082       mutt_pattern_func (M_TAG, _("Tag messages matching: "));
1083       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1084       break;
1085
1086     case OP_MAIN_UNDELETE_PATTERN:
1087
1088       CHECK_MSGCOUNT;
1089       CHECK_VISIBLE;
1090       CHECK_READONLY;
1091
1092       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
1093
1094       if (mutt_pattern_func (M_UNDELETE, _("Undelete messages matching: ")) ==
1095           0)
1096         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1097       break;
1098
1099     case OP_MAIN_UNTAG_PATTERN:
1100
1101       CHECK_MSGCOUNT;
1102       CHECK_VISIBLE;
1103       if (mutt_pattern_func (M_UNTAG, _("Untag messages matching: ")) == 0)
1104         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1105       break;
1106
1107       /* --------------------------------------------------------------------
1108        * The following operations can be performed inside of the pager.
1109        */
1110
1111 #ifdef USE_IMAP
1112     case OP_MAIN_IMAP_FETCH:
1113       if (Context->magic == M_IMAP)
1114         imap_check_mailbox (Context, &index_hint, 1);
1115       break;
1116 #endif
1117
1118     case OP_MAIN_SYNC_FOLDER:
1119
1120       if (Context && !Context->msgcount)
1121         break;
1122
1123       CHECK_MSGCOUNT;
1124       CHECK_VISIBLE;
1125       CHECK_READONLY;
1126       {
1127         int oldvcount = Context->vcount;
1128         int oldcount = Context->msgcount;
1129         int dcount = 0;
1130         int check;
1131
1132         /* calculate the number of messages _above_ the cursor,
1133          * so we can keep the cursor on the current message
1134          */
1135         for (j = 0; j <= menu->current; j++) {
1136           if (Context->hdrs[Context->v2r[j]]->deleted)
1137             dcount++;
1138         }
1139
1140         if ((check = mx_sync_mailbox (Context, &index_hint)) == 0) {
1141           if (Context->vcount != oldvcount)
1142             menu->current -= dcount;
1143           set_option (OPTSEARCHINVALID);
1144         }
1145         else if (check == M_NEW_MAIL || check == M_REOPENED)
1146           update_index (menu, Context, check, oldcount, index_hint);
1147
1148         /* 
1149          * do a sanity check even if mx_sync_mailbox failed.
1150          */
1151
1152         if (menu->current < 0 || menu->current >= Context->vcount)
1153           menu->current = ci_first_message ();
1154       }
1155
1156       /* check for a fatal error, or all messages deleted */
1157       if (!Context->path)
1158         FREE (&Context);
1159
1160       /* if we were in the pager, redisplay the message */
1161       if (menu->menu == MENU_PAGER) {
1162         op = OP_DISPLAY_MESSAGE;
1163         continue;
1164       }
1165       else
1166         menu->redraw = REDRAW_FULL;
1167       break;
1168
1169     case OP_SIDEBAR_OPEN:
1170     case OP_MAIN_CHANGE_FOLDER:
1171     case OP_MAIN_CHANGE_FOLDER_READONLY:
1172 #ifdef USE_NNTP
1173     case OP_MAIN_CHANGE_GROUP:
1174     case OP_MAIN_CHANGE_GROUP_READONLY:
1175 #endif
1176       if (attach_msg || option (OPTREADONLY) ||
1177 #ifdef USE_NNTP
1178           op == OP_MAIN_CHANGE_GROUP_READONLY ||
1179 #endif
1180           op == OP_MAIN_CHANGE_FOLDER_READONLY)
1181         flags = M_READONLY;
1182       else
1183         flags = 0;
1184
1185       if (flags)
1186         cp = _("Open mailbox in read-only mode");
1187       else
1188         cp = _("Open mailbox");
1189
1190       buf[0] = '\0';
1191 #ifdef USE_NNTP
1192       unset_option (OPTNEWS);
1193       if (op == OP_MAIN_CHANGE_GROUP || op == OP_MAIN_CHANGE_GROUP_READONLY) {
1194         set_option (OPTNEWS);
1195         if (!(CurrentNewsSrv = mutt_select_newsserver (NewsServer)))
1196           break;
1197         if (flags)
1198           cp = _("Open newsgroup in read-only mode");
1199         else
1200           cp = _("Open newsgroup");
1201         nntp_buffy (buf);
1202       }
1203       else
1204 #endif
1205       {
1206         if (Context && Context->path)
1207           strncpy (buf, Context->path, sizeof (buf));
1208         buffy_next (buf, sizeof (buf));
1209       }
1210
1211       if (op == OP_SIDEBAR_OPEN) {
1212         strncpy (buf, NONULL(sidebar_get_current ()), sizeof (buf));
1213       }
1214       else if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) ==
1215                -1)
1216         break;
1217       if (!buf[0]) {
1218         CLEARLINE (LINES - 1);
1219         break;
1220       }
1221
1222 #ifdef USE_NNTP
1223       if (option (OPTNEWS)) {
1224         unset_option (OPTNEWS);
1225         nntp_expand_path (buf, sizeof (buf), &CurrentNewsSrv->conn->account);
1226       }
1227       else
1228 #endif
1229         mutt_expand_path (buf, sizeof (buf));
1230       if (mx_get_magic (buf) <= 0) {
1231         mutt_error (_("%s is not a mailbox."), buf);
1232         break;
1233       }
1234       str_replace (&CurrentFolder, buf);
1235
1236       if (Context) {
1237         int check;
1238
1239 #ifdef USE_COMPRESSED
1240         if (Context->compressinfo && Context->realpath)
1241           str_replace (&LastFolder, Context->realpath);
1242         else
1243 #endif
1244
1245           str_replace (&LastFolder, Context->path);
1246         oldcount = Context ? Context->msgcount : 0;
1247
1248         if ((check = mx_close_mailbox (Context, &index_hint)) != 0) {
1249           if (check == M_NEW_MAIL || check == M_REOPENED)
1250             update_index (menu, Context, check, oldcount, index_hint);
1251
1252           set_option (OPTSEARCHINVALID);
1253           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1254           break;
1255         }
1256         FREE (&Context);
1257       }
1258
1259       mutt_sleep (0);
1260
1261       /* Set CurrentMenu to MENU_MAIN before executing any folder
1262        * hooks so that all the index menu functions are available to
1263        * the exec command.
1264        */
1265
1266       CurrentMenu = MENU_MAIN;
1267       mutt_folder_hook (buf);
1268
1269       if ((Context = mx_open_mailbox (buf, flags, NULL)) != NULL) {
1270         menu->current = ci_first_message ();
1271       }
1272       else
1273         menu->current = 0;
1274       sidebar_set_current (buf);
1275
1276 #ifdef USE_NNTP
1277       /* buffy_check() must be done with mail-reader mode! */
1278       menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN,
1279                                       (Context
1280                                        && (Context->magic ==
1281                                            M_NNTP)) ? IndexNewsHelp :
1282                                       IndexHelp);
1283 #endif
1284       mutt_clear_error ();
1285       buffy_check (1);     /* force the buffy check after we have changed
1286                                    the folder */
1287       menu->redraw = REDRAW_FULL;
1288       set_option (OPTSEARCHINVALID);
1289       break;
1290
1291     case OP_DISPLAY_MESSAGE:
1292     case OP_DISPLAY_HEADERS:   /* don't weed the headers */
1293
1294       CHECK_MSGCOUNT;
1295       CHECK_VISIBLE;
1296       /*
1297        * toggle the weeding of headers so that a user can press the key
1298        * again while reading the message.
1299        */
1300       if (op == OP_DISPLAY_HEADERS)
1301         toggle_option (OPTWEED);
1302
1303       unset_option (OPTNEEDRESORT);
1304
1305       if ((Sort & SORT_MASK) == SORT_THREADS && CURHDR->collapsed) {
1306         mutt_uncollapse_thread (Context, CURHDR);
1307         mutt_set_virtual (Context);
1308         if (option (OPTUNCOLLAPSEJUMP))
1309           menu->current = mutt_thread_next_unread (Context, CURHDR);
1310       }
1311
1312       if ((op = mutt_display_message (CURHDR)) == -1) {
1313         unset_option (OPTNEEDRESORT);
1314         break;
1315       }
1316
1317       menu->menu = MENU_PAGER;
1318       menu->oldcurrent = menu->current;
1319       continue;
1320
1321     case OP_EXIT:
1322
1323       close = op;
1324       if (menu->menu == MENU_MAIN && attach_msg) {
1325         done = 1;
1326         break;
1327       }
1328
1329       if ((menu->menu == MENU_MAIN)
1330           && (query_quadoption (OPT_QUIT,
1331                                 _("Exit Mutt-ng without saving?")) == M_YES))
1332       {
1333         if (Context) {
1334           mx_fastclose_mailbox (Context);
1335           FREE (&Context);
1336         }
1337         done = 1;
1338       }
1339       break;
1340
1341     case OP_EDIT_TYPE:
1342
1343       CHECK_MSGCOUNT;
1344       CHECK_VISIBLE;
1345       CHECK_ATTACH;
1346       mutt_edit_content_type (CURHDR, CURHDR->content, NULL);
1347       /* if we were in the pager, redisplay the message */
1348       if (menu->menu == MENU_PAGER) {
1349         op = OP_DISPLAY_MESSAGE;
1350         continue;
1351       }
1352       else
1353         menu->redraw = REDRAW_CURRENT;
1354       break;
1355
1356     case OP_MAIN_BREAK_THREAD:
1357
1358       CHECK_MSGCOUNT;
1359       CHECK_VISIBLE;
1360       CHECK_READONLY;
1361
1362       if ((Sort & SORT_MASK) != SORT_THREADS)
1363         mutt_error (_("Threading is not enabled."));
1364
1365       else {
1366         {
1367           HEADER *oldcur = CURHDR;
1368
1369           mutt_break_thread (CURHDR);
1370           mutt_sort_headers (Context, 1);
1371           menu->current = oldcur->virtual;
1372         }
1373
1374         Context->changed = 1;
1375         mutt_message _("Thread broken");
1376
1377         if (menu->menu == MENU_PAGER) {
1378           op = OP_DISPLAY_MESSAGE;
1379           continue;
1380         }
1381         else
1382           menu->redraw |= REDRAW_INDEX;
1383       }
1384       break;
1385
1386     case OP_MAIN_LINK_THREADS:
1387
1388       CHECK_MSGCOUNT;
1389       CHECK_VISIBLE;
1390       CHECK_READONLY;
1391
1392       if ((Sort & SORT_MASK) != SORT_THREADS)
1393         mutt_error (_("Threading is not enabled."));
1394
1395       else if (!CURHDR->env->message_id)
1396         mutt_error (_("No Message-ID: header available to link thread"));
1397
1398       else if (!tag && (!Context->last_tag || !Context->last_tag->tagged))
1399         mutt_error (_("First, please tag a message to be linked here"));
1400
1401       else {
1402         HEADER *oldcur = CURHDR;
1403
1404         if (mutt_link_threads (CURHDR, tag ? NULL : Context->last_tag,
1405                                Context)) {
1406           mutt_sort_headers (Context, 1);
1407           menu->current = oldcur->virtual;
1408
1409           Context->changed = 1;
1410           mutt_message _("Threads linked");
1411         }
1412         else
1413           mutt_error (_("No thread linked"));
1414       }
1415
1416       if (menu->menu == MENU_PAGER) {
1417         op = OP_DISPLAY_MESSAGE;
1418         continue;
1419       }
1420       else
1421         menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
1422       break;
1423
1424     case OP_MAIN_NEXT_UNDELETED:
1425
1426       CHECK_MSGCOUNT;
1427       CHECK_VISIBLE;
1428       if (menu->current >= Context->vcount - 1) {
1429         if (menu->menu == MENU_MAIN)
1430           mutt_error (_("You are on the last message."));
1431
1432         break;
1433       }
1434       if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1435         menu->current = menu->oldcurrent;
1436         if (menu->menu == MENU_MAIN)
1437           mutt_error (_("No undeleted messages."));
1438       }
1439       else if (menu->menu == MENU_PAGER) {
1440         op = OP_DISPLAY_MESSAGE;
1441         continue;
1442       }
1443       else
1444         menu->redraw = REDRAW_MOTION;
1445       break;
1446
1447     case OP_NEXT_ENTRY:
1448
1449       CHECK_MSGCOUNT;
1450       CHECK_VISIBLE;
1451       if (menu->current >= Context->vcount - 1) {
1452         if (menu->menu == MENU_MAIN)
1453           mutt_error (_("You are on the last message."));
1454
1455         break;
1456       }
1457       menu->current++;
1458       if (menu->menu == MENU_PAGER) {
1459         op = OP_DISPLAY_MESSAGE;
1460         continue;
1461       }
1462       else
1463         menu->redraw = REDRAW_MOTION;
1464       break;
1465
1466     case OP_MAIN_PREV_UNDELETED:
1467
1468       CHECK_MSGCOUNT;
1469       CHECK_VISIBLE;
1470       if (menu->current < 1) {
1471         mutt_error (_("You are on the first message."));
1472
1473         break;
1474       }
1475       if ((menu->current = ci_previous_undeleted (menu->current)) == -1) {
1476         menu->current = menu->oldcurrent;
1477         if (menu->menu == MENU_MAIN)
1478           mutt_error (_("No undeleted messages."));
1479       }
1480       else if (menu->menu == MENU_PAGER) {
1481         op = OP_DISPLAY_MESSAGE;
1482         continue;
1483       }
1484       else
1485         menu->redraw = REDRAW_MOTION;
1486       break;
1487
1488     case OP_PREV_ENTRY:
1489
1490       CHECK_MSGCOUNT;
1491       CHECK_VISIBLE;
1492       if (menu->current < 1) {
1493         if (menu->menu == MENU_MAIN)
1494           mutt_error (_("You are on the first message."));
1495
1496         break;
1497       }
1498       menu->current--;
1499       if (menu->menu == MENU_PAGER) {
1500         op = OP_DISPLAY_MESSAGE;
1501         continue;
1502       }
1503       else
1504         menu->redraw = REDRAW_MOTION;
1505       break;
1506
1507     case OP_DECRYPT_COPY:
1508     case OP_DECRYPT_SAVE:
1509       if (!WithCrypto)
1510         break;
1511       /* fall thru */
1512     case OP_COPY_MESSAGE:
1513     case OP_SAVE:
1514     case OP_DECODE_COPY:
1515     case OP_DECODE_SAVE:
1516       CHECK_MSGCOUNT;
1517       CHECK_VISIBLE;
1518       if (mutt_save_message (tag ? NULL : CURHDR,
1519                              (op == OP_DECRYPT_SAVE) ||
1520                              (op == OP_SAVE) || (op == OP_DECODE_SAVE),
1521                              (op == OP_DECODE_SAVE) || (op == OP_DECODE_COPY),
1522                              (op == OP_DECRYPT_SAVE)
1523                              || (op == OP_DECRYPT_COPY)
1524                              || 0, &menu->redraw) == 0 && (op == OP_SAVE
1525                                                            || op ==
1526                                                            OP_DECODE_SAVE
1527                                                            || op ==
1528                                                            OP_DECRYPT_SAVE)
1529         ) {
1530         if (tag)
1531           menu->redraw |= REDRAW_INDEX;
1532         else if (option (OPTRESOLVE)) {
1533           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1534             menu->current = menu->oldcurrent;
1535             menu->redraw |= REDRAW_CURRENT;
1536           }
1537           else
1538             menu->redraw |= REDRAW_MOTION_RESYNCH;
1539         }
1540         else
1541           menu->redraw |= REDRAW_CURRENT;
1542       }
1543       break;
1544
1545     case OP_MAIN_NEXT_NEW:
1546     case OP_MAIN_NEXT_UNREAD:
1547     case OP_MAIN_PREV_NEW:
1548     case OP_MAIN_PREV_UNREAD:
1549     case OP_MAIN_NEXT_NEW_THEN_UNREAD:
1550     case OP_MAIN_PREV_NEW_THEN_UNREAD:
1551
1552       {
1553         int first_unread = -1;
1554         int first_new = -1;
1555
1556         CHECK_MSGCOUNT;
1557         CHECK_VISIBLE;
1558
1559         i = menu->current;
1560         menu->current = -1;
1561         for (j = 0; j != Context->vcount; j++) {
1562 #define CURHDRi Context->hdrs[Context->v2r[i]]
1563           if (op == OP_MAIN_NEXT_NEW || op == OP_MAIN_NEXT_UNREAD
1564               || op == OP_MAIN_NEXT_NEW_THEN_UNREAD) {
1565             i++;
1566             if (i > Context->vcount - 1) {
1567               mutt_message _("Search wrapped to top.");
1568
1569               i = 0;
1570             }
1571           }
1572           else {
1573             i--;
1574             if (i < 0) {
1575               mutt_message _("Search wrapped to bottom.");
1576
1577               i = Context->vcount - 1;
1578             }
1579           }
1580
1581           if (CURHDRi->collapsed && (Sort & SORT_MASK) == SORT_THREADS) {
1582             if (UNREAD (CURHDRi) && first_unread == -1)
1583               first_unread = i;
1584             if (UNREAD (CURHDRi) == 1 && first_new == -1)
1585               first_new = i;
1586           }
1587           else if ((!CURHDRi->deleted && !CURHDRi->read)) {
1588             if (first_unread == -1)
1589               first_unread = i;
1590             if ((!CURHDRi->old) && first_new == -1)
1591               first_new = i;
1592           }
1593
1594           if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD) &&
1595               first_unread != -1)
1596             break;
1597           if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW ||
1598                op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1599                || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1600               && first_new != -1)
1601             break;
1602         }
1603 #undef CURHDRi
1604         if ((op == OP_MAIN_NEXT_NEW || op == OP_MAIN_PREV_NEW ||
1605              op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1606              || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1607             && first_new != -1)
1608           menu->current = first_new;
1609         else if ((op == OP_MAIN_NEXT_UNREAD || op == OP_MAIN_PREV_UNREAD ||
1610                   op == OP_MAIN_NEXT_NEW_THEN_UNREAD
1611                   || op == OP_MAIN_PREV_NEW_THEN_UNREAD)
1612                  && first_unread != -1)
1613           menu->current = first_unread;
1614
1615         if (menu->current == -1) {
1616           menu->current = menu->oldcurrent;
1617           mutt_error ("%s%s.",
1618                       (op == OP_MAIN_NEXT_NEW
1619                        || op ==
1620                        OP_MAIN_PREV_NEW) ? _("No new messages") :
1621                       _("No unread messages"),
1622                       Context->pattern ? _(" in this limited view") : "");
1623         }
1624         else if (menu->menu == MENU_PAGER) {
1625           op = OP_DISPLAY_MESSAGE;
1626           continue;
1627         }
1628         else
1629           menu->redraw = REDRAW_MOTION;
1630         break;
1631       }
1632     case OP_FLAG_MESSAGE:
1633
1634       CHECK_MSGCOUNT;
1635       CHECK_VISIBLE;
1636       CHECK_READONLY;
1637
1638       CHECK_MX_ACL (Context, ACL_WRITE, _("Flagging"));
1639
1640       if (tag) {
1641         for (j = 0; j < Context->vcount; j++) {
1642           if (Context->hdrs[Context->v2r[j]]->tagged)
1643             mutt_set_flag (Context, Context->hdrs[Context->v2r[j]],
1644                            M_FLAG, !Context->hdrs[Context->v2r[j]]->flagged);
1645         }
1646
1647         menu->redraw |= REDRAW_INDEX;
1648       }
1649       else {
1650         mutt_set_flag (Context, CURHDR, M_FLAG, !CURHDR->flagged);
1651         if (option (OPTRESOLVE)) {
1652           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1653             menu->current = menu->oldcurrent;
1654             menu->redraw = REDRAW_CURRENT;
1655           }
1656           else
1657             menu->redraw = REDRAW_MOTION_RESYNCH;
1658         }
1659         else
1660           menu->redraw = REDRAW_CURRENT;
1661       }
1662       menu->redraw |= REDRAW_STATUS;
1663       break;
1664
1665     case OP_TOGGLE_NEW:
1666
1667       CHECK_MSGCOUNT;
1668       CHECK_VISIBLE;
1669       CHECK_READONLY;
1670
1671       CHECK_MX_ACL (Context, ACL_SEEN, _("Toggling"));
1672
1673       if (tag) {
1674         for (j = 0; j < Context->vcount; j++) {
1675           if (Context->hdrs[Context->v2r[j]]->tagged) {
1676             if (Context->hdrs[Context->v2r[j]]->read ||
1677                 Context->hdrs[Context->v2r[j]]->old)
1678               mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_NEW,
1679                              1);
1680             else
1681               mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], M_READ,
1682                              1);
1683           }
1684         }
1685         menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
1686       }
1687       else {
1688         if (CURHDR->read || CURHDR->old)
1689           mutt_set_flag (Context, CURHDR, M_NEW, 1);
1690         else
1691           mutt_set_flag (Context, CURHDR, M_READ, 1);
1692
1693         if (option (OPTRESOLVE)) {
1694           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1695             menu->current = menu->oldcurrent;
1696             menu->redraw = REDRAW_CURRENT;
1697           }
1698           else
1699             menu->redraw = REDRAW_MOTION_RESYNCH;
1700         }
1701         else
1702           menu->redraw = REDRAW_CURRENT;
1703         menu->redraw |= REDRAW_STATUS;
1704       }
1705       break;
1706
1707     case OP_TOGGLE_WRITE:
1708
1709       CHECK_MSGCOUNT;
1710       if (mx_toggle_write (Context) == 0)
1711         menu->redraw |= REDRAW_STATUS;
1712       break;
1713
1714     case OP_MAIN_NEXT_THREAD:
1715     case OP_MAIN_NEXT_SUBTHREAD:
1716     case OP_MAIN_PREV_THREAD:
1717     case OP_MAIN_PREV_SUBTHREAD:
1718
1719       CHECK_MSGCOUNT;
1720       CHECK_VISIBLE;
1721       switch (op) {
1722       case OP_MAIN_NEXT_THREAD:
1723         menu->current = mutt_next_thread (CURHDR);
1724         break;
1725
1726       case OP_MAIN_NEXT_SUBTHREAD:
1727         menu->current = mutt_next_subthread (CURHDR);
1728         break;
1729
1730       case OP_MAIN_PREV_THREAD:
1731         menu->current = mutt_previous_thread (CURHDR);
1732         break;
1733
1734       case OP_MAIN_PREV_SUBTHREAD:
1735         menu->current = mutt_previous_subthread (CURHDR);
1736         break;
1737       }
1738
1739       if (menu->current < 0) {
1740         menu->current = menu->oldcurrent;
1741         if (op == OP_MAIN_NEXT_THREAD || op == OP_MAIN_NEXT_SUBTHREAD)
1742           mutt_error (_("No more threads."));
1743
1744         else
1745           mutt_error (_("You are on the first thread."));
1746       }
1747       else if (menu->menu == MENU_PAGER) {
1748         op = OP_DISPLAY_MESSAGE;
1749         continue;
1750       }
1751       else
1752         menu->redraw = REDRAW_MOTION;
1753       break;
1754
1755     case OP_MAIN_PARENT_MESSAGE:
1756
1757       CHECK_MSGCOUNT;
1758       CHECK_VISIBLE;
1759
1760       if ((menu->current = mutt_parent_message (Context, CURHDR)) < 0) {
1761         menu->current = menu->oldcurrent;
1762       }
1763       else if (menu->menu == MENU_PAGER) {
1764         op = OP_DISPLAY_MESSAGE;
1765         continue;
1766       }
1767       else
1768         menu->redraw = REDRAW_MOTION;
1769       break;
1770
1771     case OP_MAIN_SET_FLAG:
1772     case OP_MAIN_CLEAR_FLAG:
1773
1774       CHECK_MSGCOUNT;
1775       CHECK_VISIBLE;
1776       CHECK_READONLY;
1777
1778       if (mutt_change_flag (tag ? NULL : CURHDR, (op == OP_MAIN_SET_FLAG)) ==
1779           0) {
1780         menu->redraw = REDRAW_STATUS;
1781         if (tag)
1782           menu->redraw |= REDRAW_INDEX;
1783         else if (option (OPTRESOLVE)) {
1784           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1785             menu->current = menu->oldcurrent;
1786             menu->redraw |= REDRAW_CURRENT;
1787           }
1788           else
1789             menu->redraw |= REDRAW_MOTION_RESYNCH;
1790         }
1791         else
1792           menu->redraw |= REDRAW_CURRENT;
1793       }
1794       break;
1795
1796     case OP_MAIN_COLLAPSE_THREAD:
1797       CHECK_MSGCOUNT;
1798       CHECK_VISIBLE;
1799
1800       if ((Sort & SORT_MASK) != SORT_THREADS) {
1801         mutt_error (_("Threading is not enabled."));
1802
1803         break;
1804       }
1805
1806       if (CURHDR->collapsed) {
1807         menu->current = mutt_uncollapse_thread (Context, CURHDR);
1808         mutt_set_virtual (Context);
1809         if (option (OPTUNCOLLAPSEJUMP))
1810           menu->current = mutt_thread_next_unread (Context, CURHDR);
1811       }
1812       else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (CURHDR)) {
1813         menu->current = mutt_collapse_thread (Context, CURHDR);
1814         mutt_set_virtual (Context);
1815       }
1816       else {
1817         mutt_error (_("Thread contains unread messages."));
1818
1819         break;
1820       }
1821
1822       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1823
1824       break;
1825
1826     case OP_MAIN_COLLAPSE_ALL:
1827       CHECK_MSGCOUNT;
1828       CHECK_VISIBLE;
1829
1830       if ((Sort & SORT_MASK) != SORT_THREADS) {
1831         mutt_error (_("Threading is not enabled."));
1832
1833         break;
1834       }
1835
1836       {
1837         HEADER *h, *base;
1838         THREAD *thread, *top;
1839         int final;
1840
1841         if (CURHDR->collapsed)
1842           final = mutt_uncollapse_thread (Context, CURHDR);
1843         else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (CURHDR))
1844           final = mutt_collapse_thread (Context, CURHDR);
1845         else
1846           final = CURHDR->virtual;
1847
1848         base = Context->hdrs[Context->v2r[final]];
1849
1850         top = Context->tree;
1851         Context->collapsed = !Context->collapsed;
1852         while ((thread = top) != NULL) {
1853           while (!thread->message)
1854             thread = thread->child;
1855           h = thread->message;
1856
1857           if (h->collapsed != Context->collapsed) {
1858             if (h->collapsed)
1859               mutt_uncollapse_thread (Context, h);
1860             else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (h))
1861               mutt_collapse_thread (Context, h);
1862           }
1863           top = top->next;
1864         }
1865
1866         mutt_set_virtual (Context);
1867         for (j = 0; j < Context->vcount; j++) {
1868           if (Context->hdrs[Context->v2r[j]]->index == base->index) {
1869             menu->current = j;
1870             break;
1871           }
1872         }
1873
1874         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1875       }
1876       break;
1877
1878       /* --------------------------------------------------------------------
1879        * These functions are invoked directly from the internal-pager
1880        */
1881
1882     case OP_BOUNCE_MESSAGE:
1883
1884       CHECK_ATTACH;
1885       CHECK_MSGCOUNT;
1886       CHECK_VISIBLE;
1887       ci_bounce_message (tag ? NULL : CURHDR, &menu->redraw);
1888       break;
1889
1890     case OP_CREATE_ALIAS:
1891
1892       mutt_create_alias (Context
1893                          && Context->vcount ? CURHDR->env : NULL, NULL);
1894       MAYBE_REDRAW (menu->redraw);
1895       menu->redraw |= REDRAW_CURRENT;
1896       break;
1897
1898     case OP_QUERY:
1899       CHECK_ATTACH;
1900       mutt_query_menu (NULL, 0);
1901       MAYBE_REDRAW (menu->redraw);
1902       break;
1903
1904     case OP_PURGE_MESSAGE:
1905     case OP_DELETE:
1906
1907       CHECK_MSGCOUNT;
1908       CHECK_VISIBLE;
1909       CHECK_READONLY;
1910
1911       CHECK_MX_ACL (Context, ACL_DELETE, _("Deletion"));
1912
1913       if (tag) {
1914         mutt_tag_set_flag (M_DELETE, 1);
1915         mutt_tag_set_flag (M_PURGED, (op != OP_PURGE_MESSAGE) ? 0 : 1);
1916         if (option (OPTDELETEUNTAG))
1917           mutt_tag_set_flag (M_TAG, 0);
1918         menu->redraw = REDRAW_INDEX;
1919       }
1920       else {
1921         mutt_set_flag (Context, CURHDR, M_DELETE, 1);
1922         mutt_set_flag (Context, CURHDR, M_PURGED,
1923                        (op != OP_PURGE_MESSAGE) ? 0 : 1);
1924         if (option (OPTDELETEUNTAG))
1925           mutt_set_flag (Context, CURHDR, M_TAG, 0);
1926         if (option (OPTRESOLVE)) {
1927           if ((menu->current = ci_next_undeleted (menu->current)) == -1) {
1928             menu->current = menu->oldcurrent;
1929             menu->redraw = REDRAW_CURRENT;
1930           }
1931           else if (menu->menu == MENU_PAGER) {
1932             op = OP_DISPLAY_MESSAGE;
1933             continue;
1934           }
1935           else
1936             menu->redraw |= REDRAW_MOTION_RESYNCH;
1937         }
1938         else
1939           menu->redraw = REDRAW_CURRENT;
1940       }
1941       menu->redraw |= REDRAW_STATUS;
1942       break;
1943
1944     case OP_DELETE_THREAD:
1945     case OP_DELETE_SUBTHREAD:
1946
1947       CHECK_MSGCOUNT;
1948       CHECK_VISIBLE;
1949       CHECK_READONLY;
1950
1951       CHECK_MX_ACL (Context, ACL_DELETE, _("Deletion"));
1952
1953       rc = mutt_thread_set_flag (CURHDR, M_DELETE, 1,
1954                                  op == OP_DELETE_THREAD ? 0 : 1);
1955
1956       if (rc != -1) {
1957         if (option (OPTDELETEUNTAG))
1958           mutt_thread_set_flag (CURHDR, M_TAG, 0,
1959                                 op == OP_DELETE_THREAD ? 0 : 1);
1960         if (option (OPTRESOLVE))
1961           if ((menu->current = ci_next_undeleted (menu->current)) == -1)
1962             menu->current = menu->oldcurrent;
1963         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1964       }
1965       break;
1966
1967 #ifdef USE_NNTP
1968     case OP_CATCHUP:
1969       if (Context && Context->magic == M_NNTP) {
1970         if (mutt_newsgroup_catchup (CurrentNewsSrv,
1971                                     ((NNTP_DATA *) Context->data)->group))
1972           menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
1973       }
1974       break;
1975 #endif
1976
1977     case OP_DISPLAY_ADDRESS:
1978
1979       CHECK_MSGCOUNT;
1980       CHECK_VISIBLE;
1981       mutt_display_address (CURHDR->env);
1982       break;
1983
1984     case OP_ENTER_COMMAND:
1985
1986       CurrentMenu = MENU_MAIN;
1987       mutt_enter_command ();
1988       mutt_check_rescore (Context);
1989       if (option (OPTFORCEREDRAWINDEX))
1990         menu->redraw = REDRAW_FULL;
1991       unset_option (OPTFORCEREDRAWINDEX);
1992       unset_option (OPTFORCEREDRAWPAGER);
1993       break;
1994
1995     case OP_EDIT_MESSAGE:
1996
1997       CHECK_MSGCOUNT;
1998       CHECK_VISIBLE;
1999       CHECK_READONLY;
2000       CHECK_ATTACH;
2001
2002       CHECK_MX_ACL (Context, ACL_INSERT, _("Editing"));
2003
2004       if (option (OPTPGPAUTODEC)
2005           && (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED)))
2006         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2007       mutt_edit_message (Context, tag ? NULL : CURHDR);
2008       menu->redraw = REDRAW_FULL;
2009
2010       break;
2011
2012     case OP_FORWARD_MESSAGE:
2013
2014       CHECK_MSGCOUNT;
2015       CHECK_VISIBLE;
2016       CHECK_ATTACH;
2017       ci_send_message (SENDFORWARD, NULL, NULL, Context, tag ? NULL : CURHDR);
2018       menu->redraw = REDRAW_FULL;
2019       break;
2020
2021
2022     case OP_FORGET_PASSPHRASE:
2023       crypt_forget_passphrase ();
2024       break;
2025
2026     case OP_GROUP_REPLY:
2027
2028       CHECK_MSGCOUNT;
2029       CHECK_VISIBLE;
2030       CHECK_ATTACH;
2031       ci_send_message (SENDREPLY | SENDGROUPREPLY, NULL, NULL, Context,
2032                        tag ? NULL : CURHDR);
2033       menu->redraw = REDRAW_FULL;
2034       break;
2035
2036     case OP_LIST_REPLY:
2037
2038       CHECK_ATTACH;
2039       CHECK_MSGCOUNT;
2040       CHECK_VISIBLE;
2041       ci_send_message (SENDREPLY | SENDLISTREPLY, NULL, NULL, Context,
2042                        tag ? NULL : CURHDR);
2043       menu->redraw = REDRAW_FULL;
2044       break;
2045
2046     case OP_MAIL:
2047
2048       CHECK_ATTACH;
2049       ci_send_message (0, NULL, NULL, Context, NULL);
2050       menu->redraw = REDRAW_FULL;
2051       break;
2052
2053     case OP_MAIL_KEY:
2054       if (!(WithCrypto & APPLICATION_PGP))
2055         break;
2056       CHECK_ATTACH;
2057       ci_send_message (SENDKEY, NULL, NULL, NULL, NULL);
2058       menu->redraw = REDRAW_FULL;
2059       break;
2060
2061
2062     case OP_EXTRACT_KEYS:
2063       if (!WithCrypto)
2064         break;
2065       CHECK_MSGCOUNT;
2066       CHECK_VISIBLE;
2067       crypt_extract_keys_from_messages (tag ? NULL : CURHDR);
2068       menu->redraw = REDRAW_FULL;
2069       break;
2070
2071
2072     case OP_CHECK_TRADITIONAL:
2073       if (!(WithCrypto & APPLICATION_PGP))
2074         break;
2075       CHECK_MSGCOUNT;
2076       CHECK_VISIBLE;
2077       if (tag || !(CURHDR->security & PGP_TRADITIONAL_CHECKED))
2078         mutt_check_traditional_pgp (tag ? NULL : CURHDR, &menu->redraw);
2079
2080       if (menu->menu == MENU_PAGER) {
2081         op = OP_DISPLAY_MESSAGE;
2082         continue;
2083       }
2084       break;
2085
2086     case OP_PIPE:
2087
2088       CHECK_MSGCOUNT;
2089       CHECK_VISIBLE;
2090       mutt_pipe_message (tag ? NULL : CURHDR);
2091       MAYBE_REDRAW (menu->redraw);
2092       break;
2093
2094     case OP_PRINT:
2095
2096       CHECK_MSGCOUNT;
2097       CHECK_VISIBLE;
2098       mutt_print_message (tag ? NULL : CURHDR);
2099       break;
2100
2101     case OP_MAIN_READ_THREAD:
2102     case OP_MAIN_READ_SUBTHREAD:
2103
2104       CHECK_MSGCOUNT;
2105       CHECK_VISIBLE;
2106       CHECK_READONLY;
2107
2108       CHECK_MX_ACL (Context, ACL_SEEN, _("Marking as read"));
2109
2110       rc = mutt_thread_set_flag (CURHDR, M_READ, 1,
2111                                  op == OP_MAIN_READ_THREAD ? 0 : 1);
2112
2113       if (rc != -1) {
2114         if (option (OPTRESOLVE)) {
2115           if ((menu->current = (op == OP_MAIN_READ_THREAD ?
2116                                 mutt_next_thread (CURHDR) :
2117                                 mutt_next_subthread (CURHDR))) == -1)
2118             menu->current = menu->oldcurrent;
2119         }
2120         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2121       }
2122       break;
2123
2124     case OP_RECALL_MESSAGE:
2125
2126       CHECK_ATTACH;
2127       ci_send_message (SENDPOSTPONED, NULL, NULL, Context, NULL);
2128       menu->redraw = REDRAW_FULL;
2129       break;
2130
2131     case OP_RESEND:
2132
2133       CHECK_ATTACH;
2134       CHECK_MSGCOUNT;
2135       CHECK_VISIBLE;
2136
2137       if (tag) {
2138         for (j = 0; j < Context->vcount; j++) {
2139           if (Context->hdrs[Context->v2r[j]]->tagged)
2140             mutt_resend_message (NULL, Context,
2141                                  Context->hdrs[Context->v2r[j]]);
2142         }
2143       }
2144       else
2145         mutt_resend_message (NULL, Context, CURHDR);
2146
2147       menu->redraw = REDRAW_FULL;
2148       break;
2149
2150 #ifdef USE_NNTP
2151     case OP_POST:
2152     case OP_FOLLOWUP:
2153     case OP_FORWARD_TO_GROUP:
2154
2155       CHECK_ATTACH;
2156       if ((op == OP_FOLLOWUP || op == OP_FORWARD_TO_GROUP) &&
2157           Context && Context->msgcount == 0) {
2158         mutt_error (_("There are no messages."));
2159         sleep (2);
2160       }
2161       else if (op != OP_FOLLOWUP || !CURHDR->env->followup_to ||
2162                safe_strcasecmp (CURHDR->env->followup_to, "poster") ||
2163                query_quadoption (OPT_FOLLOWUPTOPOSTER,
2164                                  _("Reply by mail as poster prefers?")) !=
2165                M_YES) {
2166         if (Context && Context->magic == M_NNTP
2167             && !((NNTP_DATA *) Context->data)->allowed
2168             && query_quadoption (OPT_TOMODERATED,
2169                                  _
2170                                  ("Posting to this group not allowed, may be moderated. Continue?"))
2171             != M_YES)
2172           break;
2173         if (op == OP_POST)
2174           ci_send_message (SENDNEWS, NULL, NULL, Context, NULL);
2175         else {
2176           CHECK_MSGCOUNT;
2177           if (op == OP_FOLLOWUP)
2178             ci_send_message (SENDNEWS | SENDREPLY, NULL, NULL, Context,
2179                              tag ? NULL : CURHDR);
2180           else
2181             ci_send_message (SENDNEWS | SENDFORWARD, NULL, NULL, Context,
2182                              tag ? NULL : CURHDR);
2183         }
2184         menu->redraw = REDRAW_FULL;
2185         break;
2186       }
2187 #endif
2188
2189     case OP_REPLY:
2190
2191       CHECK_ATTACH;
2192       CHECK_MSGCOUNT;
2193       CHECK_VISIBLE;
2194       ci_send_message (SENDREPLY, NULL, NULL, Context, tag ? NULL : CURHDR);
2195       menu->redraw = REDRAW_FULL;
2196       break;
2197
2198     case OP_SHELL_ESCAPE:
2199
2200       mutt_shell_escape ();
2201       MAYBE_REDRAW (menu->redraw);
2202       break;
2203
2204     case OP_TAG_THREAD:
2205     case OP_TAG_SUBTHREAD:
2206
2207       CHECK_MSGCOUNT;
2208       CHECK_VISIBLE;
2209       rc = mutt_thread_set_flag (CURHDR, M_TAG, !CURHDR->tagged,
2210                                  op == OP_TAG_THREAD ? 0 : 1);
2211
2212       if (rc != -1) {
2213         if (option (OPTRESOLVE)) {
2214           menu->current = mutt_next_thread (CURHDR);
2215
2216           if (menu->current == -1)
2217             menu->current = menu->oldcurrent;
2218         }
2219         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2220       }
2221       break;
2222
2223     case OP_UNDELETE:
2224
2225       CHECK_MSGCOUNT;
2226       CHECK_VISIBLE;
2227       CHECK_READONLY;
2228
2229       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
2230
2231       if (tag) {
2232         mutt_tag_set_flag (M_DELETE, 0);
2233         mutt_tag_set_flag (M_PURGED, 0);
2234         menu->redraw = REDRAW_INDEX;
2235       }
2236       else {
2237         mutt_set_flag (Context, CURHDR, M_DELETE, 0);
2238         mutt_set_flag (Context, CURHDR, M_PURGED, 0);
2239         if (option (OPTRESOLVE) && menu->current < Context->vcount - 1) {
2240           menu->current++;
2241           menu->redraw = REDRAW_MOTION_RESYNCH;
2242         }
2243         else
2244           menu->redraw = REDRAW_CURRENT;
2245       }
2246       menu->redraw |= REDRAW_STATUS;
2247       break;
2248
2249     case OP_UNDELETE_THREAD:
2250     case OP_UNDELETE_SUBTHREAD:
2251
2252       CHECK_MSGCOUNT;
2253       CHECK_VISIBLE;
2254       CHECK_READONLY;
2255
2256       CHECK_MX_ACL (Context, ACL_DELETE, _("Undeletion"));
2257
2258       rc = mutt_thread_set_flag (CURHDR, M_DELETE, 0,
2259                                  op == OP_UNDELETE_THREAD ? 0 : 1)
2260         + mutt_thread_set_flag (CURHDR, M_PURGED, 0,
2261                                 op == OP_UNDELETE_THREAD ? 0 : 1);
2262
2263       if (rc > -1) {
2264         if (option (OPTRESOLVE)) {
2265           if (op == OP_UNDELETE_THREAD)
2266             menu->current = mutt_next_thread (CURHDR);
2267           else
2268             menu->current = mutt_next_subthread (CURHDR);
2269
2270           if (menu->current == -1)
2271             menu->current = menu->oldcurrent;
2272         }
2273         menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
2274       }
2275       break;
2276
2277     case OP_VERSION:
2278       mutt_version ();
2279       break;
2280
2281     case OP_BUFFY_LIST:
2282       buffy_list ();
2283       menu->redraw = REDRAW_FULL;
2284       break;
2285
2286     case OP_VIEW_ATTACHMENTS:
2287       CHECK_MSGCOUNT;
2288       CHECK_VISIBLE;
2289       mutt_view_attachments (CURHDR);
2290       if (CURHDR->attach_del)
2291         Context->changed = 1;
2292       menu->redraw = REDRAW_FULL;
2293       break;
2294
2295     case OP_END_COND:
2296       break;
2297
2298     case OP_WHAT_KEY:
2299       mutt_what_key ();
2300       break;
2301
2302     case OP_SIDEBAR_SCROLL_UP:
2303     case OP_SIDEBAR_SCROLL_DOWN:
2304     case OP_SIDEBAR_NEXT:
2305     case OP_SIDEBAR_PREV:
2306     case OP_SIDEBAR_NEXT_NEW:
2307     case OP_SIDEBAR_PREV_NEW:
2308       sidebar_scroll (op, menu->menu);
2309       break;
2310     default:
2311       if (menu->menu == MENU_MAIN)
2312         km_error_key (MENU_MAIN);
2313     }
2314
2315     if (menu->menu == MENU_PAGER) {
2316       menu->menu = MENU_MAIN;
2317       menu->redraw = REDRAW_FULL;
2318 #if 0
2319       set_option (OPTWEED);     /* turn header weeding back on. */
2320 #endif
2321     }
2322
2323     if (done)
2324       break;
2325   }
2326
2327 #ifdef USE_IMAP
2328   /* Close all open IMAP connections */
2329   if (!attach_msg)
2330     imap_logout_all ();
2331 #endif
2332 #ifdef USE_NNTP
2333   /* Close all open NNTP connections */
2334   if (!attach_msg)
2335     nntp_logout_all ();
2336 #endif
2337
2338   mutt_menuDestroy (&menu);
2339   return (close);
2340 }
2341
2342 void mutt_set_header_color (CONTEXT * ctx, HEADER * curhdr)
2343 {
2344   COLOR_LINE *color;
2345
2346   if (!curhdr)
2347     return;
2348
2349   for (color = ColorIndexList; color; color = color->next)
2350     if (mutt_pattern_exec
2351         (color->color_pattern, M_MATCH_FULL_ADDRESS, ctx, curhdr)) {
2352       curhdr->pair = color->pair;
2353       return;
2354     }
2355   curhdr->pair = ColorDefs[MT_COLOR_NORMAL];
2356 }