NeoMutt  2021-10-29-43-g6b8931
Teaching an old dog new tricks
DOXYGEN
display.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 <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <wchar.h>
39 #include "mutt/lib.h"
40 #include "config/lib.h"
41 #include "core/lib.h"
42 #include "gui/lib.h"
43 #include "display.h"
44 #include "lib.h"
45 #include "color/lib.h"
46 #include "protos.h"
47 
56 static int check_sig(const char *s, struct Line *info, int offset)
57 {
58  const unsigned int NUM_SIG_LINES = 4; // The amount of lines a signature takes
59  unsigned int count = 0;
60 
61  while ((offset > 0) && (count <= NUM_SIG_LINES))
62  {
63  if (info[offset].color != MT_COLOR_SIGNATURE)
64  break;
65  count++;
66  offset--;
67  }
68 
69  if (count == 0)
70  return -1;
71 
72  if (count > NUM_SIG_LINES)
73  {
74  /* check for a blank line */
75  while (*s)
76  {
77  if (!IS_SPACE(*s))
78  return 0;
79  s++;
80  }
81 
82  return -1;
83  }
84 
85  return 0;
86 }
87 
96 static int comp_syntax_t(const void *m1, const void *m2)
97 {
98  const int *cnt = (const int *) m1;
99  const struct TextSyntax *stx = (const struct TextSyntax *) m2;
100 
101  if (*cnt < stx->first)
102  return -1;
103  if (*cnt >= stx->last)
104  return 1;
105  return 0;
106 }
107 
118 static void resolve_color(struct MuttWindow *win, struct Line *lines, int line_num,
119  int cnt, PagerFlags flags, int special, struct AnsiAttr *aa)
120 {
121  int def_color; /* color without syntax highlight */
122  int color; /* final color */
123  static int last_color; /* last color set */
124  bool search = false;
125  int m;
126  struct TextSyntax *matching_chunk = NULL;
127 
128  if (cnt == 0)
129  last_color = -1;
130 
131  if (lines[line_num].cont_line)
132  {
133  const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
134  if (!cnt && c_markers)
135  {
137  mutt_window_addch(win, '+');
138  last_color = simple_colors_get(MT_COLOR_MARKERS);
139  }
140  m = (lines[line_num].syntax)[0].first;
141  cnt += (lines[line_num].syntax)[0].last;
142  }
143  else
144  m = line_num;
145  if (flags & MUTT_PAGER_LOGS)
146  {
147  def_color = simple_colors_get(lines[line_num].syntax[0].color);
148  }
149  else if (!(flags & MUTT_SHOWCOLOR))
150  def_color = simple_colors_get(MT_COLOR_NORMAL);
151  else if (lines[m].color == MT_COLOR_HEADER)
152  def_color = lines[m].syntax[0].color;
153  else
154  def_color = simple_colors_get(lines[m].color);
155 
156  if ((flags & MUTT_SHOWCOLOR) && (lines[m].color == MT_COLOR_QUOTED))
157  {
158  struct QuoteStyle *qc = lines[m].quote;
159 
160  if (qc)
161  {
162  def_color = qc->color;
163 
164  while (qc && (qc->prefix_len > cnt))
165  {
166  def_color = qc->color;
167  qc = qc->up;
168  }
169  }
170  }
171 
172  color = def_color;
173  if ((flags & MUTT_SHOWCOLOR) && lines[m].syntax_arr_size)
174  {
175  matching_chunk = bsearch(&cnt, lines[m].syntax, lines[m].syntax_arr_size,
176  sizeof(struct TextSyntax), comp_syntax_t);
177  if (matching_chunk && (cnt >= matching_chunk->first) &&
178  (cnt < matching_chunk->last))
179  {
180  color = matching_chunk->color;
181  }
182  }
183 
184  if ((flags & MUTT_SEARCH) && lines[m].search_arr_size)
185  {
186  matching_chunk = bsearch(&cnt, lines[m].search, lines[m].search_arr_size,
187  sizeof(struct TextSyntax), comp_syntax_t);
188  if (matching_chunk && (cnt >= matching_chunk->first) &&
189  (cnt < matching_chunk->last))
190  {
192  search = 1;
193  }
194  }
195 
196  /* handle "special" bold & underlined characters */
197  if (special || aa->attr)
198  {
199  if ((aa->attr & ANSI_COLOR))
200  {
201  if (aa->pair == -1)
202  aa->pair = mutt_color_alloc(aa->fg, aa->bg);
203  color = aa->pair;
204  if (aa->attr & ANSI_BOLD)
205  color |= A_BOLD;
206  }
207  else if ((special & A_BOLD) || (aa->attr & ANSI_BOLD))
208  {
211  else
212  color ^= A_BOLD;
213  }
214  if ((special & A_UNDERLINE) || (aa->attr & ANSI_UNDERLINE))
215  {
218  else
219  color ^= A_UNDERLINE;
220  }
221  else if (aa->attr & ANSI_REVERSE)
222  {
223  color ^= A_REVERSE;
224  }
225  else if (aa->attr & ANSI_BLINK)
226  {
227  color ^= A_BLINK;
228  }
229  else if (aa->attr == ANSI_OFF)
230  {
231  aa->attr = 0;
232  }
233  }
234 
235  if (color != last_color)
236  {
238  last_color = color;
239  }
240 }
241 
248 static void append_line(struct Line *lines, int line_num, int cnt)
249 {
250  int m;
251 
252  lines[line_num + 1].color = lines[line_num].color;
253  (lines[line_num + 1].syntax)[0].color = (lines[line_num].syntax)[0].color;
254  lines[line_num + 1].cont_line = 1;
255 
256  /* find the real start of the line */
257  for (m = line_num; m >= 0; m--)
258  if (lines[m].cont_line == 0)
259  break;
260 
261  (lines[line_num + 1].syntax)[0].first = m;
262  (lines[line_num + 1].syntax)[0].last =
263  (lines[line_num].cont_line) ? cnt + (lines[line_num].syntax)[0].last : cnt;
264 }
265 
272 static int check_marker(const char *q, const char *p)
273 {
274  for (; (p[0] == q[0]) && (q[0] != '\0') && (p[0] != '\0') && (q[0] != '\a') &&
275  (p[0] != '\a');
276  p++, q++)
277  {
278  }
279 
280  return (int) (*p - *q);
281 }
282 
288 static int check_attachment_marker(const char *p)
289 {
291 }
292 
298 static int check_protected_header_marker(const char *p)
299 {
301 }
302 
312 int mutt_is_quote_line(char *line, regmatch_t *pmatch)
313 {
314  bool is_quote = false;
315  const struct Regex *c_smileys = cs_subset_regex(NeoMutt->sub, "smileys");
316  regmatch_t pmatch_internal[1], smatch[1];
317 
318  if (!pmatch)
319  pmatch = pmatch_internal;
320 
321  const struct Regex *c_quote_regex =
322  cs_subset_regex(NeoMutt->sub, "quote_regex");
323  if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
324  {
325  if (mutt_regex_capture(c_smileys, line, 1, smatch))
326  {
327  if (smatch[0].rm_so > 0)
328  {
329  char c = line[smatch[0].rm_so];
330  line[smatch[0].rm_so] = 0;
331 
332  if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
333  is_quote = true;
334 
335  line[smatch[0].rm_so] = c;
336  }
337  }
338  else
339  is_quote = true;
340  }
341 
342  return is_quote;
343 }
344 
358 static void resolve_types(struct MuttWindow *win, char *buf, char *raw,
359  struct Line *lines, int line_num, int lines_used,
360  struct QuoteStyle **quote_list, int *q_level,
361  bool *force_redraw, bool q_classify)
362 {
363  struct RegexColor *color_line = NULL;
364  struct RegexColorList *head = NULL;
365  regmatch_t pmatch[1];
366  bool found;
367  bool null_rx;
368  const bool c_header_color_partial =
369  cs_subset_bool(NeoMutt->sub, "header_color_partial");
370  int offset, i = 0;
371 
372  if ((line_num == 0) || simple_color_is_header(lines[line_num - 1].color) ||
373  (check_protected_header_marker(raw) == 0))
374  {
375  if (buf[0] == '\n') /* end of header */
376  {
377  lines[line_num].color = MT_COLOR_NORMAL;
379  }
380  else
381  {
382  /* if this is a continuation of the previous line, use the previous
383  * line's color as default. */
384  if ((line_num > 0) && ((buf[0] == ' ') || (buf[0] == '\t')))
385  {
386  lines[line_num].color = lines[line_num - 1].color; /* wrapped line */
387  if (!c_header_color_partial)
388  {
389  (lines[line_num].syntax)[0].color = (lines[line_num - 1].syntax)[0].color;
390  lines[line_num].cont_header = 1;
391  }
392  }
393  else
394  {
395  lines[line_num].color = MT_COLOR_HDRDEFAULT;
396  }
397 
398  /* When this option is unset, we color the entire header the
399  * same color. Otherwise, we handle the header patterns just
400  * like body patterns (further below). */
401  if (!c_header_color_partial)
402  {
404  {
405  if (regexec(&color_line->regex, buf, 0, NULL, 0) == 0)
406  {
407  lines[line_num].color = MT_COLOR_HEADER;
408  lines[line_num].syntax[0].color = color_line->pair;
409  if (lines[line_num].cont_header)
410  {
411  /* adjust the previous continuation lines to reflect the color of this continuation line */
412  int j;
413  for (j = line_num - 1; j >= 0 && lines[j].cont_header; --j)
414  {
415  lines[j].color = lines[line_num].color;
416  lines[j].syntax[0].color = lines[line_num].syntax[0].color;
417  }
418  /* now adjust the first line of this header field */
419  if (j >= 0)
420  {
421  lines[j].color = lines[line_num].color;
422  lines[j].syntax[0].color = lines[line_num].syntax[0].color;
423  }
424  *force_redraw = true; /* the previous lines have already been drawn on the screen */
425  }
426  break;
427  }
428  }
429  }
430  }
431  }
432  else if (mutt_str_startswith(raw, "\033[0m")) // Escape: a little hack...
433  lines[line_num].color = MT_COLOR_NORMAL;
434  else if (check_attachment_marker((char *) raw) == 0)
435  lines[line_num].color = MT_COLOR_ATTACHMENT;
436  else if (mutt_str_equal("-- \n", buf) || mutt_str_equal("-- \r\n", buf))
437  {
438  i = line_num + 1;
439 
440  lines[line_num].color = MT_COLOR_SIGNATURE;
441  while ((i < lines_used) && (check_sig(buf, lines, i - 1) == 0) &&
442  ((lines[i].color == MT_COLOR_NORMAL) || (lines[i].color == MT_COLOR_QUOTED) ||
443  (lines[i].color == MT_COLOR_HEADER)))
444  {
445  /* oops... */
446  if (lines[i].syntax_arr_size)
447  {
448  lines[i].syntax_arr_size = 0;
449  mutt_mem_realloc(&(lines[line_num].syntax), sizeof(struct TextSyntax));
450  }
451  lines[i++].color = MT_COLOR_SIGNATURE;
452  }
453  }
454  else if (check_sig(buf, lines, line_num - 1) == 0)
455  lines[line_num].color = MT_COLOR_SIGNATURE;
456  else if (mutt_is_quote_line(buf, pmatch))
457 
458  {
459  if (q_classify && (lines[line_num].quote == NULL))
460  {
461  lines[line_num].quote = qstyle_classify(quote_list, buf + pmatch[0].rm_so,
462  pmatch[0].rm_eo - pmatch[0].rm_so,
463  force_redraw, q_level);
464  }
465  lines[line_num].color = MT_COLOR_QUOTED;
466  }
467  else
468  lines[line_num].color = MT_COLOR_NORMAL;
469 
470  /* body patterns */
471  if ((lines[line_num].color == MT_COLOR_NORMAL) || (lines[line_num].color == MT_COLOR_QUOTED) ||
472  ((lines[line_num].color == MT_COLOR_HDRDEFAULT) && c_header_color_partial))
473  {
474  size_t nl;
475 
476  /* don't consider line endings part of the buffer
477  * for regex matching */
478  nl = mutt_str_len(buf);
479  if ((nl > 0) && (buf[nl - 1] == '\n'))
480  buf[nl - 1] = '\0';
481 
482  i = 0;
483  offset = 0;
484  lines[line_num].syntax_arr_size = 0;
485  if (lines[line_num].color == MT_COLOR_HDRDEFAULT)
487  else
489  STAILQ_FOREACH(color_line, head, entries)
490  {
491  color_line->stop_matching = false;
492  }
493  do
494 
495  {
496  if (!buf[offset])
497  break;
498 
499  found = false;
500  null_rx = false;
501  STAILQ_FOREACH(color_line, head, entries)
502  {
503  if (!color_line->stop_matching &&
504  (regexec(&color_line->regex, buf + offset, 1, pmatch,
505  ((offset != 0) ? REG_NOTBOL : 0)) == 0))
506  {
507  if (pmatch[0].rm_eo == pmatch[0].rm_so)
508  {
509  null_rx = true; /* empty regex; don't add it, but keep looking */
510  continue;
511  }
512 
513  if (!found)
514  {
515  /* Abort if we fill up chunks.
516  * Yes, this really happened. */
517  if (lines[line_num].syntax_arr_size == SHRT_MAX)
518  {
519  null_rx = false;
520  break;
521  }
522  if (++(lines[line_num].syntax_arr_size) > 1)
523  {
524  mutt_mem_realloc(&(lines[line_num].syntax),
525  (lines[line_num].syntax_arr_size) * sizeof(struct TextSyntax));
526  }
527  }
528  i = lines[line_num].syntax_arr_size - 1;
529  pmatch[0].rm_so += offset;
530  pmatch[0].rm_eo += offset;
531  if (!found || (pmatch[0].rm_so < (lines[line_num].syntax)[i].first) ||
532  ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
533  (pmatch[0].rm_eo > (lines[line_num].syntax)[i].last)))
534  {
535  (lines[line_num].syntax)[i].color = color_line->pair;
536  (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
537  (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
538  }
539  found = true;
540  null_rx = false;
541  }
542  else
543  {
544  /* Once a regexp fails to match, don't try matching it again.
545  * On very long lines this can cause a performance issue if there
546  * are other regexps that have many matches. */
547  color_line->stop_matching = true;
548  }
549  }
550 
551  if (null_rx)
552  offset++; /* avoid degenerate cases */
553  else
554  offset = (lines[line_num].syntax)[i].last;
555  } while (found || null_rx);
556  if (nl > 0)
557  buf[nl] = '\n';
558  }
559 
560  /* attachment patterns */
561  if (lines[line_num].color == MT_COLOR_ATTACHMENT)
562  {
563  size_t nl;
564 
565  /* don't consider line endings part of the buffer for regex matching */
566  nl = mutt_str_len(buf);
567  if ((nl > 0) && (buf[nl - 1] == '\n'))
568  buf[nl - 1] = '\0';
569 
570  i = 0;
571  offset = 0;
572  lines[line_num].syntax_arr_size = 0;
573  do
574  {
575  if (!buf[offset])
576  break;
577 
578  found = false;
579  null_rx = false;
581  {
582  if (regexec(&color_line->regex, buf + offset, 1, pmatch,
583  ((offset != 0) ? REG_NOTBOL : 0)) != 0)
584  {
585  continue;
586  }
587 
588  if (pmatch[0].rm_eo != pmatch[0].rm_so)
589  {
590  if (!found)
591  {
592  if (++(lines[line_num].syntax_arr_size) > 1)
593  {
594  mutt_mem_realloc(&(lines[line_num].syntax),
595  (lines[line_num].syntax_arr_size) * sizeof(struct TextSyntax));
596  }
597  }
598  i = lines[line_num].syntax_arr_size - 1;
599  pmatch[0].rm_so += offset;
600  pmatch[0].rm_eo += offset;
601  if (!found || (pmatch[0].rm_so < (lines[line_num].syntax)[i].first) ||
602  ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
603  (pmatch[0].rm_eo > (lines[line_num].syntax)[i].last)))
604  {
605  (lines[line_num].syntax)[i].color = color_line->pair;
606  (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
607  (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
608  }
609  found = 1;
610  null_rx = false;
611  }
612  else
613  null_rx = true; /* empty regex; don't add it, but keep looking */
614  }
615 
616  if (null_rx)
617  offset++; /* avoid degenerate cases */
618  else
619  offset = (lines[line_num].syntax)[i].last;
620  } while (found || null_rx);
621  if (nl > 0)
622  buf[nl] = '\n';
623  }
624 }
625 
631 static bool is_ansi(const char *str)
632 {
633  while (*str && (isdigit(*str) || (*str == ';')))
634  str++;
635  return (*str == 'm');
636 }
637 
645 static int grok_ansi(const unsigned char *buf, int pos, struct AnsiAttr *aa)
646 {
647  int x = pos;
648 
649  while (isdigit(buf[x]) || (buf[x] == ';'))
650  x++;
651 
652  /* Character Attributes */
653  const bool c_allow_ansi = cs_subset_bool(NeoMutt->sub, "allow_ansi");
654  if (c_allow_ansi && aa && (buf[x] == 'm'))
655  {
656  if (pos == x)
657  {
658  if (aa->pair != -1)
659  mutt_color_free(aa->fg, aa->bg);
660  aa->attr = ANSI_OFF;
661  aa->pair = -1;
662  }
663  while (pos < x)
664  {
665  if ((buf[pos] == '1') && (((pos + 1) == x) || (buf[pos + 1] == ';')))
666  {
667  aa->attr |= ANSI_BOLD;
668  pos += 2;
669  }
670  else if ((buf[pos] == '4') && (((pos + 1) == x) || (buf[pos + 1] == ';')))
671  {
672  aa->attr |= ANSI_UNDERLINE;
673  pos += 2;
674  }
675  else if ((buf[pos] == '5') && (((pos + 1) == x) || (buf[pos + 1] == ';')))
676  {
677  aa->attr |= ANSI_BLINK;
678  pos += 2;
679  }
680  else if ((buf[pos] == '7') && (((pos + 1) == x) || (buf[pos + 1] == ';')))
681  {
682  aa->attr |= ANSI_REVERSE;
683  pos += 2;
684  }
685  else if ((buf[pos] == '0') && (((pos + 1) == x) || (buf[pos + 1] == ';')))
686  {
687  if (aa->pair != -1)
688  mutt_color_free(aa->fg, aa->bg);
689  aa->attr = ANSI_OFF;
690  aa->pair = -1;
691  pos += 2;
692  }
693  else if ((buf[pos] == '3') && isdigit(buf[pos + 1]))
694  {
695  if (aa->pair != -1)
696  mutt_color_free(aa->fg, aa->bg);
697  aa->pair = -1;
698  aa->attr |= ANSI_COLOR;
699  aa->fg = buf[pos + 1] - '0';
700  pos += 3;
701  }
702  else if ((buf[pos] == '4') && isdigit(buf[pos + 1]))
703  {
704  if (aa->pair != -1)
705  mutt_color_free(aa->fg, aa->bg);
706  aa->pair = -1;
707  aa->attr |= ANSI_COLOR;
708  aa->bg = buf[pos + 1] - '0';
709  pos += 3;
710  }
711  else
712  {
713  while ((pos < x) && (buf[pos] != ';'))
714  pos++;
715  pos++;
716  }
717  }
718  }
719  pos = x;
720  return pos;
721 }
722 
736 void mutt_buffer_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
737 {
738  const char *s = src;
739 
740  mutt_buffer_reset(dest);
741 
742  if (!s)
743  return;
744 
745  while (s[0] != '\0')
746  {
747  if ((s[0] == '\010') && (s > src))
748  {
749  if (s[1] == '_') /* underline */
750  s += 2;
751  else if (s[1] && mutt_buffer_len(dest)) /* bold or overstrike */
752  {
753  dest->dptr--;
754  mutt_buffer_addch(dest, s[1]);
755  s += 2;
756  }
757  else /* ^H */
758  mutt_buffer_addch(dest, *s++);
759  }
760  else if ((s[0] == '\033') && (s[1] == '[') && is_ansi(s + 2))
761  {
762  while (*s++ != 'm')
763  ; /* skip ANSI sequence */
764  }
765  else if (strip_markers && (s[0] == '\033') && (s[1] == ']') &&
767  {
768  mutt_debug(LL_DEBUG2, "Seen attachment marker\n");
769  while (*s++ != '\a')
770  ; /* skip pseudo-ANSI sequence */
771  }
772  else
773  mutt_buffer_addch(dest, *s++);
774  }
775 }
776 
789 static int fill_buffer(FILE *fp, LOFF_T *bytes_read, LOFF_T offset, unsigned char **buf,
790  unsigned char **fmt, size_t *blen, int *buf_ready)
791 {
792  static int b_read;
793  struct Buffer stripped;
794 
795  if (*buf_ready == 0)
796  {
797  if (offset != *bytes_read)
798  {
799  if (!mutt_file_seek(fp, offset, SEEK_SET))
800  {
801  return -1;
802  }
803  }
804 
805  *buf = (unsigned char *) mutt_file_read_line((char *) *buf, blen, fp, NULL, MUTT_RL_EOL);
806  if (!*buf)
807  {
808  fmt[0] = NULL;
809  return -1;
810  }
811 
812  *bytes_read = ftello(fp);
813  b_read = (int) (*bytes_read - offset);
814  *buf_ready = 1;
815 
816  mutt_buffer_init(&stripped);
817  mutt_buffer_alloc(&stripped, *blen);
818  mutt_buffer_strip_formatting(&stripped, (const char *) *buf, 1);
819  /* This should be a noop, because *fmt should be NULL */
820  FREE(fmt);
821  *fmt = (unsigned char *) stripped.data;
822  }
823 
824  return b_read;
825 }
826 
843 static int format_line(struct MuttWindow *win, struct Line **lines, int line_num,
844  unsigned char *buf, PagerFlags flags, struct AnsiAttr *aa,
845  int cnt, int *pspace, int *pvch, int *pcol, int *pspecial, int width)
846 {
847  int space = -1; /* index of the last space or TAB */
848  const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
849  size_t col = c_markers ? (*lines)[line_num].cont_line : 0;
850  size_t k;
851  int ch, vch, last_special = -1, special = 0, t;
852  wchar_t wc;
853  mbstate_t mbstate;
854  const size_t c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
855  size_t wrap_cols = mutt_window_wrap_cols(width, (flags & MUTT_PAGER_NOWRAP) ? 0 : c_wrap);
856 
857  if (check_attachment_marker((char *) buf) == 0)
858  wrap_cols = width;
859 
860  /* FIXME: this should come from lines */
861  memset(&mbstate, 0, sizeof(mbstate));
862 
863  for (ch = 0, vch = 0; ch < cnt; ch += k, vch += k)
864  {
865  /* Handle ANSI sequences */
866  while ((cnt - ch >= 2) && (buf[ch] == '\033') && (buf[ch + 1] == '[') && // Escape
867  is_ansi((char *) buf + ch + 2))
868  {
869  ch = grok_ansi(buf, ch + 2, aa) + 1;
870  }
871 
872  while ((cnt - ch >= 2) && (buf[ch] == '\033') && (buf[ch + 1] == ']') && // Escape
873  ((check_attachment_marker((char *) buf + ch) == 0) ||
874  (check_protected_header_marker((char *) buf + ch) == 0)))
875  {
876  while (buf[ch++] != '\a')
877  if (ch >= cnt)
878  break;
879  }
880 
881  /* is anything left to do? */
882  if (ch >= cnt)
883  break;
884 
885  k = mbrtowc(&wc, (char *) buf + ch, cnt - ch, &mbstate);
886  if ((k == (size_t) (-2)) || (k == (size_t) (-1)))
887  {
888  if (k == (size_t) (-1))
889  memset(&mbstate, 0, sizeof(mbstate));
890  mutt_debug(LL_DEBUG1, "mbrtowc returned %lu; errno = %d\n", k, errno);
891  if (col + 4 > wrap_cols)
892  break;
893  col += 4;
894  if (aa)
895  mutt_window_printf(win, "\\%03o", buf[ch]);
896  k = 1;
897  continue;
898  }
899  if (k == 0)
900  k = 1;
901 
902  if (CharsetIsUtf8)
903  {
904  /* zero width space, zero width no-break space */
905  if ((wc == 0x200B) || (wc == 0xFEFF))
906  {
907  mutt_debug(LL_DEBUG3, "skip zero-width character U+%04X\n", (unsigned short) wc);
908  continue;
909  }
911  {
912  mutt_debug(LL_DEBUG3, "filtered U+%04X\n", (unsigned short) wc);
913  continue;
914  }
915  }
916 
917  /* Handle backspace */
918  special = 0;
919  if (IsWPrint(wc))
920  {
921  wchar_t wc1;
922  mbstate_t mbstate1 = mbstate;
923  size_t k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
924  while ((k1 != (size_t) (-2)) && (k1 != (size_t) (-1)) && (k1 > 0) && (wc1 == '\b'))
925  {
926  const size_t k2 =
927  mbrtowc(&wc1, (char *) buf + ch + k + k1, cnt - ch - k - k1, &mbstate1);
928  if ((k2 == (size_t) (-2)) || (k2 == (size_t) (-1)) || (k2 == 0) || (!IsWPrint(wc1)))
929  break;
930 
931  if (wc == wc1)
932  {
933  special |= ((wc == '_') && (special & A_UNDERLINE)) ? A_UNDERLINE : A_BOLD;
934  }
935  else if ((wc == '_') || (wc1 == '_'))
936  {
937  special |= A_UNDERLINE;
938  wc = (wc1 == '_') ? wc : wc1;
939  }
940  else
941  {
942  /* special = 0; / * overstrike: nothing to do! */
943  wc = wc1;
944  }
945 
946  ch += k + k1;
947  k = k2;
948  mbstate = mbstate1;
949  k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
950  }
951  }
952 
953  if (aa && ((flags & (MUTT_SHOWCOLOR | MUTT_SEARCH | MUTT_PAGER_MARKER)) ||
954  special || last_special || aa->attr))
955  {
956  resolve_color(win, *lines, line_num, vch, flags, special, aa);
957  last_special = special;
958  }
959 
960  /* no-break space, narrow no-break space */
961  if (IsWPrint(wc) || (CharsetIsUtf8 && ((wc == 0x00A0) || (wc == 0x202F))))
962  {
963  if (wc == ' ')
964  {
965  space = ch;
966  }
967  t = wcwidth(wc);
968  if (col + t > wrap_cols)
969  break;
970  col += t;
971  if (aa)
972  mutt_addwch(win, wc);
973  }
974  else if (wc == '\n')
975  break;
976  else if (wc == '\t')
977  {
978  space = ch;
979  t = (col & ~7) + 8;
980  if (t > wrap_cols)
981  break;
982  if (aa)
983  for (; col < t; col++)
984  mutt_window_addch(win, ' ');
985  else
986  col = t;
987  }
988  else if ((wc < 0x20) || (wc == 0x7f))
989  {
990  if (col + 2 > wrap_cols)
991  break;
992  col += 2;
993  if (aa)
994  mutt_window_printf(win, "^%c", ('@' + wc) & 0x7f);
995  }
996  else if (wc < 0x100)
997  {
998  if (col + 4 > wrap_cols)
999  break;
1000  col += 4;
1001  if (aa)
1002  mutt_window_printf(win, "\\%03o", wc);
1003  }
1004  else
1005  {
1006  if (col + 1 > wrap_cols)
1007  break;
1008  col += k;
1009  if (aa)
1011  }
1012  }
1013  *pspace = space;
1014  *pcol = col;
1015  *pvch = vch;
1016  *pspecial = special;
1017  return ch;
1018 }
1019 
1038 int display_line(FILE *fp, LOFF_T *bytes_read, struct Line **lines,
1039  int line_num, int *lines_used, int *lines_max, PagerFlags flags,
1040  struct QuoteStyle **quote_list, int *q_level, bool *force_redraw,
1041  regex_t *search_re, struct MuttWindow *win_pager)
1042 {
1043  unsigned char *buf = NULL, *fmt = NULL;
1044  size_t buflen = 0;
1045  unsigned char *buf_ptr = NULL;
1046  int ch, vch, col, cnt, b_read;
1047  int buf_ready = 0;
1048  bool change_last = false;
1049  int special;
1050  int offset;
1051  int def_color;
1052  int m;
1053  int rc = -1;
1054  struct AnsiAttr aa = { 0, 0, 0, -1 };
1055  regmatch_t pmatch[1];
1056 
1057  if (line_num == *lines_used)
1058  {
1059  (*lines_used)++;
1060  change_last = true;
1061  }
1062 
1063  if (*lines_used == *lines_max)
1064  {
1065  mutt_mem_realloc(lines, sizeof(struct Line) * (*lines_max += LINES));
1066  for (ch = *lines_used; ch < *lines_max; ch++)
1067  {
1068  memset(&((*lines)[ch]), 0, sizeof(struct Line));
1069  (*lines)[ch].color = -1;
1070  (*lines)[ch].search_arr_size = -1;
1071  (*lines)[ch].syntax = mutt_mem_malloc(sizeof(struct TextSyntax));
1072  ((*lines)[ch].syntax)[0].first = -1;
1073  ((*lines)[ch].syntax)[0].last = -1;
1074  }
1075  }
1076 
1077  struct Line *const curr_line = &(*lines)[line_num];
1078 
1079  if (flags & MUTT_PAGER_LOGS)
1080  {
1081  /* determine the line class */
1082  if (fill_buffer(fp, bytes_read, curr_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1083  {
1084  if (change_last)
1085  (*lines_used)--;
1086  goto out;
1087  }
1088 
1089  curr_line->color = MT_COLOR_MESSAGE_LOG;
1090  if (buf[11] == 'M')
1091  curr_line->syntax[0].color = MT_COLOR_MESSAGE;
1092  else if (buf[11] == 'W')
1093  curr_line->syntax[0].color = MT_COLOR_WARNING;
1094  else if (buf[11] == 'E')
1095  curr_line->syntax[0].color = MT_COLOR_ERROR;
1096  else
1097  curr_line->syntax[0].color = MT_COLOR_NORMAL;
1098  }
1099 
1100  /* only do color highlighting if we are viewing a message */
1101  if (flags & (MUTT_SHOWCOLOR | MUTT_TYPES))
1102  {
1103  if (curr_line->color == -1)
1104  {
1105  /* determine the line class */
1106  if (fill_buffer(fp, bytes_read, curr_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1107  {
1108  if (change_last)
1109  (*lines_used)--;
1110  goto out;
1111  }
1112 
1113  resolve_types(win_pager, (char *) fmt, (char *) buf, *lines, line_num, *lines_used,
1114  quote_list, q_level, force_redraw, flags & MUTT_SHOWCOLOR);
1115 
1116  /* avoid race condition for continuation lines when scrolling up */
1117  for (m = line_num + 1;
1118  m < *lines_used && (*lines)[m].offset && (*lines)[m].cont_line; m++)
1119  (*lines)[m].color = curr_line->color;
1120  }
1121 
1122  /* this also prevents searching through the hidden lines */
1123  const short c_toggle_quoted_show_levels =
1124  cs_subset_number(NeoMutt->sub, "toggle_quoted_show_levels");
1125  if ((flags & MUTT_HIDE) && (curr_line->color == MT_COLOR_QUOTED) &&
1126  ((curr_line->quote == NULL) || (curr_line->quote->quote_n >= c_toggle_quoted_show_levels)))
1127  {
1128  flags = 0; /* MUTT_NOSHOW */
1129  }
1130  }
1131 
1132  /* At this point, (*lines[line_num]).quote may still be undefined. We
1133  * don't want to compute it every time MUTT_TYPES is set, since this
1134  * would slow down the "bottom" function unacceptably. A compromise
1135  * solution is hence to call regexec() again, just to find out the
1136  * length of the quote prefix. */
1137  if ((flags & MUTT_SHOWCOLOR) && !curr_line->cont_line &&
1138  (curr_line->color == MT_COLOR_QUOTED) && !curr_line->quote)
1139  {
1140  if (fill_buffer(fp, bytes_read, curr_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1141  {
1142  if (change_last)
1143  (*lines_used)--;
1144  goto out;
1145  }
1146 
1147  const struct Regex *c_quote_regex =
1148  cs_subset_regex(NeoMutt->sub, "quote_regex");
1149  if (mutt_regex_capture(c_quote_regex, (char *) fmt, 1, pmatch))
1150  {
1151  curr_line->quote =
1152  qstyle_classify(quote_list, (char *) fmt + pmatch[0].rm_so,
1153  pmatch[0].rm_eo - pmatch[0].rm_so, force_redraw, q_level);
1154  }
1155  else
1156  {
1157  goto out;
1158  }
1159  }
1160 
1161  if ((flags & MUTT_SEARCH) && !curr_line->cont_line && (curr_line->search_arr_size == -1))
1162  {
1163  if (fill_buffer(fp, bytes_read, curr_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1164  {
1165  if (change_last)
1166  (*lines_used)--;
1167  goto out;
1168  }
1169 
1170  offset = 0;
1171  curr_line->search_arr_size = 0;
1172  while (regexec(search_re, (char *) fmt + offset, 1, pmatch,
1173  (offset ? REG_NOTBOL : 0)) == 0)
1174  {
1175  if (++(curr_line->search_arr_size) > 1)
1176  {
1177  mutt_mem_realloc(&(curr_line->search),
1178  (curr_line->search_arr_size) * sizeof(struct TextSyntax));
1179  }
1180  else
1181  curr_line->search = mutt_mem_malloc(sizeof(struct TextSyntax));
1182  pmatch[0].rm_so += offset;
1183  pmatch[0].rm_eo += offset;
1184  (curr_line->search)[curr_line->search_arr_size - 1].first = pmatch[0].rm_so;
1185  (curr_line->search)[curr_line->search_arr_size - 1].last = pmatch[0].rm_eo;
1186 
1187  if (pmatch[0].rm_eo == pmatch[0].rm_so)
1188  offset++; /* avoid degenerate cases */
1189  else
1190  offset = pmatch[0].rm_eo;
1191  if (!fmt[offset])
1192  break;
1193  }
1194  }
1195 
1196  if (!(flags & MUTT_SHOW) && ((*lines)[line_num + 1].offset > 0))
1197  {
1198  /* we've already scanned this line, so just exit */
1199  rc = 0;
1200  goto out;
1201  }
1202  if ((flags & MUTT_SHOWCOLOR) && *force_redraw && ((*lines)[line_num + 1].offset > 0))
1203  {
1204  /* no need to try to display this line... */
1205  rc = 1;
1206  goto out; /* fake display */
1207  }
1208 
1209  b_read = fill_buffer(fp, bytes_read, curr_line->offset, &buf, &fmt, &buflen, &buf_ready);
1210  if (b_read < 0)
1211  {
1212  if (change_last)
1213  (*lines_used)--;
1214  goto out;
1215  }
1216 
1217  /* now chose a good place to break the line */
1218  cnt = format_line(win_pager, lines, line_num, buf, flags, NULL, b_read, &ch,
1219  &vch, &col, &special, win_pager->state.cols);
1220  buf_ptr = buf + cnt;
1221 
1222  /* move the break point only if smart_wrap is set */
1223  const bool c_smart_wrap = cs_subset_bool(NeoMutt->sub, "smart_wrap");
1224  if (c_smart_wrap)
1225  {
1226  if ((cnt < b_read) && (ch != -1) &&
1227  !simple_color_is_header(curr_line->color) && !IS_SPACE(buf[cnt]))
1228  {
1229  buf_ptr = buf + ch;
1230  /* skip trailing blanks */
1231  while (ch && ((buf[ch] == ' ') || (buf[ch] == '\t') || (buf[ch] == '\r')))
1232  ch--;
1233  /* A very long word with leading spaces causes infinite
1234  * wrapping when MUTT_PAGER_NSKIP is set. A folded header
1235  * with a single long word shouldn't be smartwrapped
1236  * either. So just disable smart_wrap if it would wrap at the
1237  * beginning of the line. */
1238  if (ch == 0)
1239  buf_ptr = buf + cnt;
1240  else
1241  cnt = ch + 1;
1242  }
1243  if (!(flags & MUTT_PAGER_NSKIP))
1244  {
1245  /* skip leading blanks on the next line too */
1246  while ((*buf_ptr == ' ') || (*buf_ptr == '\t'))
1247  buf_ptr++;
1248  }
1249  }
1250 
1251  if (*buf_ptr == '\r')
1252  buf_ptr++;
1253  if (*buf_ptr == '\n')
1254  buf_ptr++;
1255 
1256  if (((int) (buf_ptr - buf) < b_read) && !(*lines)[line_num + 1].cont_line)
1257  append_line(*lines, line_num, (int) (buf_ptr - buf));
1258  (*lines)[line_num + 1].offset = curr_line->offset + (long) (buf_ptr - buf);
1259 
1260  /* if we don't need to display the line we are done */
1261  if (!(flags & MUTT_SHOW))
1262  {
1263  rc = 0;
1264  goto out;
1265  }
1266 
1267  /* display the line */
1268  format_line(win_pager, lines, line_num, buf, flags, &aa, cnt, &ch, &vch, &col,
1269  &special, win_pager->state.cols);
1270 
1271  /* avoid a bug in ncurses... */
1272  if (col == 0)
1273  {
1275  mutt_window_addch(win_pager, ' ');
1276  }
1277 
1278  /* end the last color pattern (needed by S-Lang) */
1279  if (special || ((col != win_pager->state.cols) && (flags & (MUTT_SHOWCOLOR | MUTT_SEARCH))))
1280  resolve_color(win_pager, *lines, line_num, vch, flags, 0, &aa);
1281 
1282  /* Fill the blank space at the end of the line with the prevailing color.
1283  * ncurses does an implicit clrtoeol() when you do mutt_window_addch('\n') so we have
1284  * to make sure to reset the color *after* that */
1285  if (flags & MUTT_SHOWCOLOR)
1286  {
1287  m = (curr_line->cont_line) ? (curr_line->syntax)[0].first : line_num;
1288  if ((*lines)[m].color == MT_COLOR_HEADER)
1289  def_color = ((*lines)[m].syntax)[0].color;
1290  else
1291  def_color = simple_colors_get((*lines)[m].color);
1292 
1293  mutt_curses_set_attr(def_color);
1294  }
1295 
1296  if (col < win_pager->state.cols)
1297  mutt_window_clrtoeol(win_pager);
1298 
1299  /* reset the color back to normal. This *must* come after the
1300  * clrtoeol, otherwise the color for this line will not be
1301  * filled to the right margin. */
1302  if (flags & MUTT_SHOWCOLOR)
1304 
1305  /* build a return code */
1306  if (!(flags & MUTT_SHOW))
1307  flags = 0;
1308 
1309  rc = flags;
1310 
1311 out:
1312  FREE(&buf);
1313  FREE(&fmt);
1314  return rc;
1315 }
void mutt_buffer_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:265
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:46
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
Color and attribute parsing.
struct RegexColorList * regex_colors_get_list(enum ColorId id)
Return the RegexColorList for a colour id.
Definition: regex.c:129
int simple_colors_get(enum ColorId id)
Get the colour of an object by its ID.
Definition: simple.c:68
bool simple_color_is_set(enum ColorId id)
Is the object coloured?
Definition: simple.c:89
bool simple_color_is_header(enum ColorId color_id)
Colour is for an Email header.
Definition: simple.c:101
void mutt_color_free(uint32_t fg, uint32_t bg)
Free a colour.
Definition: color.c:109
int mutt_color_alloc(uint32_t fg, uint32_t bg)
Allocate a colour pair.
Definition: color.c:188
@ MT_COLOR_MARKERS
Pager: markers, line continuation.
Definition: color.h:50
@ MT_COLOR_MESSAGE
Informational message.
Definition: color.h:51
@ MT_COLOR_QUOTED
Pager: quoted text.
Definition: color.h:57
@ MT_COLOR_HEADER
Message headers (takes a pattern)
Definition: color.h:48
@ MT_COLOR_ERROR
Error message.
Definition: color.h:46
@ MT_COLOR_BOLD
Bold text.
Definition: color.h:40
@ MT_COLOR_BODY
Pager: highlight body of message (takes a pattern)
Definition: color.h:39
@ MT_COLOR_HDRDEFAULT
Header default colour.
Definition: color.h:47
@ MT_COLOR_NORMAL
Plain text.
Definition: color.h:53
@ MT_COLOR_ATTACH_HEADERS
MIME attachment test (takes a pattern)
Definition: color.h:38
@ MT_COLOR_SEARCH
Pager: search matches.
Definition: color.h:58
@ MT_COLOR_MESSAGE_LOG
Menu showing log messages.
Definition: color.h:52
@ MT_COLOR_ATTACHMENT
MIME attachments text (entire line)
Definition: color.h:37
@ MT_COLOR_WARNING
Warning messages.
Definition: color.h:74
@ MT_COLOR_UNDERLINE
Underlined text.
Definition: color.h:73
@ MT_COLOR_SIGNATURE
Pager: signature lines.
Definition: color.h:69
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
int mutt_addwch(struct MuttWindow *win, wchar_t wc)
Addwch would be provided by an up-to-date curses library.
Definition: curs_lib.c:687
static int format_line(struct MuttWindow *win, struct Line **lines, int line_num, unsigned char *buf, PagerFlags flags, struct AnsiAttr *aa, int cnt, int *pspace, int *pvch, int *pcol, int *pspecial, int width)
Display a line of text in the pager.
Definition: display.c:843
static int grok_ansi(const unsigned char *buf, int pos, struct AnsiAttr *aa)
Parse an ANSI escape sequence.
Definition: display.c:645
static int check_protected_header_marker(const char *p)
Check that the unique marker is present.
Definition: display.c:298
int mutt_is_quote_line(char *line, regmatch_t *pmatch)
Is a line of message text a quote?
Definition: display.c:312
void mutt_buffer_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
Removes ANSI and backspace formatting.
Definition: display.c:736
static int fill_buffer(FILE *fp, LOFF_T *bytes_read, LOFF_T offset, unsigned char **buf, unsigned char **fmt, size_t *blen, int *buf_ready)
Fill a buffer from a file.
Definition: display.c:789
static int check_sig(const char *s, struct Line *info, int offset)
Check for an email signature.
Definition: display.c:56
static int comp_syntax_t(const void *m1, const void *m2)
Search for a Syntax using bsearch(3)
Definition: display.c:96
static void resolve_color(struct MuttWindow *win, struct Line *lines, int line_num, int cnt, PagerFlags flags, int special, struct AnsiAttr *aa)
Set the colour for a line of text.
Definition: display.c:118
static int check_attachment_marker(const char *p)
Check that the unique marker is present.
Definition: display.c:288
static void resolve_types(struct MuttWindow *win, char *buf, char *raw, struct Line *lines, int line_num, int lines_used, struct QuoteStyle **quote_list, int *q_level, bool *force_redraw, bool q_classify)
Determine the style for a line of text.
Definition: display.c:358
static void append_line(struct Line *lines, int line_num, int cnt)
Add a new Line to the array.
Definition: display.c:248
static bool is_ansi(const char *str)
Is this an ANSI escape sequence?
Definition: display.c:631
static int check_marker(const char *q, const char *p)
Check that the unique marker is present.
Definition: display.c:272
int display_line(FILE *fp, LOFF_T *bytes_read, struct Line **lines, int line_num, int *lines_used, int *lines_max, PagerFlags flags, struct QuoteStyle **quote_list, int *q_level, bool *force_redraw, regex_t *search_re, struct MuttWindow *win_pager)
Print a line on screen.
Definition: display.c:1038
Pager Display.
#define ANSI_BOLD
Bold text.
Definition: display.h:40
#define ANSI_BLINK
Blinking text.
Definition: display.h:39
#define ANSI_UNDERLINE
Underlined text.
Definition: display.h:41
#define ANSI_REVERSE
Reverse video.
Definition: display.h:42
#define ANSI_OFF
Turn off colours and attributes.
Definition: display.h:38
#define ANSI_COLOR
Use colours.
Definition: display.h:43
int braille_col
Definition: dlg_pager.c:83
int braille_row
Definition: dlg_pager.c:82
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
Read a line from a file.
Definition: file.c:695
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:665
#define MUTT_RL_EOL
don't strip \n / \r\n
Definition: file.h:40
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
Convenience wrapper for the gui headers.
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:243
@ LL_DEBUG3
Log at debug level 3.
Definition: logging.h:42
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
bool mutt_mb_is_display_corrupting_utf8(wchar_t wc)
Will this character corrupt the display?
Definition: mbyte.c:389
#define IsWPrint(wc)
Definition: mbyte.h:39
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define FREE(x)
Definition: memory.h:40
static int search(struct Menu *menu, int op)
Search a menu.
Definition: menu.c:142
bool CharsetIsUtf8
Is the user's current character set utf-8?
Definition: charset.c:62
wchar_t ReplacementChar
When a Unicode character can't be displayed, use this instead.
Definition: charset.c:57
Convenience wrapper for the library headers.
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:610
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:715
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:158
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:475
void mutt_curses_set_attr(int attr)
Set the attributes for text.
Definition: mutt_curses.c:39
void mutt_curses_set_color_by_id(enum ColorId color)
Set the current colour for text.
Definition: mutt_curses.c:52
int mutt_window_printf(struct MuttWindow *win, const char *fmt,...)
Write a formatted string to a Window.
Definition: mutt_window.c:423
void mutt_window_get_coords(struct MuttWindow *win, int *col, int *row)
Get the cursor position in the Window.
Definition: mutt_window.c:272
int mutt_window_wrap_cols(int width, short wrap)
Calculate the wrap column for a given screen width.
Definition: mutt_window.c:364
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:241
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
Definition: mutt_window.c:380
#define MUTT_PAGER_NSKIP
Preserve whitespace with smartwrap.
Definition: lib.h:68
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition: lib.h:72
#define MUTT_HIDE
Don't show quoted text.
Definition: lib.h:62
#define MUTT_TYPES
Compute line's type.
Definition: lib.h:64
uint16_t PagerFlags
Flags for mutt_pager(), e.g. MUTT_SHOWFLAT.
Definition: lib.h:56
#define MUTT_SHOWCOLOR
Show characters in color otherwise don't show characters.
Definition: lib.h:61
#define MUTT_PAGER_MARKER
Use markers if option is set.
Definition: lib.h:69
#define MUTT_PAGER_LOGS
Logview mode.
Definition: lib.h:73
#define MUTT_SEARCH
Resolve search patterns.
Definition: lib.h:63
#define MUTT_SHOW
Definition: lib.h:65
Prototypes for many functions.
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
struct QuoteStyle * qstyle_classify(struct QuoteStyle **quote_list, const char *qptr, size_t length, bool *force_redraw, int *q_level)
Find a style for a string.
Definition: quoted.c:223
const char * state_protected_header_marker(void)
Get a unique (per-run) ANSI string to mark protected headers in an email.
Definition: state.c:57
const char * state_attachment_marker(void)
Get a unique (per-run) ANSI string to mark PGP messages in an email.
Definition: state.c:43
Key value store.
#define IS_SPACE(ch)
Definition: string2.h:38
An ANSI escape sequence.
Definition: display.h:60
AnsiFlags attr
Attributes, e.g. ANSI_BOLD.
Definition: display.h:61
int pair
Curses colour pair.
Definition: display.h:64
int fg
ANSI Foreground colour, e.g. 1 red, 2 green, etc.
Definition: display.h:62
int bg
ANSI Background colour, e.g. 1 red, 2 green, etc.
Definition: display.h:63
String manipulation buffer.
Definition: buffer.h:34
char * dptr
Current read/write position.
Definition: buffer.h:36
char * data
Pointer to data.
Definition: buffer.h:35
A line of text in the pager.
Definition: display.h:71
short search_arr_size
Number of items in search array.
Definition: display.h:80
struct TextSyntax * search
Array of search text in the line.
Definition: display.h:81
bool cont_line
Continuation of a previous line (wrapped by NeoMutt)
Definition: display.h:74
struct QuoteStyle * quote
Quoting style for this line (pointer into PagerPrivateData->quote_list)
Definition: display.h:83
LOFF_T offset
Offset into Email file (PagerPrivateData->fp)
Definition: display.h:72
bool cont_header
Continuation of a header line (wrapped by MTA)
Definition: display.h:75
short syntax_arr_size
Number of items in syntax array.
Definition: display.h:77
struct TextSyntax * syntax
Array of coloured text in the line.
Definition: display.h:78
short color
Default line colour, e.g. MT_COLOR_QUOTED.
Definition: display.h:73
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
Style of quoted text.
Definition: quoted.h:66
struct QuoteStyle * up
Definition: quoted.h:72
size_t prefix_len
Length of the prefix string.
Definition: quoted.h:70
int color
Curses colour pair.
Definition: quoted.h:68
int quote_n
The quoteN colour index for this level.
Definition: quoted.h:67
A regular expression and a color to highlight a line.
Definition: regex4.h:36
regex_t regex
Compiled regex.
Definition: regex4.h:38
int pair
Colour pair index.
Definition: regex4.h:43
bool stop_matching
Used by the pager for body patterns, to prevent the color from being retried once it fails.
Definition: regex4.h:45
Cached regular expression.
Definition: regex3.h:90
Highlighting for a piece of text.
Definition: display.h:50
int last
Last character in line to be coloured.
Definition: display.h:53
int color
Curses colour of text.
Definition: display.h:51
int first
First character in line to be coloured.
Definition: display.h:52
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:60