NeoMutt  2018-07-16 +2481-68dcde
Teaching an old dog new tricks
DOXYGEN
pager.c
Go to the documentation of this file.
1 
29 #include "config.h"
30 #include <ctype.h>
31 #include <errno.h>
32 #include <inttypes.h>
33 #include <limits.h>
34 #include <regex.h>
35 #include <stdbool.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <unistd.h>
40 #include <wchar.h>
41 #include "mutt/mutt.h"
42 #include "config/lib.h"
43 #include "email/lib.h"
44 #include "core/lib.h"
45 #include "mutt.h"
46 #include "pager.h"
47 #include "alias.h"
48 #include "color.h"
49 #include "commands.h"
50 #include "context.h"
51 #include "curs_lib.h"
52 #include "format_flags.h"
53 #include "globals.h"
54 #include "hdrline.h"
55 #include "hook.h"
56 #include "index.h"
57 #include "keymap.h"
58 #include "mutt_attach.h"
59 #include "mutt_curses.h"
60 #include "mutt_header.h"
61 #include "mutt_logging.h"
62 #include "mutt_mailbox.h"
63 #include "mutt_menu.h"
64 #include "mutt_window.h"
65 #include "muttlib.h"
66 #include "mx.h"
67 #include "ncrypt/ncrypt.h"
68 #include "opcodes.h"
69 #include "options.h"
70 #include "protos.h"
71 #include "recvattach.h"
72 #include "recvcmd.h"
73 #include "send.h"
74 #include "status.h"
75 #include "terminal.h"
76 #ifdef USE_SIDEBAR
77 #include "sidebar.h"
78 #endif
79 #ifdef USE_NNTP
80 #include "nntp/nntp.h"
81 #endif
82 #ifdef ENABLE_NLS
83 #include <libintl.h>
84 #endif
85 
86 // clang-format off
87 /* These Config Variables are only used in pager.c */
96 struct Regex *C_Smileys;
97 bool C_Tilde;
98 // clang-format on
99 
100 // clang-format off
101 typedef uint8_t AnsiFlags;
102 #define ANSI_NO_FLAGS 0
103 #define ANSI_OFF (1 << 0)
104 #define ANSI_BLINK (1 << 1)
105 #define ANSI_BOLD (1 << 2)
106 #define ANSI_UNDERLINE (1 << 3)
107 #define ANSI_REVERSE (1 << 4)
108 #define ANSI_COLOR (1 << 5)
109 // clang-format on
110 
114 struct QClass
115 {
116  size_t length;
117  int index;
118  int color;
119  char *prefix;
120  struct QClass *next, *prev;
121  struct QClass *down, *up;
122 };
123 
128 {
129  int color;
130  int first;
131  int last;
132 };
133 
137 struct Line
138 {
139  LOFF_T offset;
140  short type;
142  short chunks;
143  short search_cnt;
146  struct QClass *quote;
147  unsigned int is_cont_hdr;
148 };
149 
153 struct AnsiAttr
154 {
156  int fg;
157  int bg;
158  int pair;
159 };
160 
164 struct Resize
165 {
166  int line;
169 };
170 
175 {
177  struct Pager *extra;
178  int indexlen;
179  int indicator;
181  int lines;
182  int max_line;
184  int curline;
185  int topline;
189  int q_level;
191  LOFF_T last_pos;
192  LOFF_T last_offset;
197  struct Menu *menu;
198  regex_t search_re;
202  const char *banner;
203  const char *helpstr;
204  char *searchbuf;
205  struct Line *line_info;
206  FILE *fp;
207  struct stat sb;
208 };
209 
210 /* hack to return to position when returning from index to same message */
211 static int TopLine = 0;
212 static struct Email *OldEmail = NULL;
213 
214 static bool InHelp = false;
215 
216 static int braille_line = -1;
217 static int braille_col = -1;
218 
219 static struct Resize *Resize = NULL;
220 
221 static const char *Not_available_in_this_menu =
222  N_("Not available in this menu");
223 static const char *Mailbox_is_read_only = N_("Mailbox is read-only");
225  N_("Function not permitted in attach-message mode");
226 
227 static const struct Mapping PagerHelp[] = {
228  { N_("Exit"), OP_EXIT },
229  { N_("PrevPg"), OP_PREV_PAGE },
230  { N_("NextPg"), OP_NEXT_PAGE },
231  { NULL, 0 },
232 };
233 
234 static const struct Mapping PagerHelpExtra[] = {
235  { N_("View Attachm."), OP_VIEW_ATTACHMENTS },
236  { N_("Del"), OP_DELETE },
237  { N_("Reply"), OP_REPLY },
238  { N_("Next"), OP_MAIN_NEXT_UNDELETED },
239  { NULL, 0 },
240 };
241 
242 #ifdef USE_NNTP
243 static struct Mapping PagerNewsHelpExtra[] = {
244  { N_("Post"), OP_POST },
245  { N_("Followup"), OP_FOLLOWUP },
246  { N_("Del"), OP_DELETE },
247  { N_("Next"), OP_MAIN_NEXT_UNDELETED },
248  { NULL, 0 },
249 };
250 #endif
251 
252 #define IS_HEADER(x) ((x) == MT_COLOR_HEADER || (x) == MT_COLOR_HDEFAULT)
253 
254 #define IsAttach(pager) (pager && (pager)->body)
255 #define IsMsgAttach(pager) \
256  (pager && (pager)->fp && (pager)->body && (pager)->body->email)
257 #define IsEmail(pager) (pager && (pager)->email && !(pager)->body)
258 
259 #define NUM_SIG_LINES 4
260 
261 #define CHECK_MODE(test) \
262  if (!(test)) \
263  { \
264  mutt_flushinp(); \
265  mutt_error(_(Not_available_in_this_menu)); \
266  break; \
267  }
268 
269 #define CHECK_READONLY \
270  if (!Context || Context->mailbox->readonly) \
271  { \
272  mutt_flushinp(); \
273  mutt_error(_(Mailbox_is_read_only)); \
274  break; \
275  }
276 
277 #define CHECK_ATTACH \
278  if (OptAttachMsg) \
279  { \
280  mutt_flushinp(); \
281  mutt_error(_(Function_not_permitted_in_attach_message_mode)); \
282  break; \
283  }
284 
285 #define CHECK_ACL(aclbit, action) \
286  if (!Context || !(Context->mailbox->rights & aclbit)) \
287  { \
288  mutt_flushinp(); \
289  /* L10N: %s is one of the CHECK_ACL entries below. */ \
290  mutt_error(_("%s: Operation not permitted by ACL"), action); \
291  break; \
292  }
293 
302 static int check_sig(const char *s, struct Line *info, int n)
303 {
304  int count = 0;
305 
306  while ((n > 0) && (count <= NUM_SIG_LINES))
307  {
308  if (info[n].type != MT_COLOR_SIGNATURE)
309  break;
310  count++;
311  n--;
312  }
313 
314  if (count == 0)
315  return -1;
316 
317  if (count > NUM_SIG_LINES)
318  {
319  /* check for a blank line */
320  while (*s)
321  {
322  if (!IS_SPACE(*s))
323  return 0;
324  s++;
325  }
326 
327  return -1;
328  }
329 
330  return 0;
331 }
332 
341 static int comp_syntax_t(const void *m1, const void *m2)
342 {
343  const int *cnt = (const int *) m1;
344  const struct TextSyntax *stx = (const struct TextSyntax *) m2;
345 
346  if (*cnt < stx->first)
347  return -1;
348  if (*cnt >= stx->last)
349  return 1;
350  return 0;
351 }
352 
362 static void resolve_color(struct Line *line_info, int n, int cnt,
363  PagerFlags flags, int special, struct AnsiAttr *a)
364 {
365  int def_color; /* color without syntax highlight */
366  int color; /* final color */
367  static int last_color; /* last color set */
368  bool search = false;
369  int m;
370  struct TextSyntax *matching_chunk = NULL;
371 
372  if (cnt == 0)
373  last_color = -1; /* force attrset() */
374 
375  if (line_info[n].continuation)
376  {
377  if (!cnt && C_Markers)
378  {
380  addch('+');
381  last_color = ColorDefs[MT_COLOR_MARKERS];
382  }
383  m = (line_info[n].syntax)[0].first;
384  cnt += (line_info[n].syntax)[0].last;
385  }
386  else
387  m = n;
388  if (flags & MUTT_PAGER_LOGS)
389  {
390  def_color = ColorDefs[(line_info[n].syntax)[0].color];
391  }
392  else if (!(flags & MUTT_SHOWCOLOR))
393  def_color = ColorDefs[MT_COLOR_NORMAL];
394  else if (line_info[m].type == MT_COLOR_HEADER)
395  def_color = (line_info[m].syntax)[0].color;
396  else
397  def_color = ColorDefs[line_info[m].type];
398 
399  if ((flags & MUTT_SHOWCOLOR) && (line_info[m].type == MT_COLOR_QUOTED))
400  {
401  struct QClass *qc = line_info[m].quote;
402 
403  if (qc)
404  {
405  def_color = qc->color;
406 
407  while (qc && (qc->length > cnt))
408  {
409  def_color = qc->color;
410  qc = qc->up;
411  }
412  }
413  }
414 
415  color = def_color;
416  if ((flags & MUTT_SHOWCOLOR) && line_info[m].chunks)
417  {
418  matching_chunk = bsearch(&cnt, line_info[m].syntax, line_info[m].chunks,
419  sizeof(struct TextSyntax), comp_syntax_t);
420  if (matching_chunk && (cnt >= matching_chunk->first) &&
421  (cnt < matching_chunk->last))
422  {
423  color = matching_chunk->color;
424  }
425  }
426 
427  if ((flags & MUTT_SEARCH) && line_info[m].search_cnt)
428  {
429  matching_chunk = bsearch(&cnt, line_info[m].search, line_info[m].search_cnt,
430  sizeof(struct TextSyntax), comp_syntax_t);
431  if (matching_chunk && (cnt >= matching_chunk->first) &&
432  (cnt < matching_chunk->last))
433  {
434  color = ColorDefs[MT_COLOR_SEARCH];
435  search = 1;
436  }
437  }
438 
439  /* handle "special" bold & underlined characters */
440  if (special || a->attr)
441  {
442 #ifdef HAVE_COLOR
443  if ((a->attr & ANSI_COLOR))
444  {
445  if (a->pair == -1)
446  a->pair = mutt_alloc_color(a->fg, a->bg);
447  color = a->pair;
448  if (a->attr & ANSI_BOLD)
449  color |= A_BOLD;
450  }
451  else
452 #endif
453  if ((special & A_BOLD) || (a->attr & ANSI_BOLD))
454  {
455  if (ColorDefs[MT_COLOR_BOLD] && !search)
456  color = ColorDefs[MT_COLOR_BOLD];
457  else
458  color ^= A_BOLD;
459  }
460  if ((special & A_UNDERLINE) || (a->attr & ANSI_UNDERLINE))
461  {
462  if (ColorDefs[MT_COLOR_UNDERLINE] && !search)
463  color = ColorDefs[MT_COLOR_UNDERLINE];
464  else
465  color ^= A_UNDERLINE;
466  }
467  else if (a->attr & ANSI_REVERSE)
468  {
469  color ^= A_REVERSE;
470  }
471  else if (a->attr & ANSI_BLINK)
472  {
473  color ^= A_BLINK;
474  }
475  else if (a->attr == ANSI_OFF)
476  {
477  a->attr = 0;
478  }
479  }
480 
481  if (color != last_color)
482  {
483  ATTR_SET(color);
484  last_color = color;
485  }
486 }
487 
494 static void append_line(struct Line *line_info, int n, int cnt)
495 {
496  int m;
497 
498  line_info[n + 1].type = line_info[n].type;
499  (line_info[n + 1].syntax)[0].color = (line_info[n].syntax)[0].color;
500  line_info[n + 1].continuation = 1;
501 
502  /* find the real start of the line */
503  for (m = n; m >= 0; m--)
504  if (line_info[m].continuation == 0)
505  break;
506 
507  (line_info[n + 1].syntax)[0].first = m;
508  (line_info[n + 1].syntax)[0].last =
509  (line_info[n].continuation) ? cnt + (line_info[n].syntax)[0].last : cnt;
510 }
511 
517 static void class_color_new(struct QClass *qc, int *q_level)
518 {
519  qc->index = (*q_level)++;
520  qc->color = ColorQuote[qc->index % ColorQuoteUsed];
521 }
522 
530 static void shift_class_colors(struct QClass *quote_list,
531  struct QClass *new_class, int index, int *q_level)
532 {
533  struct QClass *q_list = quote_list;
534  new_class->index = -1;
535 
536  while (q_list)
537  {
538  if (q_list->index >= index)
539  {
540  q_list->index++;
541  q_list->color = ColorQuote[q_list->index % ColorQuoteUsed];
542  }
543  if (q_list->down)
544  q_list = q_list->down;
545  else if (q_list->next)
546  q_list = q_list->next;
547  else
548  {
549  while (!q_list->next)
550  {
551  q_list = q_list->up;
552  if (!q_list)
553  break;
554  }
555  if (q_list)
556  q_list = q_list->next;
557  }
558  }
559 
560  new_class->index = index;
561  new_class->color = ColorQuote[index % ColorQuoteUsed];
562  (*q_level)++;
563 }
564 
569 static void cleanup_quote(struct QClass **quote_list)
570 {
571  struct QClass *ptr = NULL;
572 
573  while (*quote_list)
574  {
575  if ((*quote_list)->down)
576  cleanup_quote(&((*quote_list)->down));
577  ptr = (*quote_list)->next;
578  FREE(&(*quote_list)->prefix);
579  FREE(quote_list);
580  *quote_list = ptr;
581  }
582 }
583 
593 static struct QClass *classify_quote(struct QClass **quote_list, const char *qptr,
594  size_t length, bool *force_redraw, int *q_level)
595 {
596  struct QClass *q_list = *quote_list;
597  struct QClass *qc = NULL, *tmp = NULL, *ptr = NULL, *save = NULL;
598  const char *tail_qptr = NULL;
599  int offset, tail_lng;
600  int index = -1;
601 
602  if (ColorQuoteUsed <= 1)
603  {
604  /* not much point in classifying quotes... */
605 
606  if (!*quote_list)
607  {
608  qc = mutt_mem_calloc(1, sizeof(struct QClass));
609  qc->color = ColorQuote[0];
610  *quote_list = qc;
611  }
612  return *quote_list;
613  }
614 
615  /* classify quoting prefix */
616  while (q_list)
617  {
618  if (length <= q_list->length)
619  {
620  /* case 1: check the top level nodes */
621 
622  if (mutt_str_strncmp(qptr, q_list->prefix, length) == 0)
623  {
624  if (length == q_list->length)
625  return q_list; /* same prefix: return the current class */
626 
627  /* found shorter prefix */
628  if (!tmp)
629  {
630  /* add a node above q_list */
631  tmp = mutt_mem_calloc(1, sizeof(struct QClass));
632  tmp->prefix = mutt_mem_calloc(1, length + 1);
633  strncpy(tmp->prefix, qptr, length);
634  tmp->length = length;
635 
636  /* replace q_list by tmp in the top level list */
637  if (q_list->next)
638  {
639  tmp->next = q_list->next;
640  q_list->next->prev = tmp;
641  }
642  if (q_list->prev)
643  {
644  tmp->prev = q_list->prev;
645  q_list->prev->next = tmp;
646  }
647 
648  /* make q_list a child of tmp */
649  tmp->down = q_list;
650  q_list->up = tmp;
651 
652  /* q_list has no siblings for now */
653  q_list->next = NULL;
654  q_list->prev = NULL;
655 
656  /* update the root if necessary */
657  if (q_list == *quote_list)
658  *quote_list = tmp;
659 
660  index = q_list->index;
661 
662  /* tmp should be the return class too */
663  qc = tmp;
664 
665  /* next class to test; if tmp is a shorter prefix for another
666  * node, that node can only be in the top level list, so don't
667  * go down after this point */
668  q_list = tmp->next;
669  }
670  else
671  {
672  /* found another branch for which tmp is a shorter prefix */
673 
674  /* save the next sibling for later */
675  save = q_list->next;
676 
677  /* unlink q_list from the top level list */
678  if (q_list->next)
679  q_list->next->prev = q_list->prev;
680  if (q_list->prev)
681  q_list->prev->next = q_list->next;
682 
683  /* at this point, we have a tmp->down; link q_list to it */
684  ptr = tmp->down;
685  /* sibling order is important here, q_list should be linked last */
686  while (ptr->next)
687  ptr = ptr->next;
688  ptr->next = q_list;
689  q_list->next = NULL;
690  q_list->prev = ptr;
691  q_list->up = tmp;
692 
693  index = q_list->index;
694 
695  /* next class to test; as above, we shouldn't go down */
696  q_list = save;
697  }
698 
699  /* we found a shorter prefix, so certain quotes have changed classes */
700  *force_redraw = true;
701  continue;
702  }
703  else
704  {
705  /* shorter, but not a substring of the current class: try next */
706  q_list = q_list->next;
707  continue;
708  }
709  }
710  else
711  {
712  /* case 2: try subclassing the current top level node */
713 
714  /* tmp != NULL means we already found a shorter prefix at case 1 */
715  if (!tmp && (mutt_str_strncmp(qptr, q_list->prefix, q_list->length) == 0))
716  {
717  /* ok, it's a subclass somewhere on this branch */
718 
719  ptr = q_list;
720  offset = q_list->length;
721 
722  q_list = q_list->down;
723  tail_lng = length - offset;
724  tail_qptr = qptr + offset;
725 
726  while (q_list)
727  {
728  if (length <= q_list->length)
729  {
730  if (mutt_str_strncmp(tail_qptr, (q_list->prefix) + offset, tail_lng) == 0)
731  {
732  /* same prefix: return the current class */
733  if (length == q_list->length)
734  return q_list;
735 
736  /* found shorter common prefix */
737  if (!tmp)
738  {
739  /* add a node above q_list */
740  tmp = mutt_mem_calloc(1, sizeof(struct QClass));
741  tmp->prefix = mutt_mem_calloc(1, length + 1);
742  strncpy(tmp->prefix, qptr, length);
743  tmp->length = length;
744 
745  /* replace q_list by tmp */
746  if (q_list->next)
747  {
748  tmp->next = q_list->next;
749  q_list->next->prev = tmp;
750  }
751  if (q_list->prev)
752  {
753  tmp->prev = q_list->prev;
754  q_list->prev->next = tmp;
755  }
756 
757  /* make q_list a child of tmp */
758  tmp->down = q_list;
759  tmp->up = q_list->up;
760  q_list->up = tmp;
761  if (tmp->up->down == q_list)
762  tmp->up->down = tmp;
763 
764  /* q_list has no siblings */
765  q_list->next = NULL;
766  q_list->prev = NULL;
767 
768  index = q_list->index;
769 
770  /* tmp should be the return class too */
771  qc = tmp;
772 
773  /* next class to test */
774  q_list = tmp->next;
775  }
776  else
777  {
778  /* found another branch for which tmp is a shorter prefix */
779 
780  /* save the next sibling for later */
781  save = q_list->next;
782 
783  /* unlink q_list from the top level list */
784  if (q_list->next)
785  q_list->next->prev = q_list->prev;
786  if (q_list->prev)
787  q_list->prev->next = q_list->next;
788 
789  /* at this point, we have a tmp->down; link q_list to it */
790  ptr = tmp->down;
791  while (ptr->next)
792  ptr = ptr->next;
793  ptr->next = q_list;
794  q_list->next = NULL;
795  q_list->prev = ptr;
796  q_list->up = tmp;
797 
798  index = q_list->index;
799 
800  /* next class to test */
801  q_list = save;
802  }
803 
804  /* we found a shorter prefix, so we need a redraw */
805  *force_redraw = true;
806  continue;
807  }
808  else
809  {
810  q_list = q_list->next;
811  continue;
812  }
813  }
814  else
815  {
816  /* longer than the current prefix: try subclassing it */
817  if (!tmp && (mutt_str_strncmp(tail_qptr, (q_list->prefix) + offset,
818  q_list->length - offset) == 0))
819  {
820  /* still a subclass: go down one level */
821  ptr = q_list;
822  offset = q_list->length;
823 
824  q_list = q_list->down;
825  tail_lng = length - offset;
826  tail_qptr = qptr + offset;
827 
828  continue;
829  }
830  else
831  {
832  /* nope, try the next prefix */
833  q_list = q_list->next;
834  continue;
835  }
836  }
837  }
838 
839  /* still not found so far: add it as a sibling to the current node */
840  if (!qc)
841  {
842  tmp = mutt_mem_calloc(1, sizeof(struct QClass));
843  tmp->prefix = mutt_mem_calloc(1, length + 1);
844  strncpy(tmp->prefix, qptr, length);
845  tmp->length = length;
846 
847  if (ptr->down)
848  {
849  tmp->next = ptr->down;
850  ptr->down->prev = tmp;
851  }
852  ptr->down = tmp;
853  tmp->up = ptr;
854 
855  class_color_new(tmp, q_level);
856 
857  return tmp;
858  }
859  else
860  {
861  if (index != -1)
862  shift_class_colors(*quote_list, tmp, index, q_level);
863 
864  return qc;
865  }
866  }
867  else
868  {
869  /* nope, try the next prefix */
870  q_list = q_list->next;
871  continue;
872  }
873  }
874  }
875 
876  if (!qc)
877  {
878  /* not found so far: add it as a top level class */
879  qc = mutt_mem_calloc(1, sizeof(struct QClass));
880  qc->prefix = mutt_mem_calloc(1, length + 1);
881  strncpy(qc->prefix, qptr, length);
882  qc->length = length;
883  class_color_new(qc, q_level);
884 
885  if (*quote_list)
886  {
887  qc->next = *quote_list;
888  (*quote_list)->prev = qc;
889  }
890  *quote_list = qc;
891  }
892 
893  if (index != -1)
894  shift_class_colors(*quote_list, tmp, index, q_level);
895 
896  return qc;
897 }
898 
905 static int check_marker(const char *q, const char *p)
906 {
907  for (; (p[0] == q[0]) && (q[0] != '\0') && (p[0] != '\0') && (q[0] != '\a') &&
908  (p[0] != '\a');
909  p++, q++)
910  {
911  }
912 
913  return (int) (*p - *q);
914 }
915 
921 static int check_attachment_marker(const char *p)
922 {
923  return check_marker(AttachmentMarker, p);
924 }
925 
931 static int check_protected_header_marker(const char *p)
932 {
934 }
935 
945 int mutt_is_quote_line(char *line, regmatch_t *pmatch)
946 {
947  bool is_quote = false;
948  regmatch_t pmatch_internal[1], smatch[1];
949 
950  if (!pmatch)
951  pmatch = pmatch_internal;
952 
953  if (mutt_regex_capture(C_QuoteRegex, line, 1, pmatch))
954  {
955  if (mutt_regex_capture(C_Smileys, line, 1, smatch))
956  {
957  if (smatch[0].rm_so > 0)
958  {
959  char c = line[smatch[0].rm_so];
960  line[smatch[0].rm_so] = 0;
961 
962  if (mutt_regex_capture(C_QuoteRegex, line, 1, pmatch))
963  is_quote = true;
964 
965  line[smatch[0].rm_so] = c;
966  }
967  }
968  else
969  is_quote = true;
970  }
971 
972  return is_quote;
973 }
974 
987 static void resolve_types(char *buf, char *raw, struct Line *line_info, int n,
988  int last, struct QClass **quote_list, int *q_level,
989  bool *force_redraw, bool q_classify)
990 {
991  struct ColorLine *color_line = NULL;
992  struct ColorLineList *head = NULL;
993  regmatch_t pmatch[1];
994  bool found;
995  bool null_rx;
996  int offset, i = 0;
997 
998  if ((n == 0) || IS_HEADER(line_info[n - 1].type) ||
999  (check_protected_header_marker(raw) == 0))
1000  {
1001  if (buf[0] == '\n') /* end of header */
1002  {
1003  line_info[n].type = MT_COLOR_NORMAL;
1004  getyx(stdscr, braille_line, braille_col);
1005  }
1006  else
1007  {
1008  /* if this is a continuation of the previous line, use the previous
1009  * line's color as default. */
1010  if ((n > 0) && ((buf[0] == ' ') || (buf[0] == '\t')))
1011  {
1012  line_info[n].type = line_info[n - 1].type; /* wrapped line */
1013  if (!C_HeaderColorPartial)
1014  {
1015  (line_info[n].syntax)[0].color = (line_info[n - 1].syntax)[0].color;
1016  line_info[n].is_cont_hdr = 1;
1017  }
1018  }
1019  else
1020  {
1021  line_info[n].type = MT_COLOR_HDEFAULT;
1022  }
1023 
1024  /* When this option is unset, we color the entire header the
1025  * same color. Otherwise, we handle the header patterns just
1026  * like body patterns (further below). */
1027  if (!C_HeaderColorPartial)
1028  {
1029  STAILQ_FOREACH(color_line, &ColorHdrList, entries)
1030  {
1031  if (regexec(&color_line->regex, buf, 0, NULL, 0) == 0)
1032  {
1033  line_info[n].type = MT_COLOR_HEADER;
1034  line_info[n].syntax[0].color = color_line->pair;
1035  if (line_info[n].is_cont_hdr)
1036  {
1037  /* adjust the previous continuation lines to reflect the color of this continuation line */
1038  int j;
1039  for (j = n - 1; j >= 0 && line_info[j].is_cont_hdr; --j)
1040  {
1041  line_info[j].type = line_info[n].type;
1042  line_info[j].syntax[0].color = line_info[n].syntax[0].color;
1043  }
1044  /* now adjust the first line of this header field */
1045  if (j >= 0)
1046  {
1047  line_info[j].type = line_info[n].type;
1048  line_info[j].syntax[0].color = line_info[n].syntax[0].color;
1049  }
1050  *force_redraw = true; /* the previous lines have already been drawn on the screen */
1051  }
1052  break;
1053  }
1054  }
1055  }
1056  }
1057  }
1058  else if (mutt_str_startswith(raw, "\033[0m", CASE_MATCH)) // Escape: a little hack...
1059  line_info[n].type = MT_COLOR_NORMAL;
1060  else if (check_attachment_marker((char *) raw) == 0)
1061  line_info[n].type = MT_COLOR_ATTACHMENT;
1062  else if ((mutt_str_strcmp("-- \n", buf) == 0) || (mutt_str_strcmp("-- \r\n", buf) == 0))
1063  {
1064  i = n + 1;
1065 
1066  line_info[n].type = MT_COLOR_SIGNATURE;
1067  while ((i < last) && (check_sig(buf, line_info, i - 1) == 0) &&
1068  ((line_info[i].type == MT_COLOR_NORMAL) || (line_info[i].type == MT_COLOR_QUOTED) ||
1069  (line_info[i].type == MT_COLOR_HEADER)))
1070  {
1071  /* oops... */
1072  if (line_info[i].chunks)
1073  {
1074  line_info[i].chunks = 0;
1075  mutt_mem_realloc(&(line_info[n].syntax), sizeof(struct TextSyntax));
1076  }
1077  line_info[i++].type = MT_COLOR_SIGNATURE;
1078  }
1079  }
1080  else if (check_sig(buf, line_info, n - 1) == 0)
1081  line_info[n].type = MT_COLOR_SIGNATURE;
1082  else if (mutt_is_quote_line(buf, pmatch))
1083 
1084  {
1085  if (q_classify && (line_info[n].quote == NULL))
1086  {
1087  line_info[n].quote = classify_quote(quote_list, buf + pmatch[0].rm_so,
1088  pmatch[0].rm_eo - pmatch[0].rm_so,
1089  force_redraw, q_level);
1090  }
1091  line_info[n].type = MT_COLOR_QUOTED;
1092  }
1093  else
1094  line_info[n].type = MT_COLOR_NORMAL;
1095 
1096  /* body patterns */
1097  if ((line_info[n].type == MT_COLOR_NORMAL) || (line_info[n].type == MT_COLOR_QUOTED) ||
1098  ((line_info[n].type == MT_COLOR_HDEFAULT) && C_HeaderColorPartial))
1099  {
1100  size_t nl;
1101 
1102  /* don't consider line endings part of the buffer
1103  * for regex matching */
1104  nl = mutt_str_strlen(buf);
1105  if ((nl > 0) && (buf[nl - 1] == '\n'))
1106  buf[nl - 1] = '\0';
1107 
1108  i = 0;
1109  offset = 0;
1110  line_info[n].chunks = 0;
1111  if (line_info[n].type == MT_COLOR_HDEFAULT)
1112  head = &ColorHdrList;
1113  else
1114  head = &ColorBodyList;
1115  STAILQ_FOREACH(color_line, head, entries)
1116  {
1117  color_line->stop_matching = false;
1118  }
1119  do
1120  {
1121  if (!buf[offset])
1122  break;
1123 
1124  found = false;
1125  null_rx = false;
1126  STAILQ_FOREACH(color_line, head, entries)
1127  {
1128  if (!color_line->stop_matching &&
1129  (regexec(&color_line->regex, buf + offset, 1, pmatch,
1130  ((offset != 0) ? REG_NOTBOL : 0)) == 0))
1131  {
1132  if (pmatch[0].rm_eo != pmatch[0].rm_so)
1133  {
1134  if (!found)
1135  {
1136  /* Abort if we fill up chunks.
1137  * Yes, this really happened. See #3888 */
1138  if (line_info[n].chunks == SHRT_MAX)
1139  {
1140  null_rx = false;
1141  break;
1142  }
1143  if (++(line_info[n].chunks) > 1)
1144  {
1145  mutt_mem_realloc(&(line_info[n].syntax),
1146  (line_info[n].chunks) * sizeof(struct TextSyntax));
1147  }
1148  }
1149  i = line_info[n].chunks - 1;
1150  pmatch[0].rm_so += offset;
1151  pmatch[0].rm_eo += offset;
1152  if (!found || (pmatch[0].rm_so < (line_info[n].syntax)[i].first) ||
1153  ((pmatch[0].rm_so == (line_info[n].syntax)[i].first) &&
1154  (pmatch[0].rm_eo > (line_info[n].syntax)[i].last)))
1155  {
1156  (line_info[n].syntax)[i].color = color_line->pair;
1157  (line_info[n].syntax)[i].first = pmatch[0].rm_so;
1158  (line_info[n].syntax)[i].last = pmatch[0].rm_eo;
1159  }
1160  found = true;
1161  null_rx = false;
1162  }
1163  else
1164  null_rx = true; /* empty regex; don't add it, but keep looking */
1165  }
1166  else
1167  {
1168  /* Once a regexp fails to match, don't try matching it again.
1169  * On very long lines this can cause a performance issue if there
1170  * are other regexps that have many matches. */
1171  color_line->stop_matching = true;
1172  }
1173  }
1174 
1175  if (null_rx)
1176  offset++; /* avoid degenerate cases */
1177  else
1178  offset = (line_info[n].syntax)[i].last;
1179  } while (found || null_rx);
1180  if (nl > 0)
1181  buf[nl] = '\n';
1182  }
1183 
1184  /* attachment patterns */
1185  if (line_info[n].type == MT_COLOR_ATTACHMENT)
1186  {
1187  size_t nl;
1188 
1189  /* don't consider line endings part of the buffer for regex matching */
1190  nl = mutt_str_strlen(buf);
1191  if ((nl > 0) && (buf[nl - 1] == '\n'))
1192  buf[nl - 1] = '\0';
1193 
1194  i = 0;
1195  offset = 0;
1196  line_info[n].chunks = 0;
1197  do
1198  {
1199  if (!buf[offset])
1200  break;
1201 
1202  found = false;
1203  null_rx = false;
1204  STAILQ_FOREACH(color_line, &ColorAttachList, entries)
1205  {
1206  if (regexec(&color_line->regex, buf + offset, 1, pmatch,
1207  ((offset != 0) ? REG_NOTBOL : 0)) == 0)
1208  {
1209  if (pmatch[0].rm_eo != pmatch[0].rm_so)
1210  {
1211  if (!found)
1212  {
1213  if (++(line_info[n].chunks) > 1)
1214  {
1215  mutt_mem_realloc(&(line_info[n].syntax),
1216  (line_info[n].chunks) * sizeof(struct TextSyntax));
1217  }
1218  }
1219  i = line_info[n].chunks - 1;
1220  pmatch[0].rm_so += offset;
1221  pmatch[0].rm_eo += offset;
1222  if (!found || (pmatch[0].rm_so < (line_info[n].syntax)[i].first) ||
1223  ((pmatch[0].rm_so == (line_info[n].syntax)[i].first) &&
1224  (pmatch[0].rm_eo > (line_info[n].syntax)[i].last)))
1225  {
1226  (line_info[n].syntax)[i].color = color_line->pair;
1227  (line_info[n].syntax)[i].first = pmatch[0].rm_so;
1228  (line_info[n].syntax)[i].last = pmatch[0].rm_eo;
1229  }
1230  found = 1;
1231  null_rx = 0;
1232  }
1233  else
1234  null_rx = 1; /* empty regex; don't add it, but keep looking */
1235  }
1236  }
1237 
1238  if (null_rx)
1239  offset++; /* avoid degenerate cases */
1240  else
1241  offset = (line_info[n].syntax)[i].last;
1242  } while (found || null_rx);
1243  if (nl > 0)
1244  buf[nl] = '\n';
1245  }
1246 }
1247 
1253 static bool is_ansi(unsigned char *buf)
1254 {
1255  while ((*buf != '\0') && (isdigit(*buf) || (*buf == ';')))
1256  buf++;
1257  return *buf == 'm';
1258 }
1259 
1267 static int grok_ansi(unsigned char *buf, int pos, struct AnsiAttr *a)
1268 {
1269  int x = pos;
1270 
1271  while (isdigit(buf[x]) || (buf[x] == ';'))
1272  x++;
1273 
1274  /* Character Attributes */
1275  if (C_AllowAnsi && a && (buf[x] == 'm'))
1276  {
1277  if (pos == x)
1278  {
1279 #ifdef HAVE_COLOR
1280  if (a->pair != -1)
1281  mutt_color_free(a->fg, a->bg);
1282 #endif
1283  a->attr = ANSI_OFF;
1284  a->pair = -1;
1285  }
1286  while (pos < x)
1287  {
1288  if ((buf[pos] == '1') && ((pos + 1 == x) || (buf[pos + 1] == ';')))
1289  {
1290  a->attr |= ANSI_BOLD;
1291  pos += 2;
1292  }
1293  else if ((buf[pos] == '4') && ((pos + 1 == x) || (buf[pos + 1] == ';')))
1294  {
1295  a->attr |= ANSI_UNDERLINE;
1296  pos += 2;
1297  }
1298  else if ((buf[pos] == '5') && ((pos + 1 == x) || (buf[pos + 1] == ';')))
1299  {
1300  a->attr |= ANSI_BLINK;
1301  pos += 2;
1302  }
1303  else if ((buf[pos] == '7') && ((pos + 1 == x) || (buf[pos + 1] == ';')))
1304  {
1305  a->attr |= ANSI_REVERSE;
1306  pos += 2;
1307  }
1308  else if ((buf[pos] == '0') && ((pos + 1 == x) || (buf[pos + 1] == ';')))
1309  {
1310 #ifdef HAVE_COLOR
1311  if (a->pair != -1)
1312  mutt_color_free(a->fg, a->bg);
1313 #endif
1314  a->attr = ANSI_OFF;
1315  a->pair = -1;
1316  pos += 2;
1317  }
1318  else if ((buf[pos] == '3') && isdigit(buf[pos + 1]))
1319  {
1320 #ifdef HAVE_COLOR
1321  if (a->pair != -1)
1322  mutt_color_free(a->fg, a->bg);
1323 #endif
1324  a->pair = -1;
1325  a->attr |= ANSI_COLOR;
1326  a->fg = buf[pos + 1] - '0';
1327  pos += 3;
1328  }
1329  else if ((buf[pos] == '4') && isdigit(buf[pos + 1]))
1330  {
1331 #ifdef HAVE_COLOR
1332  if (a->pair != -1)
1333  mutt_color_free(a->fg, a->bg);
1334 #endif
1335  a->pair = -1;
1336  a->attr |= ANSI_COLOR;
1337  a->bg = buf[pos + 1] - '0';
1338  pos += 3;
1339  }
1340  else
1341  {
1342  while ((pos < x) && (buf[pos] != ';'))
1343  pos++;
1344  pos++;
1345  }
1346  }
1347  }
1348  pos = x;
1349  return pos;
1350 }
1351 
1364 static int fill_buffer(FILE *fp, LOFF_T *last_pos, LOFF_T offset, unsigned char **buf,
1365  unsigned char **fmt, size_t *blen, int *buf_ready)
1366 {
1367  unsigned char *p = NULL, *q = NULL;
1368  static int b_read;
1369  int l = 0;
1370 
1371  if (*buf_ready == 0)
1372  {
1373  if (offset != *last_pos)
1374  fseeko(fp, offset, SEEK_SET);
1375  *buf = (unsigned char *) mutt_file_read_line((char *) *buf, blen, fp, &l, MUTT_EOL);
1376  if (!*buf)
1377  {
1378  fmt[0] = NULL;
1379  return -1;
1380  }
1381  *last_pos = ftello(fp);
1382  b_read = (int) (*last_pos - offset);
1383  *buf_ready = 1;
1384 
1385  mutt_mem_realloc(fmt, *blen);
1386 
1387  /* copy "buf" to "fmt", but without bold and underline controls */
1388  p = *buf;
1389  q = *fmt;
1390  while (*p)
1391  {
1392  if ((p[0] == '\010') && (p > *buf)) // Ctrl-H (backspace)
1393  {
1394  if (p[1] == '_') /* underline */
1395  p += 2;
1396  else if ((p[1] != '\0') && (q > *fmt)) /* bold or overstrike */
1397  {
1398  q[-1] = p[1];
1399  p += 2;
1400  }
1401  else /* ^H */
1402  *q++ = *p++;
1403  }
1404  else if ((p[0] == '\033') && (p[1] == '[') && is_ansi(p + 2)) // Escape
1405  {
1406  while (*p++ != 'm') /* skip ANSI sequence */
1407  ;
1408  }
1409  else if ((p[0] == '\033') && (p[1] == ']') && // Escape
1410  ((check_attachment_marker((char *) p) == 0) ||
1411  (check_protected_header_marker((char *) p) == 0)))
1412  {
1413  mutt_debug(LL_DEBUG2, "Seen attachment marker\n");
1414  while (*p++ != '\a') /* skip pseudo-ANSI sequence */
1415  ;
1416  }
1417  else
1418  *q++ = *p++;
1419  }
1420  *q = '\0';
1421  }
1422  return b_read;
1423 }
1424 
1440 static int format_line(struct Line **line_info, int n, unsigned char *buf,
1441  PagerFlags flags, struct AnsiAttr *pa, int cnt,
1442  int *pspace, int *pvch, int *pcol, int *pspecial, int width)
1443 {
1444  int space = -1; /* index of the last space or TAB */
1445  int col = C_Markers ? (*line_info)[n].continuation : 0;
1446  size_t k;
1447  int ch, vch, last_special = -1, special = 0, t;
1448  wchar_t wc;
1449  mbstate_t mbstate;
1450  int wrap_cols = mutt_window_wrap_cols(width, (flags & MUTT_PAGER_NOWRAP) ? 0 : C_Wrap);
1451 
1452  if (check_attachment_marker((char *) buf) == 0)
1453  wrap_cols = width;
1454 
1455  /* FIXME: this should come from line_info */
1456  memset(&mbstate, 0, sizeof(mbstate));
1457 
1458  for (ch = 0, vch = 0; ch < cnt; ch += k, vch += k)
1459  {
1460  /* Handle ANSI sequences */
1461  while ((cnt - ch >= 2) && (buf[ch] == '\033') && (buf[ch + 1] == '[') && // Escape
1462  is_ansi(buf + ch + 2))
1463  {
1464  ch = grok_ansi(buf, ch + 2, pa) + 1;
1465  }
1466 
1467  while ((cnt - ch >= 2) && (buf[ch] == '\033') && (buf[ch + 1] == ']') && // Escape
1468  ((check_attachment_marker((char *) buf + ch) == 0) ||
1469  (check_protected_header_marker((char *) buf + ch) == 0)))
1470  {
1471  while (buf[ch++] != '\a')
1472  if (ch >= cnt)
1473  break;
1474  }
1475 
1476  /* is anything left to do? */
1477  if (ch >= cnt)
1478  break;
1479 
1480  k = mbrtowc(&wc, (char *) buf + ch, cnt - ch, &mbstate);
1481  if ((k == (size_t)(-2)) || (k == (size_t)(-1)))
1482  {
1483  if (k == (size_t)(-1))
1484  memset(&mbstate, 0, sizeof(mbstate));
1485  mutt_debug(LL_DEBUG1, "mbrtowc returned %lu; errno = %d\n", k, errno);
1486  if (col + 4 > wrap_cols)
1487  break;
1488  col += 4;
1489  if (pa)
1490  printw("\\%03o", buf[ch]);
1491  k = 1;
1492  continue;
1493  }
1494  if (k == 0)
1495  k = 1;
1496 
1497  if (CharsetIsUtf8)
1498  {
1499  /* zero width space, zero width no-break space */
1500  if ((wc == 0x200B) || (wc == 0xFEFF))
1501  {
1502  mutt_debug(LL_DEBUG3, "skip zero-width character U+%04X\n", (unsigned short) wc);
1503  continue;
1504  }
1506  {
1507  mutt_debug(LL_DEBUG3, "filtered U+%04X\n", (unsigned short) wc);
1508  continue;
1509  }
1510  }
1511 
1512  /* Handle backspace */
1513  special = 0;
1514  if (IsWPrint(wc))
1515  {
1516  wchar_t wc1;
1517  mbstate_t mbstate1 = mbstate;
1518  size_t k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
1519  while ((k1 != (size_t)(-2)) && (k1 != (size_t)(-1)) && (k1 > 0) && (wc1 == '\b'))
1520  {
1521  const size_t k2 =
1522  mbrtowc(&wc1, (char *) buf + ch + k + k1, cnt - ch - k - k1, &mbstate1);
1523  if ((k2 == (size_t)(-2)) || (k2 == (size_t)(-1)) || (k2 == 0) || (!IsWPrint(wc1)))
1524  break;
1525 
1526  if (wc == wc1)
1527  {
1528  special |= ((wc == '_') && (special & A_UNDERLINE)) ? A_UNDERLINE : A_BOLD;
1529  }
1530  else if ((wc == '_') || (wc1 == '_'))
1531  {
1532  special |= A_UNDERLINE;
1533  wc = (wc1 == '_') ? wc : wc1;
1534  }
1535  else
1536  {
1537  /* special = 0; / * overstrike: nothing to do! */
1538  wc = wc1;
1539  }
1540 
1541  ch += k + k1;
1542  k = k2;
1543  mbstate = mbstate1;
1544  k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
1545  }
1546  }
1547 
1548  if (pa && ((flags & (MUTT_SHOWCOLOR | MUTT_SEARCH | MUTT_PAGER_MARKER)) ||
1549  special || last_special || pa->attr))
1550  {
1551  resolve_color(*line_info, n, vch, flags, special, pa);
1552  last_special = special;
1553  }
1554 
1555  /* no-break space, narrow no-break space */
1556  if (IsWPrint(wc) || (CharsetIsUtf8 && ((wc == 0x00A0) || (wc == 0x202F))))
1557  {
1558  if (wc == ' ')
1559  {
1560  space = ch;
1561  }
1562  t = wcwidth(wc);
1563  if (col + t > wrap_cols)
1564  break;
1565  col += t;
1566  if (pa)
1567  mutt_addwch(wc);
1568  }
1569  else if (wc == '\n')
1570  break;
1571  else if (wc == '\t')
1572  {
1573  space = ch;
1574  t = (col & ~7) + 8;
1575  if (t > wrap_cols)
1576  break;
1577  if (pa)
1578  for (; col < t; col++)
1579  addch(' ');
1580  else
1581  col = t;
1582  }
1583  else if ((wc < 0x20) || (wc == 0x7f))
1584  {
1585  if (col + 2 > wrap_cols)
1586  break;
1587  col += 2;
1588  if (pa)
1589  printw("^%c", ('@' + wc) & 0x7f);
1590  }
1591  else if (wc < 0x100)
1592  {
1593  if (col + 4 > wrap_cols)
1594  break;
1595  col += 4;
1596  if (pa)
1597  printw("\\%03o", wc);
1598  }
1599  else
1600  {
1601  if (col + 1 > wrap_cols)
1602  break;
1603  col += k;
1604  if (pa)
1605  addch(ReplacementChar);
1606  }
1607  }
1608  *pspace = space;
1609  *pcol = col;
1610  *pvch = vch;
1611  *pspecial = special;
1612  return ch;
1613 }
1614 
1633 static int display_line(FILE *fp, LOFF_T *last_pos, struct Line **line_info,
1634  int n, int *last, int *max, PagerFlags flags,
1635  struct QClass **quote_list, int *q_level, bool *force_redraw,
1636  regex_t *search_re, struct MuttWindow *pager_window)
1637 {
1638  unsigned char *buf = NULL, *fmt = NULL;
1639  size_t buflen = 0;
1640  unsigned char *buf_ptr = NULL;
1641  int ch, vch, col, cnt, b_read;
1642  int buf_ready = 0;
1643  bool change_last = false;
1644  int special;
1645  int offset;
1646  int def_color;
1647  int m;
1648  int rc = -1;
1649  struct AnsiAttr a = { 0, 0, 0, -1 };
1650  regmatch_t pmatch[1];
1651 
1652  if (n == *last)
1653  {
1654  (*last)++;
1655  change_last = true;
1656  }
1657 
1658  if (*last == *max)
1659  {
1660  mutt_mem_realloc(line_info, sizeof(struct Line) * (*max += LINES));
1661  for (ch = *last; ch < *max; ch++)
1662  {
1663  memset(&((*line_info)[ch]), 0, sizeof(struct Line));
1664  (*line_info)[ch].type = -1;
1665  (*line_info)[ch].search_cnt = -1;
1666  (*line_info)[ch].syntax = mutt_mem_malloc(sizeof(struct TextSyntax));
1667  ((*line_info)[ch].syntax)[0].first = -1;
1668  ((*line_info)[ch].syntax)[0].last = -1;
1669  }
1670  }
1671 
1672  if (flags & MUTT_PAGER_LOGS)
1673  {
1674  /* determine the line class */
1675  if (fill_buffer(fp, last_pos, (*line_info)[n].offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1676  {
1677  if (change_last)
1678  (*last)--;
1679  goto out;
1680  }
1681 
1682  (*line_info)[n].type = MT_COLOR_MESSAGE_LOG;
1683  if (buf[11] == 'M')
1684  (*line_info)[n].syntax[0].color = MT_COLOR_MESSAGE;
1685  else if (buf[11] == 'E')
1686  (*line_info)[n].syntax[0].color = MT_COLOR_ERROR;
1687  else
1688  (*line_info)[n].syntax[0].color = MT_COLOR_NORMAL;
1689  }
1690 
1691  /* only do color highlighting if we are viewing a message */
1692  if (flags & (MUTT_SHOWCOLOR | MUTT_TYPES))
1693  {
1694  if ((*line_info)[n].type == -1)
1695  {
1696  /* determine the line class */
1697  if (fill_buffer(fp, last_pos, (*line_info)[n].offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1698  {
1699  if (change_last)
1700  (*last)--;
1701  goto out;
1702  }
1703 
1704  resolve_types((char *) fmt, (char *) buf, *line_info, n, *last,
1705  quote_list, q_level, force_redraw, flags & MUTT_SHOWCOLOR);
1706 
1707  /* avoid race condition for continuation lines when scrolling up */
1708  for (m = n + 1; m < *last && (*line_info)[m].offset && (*line_info)[m].continuation; m++)
1709  (*line_info)[m].type = (*line_info)[n].type;
1710  }
1711 
1712  /* this also prevents searching through the hidden lines */
1713  if ((flags & MUTT_HIDE) && ((*line_info)[n].type == MT_COLOR_QUOTED))
1714  flags = 0; /* MUTT_NOSHOW */
1715  }
1716 
1717  /* At this point, (*line_info[n]).quote may still be undefined. We
1718  * don't want to compute it every time MUTT_TYPES is set, since this
1719  * would slow down the "bottom" function unacceptably. A compromise
1720  * solution is hence to call regexec() again, just to find out the
1721  * length of the quote prefix. */
1722  if ((flags & MUTT_SHOWCOLOR) && !(*line_info)[n].continuation &&
1723  ((*line_info)[n].type == MT_COLOR_QUOTED) && !(*line_info)[n].quote)
1724  {
1725  if (fill_buffer(fp, last_pos, (*line_info)[n].offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1726  {
1727  if (change_last)
1728  (*last)--;
1729  goto out;
1730  }
1731 
1732  if (mutt_regex_capture(C_QuoteRegex, (char *) fmt, 1, pmatch))
1733  {
1734  (*line_info)[n].quote =
1735  classify_quote(quote_list, (char *) fmt + pmatch[0].rm_so,
1736  pmatch[0].rm_eo - pmatch[0].rm_so, force_redraw, q_level);
1737  }
1738  else
1739  {
1740  goto out;
1741  }
1742  }
1743 
1744  if ((flags & MUTT_SEARCH) && !(*line_info)[n].continuation &&
1745  ((*line_info)[n].search_cnt == -1))
1746  {
1747  if (fill_buffer(fp, last_pos, (*line_info)[n].offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1748  {
1749  if (change_last)
1750  (*last)--;
1751  goto out;
1752  }
1753 
1754  offset = 0;
1755  (*line_info)[n].search_cnt = 0;
1756  while (regexec(search_re, (char *) fmt + offset, 1, pmatch,
1757  (offset ? REG_NOTBOL : 0)) == 0)
1758  {
1759  if (++((*line_info)[n].search_cnt) > 1)
1760  {
1761  mutt_mem_realloc(&((*line_info)[n].search),
1762  ((*line_info)[n].search_cnt) * sizeof(struct TextSyntax));
1763  }
1764  else
1765  (*line_info)[n].search = mutt_mem_malloc(sizeof(struct TextSyntax));
1766  pmatch[0].rm_so += offset;
1767  pmatch[0].rm_eo += offset;
1768  ((*line_info)[n].search)[(*line_info)[n].search_cnt - 1].first = pmatch[0].rm_so;
1769  ((*line_info)[n].search)[(*line_info)[n].search_cnt - 1].last = pmatch[0].rm_eo;
1770 
1771  if (pmatch[0].rm_eo == pmatch[0].rm_so)
1772  offset++; /* avoid degenerate cases */
1773  else
1774  offset = pmatch[0].rm_eo;
1775  if (!fmt[offset])
1776  break;
1777  }
1778  }
1779 
1780  if (!(flags & MUTT_SHOW) && ((*line_info)[n + 1].offset > 0))
1781  {
1782  /* we've already scanned this line, so just exit */
1783  rc = 0;
1784  goto out;
1785  }
1786  if ((flags & MUTT_SHOWCOLOR) && *force_redraw && ((*line_info)[n + 1].offset > 0))
1787  {
1788  /* no need to try to display this line... */
1789  rc = 1;
1790  goto out; /* fake display */
1791  }
1792 
1793  b_read = fill_buffer(fp, last_pos, (*line_info)[n].offset, &buf, &fmt, &buflen, &buf_ready);
1794  if (b_read < 0)
1795  {
1796  if (change_last)
1797  (*last)--;
1798  goto out;
1799  }
1800 
1801  /* now chose a good place to break the line */
1802  cnt = format_line(line_info, n, buf, flags, NULL, b_read, &ch, &vch, &col,
1803  &special, pager_window->cols);
1804  buf_ptr = buf + cnt;
1805 
1806  /* move the break point only if smart_wrap is set */
1807  if (C_SmartWrap)
1808  {
1809  if ((cnt < b_read) && (ch != -1) && !IS_HEADER((*line_info)[n].type) &&
1810  !IS_SPACE(buf[cnt]))
1811  {
1812  buf_ptr = buf + ch;
1813  /* skip trailing blanks */
1814  while (ch && ((buf[ch] == ' ') || (buf[ch] == '\t') || (buf[ch] == '\r')))
1815  ch--;
1816  /* A very long word with leading spaces causes infinite
1817  * wrapping when MUTT_PAGER_NSKIP is set. A folded header
1818  * with a single long word shouldn't be smartwrapped
1819  * either. So just disable smart_wrap if it would wrap at the
1820  * beginning of the line. */
1821  if (ch == 0)
1822  buf_ptr = buf + cnt;
1823  else
1824  cnt = ch + 1;
1825  }
1826  if (!(flags & MUTT_PAGER_NSKIP))
1827  {
1828  /* skip leading blanks on the next line too */
1829  while ((*buf_ptr == ' ') || (*buf_ptr == '\t'))
1830  buf_ptr++;
1831  }
1832  }
1833 
1834  if (*buf_ptr == '\r')
1835  buf_ptr++;
1836  if (*buf_ptr == '\n')
1837  buf_ptr++;
1838 
1839  if (((int) (buf_ptr - buf) < b_read) && !(*line_info)[n + 1].continuation)
1840  append_line(*line_info, n, (int) (buf_ptr - buf));
1841  (*line_info)[n + 1].offset = (*line_info)[n].offset + (long) (buf_ptr - buf);
1842 
1843  /* if we don't need to display the line we are done */
1844  if (!(flags & MUTT_SHOW))
1845  {
1846  rc = 0;
1847  goto out;
1848  }
1849 
1850  /* display the line */
1851  format_line(line_info, n, buf, flags, &a, cnt, &ch, &vch, &col, &special,
1852  pager_window->cols);
1853 
1854 /* avoid a bug in ncurses... */
1855 #ifndef USE_SLANG_CURSES
1856  if (col == 0)
1857  {
1858  NORMAL_COLOR;
1859  addch(' ');
1860  }
1861 #endif
1862 
1863  /* end the last color pattern (needed by S-Lang) */
1864  if (special || ((col != pager_window->cols) && (flags & (MUTT_SHOWCOLOR | MUTT_SEARCH))))
1865  resolve_color(*line_info, n, vch, flags, 0, &a);
1866 
1867  /* Fill the blank space at the end of the line with the prevailing color.
1868  * ncurses does an implicit clrtoeol() when you do addch('\n') so we have
1869  * to make sure to reset the color *after* that */
1870  if (flags & MUTT_SHOWCOLOR)
1871  {
1872  m = ((*line_info)[n].continuation) ? ((*line_info)[n].syntax)[0].first : n;
1873  if ((*line_info)[m].type == MT_COLOR_HEADER)
1874  def_color = ((*line_info)[m].syntax)[0].color;
1875  else
1876  def_color = ColorDefs[(*line_info)[m].type];
1877 
1878  ATTR_SET(def_color);
1879  }
1880 
1881  if (col < pager_window->cols)
1882  mutt_window_clrtoeol(pager_window);
1883 
1884  /* reset the color back to normal. This *must* come after the
1885  * clrtoeol, otherwise the color for this line will not be
1886  * filled to the right margin. */
1887  if (flags & MUTT_SHOWCOLOR)
1888  NORMAL_COLOR;
1889 
1890  /* build a return code */
1891  if (!(flags & MUTT_SHOW))
1892  flags = 0;
1893 
1894  rc = flags;
1895 
1896 out:
1897  FREE(&buf);
1898  FREE(&fmt);
1899  return rc;
1900 }
1901 
1910 static int up_n_lines(int nlines, struct Line *info, int cur, bool hiding)
1911 {
1912  while ((cur > 0) && (nlines > 0))
1913  {
1914  cur--;
1915  if (!hiding || (info[cur].type != MT_COLOR_QUOTED))
1916  nlines--;
1917  }
1918 
1919  return cur;
1920 }
1921 
1926 {
1927  TopLine = 0;
1928  OldEmail = NULL;
1929 }
1930 
1934 static void pager_custom_redraw(struct Menu *pager_menu)
1935 {
1936  struct PagerRedrawData *rd = pager_menu->redraw_data;
1937  char buf[1024];
1938 
1939  if (!rd)
1940  return;
1941 
1942  if (pager_menu->redraw & REDRAW_FULL)
1943  {
1944  NORMAL_COLOR;
1945  /* clear() doesn't optimize screen redraws */
1946  move(0, 0);
1947  clrtobot();
1948 
1949  if (IsEmail(rd->extra) && Context && ((Context->mailbox->vcount + 1) < C_PagerIndexLines))
1950  rd->indexlen = Context->mailbox->vcount + 1;
1951  else
1953 
1954  rd->indicator = rd->indexlen / 3;
1955 
1958  rd->index_status_window->rows = 0;
1959  rd->index_window->rows = 0;
1960 
1961  if (IsEmail(rd->extra) && (C_PagerIndexLines != 0))
1962  {
1964  rd->index_window->rows = (rd->indexlen > 0) ? rd->indexlen - 1 : 0;
1965 
1966  if (C_StatusOnTop)
1967  {
1969 
1971  rd->pager_status_window->rows = 1;
1973 
1974  rd->pager_window->rows -=
1976  rd->pager_window->row_offset +=
1978  }
1979  else
1980  {
1982  rd->index_status_window->rows = 1;
1984 
1985  rd->pager_window->rows -=
1987  rd->pager_window->row_offset +=
1989  }
1990  }
1991 
1992  if (C_Help)
1993  {
1997  NORMAL_COLOR;
1998  }
1999 
2000  if (Resize)
2001  {
2002  rd->search_compiled = Resize->search_compiled;
2003  if (rd->search_compiled)
2004  {
2005  int flags = mutt_mb_is_lower(rd->searchbuf) ? REG_ICASE : 0;
2006  const int err = REG_COMP(&rd->search_re, rd->searchbuf, REG_NEWLINE | flags);
2007  if (err != 0)
2008  {
2009  regerror(err, &rd->search_re, buf, sizeof(buf));
2010  mutt_error("%s", buf);
2011  rd->search_compiled = false;
2012  }
2013  else
2014  {
2015  rd->search_flag = MUTT_SEARCH;
2016  rd->search_back = Resize->search_back;
2017  }
2018  }
2019  rd->lines = Resize->line;
2020  pager_menu->redraw |= REDRAW_FLOW;
2021 
2022  FREE(&Resize);
2023  }
2024 
2025  if (IsEmail(rd->extra) && (C_PagerIndexLines != 0))
2026  {
2027  if (!rd->menu)
2028  {
2029  /* only allocate the space if/when we need the index.
2030  * Initialise the menu as per the main index */
2031  rd->menu = mutt_menu_new(MENU_MAIN);
2033  rd->menu->menu_color = index_color;
2034  rd->menu->max = Context ? Context->mailbox->vcount : 0;
2035  rd->menu->current = rd->extra->email->vnum;
2036  rd->menu->indexwin = rd->index_window;
2037  rd->menu->statuswin = rd->index_status_window;
2038  }
2039 
2040  NORMAL_COLOR;
2041  rd->menu->pagelen = rd->index_window->rows;
2042 
2043  /* some fudge to work out whereabouts the indicator should go */
2044  if (rd->menu->current - rd->indicator < 0)
2045  rd->menu->top = 0;
2046  else if (rd->menu->max - rd->menu->current < rd->menu->pagelen - rd->indicator)
2047  rd->menu->top = rd->menu->max - rd->menu->pagelen;
2048  else
2049  rd->menu->top = rd->menu->current - rd->indicator;
2050 
2051  menu_redraw_index(rd->menu);
2052  }
2053 
2054  pager_menu->redraw |= REDRAW_BODY | REDRAW_INDEX | REDRAW_STATUS;
2055 #ifdef USE_SIDEBAR
2056  pager_menu->redraw |= REDRAW_SIDEBAR;
2057 #endif
2058  mutt_show_error();
2059  }
2060 
2061  if (pager_menu->redraw & REDRAW_FLOW)
2062  {
2063  if (!(rd->flags & MUTT_PAGER_RETWINCH))
2064  {
2065  rd->lines = -1;
2066  for (int i = 0; i <= rd->topline; i++)
2067  if (!rd->line_info[i].continuation)
2068  rd->lines++;
2069  for (int i = 0; i < rd->max_line; i++)
2070  {
2071  rd->line_info[i].offset = 0;
2072  rd->line_info[i].type = -1;
2073  rd->line_info[i].continuation = 0;
2074  rd->line_info[i].chunks = 0;
2075  rd->line_info[i].search_cnt = -1;
2076  rd->line_info[i].quote = NULL;
2077 
2078  mutt_mem_realloc(&(rd->line_info[i].syntax), sizeof(struct TextSyntax));
2079  if (rd->search_compiled && rd->line_info[i].search)
2080  FREE(&(rd->line_info[i].search));
2081  }
2082 
2083  rd->last_line = 0;
2084  rd->topline = 0;
2085  }
2086  int i = -1;
2087  int j = -1;
2088  while (display_line(rd->fp, &rd->last_pos, &rd->line_info, ++i, &rd->last_line,
2089  &rd->max_line, rd->has_types | rd->search_flag | (rd->flags & MUTT_PAGER_NOWRAP),
2090  &rd->quote_list, &rd->q_level, &rd->force_redraw,
2091  &rd->search_re, rd->pager_window) == 0)
2092  {
2093  if (!rd->line_info[i].continuation && (++j == rd->lines))
2094  {
2095  rd->topline = i;
2096  if (!rd->search_flag)
2097  break;
2098  }
2099  }
2100  }
2101 
2102 #ifdef USE_SIDEBAR
2103  if (pager_menu->redraw & REDRAW_SIDEBAR)
2104  {
2105  menu_redraw_sidebar(pager_menu);
2106  }
2107 #endif
2108 
2109  if ((pager_menu->redraw & REDRAW_BODY) || (rd->topline != rd->oldtopline))
2110  {
2111  do
2112  {
2113  mutt_window_move(rd->pager_window, 0, 0);
2114  rd->curline = rd->topline;
2115  rd->oldtopline = rd->topline;
2116  rd->lines = 0;
2117  rd->force_redraw = false;
2118 
2119  while ((rd->lines < rd->pager_window->rows) &&
2120  (rd->line_info[rd->curline].offset <= rd->sb.st_size - 1))
2121  {
2122  if (display_line(rd->fp, &rd->last_pos, &rd->line_info, rd->curline,
2123  &rd->last_line, &rd->max_line,
2124  (rd->flags & MUTT_DISPLAYFLAGS) | rd->hide_quoted |
2125  rd->search_flag | (rd->flags & MUTT_PAGER_NOWRAP),
2126  &rd->quote_list, &rd->q_level, &rd->force_redraw,
2127  &rd->search_re, rd->pager_window) > 0)
2128  {
2129  rd->lines++;
2130  }
2131  rd->curline++;
2132  mutt_window_move(rd->pager_window, rd->lines, 0);
2133  }
2134  rd->last_offset = rd->line_info[rd->curline].offset;
2135  } while (rd->force_redraw);
2136 
2138  while (rd->lines < rd->pager_window->rows)
2139  {
2141  if (C_Tilde)
2142  addch('~');
2143  rd->lines++;
2144  mutt_window_move(rd->pager_window, rd->lines, 0);
2145  }
2146  NORMAL_COLOR;
2147 
2148  /* We are going to update the pager status bar, so it isn't
2149  * necessary to reset to normal color now. */
2150 
2151  pager_menu->redraw |= REDRAW_STATUS; /* need to update the % seen */
2152  }
2153 
2154  if (pager_menu->redraw & REDRAW_STATUS)
2155  {
2156  struct HdrFormatInfo hfi;
2157  char pager_progress_str[65]; /* Lots of space for translations */
2158 
2159  hfi.ctx = Context;
2160  hfi.mailbox = Context ? Context->mailbox : NULL;
2161  hfi.pager_progress = pager_progress_str;
2162 
2163  if (rd->last_pos < rd->sb.st_size - 1)
2164  {
2165  snprintf(pager_progress_str, sizeof(pager_progress_str), OFF_T_FMT "%%",
2166  (100 * rd->last_offset / rd->sb.st_size));
2167  }
2168  else
2169  {
2170  const char *msg = (rd->topline == 0) ?
2171  /* L10N: Status bar message: the entire email is visible in the pager */
2172  _("all") :
2173  /* L10N: Status bar message: the end of the email is visible in the pager */
2174  _("end");
2175  mutt_str_strfcpy(pager_progress_str, msg, sizeof(pager_progress_str));
2176  }
2177 
2178  /* print out the pager status bar */
2181 
2182  if (IsEmail(rd->extra) || IsMsgAttach(rd->extra))
2183  {
2184  size_t l1 = rd->pager_status_window->cols * MB_LEN_MAX;
2185  size_t l2 = sizeof(buf);
2186  hfi.email = (IsEmail(rd->extra)) ? rd->extra->email : rd->extra->body->email;
2187  mutt_make_string_info(buf, (l1 < l2) ? l1 : l2, rd->pager_status_window->cols,
2190  }
2191  else
2192  {
2193  char bn[256];
2194  snprintf(bn, sizeof(bn), "%s (%s)", rd->banner, pager_progress_str);
2195  mutt_draw_statusline(rd->pager_status_window->cols, bn, sizeof(bn));
2196  }
2197  NORMAL_COLOR;
2198  if (C_TsEnabled && TsSupported && rd->menu)
2199  {
2200  menu_status_line(buf, sizeof(buf), rd->menu, NONULL(C_TsStatusFormat));
2201  mutt_ts_status(buf);
2202  menu_status_line(buf, sizeof(buf), rd->menu, NONULL(C_TsIconFormat));
2203  mutt_ts_icon(buf);
2204  }
2205  }
2206 
2207  if ((pager_menu->redraw & REDRAW_INDEX) && rd->menu)
2208  {
2209  /* redraw the pager_index indicator, because the
2210  * flags for this message might have changed. */
2211  if (rd->index_window->rows > 0)
2213 
2214  /* print out the index status bar */
2215  menu_status_line(buf, sizeof(buf), rd->menu, NONULL(C_StatusFormat));
2216 
2219  mutt_draw_statusline(rd->index_status_window->cols, buf, sizeof(buf));
2220  NORMAL_COLOR;
2221  }
2222 
2223  pager_menu->redraw = REDRAW_NO_FLAGS;
2224 }
2225 
2241 int mutt_pager(const char *banner, const char *fname, PagerFlags flags, struct Pager *extra)
2242 {
2243  static char searchbuf[256] = { 0 };
2244  char buf[1024];
2245  int ch = 0, rc = -1;
2246  bool first = true;
2247  int searchctx = 0;
2248  bool wrapped = false;
2249 
2250  struct Menu *pager_menu = NULL;
2251  int old_PagerIndexLines; /* some people want to resize it while inside the pager */
2252 #ifdef USE_NNTP
2253  char *followup_to = NULL;
2254 #endif
2255 
2256  if (!(flags & MUTT_SHOWCOLOR))
2257  flags |= MUTT_SHOWFLAT;
2258 
2259  struct PagerRedrawData rd = { 0 };
2260  rd.banner = banner;
2261  rd.flags = flags;
2262  rd.extra = extra;
2264  rd.indicator = rd.indexlen / 3;
2265  rd.searchbuf = searchbuf;
2266  rd.has_types = (IsEmail(extra) || (flags & MUTT_SHOWCOLOR)) ? MUTT_TYPES : 0; /* main message or rfc822 attachment */
2267 
2268  rd.fp = fopen(fname, "r");
2269  if (!rd.fp)
2270  {
2271  mutt_perror(fname);
2272  return -1;
2273  }
2274 
2275  if (stat(fname, &rd.sb) != 0)
2276  {
2277  mutt_perror(fname);
2278  mutt_file_fclose(&rd.fp);
2279  return -1;
2280  }
2281  unlink(fname);
2282 
2283  /* Initialize variables */
2284 
2285  if (Context && IsEmail(extra) && !extra->email->read)
2286  {
2287  Context->msg_not_read_yet = extra->email->msgno;
2288  mutt_set_flag(Context->mailbox, extra->email, MUTT_READ, true);
2289  }
2290 
2291  rd.max_line = LINES; /* number of lines on screen, from curses */
2292  rd.line_info = mutt_mem_calloc(rd.max_line, sizeof(struct Line));
2293  for (size_t i = 0; i < rd.max_line; i++)
2294  {
2295  rd.line_info[i].type = -1;
2296  rd.line_info[i].search_cnt = -1;
2297  rd.line_info[i].syntax = mutt_mem_malloc(sizeof(struct TextSyntax));
2298  (rd.line_info[i].syntax)[0].first = -1;
2299  (rd.line_info[i].syntax)[0].last = -1;
2300  }
2301 
2302  struct Buffer helpstr = mutt_buffer_make(0);
2303  mutt_compile_help(buf, sizeof(buf), MENU_PAGER, PagerHelp);
2304  mutt_buffer_strcpy(&helpstr, buf);
2305  if (IsEmail(extra))
2306  {
2307  mutt_compile_help(buf, sizeof(buf), MENU_PAGER,
2308 #ifdef USE_NNTP
2309  (Context && (Context->mailbox->magic == MUTT_NNTP)) ?
2310  PagerNewsHelpExtra :
2311 #endif
2312  PagerHelpExtra);
2313  mutt_buffer_addch(&helpstr, ' ');
2314  mutt_buffer_addstr(&helpstr, buf);
2315  }
2316  if (!InHelp)
2317  {
2318  mutt_make_help(buf, sizeof(buf), _("Help"), MENU_PAGER, OP_HELP);
2319  mutt_buffer_addch(&helpstr, ' ');
2320  mutt_buffer_addstr(&helpstr, buf);
2321  }
2322  rd.helpstr = mutt_b2s(&helpstr);
2323 
2328 
2329  pager_menu = mutt_menu_new(MENU_PAGER);
2331  pager_menu->redraw_data = &rd;
2332  mutt_menu_push_current(pager_menu);
2333 
2334  while (ch != -1)
2335  {
2336  mutt_curs_set(0);
2337 
2338  pager_custom_redraw(pager_menu);
2339 
2340  if (C_BrailleFriendly)
2341  {
2342  if (braille_line != -1)
2343  {
2344  move(braille_line + 1, 0);
2345  braille_line = -1;
2346  }
2347  }
2348  else
2350 
2351  mutt_refresh();
2352 
2353  if (IsEmail(extra) && (OldEmail == extra->email) && (TopLine != rd.topline) &&
2354  (rd.line_info[rd.curline].offset < (rd.sb.st_size - 1)))
2355  {
2356  if ((TopLine - rd.topline) > rd.lines)
2357  rd.topline += rd.lines;
2358  else
2359  rd.topline = TopLine;
2360  continue;
2361  }
2362  else
2363  OldEmail = NULL;
2364 
2365  ch = km_dokey(MENU_PAGER);
2366  if (ch >= 0)
2367  {
2368  mutt_clear_error();
2369  }
2370  mutt_curs_set(1);
2371 
2372  bool do_new_mail = false;
2373 
2374  if (Context && Context->mailbox && !OptAttachMsg)
2375  {
2376  int index_hint = 0; /* used to restore cursor position */
2377  int oldcount = Context->mailbox->msg_count;
2378  /* check for new mail */
2379  int check = mx_mbox_check(Context->mailbox, &index_hint);
2380  if (check < 0)
2381  {
2383  {
2384  /* fatal error occurred */
2385  ctx_free(&Context);
2386  pager_menu->redraw = REDRAW_FULL;
2387  break;
2388  }
2389  }
2390  else if ((check == MUTT_NEW_MAIL) || (check == MUTT_REOPENED) || (check == MUTT_FLAGS))
2391  {
2392  /* notify user of newly arrived mail */
2393  if (check == MUTT_NEW_MAIL)
2394  {
2395  for (size_t i = oldcount; i < Context->mailbox->msg_count; i++)
2396  {
2397  struct Email *e = Context->mailbox->emails[i];
2398 
2399  if (e && !e->read)
2400  {
2401  mutt_message(_("New mail in this mailbox"));
2402  do_new_mail = true;
2403  break;
2404  }
2405  }
2406  }
2407 
2408  if ((check == MUTT_NEW_MAIL) || (check == MUTT_REOPENED))
2409  {
2410  if (rd.menu && Context)
2411  {
2412  /* After the mailbox has been updated,
2413  * rd.menu->current might be invalid */
2414  rd.menu->current =
2415  MIN(rd.menu->current, MAX(Context->mailbox->msg_count - 1, 0));
2416  index_hint = Context->mailbox
2417  ->emails[Context->mailbox->v2r[rd.menu->current]]
2418  ->index;
2419 
2420  bool q = Context->mailbox->quiet;
2421  Context->mailbox->quiet = true;
2422  update_index(rd.menu, Context, check, oldcount, index_hint);
2423  Context->mailbox->quiet = q;
2424 
2425  rd.menu->max = Context->mailbox->vcount;
2426 
2427  /* If these header pointers don't match, then our email may have
2428  * been deleted. Make the pointer safe, then leave the pager.
2429  * This have a unpleasant behaviour to close the pager even the
2430  * deleted message is not the opened one, but at least it's safe. */
2431  if (extra->email !=
2433  {
2434  extra->email =
2436  break;
2437  }
2438  }
2439 
2440  pager_menu->redraw = REDRAW_FULL;
2441  OptSearchInvalid = true;
2442  }
2443  }
2444 
2445  if (mutt_mailbox_notify(Context ? Context->mailbox : NULL) || do_new_mail)
2446  {
2447  if (C_BeepNew)
2448  beep();
2449  if (C_NewMailCommand)
2450  {
2451  char cmd[1024];
2452  menu_status_line(cmd, sizeof(cmd), rd.menu, NONULL(C_NewMailCommand));
2453  if (mutt_system(cmd) != 0)
2454  mutt_error(_("Error running \"%s\""), cmd);
2455  }
2456  }
2457  }
2458 
2459  if (SigWinch)
2460  {
2461  SigWinch = 0;
2463  clearok(stdscr, true); /* force complete redraw */
2464 
2465  if (flags & MUTT_PAGER_RETWINCH)
2466  {
2467  /* Store current position. */
2468  rd.lines = -1;
2469  for (size_t i = 0; i <= rd.topline; i++)
2470  if (!rd.line_info[i].continuation)
2471  rd.lines++;
2472 
2473  Resize = mutt_mem_malloc(sizeof(struct Resize));
2474 
2475  Resize->line = rd.lines;
2476  Resize->search_compiled = rd.search_compiled;
2477  Resize->search_back = rd.search_back;
2478 
2479  ch = -1;
2480  rc = OP_REFORMAT_WINCH;
2481  }
2482  else
2483  {
2484  /* note: mutt_resize_screen() -> mutt_window_reflow() sets
2485  * REDRAW_FULL and REDRAW_FLOW */
2486  ch = 0;
2487  }
2488  continue;
2489  }
2490 
2491  if (ch < 0)
2492  {
2493  ch = 0;
2495  continue;
2496  }
2497 
2498  rc = ch;
2499 
2500  switch (ch)
2501  {
2502  case OP_EXIT:
2503  rc = -1;
2504  ch = -1;
2505  break;
2506 
2507  case OP_QUIT:
2508  if (query_quadoption(C_Quit, _("Quit NeoMutt?")) == MUTT_YES)
2509  {
2510  /* avoid prompting again in the index menu */
2511  cs_str_native_set(Config, "quit", MUTT_YES, NULL);
2512  ch = -1;
2513  }
2514  break;
2515 
2516  case OP_NEXT_PAGE:
2517  if (rd.line_info[rd.curline].offset < (rd.sb.st_size - 1))
2518  {
2520  }
2521  else if (C_PagerStop)
2522  {
2523  /* emulate "less -q" and don't go on to the next message. */
2524  mutt_error(_("Bottom of message is shown"));
2525  }
2526  else
2527  {
2528  /* end of the current message, so display the next message. */
2529  rc = OP_MAIN_NEXT_UNDELETED;
2530  ch = -1;
2531  }
2532  break;
2533 
2534  case OP_PREV_PAGE:
2535  if (rd.topline != 0)
2536  {
2538  rd.line_info, rd.topline, rd.hide_quoted);
2539  }
2540  else
2541  mutt_message(_("Top of message is shown"));
2542  break;
2543 
2544  case OP_NEXT_LINE:
2545  if (rd.line_info[rd.curline].offset < (rd.sb.st_size - 1))
2546  {
2547  rd.topline++;
2548  if (rd.hide_quoted)
2549  {
2550  while ((rd.line_info[rd.topline].type == MT_COLOR_QUOTED) &&
2551  (rd.topline < rd.last_line))
2552  {
2553  rd.topline++;
2554  }
2555  }
2556  }
2557  else
2558  mutt_message(_("Bottom of message is shown"));
2559  break;
2560 
2561  case OP_PREV_LINE:
2562  if (rd.topline)
2563  rd.topline = up_n_lines(1, rd.line_info, rd.topline, rd.hide_quoted);
2564  else
2565  mutt_error(_("Top of message is shown"));
2566  break;
2567 
2568  case OP_PAGER_TOP:
2569  if (rd.topline)
2570  rd.topline = 0;
2571  else
2572  mutt_error(_("Top of message is shown"));
2573  break;
2574 
2575  case OP_HALF_UP:
2576  if (rd.topline)
2577  {
2578  rd.topline = up_n_lines(rd.pager_window->rows / 2 + (rd.pager_window->rows % 2),
2579  rd.line_info, rd.topline, rd.hide_quoted);
2580  }
2581  else
2582  mutt_error(_("Top of message is shown"));
2583  break;
2584 
2585  case OP_HALF_DOWN:
2586  if (rd.line_info[rd.curline].offset < (rd.sb.st_size - 1))
2587  {
2588  rd.topline = up_n_lines(rd.pager_window->rows / 2, rd.line_info,
2589  rd.curline, rd.hide_quoted);
2590  }
2591  else if (C_PagerStop)
2592  {
2593  /* emulate "less -q" and don't go on to the next message. */
2594  mutt_error(_("Bottom of message is shown"));
2595  }
2596  else
2597  {
2598  /* end of the current message, so display the next message. */
2599  rc = OP_MAIN_NEXT_UNDELETED;
2600  ch = -1;
2601  }
2602  break;
2603 
2604  case OP_SEARCH_NEXT:
2605  case OP_SEARCH_OPPOSITE:
2606  if (rd.search_compiled)
2607  {
2608  wrapped = false;
2609 
2610  if (C_SearchContext < rd.pager_window->rows)
2611  searchctx = C_SearchContext;
2612  else
2613  searchctx = 0;
2614 
2615  search_next:
2616  if ((!rd.search_back && (ch == OP_SEARCH_NEXT)) ||
2617  (rd.search_back && (ch == OP_SEARCH_OPPOSITE)))
2618  {
2619  /* searching forward */
2620  int i;
2621  for (i = wrapped ? 0 : rd.topline + searchctx + 1; i < rd.last_line; i++)
2622  {
2623  if ((!rd.hide_quoted || (rd.line_info[i].type != MT_COLOR_QUOTED)) &&
2624  !rd.line_info[i].continuation && (rd.line_info[i].search_cnt > 0))
2625  {
2626  break;
2627  }
2628  }
2629 
2630  if (i < rd.last_line)
2631  rd.topline = i;
2632  else if (wrapped || !C_WrapSearch)
2633  mutt_error(_("Not found"));
2634  else
2635  {
2636  mutt_message(_("Search wrapped to top"));
2637  wrapped = true;
2638  goto search_next;
2639  }
2640  }
2641  else
2642  {
2643  /* searching backward */
2644  int i;
2645  for (i = wrapped ? rd.last_line : rd.topline + searchctx - 1; i >= 0; i--)
2646  {
2647  if ((!rd.hide_quoted ||
2648  (rd.has_types && (rd.line_info[i].type != MT_COLOR_QUOTED))) &&
2649  !rd.line_info[i].continuation && (rd.line_info[i].search_cnt > 0))
2650  {
2651  break;
2652  }
2653  }
2654 
2655  if (i >= 0)
2656  rd.topline = i;
2657  else if (wrapped || !C_WrapSearch)
2658  mutt_error(_("Not found"));
2659  else
2660  {
2661  mutt_message(_("Search wrapped to bottom"));
2662  wrapped = true;
2663  goto search_next;
2664  }
2665  }
2666 
2667  if (rd.line_info[rd.topline].search_cnt > 0)
2668  {
2669  rd.search_flag = MUTT_SEARCH;
2670  /* give some context for search results */
2671  if (rd.topline - searchctx > 0)
2672  rd.topline -= searchctx;
2673  }
2674 
2675  break;
2676  }
2677  /* no previous search pattern */
2678  /* fallthrough */
2679 
2680  case OP_SEARCH:
2681  case OP_SEARCH_REVERSE:
2682  mutt_str_strfcpy(buf, searchbuf, sizeof(buf));
2683  if (mutt_get_field(((ch == OP_SEARCH) || (ch == OP_SEARCH_NEXT)) ?
2684  _("Search for: ") :
2685  _("Reverse search for: "),
2686  buf, sizeof(buf), MUTT_CLEAR) != 0)
2687  {
2688  break;
2689  }
2690 
2691  if (strcmp(buf, searchbuf) == 0)
2692  {
2693  if (rd.search_compiled)
2694  {
2695  /* do an implicit search-next */
2696  if (ch == OP_SEARCH)
2697  ch = OP_SEARCH_NEXT;
2698  else
2699  ch = OP_SEARCH_OPPOSITE;
2700 
2701  wrapped = false;
2702  goto search_next;
2703  }
2704  }
2705 
2706  if (buf[0] == '\0')
2707  break;
2708 
2709  mutt_str_strfcpy(searchbuf, buf, sizeof(searchbuf));
2710 
2711  /* leave search_back alone if ch == OP_SEARCH_NEXT */
2712  if (ch == OP_SEARCH)
2713  rd.search_back = false;
2714  else if (ch == OP_SEARCH_REVERSE)
2715  rd.search_back = true;
2716 
2717  if (rd.search_compiled)
2718  {
2719  regfree(&rd.search_re);
2720  for (size_t i = 0; i < rd.last_line; i++)
2721  {
2722  FREE(&(rd.line_info[i].search));
2723  rd.line_info[i].search_cnt = -1;
2724  }
2725  }
2726 
2727  int rflags = mutt_mb_is_lower(searchbuf) ? REG_ICASE : 0;
2728  int err = REG_COMP(&rd.search_re, searchbuf, REG_NEWLINE | rflags);
2729  if (err != 0)
2730  {
2731  regerror(err, &rd.search_re, buf, sizeof(buf));
2732  mutt_error("%s", buf);
2733  for (size_t i = 0; i < rd.max_line; i++)
2734  {
2735  /* cleanup */
2736  FREE(&(rd.line_info[i].search));
2737  rd.line_info[i].search_cnt = -1;
2738  }
2739  rd.search_flag = 0;
2740  rd.search_compiled = false;
2741  }
2742  else
2743  {
2744  rd.search_compiled = true;
2745  /* update the search pointers */
2746  int line_num = 0;
2747  while (display_line(rd.fp, &rd.last_pos, &rd.line_info, line_num,
2748  &rd.last_line, &rd.max_line,
2749  MUTT_SEARCH | (flags & MUTT_PAGER_NSKIP) | (flags & MUTT_PAGER_NOWRAP),
2750  &rd.quote_list, &rd.q_level, &rd.force_redraw,
2751  &rd.search_re, rd.pager_window) == 0)
2752  {
2753  line_num++;
2754  }
2755 
2756  if (!rd.search_back)
2757  {
2758  /* searching forward */
2759  int i;
2760  for (i = rd.topline; i < rd.last_line; i++)
2761  {
2762  if ((!rd.hide_quoted || (rd.line_info[i].type != MT_COLOR_QUOTED)) &&
2763  !rd.line_info[i].continuation && (rd.line_info[i].search_cnt > 0))
2764  {
2765  break;
2766  }
2767  }
2768 
2769  if (i < rd.last_line)
2770  rd.topline = i;
2771  }
2772  else
2773  {
2774  /* searching backward */
2775  int i;
2776  for (i = rd.topline; i >= 0; i--)
2777  {
2778  if ((!rd.hide_quoted || (rd.line_info[i].type != MT_COLOR_QUOTED)) &&
2779  !rd.line_info[i].continuation && (rd.line_info[i].search_cnt > 0))
2780  {
2781  break;
2782  }
2783  }
2784 
2785  if (i >= 0)
2786  rd.topline = i;
2787  }
2788 
2789  if (rd.line_info[rd.topline].search_cnt == 0)
2790  {
2791  rd.search_flag = 0;
2792  mutt_error(_("Not found"));
2793  }
2794  else
2795  {
2796  rd.search_flag = MUTT_SEARCH;
2797  /* give some context for search results */
2798  if (C_SearchContext < rd.pager_window->rows)
2799  searchctx = C_SearchContext;
2800  else
2801  searchctx = 0;
2802  if (rd.topline - searchctx > 0)
2803  rd.topline -= searchctx;
2804  }
2805  }
2806  pager_menu->redraw = REDRAW_BODY;
2807  break;
2808 
2809  case OP_SEARCH_TOGGLE:
2810  if (rd.search_compiled)
2811  {
2812  rd.search_flag ^= MUTT_SEARCH;
2813  pager_menu->redraw = REDRAW_BODY;
2814  }
2815  break;
2816 
2817  case OP_SORT:
2818  case OP_SORT_REVERSE:
2819  CHECK_MODE(IsEmail(extra))
2820  if (mutt_select_sort((ch == OP_SORT_REVERSE)) == 0)
2821  {
2822  OptNeedResort = true;
2823  ch = -1;
2824  rc = OP_DISPLAY_MESSAGE;
2825  }
2826  break;
2827 
2828  case OP_HELP:
2829  /* don't let the user enter the help-menu from the help screen! */
2830  if (!InHelp)
2831  {
2832  InHelp = true;
2834  pager_menu->redraw = REDRAW_FULL;
2835  InHelp = false;
2836  }
2837  else
2838  mutt_error(_("Help is currently being shown"));
2839  break;
2840 
2841  case OP_PAGER_HIDE_QUOTED:
2842  if (rd.has_types)
2843  {
2844  rd.hide_quoted ^= MUTT_HIDE;
2845  if (rd.hide_quoted && (rd.line_info[rd.topline].type == MT_COLOR_QUOTED))
2846  rd.topline = up_n_lines(1, rd.line_info, rd.topline, rd.hide_quoted);
2847  else
2848  pager_menu->redraw = REDRAW_BODY;
2849  }
2850  break;
2851 
2852  case OP_PAGER_SKIP_QUOTED:
2853  if (rd.has_types)
2854  {
2855  int dretval = 0;
2856  int new_topline = rd.topline;
2857 
2858  /* Skip all the email headers */
2859  if (IS_HEADER(rd.line_info[new_topline].type))
2860  {
2861  while (((new_topline < rd.last_line) ||
2862  (0 == (dretval = display_line(
2863  rd.fp, &rd.last_pos, &rd.line_info, new_topline, &rd.last_line,
2864  &rd.max_line, MUTT_TYPES | (flags & MUTT_PAGER_NOWRAP),
2865  &rd.quote_list, &rd.q_level, &rd.force_redraw,
2866  &rd.search_re, rd.pager_window)))) &&
2867  IS_HEADER(rd.line_info[new_topline].type))
2868  {
2869  new_topline++;
2870  }
2871  rd.topline = new_topline;
2872  break;
2873  }
2874 
2875  while ((((new_topline + C_SkipQuotedOffset) < rd.last_line) ||
2876  (0 == (dretval = display_line(
2877  rd.fp, &rd.last_pos, &rd.line_info, new_topline, &rd.last_line,
2878  &rd.max_line, MUTT_TYPES | (flags & MUTT_PAGER_NOWRAP),
2879  &rd.quote_list, &rd.q_level, &rd.force_redraw,
2880  &rd.search_re, rd.pager_window)))) &&
2881  (rd.line_info[new_topline + C_SkipQuotedOffset].type != MT_COLOR_QUOTED))
2882  {
2883  new_topline++;
2884  }
2885 
2886  if (dretval < 0)
2887  {
2888  mutt_error(_("No more quoted text"));
2889  break;
2890  }
2891 
2892  while ((((new_topline + C_SkipQuotedOffset) < rd.last_line) ||
2893  (0 == (dretval = display_line(
2894  rd.fp, &rd.last_pos, &rd.line_info, new_topline, &rd.last_line,
2895  &rd.max_line, MUTT_TYPES | (flags & MUTT_PAGER_NOWRAP),
2896  &rd.quote_list, &rd.q_level, &rd.force_redraw,
2897  &rd.search_re, rd.pager_window)))) &&
2898  (rd.line_info[new_topline + C_SkipQuotedOffset].type == MT_COLOR_QUOTED))
2899  {
2900  new_topline++;
2901  }
2902 
2903  if (dretval < 0)
2904  {
2905  mutt_error(_("No more unquoted text after quoted text"));
2906  break;
2907  }
2908  rd.topline = new_topline;
2909  }
2910  break;
2911 
2912  case OP_PAGER_BOTTOM: /* move to the end of the file */
2913  if (rd.line_info[rd.curline].offset < (rd.sb.st_size - 1))
2914  {
2915  int line_num = rd.curline;
2916  /* make sure the types are defined to the end of file */
2917  while (display_line(rd.fp, &rd.last_pos, &rd.line_info, line_num, &rd.last_line,
2918  &rd.max_line, rd.has_types | (flags & MUTT_PAGER_NOWRAP),
2919  &rd.quote_list, &rd.q_level, &rd.force_redraw,
2920  &rd.search_re, rd.pager_window) == 0)
2921  {
2922  line_num++;
2923  }
2925  rd.last_line, rd.hide_quoted);
2926  }
2927  else
2928  mutt_error(_("Bottom of message is shown"));
2929  break;
2930 
2931  case OP_REDRAW:
2932  clearok(stdscr, true);
2933  pager_menu->redraw = REDRAW_FULL;
2934  break;
2935 
2936  case OP_NULL:
2938  break;
2939 
2940  /* --------------------------------------------------------------------
2941  * The following are operations on the current message rather than
2942  * adjusting the view of the message. */
2943 
2944  case OP_BOUNCE_MESSAGE:
2945  {
2946  struct Mailbox *m = Context ? Context->mailbox : NULL;
2947  CHECK_MODE(IsEmail(extra) || IsMsgAttach(extra))
2948  CHECK_ATTACH;
2949  if (IsMsgAttach(extra))
2950  mutt_attach_bounce(m, extra->fp, extra->actx, extra->body);
2951  else
2952  {
2953  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
2954  el_add_email(&el, extra->email);
2955  ci_bounce_message(m, &el);
2956  emaillist_clear(&el);
2957  }
2958  break;
2959  }
2960 
2961  case OP_RESEND:
2962  CHECK_MODE(IsEmail(extra) || IsMsgAttach(extra))
2963  CHECK_ATTACH;
2964  if (IsMsgAttach(extra))
2965  mutt_attach_resend(extra->fp, extra->actx, extra->body);
2966  else
2967  mutt_resend_message(NULL, extra->ctx, extra->email);
2968  pager_menu->redraw = REDRAW_FULL;
2969  break;
2970 
2971  case OP_COMPOSE_TO_SENDER:
2972  CHECK_MODE(IsEmail(extra) || IsMsgAttach(extra));
2973  CHECK_ATTACH;
2974  if (IsMsgAttach(extra))
2975  mutt_attach_mail_sender(extra->fp, extra->email, extra->actx, extra->body);
2976  else
2977  {
2978  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
2979  el_add_email(&el, extra->email);
2980  ci_send_message(SEND_TO_SENDER, NULL, NULL, extra->ctx, &el);
2981  emaillist_clear(&el);
2982  }
2983  pager_menu->redraw = REDRAW_FULL;
2984  break;
2985 
2986  case OP_CHECK_TRADITIONAL:
2987  CHECK_MODE(IsEmail(extra));
2988  if (!(WithCrypto & APPLICATION_PGP))
2989  break;
2990  if (!(extra->email->security & PGP_TRADITIONAL_CHECKED))
2991  {
2992  ch = -1;
2993  rc = OP_CHECK_TRADITIONAL;
2994  }
2995  break;
2996 
2997  case OP_CREATE_ALIAS:
2998  CHECK_MODE(IsEmail(extra) || IsMsgAttach(extra));
2999  if (IsMsgAttach(extra))
3000  mutt_alias_create(extra->body->email->env, NULL);
3001  else
3002  mutt_alias_create(extra->email->env, NULL);
3003  break;
3004 
3005  case OP_PURGE_MESSAGE:
3006  case OP_DELETE:
3007  CHECK_MODE(IsEmail(extra));
3009  /* L10N: CHECK_ACL */
3010  CHECK_ACL(MUTT_ACL_DELETE, _("Can't delete message"));
3011 
3012  mutt_set_flag(Context->mailbox, extra->email, MUTT_DELETE, true);
3013  mutt_set_flag(Context->mailbox, extra->email, MUTT_PURGE, (ch == OP_PURGE_MESSAGE));
3014  if (C_DeleteUntag)
3015  mutt_set_flag(Context->mailbox, extra->email, MUTT_TAG, false);
3016  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3017  if (C_Resolve)
3018  {
3019  ch = -1;
3020  rc = OP_MAIN_NEXT_UNDELETED;
3021  }
3022  break;
3023 
3024  case OP_MAIN_SET_FLAG:
3025  case OP_MAIN_CLEAR_FLAG:
3026  {
3027  CHECK_MODE(IsEmail(extra));
3029 
3030  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3031  el_add_email(&el, extra->email);
3032 
3033  if (mutt_change_flag(Context->mailbox, &el, (ch == OP_MAIN_SET_FLAG)) == 0)
3034  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3035  if (extra->email->deleted && C_Resolve)
3036  {
3037  ch = -1;
3038  rc = OP_MAIN_NEXT_UNDELETED;
3039  }
3040  emaillist_clear(&el);
3041  break;
3042  }
3043 
3044  case OP_DELETE_THREAD:
3045  case OP_DELETE_SUBTHREAD:
3046  case OP_PURGE_THREAD:
3047  {
3048  CHECK_MODE(IsEmail(extra));
3050  /* L10N: CHECK_ACL */
3051  /* L10N: Due to the implementation details we do not know whether we
3052  delete zero, 1, 12, ... messages. So in English we use
3053  "messages". Your language might have other means to express this.
3054  */
3055  CHECK_ACL(MUTT_ACL_DELETE, _("Can't delete messages"));
3056 
3057  int subthread = (ch == OP_DELETE_SUBTHREAD);
3058  int r = mutt_thread_set_flag(extra->email, MUTT_DELETE, 1, subthread);
3059  if (r == -1)
3060  break;
3061  if (ch == OP_PURGE_THREAD)
3062  {
3063  r = mutt_thread_set_flag(extra->email, MUTT_PURGE, true, subthread);
3064  if (r == -1)
3065  break;
3066  }
3067 
3068  if (C_DeleteUntag)
3069  mutt_thread_set_flag(extra->email, MUTT_TAG, 0, subthread);
3070  if (C_Resolve)
3071  {
3072  rc = OP_MAIN_NEXT_UNDELETED;
3073  ch = -1;
3074  }
3075 
3076  if (!C_Resolve && (C_PagerIndexLines != 0))
3077  pager_menu->redraw = REDRAW_FULL;
3078  else
3079  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3080 
3081  break;
3082  }
3083 
3084  case OP_DISPLAY_ADDRESS:
3085  CHECK_MODE(IsEmail(extra) || IsMsgAttach(extra));
3086  if (IsMsgAttach(extra))
3087  mutt_display_address(extra->body->email->env);
3088  else
3089  mutt_display_address(extra->email->env);
3090  break;
3091 
3092  case OP_ENTER_COMMAND:
3093  old_PagerIndexLines = C_PagerIndexLines;
3094 
3096  pager_menu->redraw = REDRAW_FULL;
3097 
3098  if (OptNeedResort)
3099  {
3100  OptNeedResort = false;
3101  CHECK_MODE(IsEmail(extra));
3102  OptNeedResort = true;
3103  }
3104 
3105  if (old_PagerIndexLines != C_PagerIndexLines)
3106  {
3107  mutt_menu_free(&rd.menu);
3108  }
3109 
3110  if ((pager_menu->redraw & REDRAW_FLOW) && (flags & MUTT_PAGER_RETWINCH))
3111  {
3112  ch = -1;
3113  rc = OP_REFORMAT_WINCH;
3114  continue;
3115  }
3116 
3117  ch = 0;
3118  break;
3119 
3120  case OP_FLAG_MESSAGE:
3121  CHECK_MODE(IsEmail(extra));
3123  /* L10N: CHECK_ACL */
3124  CHECK_ACL(MUTT_ACL_WRITE, "Can't flag message");
3125 
3126  mutt_set_flag(Context->mailbox, extra->email, MUTT_FLAG, !extra->email->flagged);
3127  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3128  if (C_Resolve)
3129  {
3130  ch = -1;
3131  rc = OP_MAIN_NEXT_UNDELETED;
3132  }
3133  break;
3134 
3135  case OP_PIPE:
3136  CHECK_MODE(IsEmail(extra) || IsAttach(extra));
3137  if (IsAttach(extra))
3138  mutt_pipe_attachment_list(extra->actx, extra->fp, false, extra->body, false);
3139  else
3140  {
3141  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3142  el_add_tagged(&el, extra->ctx, extra->email, false);
3143  mutt_pipe_message(extra->ctx->mailbox, &el);
3144  emaillist_clear(&el);
3145  }
3146  break;
3147 
3148  case OP_PRINT:
3149  CHECK_MODE(IsEmail(extra) || IsAttach(extra));
3150  if (IsAttach(extra))
3151  mutt_print_attachment_list(extra->actx, extra->fp, false, extra->body);
3152  else
3153  {
3154  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3155  el_add_tagged(&el, extra->ctx, extra->email, false);
3156  mutt_print_message(extra->ctx->mailbox, &el);
3157  emaillist_clear(&el);
3158  }
3159  break;
3160 
3161  case OP_MAIL:
3162  CHECK_MODE(IsEmail(extra) && !IsAttach(extra));
3163  CHECK_ATTACH;
3164  ci_send_message(SEND_NO_FLAGS, NULL, NULL, extra->ctx, NULL);
3165  pager_menu->redraw = REDRAW_FULL;
3166  break;
3167 
3168 #ifdef USE_NNTP
3169  case OP_POST:
3170  CHECK_MODE(IsEmail(extra) && !IsAttach(extra));
3171  CHECK_ATTACH;
3172  if (extra->ctx && (extra->ctx->mailbox->magic == MUTT_NNTP) &&
3173  !((struct NntpMboxData *) extra->ctx->mailbox->mdata)->allowed && (query_quadoption(C_PostModerated, _("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES))
3174  {
3175  break;
3176  }
3177  ci_send_message(SEND_NEWS, NULL, NULL, extra->ctx, NULL);
3178  pager_menu->redraw = REDRAW_FULL;
3179  break;
3180 
3181  case OP_FORWARD_TO_GROUP:
3182  CHECK_MODE(IsEmail(extra) || IsMsgAttach(extra));
3183  CHECK_ATTACH;
3184  if (extra->ctx && (extra->ctx->mailbox->magic == MUTT_NNTP) &&
3185  !((struct NntpMboxData *) extra->ctx->mailbox->mdata)->allowed && (query_quadoption(C_PostModerated, _("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES))
3186  {
3187  break;
3188  }
3189  if (IsMsgAttach(extra))
3190  mutt_attach_forward(extra->fp, extra->email, extra->actx, extra->body, SEND_NEWS);
3191  else
3192  {
3193  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3194  el_add_email(&el, extra->email);
3195  ci_send_message(SEND_NEWS | SEND_FORWARD, NULL, NULL, extra->ctx, &el);
3196  emaillist_clear(&el);
3197  }
3198  pager_menu->redraw = REDRAW_FULL;
3199  break;
3200 
3201  case OP_FOLLOWUP:
3202  CHECK_MODE(IsEmail(extra) || IsMsgAttach(extra));
3203  CHECK_ATTACH;
3204 
3205  if (IsMsgAttach(extra))
3206  followup_to = extra->body->email->env->followup_to;
3207  else
3208  followup_to = extra->email->env->followup_to;
3209 
3210  if (!followup_to || (mutt_str_strcasecmp(followup_to, "poster") != 0) ||
3212  _("Reply by mail as poster prefers?")) != MUTT_YES))
3213  {
3214  if (extra->ctx && (extra->ctx->mailbox->magic == MUTT_NNTP) &&
3215  !((struct NntpMboxData *) extra->ctx->mailbox->mdata)->allowed && (query_quadoption(C_PostModerated, _("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES))
3216  {
3217  break;
3218  }
3219  if (IsMsgAttach(extra))
3220  {
3221  mutt_attach_reply(extra->fp, extra->email, extra->actx, extra->body,
3222  SEND_NEWS | SEND_REPLY);
3223  }
3224  else
3225  {
3226  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3227  el_add_email(&el, extra->email);
3228  ci_send_message(SEND_NEWS | SEND_REPLY, NULL, NULL, extra->ctx, &el);
3229  emaillist_clear(&el);
3230  }
3231  pager_menu->redraw = REDRAW_FULL;
3232  break;
3233  }
3234 #endif
3235  /* fallthrough */
3236  case OP_REPLY:
3237  case OP_GROUP_REPLY:
3238  case OP_GROUP_CHAT_REPLY:
3239  case OP_LIST_REPLY:
3240  {
3241  CHECK_MODE(IsEmail(extra) || IsMsgAttach(extra));
3242  CHECK_ATTACH;
3243 
3244  SendFlags replyflags = SEND_REPLY;
3245  if (ch == OP_GROUP_REPLY)
3246  replyflags |= SEND_GROUP_REPLY;
3247  else if (ch == OP_GROUP_CHAT_REPLY)
3248  replyflags |= SEND_GROUP_CHAT_REPLY;
3249  else if (ch == OP_LIST_REPLY)
3250  replyflags |= SEND_LIST_REPLY;
3251 
3252  if (IsMsgAttach(extra))
3253  mutt_attach_reply(extra->fp, extra->email, extra->actx, extra->body, replyflags);
3254  else
3255  {
3256  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3257  el_add_email(&el, extra->email);
3258  ci_send_message(replyflags, NULL, NULL, extra->ctx, &el);
3259  emaillist_clear(&el);
3260  }
3261  pager_menu->redraw = REDRAW_FULL;
3262  break;
3263  }
3264 
3265  case OP_RECALL_MESSAGE:
3266  {
3267  CHECK_MODE(IsEmail(extra) && !IsAttach(extra));
3268  CHECK_ATTACH;
3269  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3270  el_add_email(&el, extra->email);
3271  ci_send_message(SEND_POSTPONED, NULL, NULL, extra->ctx, &el);
3272  emaillist_clear(&el);
3273  pager_menu->redraw = REDRAW_FULL;
3274  break;
3275  }
3276 
3277  case OP_FORWARD_MESSAGE:
3278  CHECK_MODE(IsEmail(extra) || IsMsgAttach(extra));
3279  CHECK_ATTACH;
3280  if (IsMsgAttach(extra))
3281  mutt_attach_forward(extra->fp, extra->email, extra->actx, extra->body, SEND_NO_FLAGS);
3282  else
3283  {
3284  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3285  el_add_email(&el, extra->email);
3286  ci_send_message(SEND_FORWARD, NULL, NULL, extra->ctx, &el);
3287  emaillist_clear(&el);
3288  }
3289  pager_menu->redraw = REDRAW_FULL;
3290  break;
3291 
3292  case OP_DECRYPT_SAVE:
3293  if (!WithCrypto)
3294  {
3295  ch = -1;
3296  break;
3297  }
3298  /* fallthrough */
3299  case OP_SAVE:
3300  if (IsAttach(extra))
3301  {
3302  mutt_save_attachment_list(extra->actx, extra->fp, false, extra->body,
3303  extra->email, NULL);
3304  break;
3305  }
3306  /* fallthrough */
3307  case OP_COPY_MESSAGE:
3308  case OP_DECODE_SAVE:
3309  case OP_DECODE_COPY:
3310  case OP_DECRYPT_COPY:
3311  {
3312  if (!(WithCrypto != 0) && (ch == OP_DECRYPT_COPY))
3313  {
3314  ch = -1;
3315  break;
3316  }
3317  CHECK_MODE(IsEmail(extra));
3318  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3319  el_add_email(&el, extra->email);
3320  if ((mutt_save_message(Context->mailbox, &el,
3321  (ch == OP_DECRYPT_SAVE) || (ch == OP_SAVE) || (ch == OP_DECODE_SAVE),
3322  (ch == OP_DECODE_SAVE) || (ch == OP_DECODE_COPY),
3323  (ch == OP_DECRYPT_SAVE) || (ch == OP_DECRYPT_COPY)) == 0) &&
3324  ((ch == OP_SAVE) || (ch == OP_DECODE_SAVE) || (ch == OP_DECRYPT_SAVE)))
3325  {
3326  if (C_Resolve)
3327  {
3328  ch = -1;
3329  rc = OP_MAIN_NEXT_UNDELETED;
3330  }
3331  else
3332  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3333  }
3334  emaillist_clear(&el);
3335  break;
3336  }
3337 
3338  case OP_SHELL_ESCAPE:
3340  break;
3341 
3342  case OP_TAG:
3343  CHECK_MODE(IsEmail(extra));
3344  if (Context)
3345  {
3346  mutt_set_flag(Context->mailbox, extra->email, MUTT_TAG, !extra->email->tagged);
3347 
3348  Context->last_tag =
3349  extra->email->tagged ?
3350  extra->email :
3351  ((Context->last_tag == extra->email && !extra->email->tagged) ?
3352  NULL :
3353  Context->last_tag);
3354  }
3355 
3356  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3357  if (C_Resolve)
3358  {
3359  ch = -1;
3360  rc = OP_NEXT_ENTRY;
3361  }
3362  break;
3363 
3364  case OP_TOGGLE_NEW:
3365  CHECK_MODE(IsEmail(extra));
3367  /* L10N: CHECK_ACL */
3368  CHECK_ACL(MUTT_ACL_SEEN, _("Can't toggle new"));
3369 
3370  if (extra->email->read || extra->email->old)
3371  mutt_set_flag(Context->mailbox, extra->email, MUTT_NEW, true);
3372  else if (!first)
3373  mutt_set_flag(Context->mailbox, extra->email, MUTT_READ, true);
3374  first = false;
3375  Context->msg_not_read_yet = -1;
3376  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3377  if (C_Resolve)
3378  {
3379  ch = -1;
3380  rc = OP_MAIN_NEXT_UNDELETED;
3381  }
3382  break;
3383 
3384  case OP_UNDELETE:
3385  CHECK_MODE(IsEmail(extra));
3387  /* L10N: CHECK_ACL */
3388  CHECK_ACL(MUTT_ACL_DELETE, _("Can't undelete message"));
3389 
3390  mutt_set_flag(Context->mailbox, extra->email, MUTT_DELETE, false);
3391  mutt_set_flag(Context->mailbox, extra->email, MUTT_PURGE, false);
3392  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3393  if (C_Resolve)
3394  {
3395  ch = -1;
3396  rc = OP_NEXT_ENTRY;
3397  }
3398  break;
3399 
3400  case OP_UNDELETE_THREAD:
3401  case OP_UNDELETE_SUBTHREAD:
3402  {
3403  CHECK_MODE(IsEmail(extra));
3405  /* L10N: CHECK_ACL */
3406  /* L10N: Due to the implementation details we do not know whether we
3407  undelete zero, 1, 12, ... messages. So in English we use
3408  "messages". Your language might have other means to express this. */
3409  CHECK_ACL(MUTT_ACL_DELETE, _("Can't undelete messages"));
3410 
3411  int r = mutt_thread_set_flag(extra->email, MUTT_DELETE, false,
3412  (ch != OP_UNDELETE_THREAD));
3413  if (r != -1)
3414  {
3415  r = mutt_thread_set_flag(extra->email, MUTT_PURGE, false,
3416  (ch != OP_UNDELETE_THREAD));
3417  }
3418  if (r != -1)
3419  {
3420  if (C_Resolve)
3421  {
3422  rc = (ch == OP_DELETE_THREAD) ? OP_MAIN_NEXT_THREAD : OP_MAIN_NEXT_SUBTHREAD;
3423  ch = -1;
3424  }
3425 
3426  if (!C_Resolve && (C_PagerIndexLines != 0))
3427  pager_menu->redraw = REDRAW_FULL;
3428  else
3429  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3430  }
3431  break;
3432  }
3433 
3434  case OP_VERSION:
3436  break;
3437 
3438  case OP_MAILBOX_LIST:
3440  break;
3441 
3442  case OP_VIEW_ATTACHMENTS:
3443  if (flags & MUTT_PAGER_ATTACHMENT)
3444  {
3445  ch = -1;
3446  rc = OP_ATTACH_COLLAPSE;
3447  break;
3448  }
3449  CHECK_MODE(IsEmail(extra));
3450  mutt_view_attachments(extra->email);
3451  if (Context && extra->email->attach_del)
3452  Context->mailbox->changed = true;
3453  pager_menu->redraw = REDRAW_FULL;
3454  break;
3455 
3456  case OP_MAIL_KEY:
3457  {
3458  if (!(WithCrypto & APPLICATION_PGP))
3459  {
3460  ch = -1;
3461  break;
3462  }
3463  CHECK_MODE(IsEmail(extra));
3464  CHECK_ATTACH;
3465  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3466  el_add_email(&el, extra->email);
3467  ci_send_message(SEND_KEY, NULL, NULL, extra->ctx, &el);
3468  emaillist_clear(&el);
3469  pager_menu->redraw = REDRAW_FULL;
3470  break;
3471  }
3472 
3473  case OP_EDIT_LABEL:
3474  {
3475  CHECK_MODE(IsEmail(extra));
3476 
3477  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3478  el_add_email(&el, extra->email);
3479  rc = mutt_label_message(Context->mailbox, &el);
3480  emaillist_clear(&el);
3481 
3482  if (rc > 0)
3483  {
3484  Context->mailbox->changed = true;
3485  pager_menu->redraw = REDRAW_FULL;
3486  mutt_message(ngettext("%d label changed", "%d labels changed", rc), rc);
3487  }
3488  else
3489  {
3490  mutt_message(_("No labels changed"));
3491  }
3492  break;
3493  }
3494 
3495  case OP_FORGET_PASSPHRASE:
3497  break;
3498 
3499  case OP_EXTRACT_KEYS:
3500  {
3501  if (!WithCrypto)
3502  {
3503  ch = -1;
3504  break;
3505  }
3506  CHECK_MODE(IsEmail(extra));
3507  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3508  el_add_email(&el, extra->email);
3510  emaillist_clear(&el);
3511  pager_menu->redraw = REDRAW_FULL;
3512  break;
3513  }
3514 
3515  case OP_WHAT_KEY:
3516  mutt_what_key();
3517  break;
3518 
3519  case OP_CHECK_STATS:
3520  mutt_check_stats();
3521  break;
3522 
3523 #ifdef USE_SIDEBAR
3524  case OP_SIDEBAR_NEXT:
3525  case OP_SIDEBAR_NEXT_NEW:
3526  case OP_SIDEBAR_PAGE_DOWN:
3527  case OP_SIDEBAR_PAGE_UP:
3528  case OP_SIDEBAR_PREV:
3529  case OP_SIDEBAR_PREV_NEW:
3531  break;
3532 
3533  case OP_SIDEBAR_TOGGLE_VISIBLE:
3534  bool_str_toggle(Config, "sidebar_visible", NULL);
3536  break;
3537 #endif
3538 
3539  default:
3540  ch = -1;
3541  break;
3542  }
3543  }
3544 
3545  mutt_file_fclose(&rd.fp);
3546  if (IsEmail(extra))
3547  {
3548  if (Context)
3549  Context->msg_not_read_yet = -1;
3550  switch (rc)
3551  {
3552  case -1:
3553  case OP_DISPLAY_HEADERS:
3555  break;
3556  default:
3557  TopLine = rd.topline;
3558  OldEmail = extra->email;
3559  break;
3560  }
3561  }
3562 
3564 
3565  for (size_t i = 0; i < rd.max_line; i++)
3566  {
3567  FREE(&(rd.line_info[i].syntax));
3568  if (rd.search_compiled && rd.line_info[i].search)
3569  FREE(&(rd.line_info[i].search));
3570  }
3571  if (rd.search_compiled)
3572  {
3573  regfree(&rd.search_re);
3574  rd.search_compiled = false;
3575  }
3576  FREE(&rd.line_info);
3577  mutt_menu_pop_current(pager_menu);
3578  mutt_menu_free(&pager_menu);
3579  mutt_menu_free(&rd.menu);
3580 
3581  mutt_buffer_dealloc(&helpstr);
3586 
3587  return (rc != -1) ? rc : 0;
3588 }
int km_dokey(enum MenuType menu)
Determine what a keypress should do.
Definition: keymap.c:571
struct Menu * menu
the Pager Index (PI)
Definition: pager.c:197
struct Email ** emails
Array of Emails.
Definition: mailbox.h:110
int mutt_save_message(struct Mailbox *m, struct EmailList *el, bool delete_original, bool decode, bool decrypt)
Save an email.
Definition: commands.c:1002
struct Context * ctx
Definition: hdrline.h:47
The "current" mailbox.
Definition: context.h:36
WHERE bool C_StatusOnTop
Config: Display the status bar at the top.
Definition: globals.h:266
FILE * fp
Source stream.
Definition: pager.h:70
WHERE char * C_TsIconFormat
Config: printf-like format string for the terminal&#39;s icon title.
Definition: globals.h:152
Pager: signature lines.
Definition: mutt_curses.h:127
#define MUTT_TYPES
Compute line&#39;s type.
Definition: pager.h:48
int mutt_thread_set_flag(struct Email *e, int flag, bool bf, bool subthread)
Set a flag on an entire thread.
Definition: flags.c:375
#define REDRAW_FULL
Redraw everything.
Definition: mutt_menu.h:47
MIME attachments text (entire line)
Definition: mutt_curses.h:138
Manage keymappings.
bool mutt_mailbox_list(void)
List the mailboxes with new mail.
Definition: mutt_mailbox.c:201
#define NONULL(x)
Definition: string2.h:37
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
Define wrapper functions around Curses/Slang.
int msg_count
Total number of messages.
Definition: mailbox.h:102
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:69
bool C_AllowAnsi
Config: Allow ANSI colour codes in rich text messages.
Definition: pager.c:88
Representation of the email&#39;s header.
short C_PagerContext
Config: Number of lines of overlap when changing pages in the pager.
Definition: pager.c:90
#define SEND_TO_SENDER
Compose new email to sender.
Definition: send.h:99
const char * banner
Definition: pager.c:202
void mutt_pipe_message(struct Mailbox *m, struct EmailList *el)
Pipe a message.
Definition: commands.c:678
The envelope/body of an email.
Definition: email.h:39
#define MUTT_CLEAR
Clear input if printable character is pressed.
Definition: mutt.h:70
static int check_protected_header_marker(const char *p)
Check that the unique marker is present.
Definition: pager.c:931
PagerFlags hide_quoted
Definition: pager.c:188
struct MuttWindow * MuttStatusWindow
Status Window.
Definition: mutt_window.c:41
#define MIN(a, b)
Definition: memory.h:31
bool mutt_mb_is_display_corrupting_utf8(wchar_t wc)
Will this character corrupt the display?
Definition: mbyte.c:390
#define mutt_perror(...)
Definition: logging.h:85
#define ANSI_COLOR
Use colours.
Definition: pager.c:108
GUI selectable list of items.
Definition: mutt_menu.h:82
WHERE bool C_BeepNew
Config: Make a noise when new mail arrives.
Definition: globals.h:211
#define IsWPrint(wc)
Definition: mbyte.h:39
int mx_mbox_check(struct Mailbox *m, int *index_hint)
Check for new mail - Wrapper for MxOps::mbox_check()
Definition: mx.c:1020
Window management.
GUI miscellaneous curses (window drawing) routines.
PagerFlags search_flag
Definition: pager.c:200
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:51
#define MUTT_DISPLAYFLAGS
Definition: pager.h:60
Structs that make up an email.
String processing routines to generate the mail index.
The "currently-open" mailbox.
short chunks
Definition: pager.c:142
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: init.c:3331
GUI manage the main index (list of emails)
#define mutt_message(...)
Definition: logging.h:83
int * ColorQuote
Array of colours for quoted email text.
Definition: color.c:53
Keep track when the pager needs redrawing.
Definition: pager.c:174
static int up_n_lines(int nlines, struct Line *info, int cur, bool hiding)
Reposition the pager&#39;s view up by n lines.
Definition: pager.c:1910
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:39
WHERE unsigned char C_Quit
Config: Prompt before exiting NeoMutt.
Definition: globals.h:191
void mutt_display_address(struct Envelope *env)
Display the address of a message.
Definition: commands.c:885
#define NORMAL_COLOR
Definition: mutt_curses.h:239
#define MUTT_SHOWCOLOR
Show characters in color otherwise don&#39;t show characters.
Definition: pager.h:45
Pager: empty lines after message.
Definition: mutt_curses.h:133
size_t length
Definition: pager.c:116
int pair
Curses colour pair.
Definition: pager.c:158
#define REDRAW_FLOW
Used by pager to reflow text.
Definition: mutt_menu.h:49
WHERE unsigned char C_FollowupToPoster
Config: (nntp) Reply to the poster if &#39;poster&#39; is in the &#39;Followup-To&#39; header.
Definition: globals.h:197
void mutt_view_attachments(struct Email *e)
Show the attachments in a Menu.
Definition: recvattach.c:1412
static int TopLine
Definition: pager.c:211
static void cleanup_quote(struct QClass **quote_list)
Free a quote list.
Definition: pager.c:569
Pager: quoted text.
Definition: mutt_curses.h:126
struct MuttWindow * pager_status_window
Definition: pager.c:195
void mutt_save_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top, struct Email *e, struct Menu *menu)
Save a list of attachments.
Definition: recvattach.c:651
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:51
#define CHECK_ACL(aclbit, action)
Definition: pager.c:285
Bold text.
Definition: mutt_curses.h:141
#define SEND_FORWARD
Forward email.
Definition: send.h:90
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:107
void mutt_resize_screen(void)
Update NeoMutt&#39;s opinion about the window size (CURSES)
Definition: resize.c:98
NeoMutt Logging.
WHERE bool C_Help
Config: Display a help line with common key bindings.
Definition: globals.h:227
WHERE bool OptNeedResort
(pseudo) used to force a re-sort
Definition: options.h:41
void index_make_entry(char *buf, size_t buflen, struct Menu *menu, int line)
Format a menu item for the index list - Implements Menu::menu_make_entry()
Definition: index.c:748
void mutt_check_stats(void)
Forcibly update mailbox stats.
Definition: commands.c:1369
WHERE char AttachmentMarker[256]
Unique ANSI string to mark PGP messages in an email.
Definition: globals.h:46
void crypt_forget_passphrase(void)
Forget a passphrase and display a message.
Definition: crypt.c:105
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
#define IsMsgAttach(pager)
Definition: pager.c:255
int pair
Colour pair index.
Definition: mutt_curses.h:188
void mutt_enter_command(void)
enter a neomutt command
Definition: commands.c:841
static void pager_custom_redraw(struct Menu *pager_menu)
Redraw the pager window - Implements Menu::menu_custom_redraw()
Definition: pager.c:1934
String manipulation buffer.
Definition: buffer.h:33
Flagged messages.
Definition: mutt.h:108
#define PGP_TRADITIONAL_CHECKED
Email has a traditional (inline) signature.
Definition: ncrypt.h:136
#define _(a)
Definition: message.h:28
const char * helpstr
Definition: pager.c:203
WHERE struct ConfigSet * Config
Wrapper around the user&#39;s config settings.
Definition: globals.h:40
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, int flags)
Read a line from a file.
Definition: file.c:662
WHERE bool C_WrapSearch
Config: Wrap around when the search hits the end.
Definition: globals.h:272
struct Line * line_info
Definition: pager.c:205
WHERE short C_Wrap
Config: Width to wrap text in the pager.
Definition: globals.h:157
WHERE bool C_BrailleFriendly
Config: Move the cursor to the beginning of the line.
Definition: globals.h:212
int index_color(int line)
Calculate the colour for a line of the index - Implements Menu::menu_color()
Definition: index.c:822
Message headers (takes a pattern)
Definition: mutt_curses.h:136
int ColorQuoteUsed
Number of colours for quoted email text.
Definition: color.c:54
void menu_status_line(char *buf, size_t buflen, struct Menu *menu, const char *p)
Create the status line.
Definition: status.c:408
struct MuttWindow * MuttHelpWindow
Help Window.
Definition: mutt_window.c:39
struct Body * body
Current attachment.
Definition: pager.h:69
Messages to be purged (bypass trash)
Definition: mutt.h:106
A division of the screen.
Definition: mutt_window.h:35
Match case when comparing strings.
Definition: string2.h:67
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:666
PagerFlags flags
Definition: pager.c:176
int last
Definition: pager.c:131
Index panel (list of emails)
Definition: keymap.h:68
static bool InHelp
Definition: pager.c:214
Pager: search matches.
Definition: mutt_curses.h:140
bool search_back
Definition: pager.c:168
An ANSI escape sequence.
Definition: pager.c:153
static const char * Not_available_in_this_menu
Definition: pager.c:221
Pager: markers, line continuation.
Definition: mutt_curses.h:134
WHERE char * C_StatusFormat
Config: printf-like format string for the index&#39;s status line.
Definition: globals.h:150
bool search_compiled
Definition: pager.c:167
short type
Definition: pager.c:140
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition: pager.h:56
An email being displayed.
Definition: pager.h:65
uint16_t SendFlags
Flags for ci_send_message(), e.g. SEND_REPLY.
Definition: send.h:85
void mutt_color_free(uint32_t fg, uint32_t bg)
Flags to control mutt_expando_format()
All user-callable functions.
#define SEND_POSTPONED
Recall a postponed email.
Definition: send.h:91
#define ANSI_BOLD
Bold text.
Definition: pager.c:105
void mutt_help(enum MenuType menu)
Display the help menu.
Definition: help.c:439
WHERE bool C_TsEnabled
Config: Allow NeoMutt to set the terminal status line and icon.
Definition: globals.h:269
void(* menu_make_entry)(char *buf, size_t buflen, struct Menu *menu, int line)
Format a item for a menu.
Definition: mutt_menu.h:120
Set the terminal title/icon.
Representation of a single alias to an email address.
#define MUTT_ACL_DELETE
Delete a message.
Definition: mailbox.h:77
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
#define mutt_get_field(field, buf, buflen, complete)
Definition: curs_lib.h:86
int vcount
The number of virtual messages.
Definition: mailbox.h:113
#define REDRAW_SIDEBAR
Redraw the sidebar.
Definition: mutt_menu.h:51
#define MUTT_PAGER_ATTACHMENT
Attachments may exist.
Definition: pager.h:55
Convenience wrapper for the config headers.
void mutt_paddstr(int n, const char *s)
Display a string on screen, padded if necessary.
Definition: curs_lib.c:1173
struct ColorLineList ColorHdrList
List of colours applied to the email headers.
Definition: color.c:58
LOFF_T last_pos
Definition: pager.c:191
Plain text.
Definition: mutt_curses.h:131
Prepare and send an email.
Hundreds of global variables to back the user variables.
static void class_color_new(struct QClass *qc, int *q_level)
Create a new quoting colour.
Definition: pager.c:517
struct MuttWindow * index_status_window
Definition: pager.c:193
wchar_t ReplacementChar
When a Unicode character can&#39;t be displayed, use this instead.
Definition: charset.c:59
void mutt_print_message(struct Mailbox *m, struct EmailList *el)
Print a message.
Definition: commands.c:700
void mutt_window_free(struct MuttWindow **ptr)
Free a Window.
Definition: mutt_window.c:62
#define SEND_NO_FLAGS
No flags are set.
Definition: send.h:86
Some miscellaneous functions.
#define MAX(a, b)
Definition: memory.h:30
AnsiFlags attr
Attributes, e.g. underline, bold, etc.
Definition: pager.c:155
int line
Definition: pager.c:166
void mutt_attach_reply(FILE *fp, struct Email *e, struct AttachCtx *actx, struct Body *e_cur, SendFlags flags)
Attach a reply.
Definition: recvcmd.c:897
Pager pager (email viewer)
Definition: keymap.h:69
bool tagged
Email is tagged.
Definition: email.h:46
void * redraw_data
Definition: mutt_menu.h:150
bool read
Email is read.
Definition: email.h:53
void mutt_print_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top)
Print a list of Attachments.
Definition: recvattach.c:1029
static int comp_syntax_t(const void *m1, const void *m2)
Search for a Syntax using bsearch.
Definition: pager.c:341
#define ANSI_BLINK
Blinking text.
Definition: pager.c:104
struct Mailbox * mailbox
Definition: context.h:50
Parse and execute user-defined hooks.
Log at debug level 2.
Definition: logging.h:57
API for mailboxes.
static int braille_line
Definition: pager.c:216
bool old
Email is seen, but unread.
Definition: email.h:52
Data passed to index_format_str()
Definition: hdrline.h:45
void mutt_window_copy_size(const struct MuttWindow *win_src, struct MuttWindow *win_dst)
Copy the size of one Window to another.
Definition: mutt_window.c:236
static void resolve_types(char *buf, char *raw, struct Line *line_info, int n, int last, struct QClass **quote_list, int *q_level, bool *force_redraw, bool q_classify)
Determine the style for a line of text.
Definition: pager.c:987
int indexlen
Definition: pager.c:178
enum MailboxType magic
Mailbox type.
Definition: mailbox.h:116
LOFF_T last_offset
Definition: pager.c:192
struct MuttWindow * mutt_window_new(void)
Create a new Window.
Definition: mutt_window.c:51
void emaillist_clear(struct EmailList *el)
Drop a private list of Emails.
Definition: email.c:127
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:150
static int format_line(struct Line **line_info, int n, unsigned char *buf, PagerFlags flags, struct AnsiAttr *pa, int cnt, int *pspace, int *pvch, int *pcol, int *pspecial, int width)
Display a line of text in the pager.
Definition: pager.c:1440
void mutt_what_key(void)
Ask the user to press a key.
Definition: keymap.c:1576
struct Envelope * env
Envelope information.
Definition: email.h:91
int has_types
Definition: pager.c:187
Convenience wrapper for the core headers.
FILE * fp
Definition: pager.c:206
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:52
struct Mailbox * mailbox
Definition: hdrline.h:48
bool mutt_mailbox_notify(struct Mailbox *m_cur)
Notify the user if there&#39;s new mail.
Definition: mutt_mailbox.c:188
short continuation
Definition: pager.c:141
void mutt_shell_escape(void)
invoke a command in a subshell
Definition: commands.c:813
int el_add_email(struct EmailList *el, struct Email *e)
Get a list of the selected Emails.
Definition: context.c:398
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:293
WHERE SIG_ATOMIC_VOLATILE_T SigWinch
true after SIGWINCH is received
Definition: globals.h:85
void(* menu_custom_redraw)(struct Menu *menu)
Redraw the menu.
Definition: mutt_menu.h:149
WHERE unsigned char C_PostModerated
Config: (nntp) Allow posting to moderated newsgroups.
Definition: globals.h:196
#define MUTT_PAGER_NSKIP
Preserve whitespace with smartwrap.
Definition: pager.h:52
#define SEND_LIST_REPLY
Reply to mailing list.
Definition: send.h:89
#define NUM_SIG_LINES
Definition: pager.c:259
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:109
bool force_redraw
Definition: pager.c:186
void * mdata
Driver specific data.
Definition: mailbox.h:147
Underlined text.
Definition: mutt_curses.h:142
struct Regex * C_Smileys
Config: Regex to match smileys to prevent mistakes when quoting text.
Definition: pager.c:96
#define MUTT_SHOW
Definition: pager.h:49
#define MUTT_ACL_WRITE
Write to a message (for flagging or linking threads)
Definition: mailbox.h:85
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
void mutt_window_reflow(void)
Resize the Windows to fit the screen.
Definition: mutt_window.c:250
#define SEND_KEY
Mail a PGP public key.
Definition: send.h:94
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
int msg_not_read_yet
Which msg "new" in pager, -1 if none.
Definition: context.h:44
void mutt_make_string_info(char *buf, size_t buflen, int cols, const char *s, struct HdrFormatInfo *hfi, MuttFormatFlags flags)
Create pager status bar string.
Definition: hdrline.c:1520
int oldtopline
Definition: pager.c:180
#define ANSI_REVERSE
Reverse video.
Definition: pager.c:107
#define mutt_b2s(buf)
Definition: buffer.h:41
struct Pager * extra
Definition: pager.c:177
struct Context * ctx
Current mailbox.
Definition: pager.h:67
void mutt_alias_create(struct Envelope *cur, struct AddressList *al)
Create a new Alias from an Envelope or an Address.
Definition: alias.c:376
void mutt_refresh(void)
Force a refresh of the screen.
Definition: curs_lib.c:94
struct QClass * quote
Definition: pager.c:146
Prototypes for many functions.
void mutt_attach_bounce(struct Mailbox *m, FILE *fp, struct AttachCtx *actx, struct Body *cur)
Bounce function, from the attachment menu.
Definition: recvcmd.c:168
const char * line
Definition: common.c:36
short search_cnt
Definition: pager.c:143
void mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:310
static int braille_col
Definition: pager.c:217
int bg
Background colour.
Definition: pager.c:157
struct MuttWindow * statuswin
Definition: mutt_menu.h:96
WHERE struct Context * Context
Definition: globals.h:42
struct TextSyntax * search
Definition: pager.c:145
WHERE bool OptAttachMsg
(pseudo) used by attach-message
Definition: options.h:31
#define REDRAW_STATUS
Redraw the status bar.
Definition: mutt_menu.h:46
bool stop_matching
used by the pager for body patterns, to prevent the color from being retried once it fails ...
Definition: mutt_curses.h:190
bool search_compiled
Definition: pager.c:199
void km_error_key(enum MenuType menu)
Handle an unbound key sequence.
Definition: keymap.c:1051
#define CHECK_ATTACH
Definition: pager.c:277
Informational message.
Definition: mutt_curses.h:137
Messages to be deleted.
Definition: mutt.h:104
A mailbox.
Definition: mailbox.h:92
struct MuttWindow * MuttIndexWindow
Index Window.
Definition: mutt_window.c:40
struct MuttWindow * pager_window
Definition: pager.c:196
#define ATTR_SET
Definition: mutt_curses.h:232
struct MuttWindow * indexwin
Definition: mutt_menu.h:95
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
struct MuttWindow * index_window
Definition: pager.c:194
int top
Entry that is the top of the current page.
Definition: mutt_menu.h:108
Nondestructive flags change (IMAP)
Definition: mx.h:75
Manage where the email is piped to external commands.
static const char * Mailbox_is_read_only
Definition: pager.c:223
WHERE bool C_Resolve
Config: Move to the next email whenever a command modifies an email.
Definition: globals.h:253
struct QClass * down
Definition: pager.c:121
#define IS_HEADER(x)
Definition: pager.c:252
struct AttachCtx * actx
Attachment information.
Definition: pager.h:71
Tagged messages.
Definition: mutt.h:109
uint16_t PagerFlags
Flags for mutt_pager(), e.g. MUTT_SHOWFLAT.
Definition: pager.h:42
int max_line
Definition: pager.c:182
WHERE char * C_NewMailCommand
Config: External command to run when new mail arrives.
Definition: globals.h:142
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:750
#define CHECK_READONLY
Definition: pager.c:269
WHERE struct Regex * C_QuoteRegex
Config: Regex to match quoted text in a reply.
Definition: globals.h:182
A line of text in the pager.
Definition: pager.c:137
New messages.
Definition: mutt.h:99
Messages that have been read.
Definition: mutt.h:102
char * prefix
Definition: pager.c:119
GUI present the user with a selectable list.
int mutt_window_wrap_cols(int width, short wrap)
Calculate the wrap column for a given screen width.
Definition: mutt_window.c:337
int last_line
Definition: pager.c:183
bool quiet
Inhibit status messages?
Definition: mailbox.h:129
struct QClass * next
Definition: pager.c:120
int vnum
Virtual message number.
Definition: email.h:89
#define ANSI_UNDERLINE
Underlined text.
Definition: pager.c:106
#define SEND_NEWS
Reply to a news article.
Definition: send.h:101
int ci_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile, struct Context *ctx, struct EmailList *el)
Send an email.
Definition: send.c:1837
Handling of email attachments.
WHERE bool OptSearchInvalid
(pseudo) used to invalidate the search pat
Definition: options.h:50
API for encryption/signing of emails.
#define MUTT_HIDE
Don&#39;t show quoted text.
Definition: pager.h:46
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:350
void update_index(struct Menu *menu, struct Context *ctx, int check, int oldcount, int index_hint)
Update the index.
Definition: index.c:569
int pagelen
Number of entries per screen.
Definition: mutt_menu.h:92
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
bool TsSupported
Terminal Setting is supported.
Definition: terminal.c:42
static int check_marker(const char *q, const char *p)
Check that the unique marker is present.
Definition: pager.c:905
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/ncrypt.h pgplib.h, smime.h
Definition: email.h:41
int ColorDefs[MT_COLOR_MAX]
Array of all fixed colours, see enum ColorId.
Definition: color.c:55
Color and attribute parsing.
#define SET_COLOR(X)
Definition: mutt_curses.h:224
#define SEND_REPLY
Reply to sender.
Definition: send.h:87
#define MUTT_PAGER_LOGS
Logview mode.
Definition: pager.h:57
NNTP-specific Mailbox data -.
Definition: nntp.h:138
static const char * Function_not_permitted_in_attach_message_mode
Definition: pager.c:224
int mutt_window_move(struct MuttWindow *win, int row, int col)
Move the cursor in a Window.
Definition: mutt_window.c:188
void mutt_clear_pager_position(void)
Reset the pager&#39;s viewing position.
Definition: pager.c:1925
WHERE char * C_TsStatusFormat
Config: printf-like format string for the terminal&#39;s status (window title)
Definition: globals.h:151
int(* menu_color)(int line)
Calculate the colour for a line of the menu.
Definition: mutt_menu.h:144
bool C_SmartWrap
Config: Wrap text at word boundaries.
Definition: pager.c:95
int max
Number of entries in the menu.
Definition: mutt_menu.h:88
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: ncrypt.h:134
WHERE char ProtectedHeaderMarker[256]
Unique ANSI string to mark protected headers in an email.
Definition: globals.h:47
size_t mutt_str_startswith(const char *str, const char *prefix, enum CaseSensitivity cs)
Check whether a string starts with a prefix.
Definition: string.c:168
Menu showing log messages.
Definition: mutt_curses.h:154
WHERE bool C_DeleteUntag
Config: Untag messages when they are marked for deletion.
Definition: globals.h:216
bool C_PagerStop
Config: Don&#39;t automatically open the next message when at the end of a message.
Definition: pager.c:92
struct ColorLineList ColorAttachList
List of colours applied to the attachment headers.
Definition: color.c:56
GUI display a user-configurable status line.
MuttRedrawFlags redraw
When to redraw the screen.
Definition: mutt_menu.h:89
void mutt_attach_forward(FILE *fp, struct Email *e, struct AttachCtx *actx, struct Body *cur, SendFlags flags)
Forward an Attachment.
Definition: recvcmd.c:749
bool mutt_regex_capture(const struct Regex *regex, const char *str, size_t nmatch, regmatch_t matches[])
match a regex against a string, with provided options
Definition: regex.c:594
#define IS_SPACE(ch)
Definition: string2.h:38
int * v2r
Mapping from virtual to real msgno.
Definition: mailbox.h:112
struct QClass * quote_list
Definition: pager.c:190
regex_t regex
Compiled regex.
Definition: mutt_curses.h:182
#define IsEmail(pager)
Definition: pager.c:257
int indicator
the indicator line of the PI
Definition: pager.c:179
void mutt_pipe_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top, bool filter)
Pipe a list of attachments to a command.
Definition: recvattach.c:879
int fg
Foreground colour.
Definition: pager.c:156
GUI display a file/email/help in a viewport with paging.
struct Email * email
Current message.
Definition: pager.h:68
Highlighting for a line of text.
Definition: pager.c:127
void ci_bounce_message(struct Mailbox *m, struct EmailList *el)
Bounce an email.
Definition: commands.c:393
#define MUTT_PAGER_RETWINCH
Need reformatting on SIGWINCH.
Definition: pager.h:54
#define SEND_GROUP_REPLY
Reply to all.
Definition: send.h:88
bool CharsetIsUtf8
Is the user&#39;s current character set utf-8?
Definition: charset.c:64
#define MUTT_SEARCH
Resolve search patterns.
Definition: pager.h:47
#define REDRAW_BODY
Redraw the pager.
Definition: mutt_menu.h:48
int mutt_label_message(struct Mailbox *m, struct EmailList *el)
Let the user label a message.
Definition: mutt_header.c:124
char * searchbuf
Definition: pager.c:204
static int grok_ansi(unsigned char *buf, int pos, struct AnsiAttr *a)
Parse an ANSI escape sequence.
Definition: pager.c:1267
Routines for managing attachments.
Log at debug level 1.
Definition: logging.h:56
Send/reply with an attachment.
bool flagged
Marked important?
Definition: email.h:45
#define mutt_curs_set(x)
Definition: mutt_curses.h:94
#define REDRAW_INDEX
Redraw the index.
Definition: mutt_menu.h:42
int mutt_str_strncmp(const char *a, const char *b, size_t l)
Compare two strings (to a maximum), safely.
Definition: string.c:642
bool deleted
Email is deleted.
Definition: email.h:47
Cached regular expression.
Definition: regex3.h:57
int color
Definition: pager.c:118
#define mutt_error(...)
Definition: logging.h:84
int row_offset
Definition: mutt_window.h:39
static void shift_class_colors(struct QClass *quote_list, struct QClass *new_class, int index, int *q_level)
Insert a new quote colour class into a list.
Definition: pager.c:530
char * followup_to
List of &#39;followup-to&#39; fields.
Definition: envelope.h:77
int mutt_str_strcasecmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:628
Header default colour.
Definition: mutt_curses.h:125
void mutt_timeout_hook(void)
Execute any timeout hooks.
Definition: hook.c:806
int mutt_select_sort(bool reverse)
Ask the user for a sort method.
Definition: commands.c:739
short C_SkipQuotedOffset
Config: Lines of context to show when skipping quoted text.
Definition: pager.c:94
int index
The absolute (unsorted) message number.
Definition: email.h:87
#define FREE(x)
Definition: memory.h:40
Status bar.
Definition: mutt_curses.h:129
const char * pager_progress
Definition: hdrline.h:50
Error message.
Definition: mutt_curses.h:132
#define MUTT_EOL
don&#39;t strip \n / \r\n
Definition: file.h:39
Mapping between user-readable string and a constant.
Definition: mapping.h:29
bool C_HeaderColorPartial
Config: Only colour the part of the header matching the regex.
Definition: pager.c:89
void crypt_extract_keys_from_messages(struct Mailbox *m, struct EmailList *el)
Extract keys from a message.
Definition: crypt.c:855
#define MUTT_ACL_SEEN
Change the &#39;seen&#39; status of a message.
Definition: mailbox.h:84
int index
Definition: pager.c:117
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:322
void mutt_attach_mail_sender(FILE *fp, struct Email *e, struct AttachCtx *actx, struct Body *cur)
Compose an email to the sender in the email attachment.
Definition: recvcmd.c:1059
void mutt_ts_icon(char *str)
Set the icon in the terminal title bar.
Definition: terminal.c:117
bool C_Tilde
Config: Character to pad blank lines in the pager.
Definition: pager.c:97
struct TextSyntax * syntax
Definition: pager.c:144
void mutt_show_error(void)
Show the user an error message.
Definition: curs_lib.c:485
void mutt_make_help(char *buf, size_t buflen, const char *txt, enum MenuType menu, int op)
Create one entry for the help bar.
Definition: help.c:92
#define MUTT_PAGER_MARKER
Use markers if option is set.
Definition: pager.h:53
int mutt_is_quote_line(char *line, regmatch_t *pmatch)
Is a line of message text a quote?
Definition: pager.c:945
bool changed
Mailbox has been modified.
Definition: mailbox.h:125
int mutt_change_flag(struct Mailbox *m, struct EmailList *el, bool bf)
Change the flag on a Message.
Definition: flags.c:433
void ctx_free(struct Context **ptr)
Free a Context.
Definition: context.c:48
Handling of global boolean variables.
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
int bool_str_toggle(struct ConfigSet *cs, const char *name, struct Buffer *err)
Toggle the value of a bool.
Definition: bool.c:240
static int fill_buffer(FILE *fp, LOFF_T *last_pos, LOFF_T offset, unsigned char **buf, unsigned char **fmt, size_t *blen, int *buf_ready)
Fill a buffer from a file.
Definition: pager.c:1364
New mail received in Mailbox.
Definition: mx.h:72
struct QClass * prev
Definition: pager.c:120
void mutt_ts_status(char *str)
Set the text of the terminal title bar.
Definition: terminal.c:103
static struct QClass * classify_quote(struct QClass **quote_list, const char *qptr, size_t length, bool *force_redraw, int *q_level)
Find a style for a string.
Definition: pager.c:593
#define SEND_GROUP_CHAT_REPLY
Reply to all recipients preserving To/Cc.
Definition: send.h:100
struct stat sb
Definition: pager.c:207
int mutt_pager(const char *banner, const char *fname, PagerFlags flags, struct Pager *extra)
Display a file, or help, in a window.
Definition: pager.c:2241
#define IsAttach(pager)
Definition: pager.c:254
int current
Current entry.
Definition: mutt_menu.h:87
int el_add_tagged(struct EmailList *el, struct Context *ctx, struct Email *e, bool use_tagged)
Get a list of the tagged Emails.
Definition: context.c:357
int first
Definition: pager.c:130
static bool is_ansi(unsigned char *buf)
Is this an ANSI escape sequence?
Definition: pager.c:1253
uint8_t AnsiFlags
Flags, e.g. ANSI_OFF.
Definition: pager.c:101
struct Buffer pathbuf
Definition: mailbox.h:94
char * mutt_compile_help(char *buf, size_t buflen, enum MenuType menu, const struct Mapping *items)
Create the text for the help menu.
Definition: help.c:115
static void resolve_color(struct Line *line_info, int n, int cnt, PagerFlags flags, int special, struct AnsiAttr *a)
Set the colour for a line of text.
Definition: pager.c:362
static int check_attachment_marker(const char *p)
Check that the unique marker is present.
Definition: pager.c:921
struct Email * email
Definition: hdrline.h:49
WHERE bool C_Markers
Config: Display a &#39;+&#39; at the beginning of wrapped lines in the pager.
Definition: globals.h:248
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
int mutt_alloc_color(uint32_t fg, uint32_t bg)
#define REDRAW_NO_FLAGS
No flags are set.
Definition: mutt_menu.h:41
LOFF_T offset
Definition: pager.c:139
Mailbox was reopened.
Definition: mx.h:74
int mutt_resend_message(FILE *fp, struct Context *ctx, struct Email *e_cur)
Resend an email.
Definition: send.c:1467
short C_SearchContext
Config: Context to display around search matches.
Definition: pager.c:93
#define CHECK_MODE(test)
Definition: pager.c:261
struct ColorLineList ColorBodyList
List of colours applied to the email body.
Definition: color.c:57
#define N_(a)
Definition: message.h:32
bool mutt_mb_is_lower(const char *s)
Does a multi-byte string contain only lowercase characters?
Definition: mbyte.c:358
struct Email * email
header information for message/rfc822
Definition: body.h:55
#define WithCrypto
Definition: ncrypt.h:160
#define MUTT_SHOWFLAT
Show characters (used for displaying help)
Definition: pager.h:44
Convenience wrapper for the library headers.
void mutt_attach_resend(FILE *fp, struct AttachCtx *actx, struct Body *cur)
resend-message, from the attachment menu
Definition: recvcmd.c:290
static int check_sig(const char *s, struct Line *info, int n)
Check for an email signature.
Definition: pager.c:302
static int display_line(FILE *fp, LOFF_T *last_pos, struct Line **line_info, int n, int *last, int *max, PagerFlags flags, struct QClass **quote_list, int *q_level, bool *force_redraw, regex_t *search_re, struct MuttWindow *pager_window)
Print a line on screen.
Definition: pager.c:1633
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:615
int mutt_system(const char *cmd)
Run an external command.
Definition: system.c:51
Log at debug level 3.
Definition: logging.h:58
unsigned int is_cont_hdr
this line is a continuation of the previous header line
Definition: pager.c:147
struct QClass * up
Definition: pager.c:121
static void append_line(struct Line *line_info, int n, int cnt)
Add a new Line to the array.
Definition: pager.c:494
Style of quoted text.
Definition: pager.c:114
void mutt_draw_statusline(int cols, const char *buf, size_t buflen)
Draw a highlighted status bar.
Definition: index.c:851
short C_PagerIndexLines
Config: Number of index lines to display above the pager.
Definition: pager.c:91
A regular expression and a color to highlight a line.
Definition: mutt_curses.h:180
struct Email * last_tag
Last tagged msg (used to link threads)
Definition: context.h:41
WHERE char * C_PagerFormat
Config: printf-like format string for the pager&#39;s status bar.
Definition: globals.h:138
int color
Definition: pager.c:129
Usenet network mailbox type; talk to an NNTP server.
regex_t search_re
Definition: pager.c:198
int msgno
Number displayed to the user.
Definition: email.h:88
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1523
int mutt_addwch(wchar_t wc)
addwch would be provided by an up-to-date curses library
Definition: curs_lib.c:966
int cs_str_native_set(const struct ConfigSet *cs, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition: set.c:772
#define ANSI_OFF
Turn off colours and attributes.
Definition: pager.c:103
bool search_back
Definition: pager.c:201