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