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