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