NeoMutt  2025-09-05-7-geaa2bd
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
display.c
Go to the documentation of this file.
1
31#include "config.h"
32#include <errno.h>
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 "private_data.h"
47
56static 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].cid != 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 (!mutt_isspace(*s))
78 return 0;
79 s++;
80 }
81
82 return -1;
83 }
84
85 return 0;
86}
87
96static 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
118static void resolve_color(struct MuttWindow *win, struct Line *lines, int line_num,
119 int cnt, PagerFlags flags, int special, struct AnsiColor *ansi)
120{
121 struct AttrColor def_color = { 0 }; /* color without syntax highlight */
122 struct AttrColor color = { 0 }; /* final color */
123 static struct AttrColor last_color = { 0 }; /* last color set */
124 bool search = false;
125 int m;
126 struct TextSyntax *matching_chunk = NULL;
127
128 if (cnt == 0)
129 {
130 last_color.curses_color = NULL;
131 last_color.attrs = A_NORMAL;
132 }
133
134 if (lines[line_num].cont_line)
135 {
136 const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
137 if (!cnt && c_markers)
138 {
140 mutt_window_addch(win, '+');
141 }
142 m = (lines[line_num].syntax)[0].first;
143 cnt += (lines[line_num].syntax)[0].last;
144 }
145 else
146 {
147 m = line_num;
148 }
149 if (flags & MUTT_PAGER_LOGS)
150 {
151 def_color = *(lines[line_num].syntax[0].attr_color);
152 }
153 else if (!(flags & MUTT_SHOWCOLOR))
154 {
155 if (flags & MUTT_PAGER_STRIPES)
156 {
157 def_color = *simple_color_get(((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD :
159 }
160 else
161 {
162 def_color = *simple_color_get(MT_COLOR_NORMAL);
163 }
164 }
165 else if ((lines[m].cid == MT_COLOR_HEADER) && lines[m].syntax[0].attr_color)
166 {
167 def_color = *lines[m].syntax[0].attr_color;
168 }
169 else
170 {
171 def_color = *simple_color_get(lines[m].cid);
172 }
173
174 if ((flags & MUTT_SHOWCOLOR) && COLOR_QUOTED(lines[m].cid))
175 {
176 struct QuoteStyle *qc = lines[m].quote;
177
178 if (qc)
179 {
180 def_color = attr_color_copy(qc->attr_color);
181
182 while (qc && (qc->prefix_len > cnt))
183 {
184 def_color = attr_color_copy(qc->attr_color);
185 qc = qc->up;
186 }
187 }
188 }
189
190 color = def_color;
191 if ((flags & MUTT_SHOWCOLOR) && lines[m].syntax)
192 {
193 matching_chunk = bsearch(&cnt, lines[m].syntax, lines[m].syntax_arr_size,
194 sizeof(struct TextSyntax), comp_syntax_t);
195 if (matching_chunk && (cnt >= matching_chunk->first) &&
196 (cnt < matching_chunk->last))
197 {
198 if (matching_chunk->attr_color)
199 color = *matching_chunk->attr_color;
200 }
201 }
202
203 if ((flags & MUTT_SEARCH) && lines[m].search)
204 {
205 matching_chunk = bsearch(&cnt, lines[m].search, lines[m].search_arr_size,
206 sizeof(struct TextSyntax), comp_syntax_t);
207 if (matching_chunk && (cnt >= matching_chunk->first) &&
208 (cnt < matching_chunk->last))
209 {
211 search = true;
212 }
213 }
214
215 /* handle "special" bold & underlined characters */
216 if (special & A_BOLD)
217 {
220 else
221 color.attrs |= A_BOLD;
222 }
223 else if (special & A_UNDERLINE)
224 {
227 else
228 color.attrs |= A_UNDERLINE;
229 }
230 else if (special & A_ITALIC)
231 {
234 else
235 color.attrs |= A_ITALIC;
236 }
237 else if (ansi->attr_color)
238 {
239 color = *ansi->attr_color;
240 }
241
242 if (!attr_color_match(&color, &last_color))
243 {
245 &color);
246 mutt_curses_set_color(ac_merge);
247 last_color = color;
248 }
249}
250
257static void append_line(struct Line *lines, int line_num, int cnt)
258{
259 int m;
260
261 lines[line_num + 1].cid = lines[line_num].cid;
262 (lines[line_num + 1].syntax)[0].attr_color = (lines[line_num].syntax)[0].attr_color;
263 lines[line_num + 1].cont_line = true;
264
265 /* find the real start of the line */
266 for (m = line_num; m >= 0; m--)
267 if (!lines[m].cont_line)
268 break;
269
270 (lines[line_num + 1].syntax)[0].first = m;
271 (lines[line_num + 1].syntax)[0].last = (lines[line_num].cont_line) ?
272 cnt + (lines[line_num].syntax)[0].last :
273 cnt;
274}
275
282static int check_marker(const char *q, const char *p)
283{
284 for (; (p[0] == q[0]) && (q[0] != '\0') && (p[0] != '\0') && (q[0] != '\a') &&
285 (p[0] != '\a');
286 p++, q++)
287 {
288 }
289
290 return (int) (*p - *q);
291}
292
298static int check_attachment_marker(const char *p)
299{
301}
302
308static int check_protected_header_marker(const char *p)
309{
311}
312
322bool mutt_is_quote_line(char *line, regmatch_t *pmatch)
323{
324 bool is_quote = false;
325 const struct Regex *c_smileys = cs_subset_regex(NeoMutt->sub, "smileys");
326 regmatch_t pmatch_internal[1] = { 0 };
327
328 if (!pmatch)
329 pmatch = pmatch_internal;
330
331 const struct Regex *c_quote_regex = cs_subset_regex(NeoMutt->sub, "quote_regex");
332 if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
333 {
334 regmatch_t smatch[1] = { 0 };
335 if (mutt_regex_capture(c_smileys, line, 1, smatch))
336 {
337 if (smatch[0].rm_so > 0)
338 {
339 char c = line[smatch[0].rm_so];
340 line[smatch[0].rm_so] = 0;
341
342 if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
343 is_quote = true;
344
345 line[smatch[0].rm_so] = c;
346 }
347 }
348 else
349 {
350 is_quote = true;
351 }
352 }
353
354 return is_quote;
355}
356
363static void match_body_patterns(char *pat, struct Line *lines, int line_num)
364{
365 // don't consider line endings part of the buffer for regex matching
366 bool has_nl = false;
367 size_t buflen = mutt_str_len(pat);
368 if ((buflen > 0) && (pat[buflen - 1] == '\n'))
369 {
370 has_nl = true;
371 pat[buflen - 1] = '\0';
372 }
373
374 int i = 0;
375 int offset = 0;
376 struct RegexColor *color_line = NULL;
377 struct RegexColorList *head = NULL;
378 bool found = false;
379 bool null_rx = false;
380 regmatch_t pmatch[1] = { 0 };
381
382 lines[line_num].syntax_arr_size = 0;
383 if (lines[line_num].cid == MT_COLOR_HDRDEFAULT)
384 {
386 }
387 else
388 {
390 }
391 STAILQ_FOREACH(color_line, head, entries)
392 {
393 color_line->stop_matching = false;
394 }
395
396 do
397 {
398 /* if has_nl, we've stripped off a trailing newline */
399 if (offset >= (buflen - has_nl))
400 break;
401
402 if (!pat[offset])
403 break;
404
405 found = false;
406 null_rx = false;
407 STAILQ_FOREACH(color_line, head, entries)
408 {
409 if (color_line->stop_matching)
410 continue;
411
412 if ((regexec(&color_line->regex, pat + offset, 1, pmatch,
413 ((offset != 0) ? REG_NOTBOL : 0)) != 0))
414 {
415 /* Once a regex fails to match, don't try matching it again.
416 * On very long lines this can cause a performance issue if there
417 * are other regexes that have many matches. */
418 color_line->stop_matching = true;
419 continue;
420 }
421
422 if (pmatch[0].rm_eo == pmatch[0].rm_so)
423 {
424 null_rx = true; // empty regex; don't add it, but keep looking
425 continue;
426 }
427
428 if (!found)
429 {
430 // Abort if we fill up chunks. Yes, this really happened.
431 if (lines[line_num].syntax_arr_size == SHRT_MAX)
432 {
433 null_rx = false;
434 break;
435 }
436 if (++(lines[line_num].syntax_arr_size) > 1)
437 {
438 MUTT_MEM_REALLOC(&(lines[line_num].syntax),
439 lines[line_num].syntax_arr_size, struct TextSyntax);
440 // Zero the new entry
441 const int index = lines[line_num].syntax_arr_size - 1;
442 struct TextSyntax *ts = &lines[line_num].syntax[index];
443 memset(ts, 0, sizeof(*ts));
444 }
445 }
446 i = lines[line_num].syntax_arr_size - 1;
447 pmatch[0].rm_so += offset;
448 pmatch[0].rm_eo += offset;
449
450 if (!found || (pmatch[0].rm_so < (lines[line_num].syntax)[i].first) ||
451 ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
452 (pmatch[0].rm_eo > (lines[line_num].syntax)[i].last)))
453 {
454 (lines[line_num].syntax)[i].attr_color = &color_line->attr_color;
455 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
456 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
457 }
458 else if ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
459 (pmatch[0].rm_eo == (lines[line_num].syntax)[i].last))
460 {
461 (lines[line_num].syntax)[i].attr_color = merged_color_overlay(
462 (lines[line_num].syntax)[i].attr_color, &color_line->attr_color);
463 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
464 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
465 }
466
467 found = true;
468 null_rx = false;
469 }
470
471 if (null_rx)
472 offset++; /* avoid degenerate cases */
473 else
474 offset = (lines[line_num].syntax)[i].last;
475 } while (found || null_rx);
476
477 if (has_nl)
478 pat[buflen - 1] = '\n';
479}
480
487{
488 return (cid == MT_COLOR_HEADER) || (cid == MT_COLOR_HDRDEFAULT);
489}
490
504static void resolve_types(struct MuttWindow *win, char *buf, char *raw,
505 struct Line *lines, int line_num, int lines_used,
506 struct QuoteStyle **quote_list, int *q_level,
507 bool *force_redraw, bool q_classify)
508{
509 struct RegexColor *color_line = NULL;
510 regmatch_t pmatch[1] = { 0 };
511 const bool c_header_color_partial = cs_subset_bool(NeoMutt->sub, "header_color_partial");
512 int offset, i = 0;
513
514 if ((line_num == 0) || color_is_header(lines[line_num - 1].cid) ||
516 {
517 if (buf[0] == '\n') /* end of header */
518 {
519 lines[line_num].cid = MT_COLOR_NORMAL;
521 }
522 else
523 {
524 /* if this is a continuation of the previous line, use the previous
525 * line's color as default. */
526 if ((line_num > 0) && ((buf[0] == ' ') || (buf[0] == '\t')))
527 {
528 lines[line_num].cid = lines[line_num - 1].cid; /* wrapped line */
529 if (!c_header_color_partial)
530 {
531 (lines[line_num].syntax)[0].attr_color =
532 (lines[line_num - 1].syntax)[0].attr_color;
533 lines[line_num].cont_header = true;
534 }
535 }
536 else
537 {
538 lines[line_num].cid = MT_COLOR_HDRDEFAULT;
539 }
540
541 /* When this option is unset, we color the entire header the
542 * same color. Otherwise, we handle the header patterns just
543 * like body patterns (further below). */
544 if (!c_header_color_partial)
545 {
547 {
548 if (regexec(&color_line->regex, buf, 0, NULL, 0) == 0)
549 {
550 lines[line_num].cid = MT_COLOR_HEADER;
551 lines[line_num].syntax[0].attr_color =
552 merged_color_overlay(lines[line_num].syntax[0].attr_color,
554 lines[line_num].syntax[0].attr_color = merged_color_overlay(
555 lines[line_num].syntax[0].attr_color, &color_line->attr_color);
556 if (lines[line_num].cont_header)
557 {
558 /* adjust the previous continuation lines to reflect the color of this continuation line */
559 int j;
560 for (j = line_num - 1; j >= 0 && lines[j].cont_header; j--)
561 {
562 lines[j].cid = lines[line_num].cid;
563 lines[j].syntax[0].attr_color = lines[line_num].syntax[0].attr_color;
564 }
565 /* now adjust the first line of this header field */
566 if (j >= 0)
567 {
568 lines[j].cid = lines[line_num].cid;
569 lines[j].syntax[0].attr_color = lines[line_num].syntax[0].attr_color;
570 }
571 *force_redraw = true; /* the previous lines have already been drawn on the screen */
572 }
573 }
574 }
575 }
576 }
577 }
578 else if (mutt_str_startswith(raw, "\033[0m")) // Escape: a little hack...
579 {
580 lines[line_num].cid = MT_COLOR_NORMAL;
581 }
582 else if (check_attachment_marker((char *) raw) == 0)
583 {
584 lines[line_num].cid = MT_COLOR_ATTACHMENT;
585 }
586 else if (mutt_str_equal("-- \n", buf) || mutt_str_equal("-- \r\n", buf))
587 {
588 i = line_num + 1;
589
590 lines[line_num].cid = MT_COLOR_SIGNATURE;
591 while ((i < lines_used) && (check_sig(buf, lines, i - 1) == 0) &&
592 ((lines[i].cid == MT_COLOR_NORMAL) || COLOR_QUOTED(lines[i].cid) ||
593 (lines[i].cid == MT_COLOR_HEADER)))
594 {
595 /* oops... */
596 if (lines[i].syntax_arr_size)
597 {
598 lines[i].syntax_arr_size = 0;
599 MUTT_MEM_REALLOC(&(lines[line_num].syntax), 1, struct TextSyntax);
600 }
601 lines[i++].cid = MT_COLOR_SIGNATURE;
602 }
603 }
604 else if (check_sig(buf, lines, line_num - 1) == 0)
605 {
606 lines[line_num].cid = MT_COLOR_SIGNATURE;
607 }
608 else if (mutt_is_quote_line(buf, pmatch))
609 {
610 if (q_classify && !lines[line_num].quote)
611 {
612 lines[line_num].quote = qstyle_classify(quote_list, buf + pmatch[0].rm_so,
613 pmatch[0].rm_eo - pmatch[0].rm_so,
614 force_redraw, q_level);
615 }
616 lines[line_num].cid = MT_COLOR_QUOTED0;
617 }
618 else
619 {
620 lines[line_num].cid = MT_COLOR_NORMAL;
621 }
622
623 /* body patterns */
624 if ((lines[line_num].cid == MT_COLOR_NORMAL) || COLOR_QUOTED(lines[line_num].cid) ||
625 ((lines[line_num].cid == MT_COLOR_HDRDEFAULT) && c_header_color_partial))
626 {
627 match_body_patterns(buf, lines, line_num);
628 }
629
630 /* attachment patterns */
631 if (lines[line_num].cid == MT_COLOR_ATTACHMENT)
632 {
633 size_t nl;
634
635 /* don't consider line endings part of the buffer for regex matching */
636 nl = mutt_str_len(buf);
637 if ((nl > 0) && (buf[nl - 1] == '\n'))
638 buf[nl - 1] = '\0';
639
640 i = 0;
641 offset = 0;
642 lines[line_num].syntax_arr_size = 0;
644 bool found = false;
645 bool null_rx = false;
646 do
647 {
648 if (!buf[offset])
649 break;
650
651 found = false;
652 null_rx = false;
654 {
655 if (regexec(&color_line->regex, buf + offset, 1, pmatch,
656 ((offset != 0) ? REG_NOTBOL : 0)) != 0)
657 {
658 continue;
659 }
660
661 if (pmatch[0].rm_eo != pmatch[0].rm_so)
662 {
663 if (!found)
664 {
665 if (++(lines[line_num].syntax_arr_size) > 1)
666 {
667 MUTT_MEM_REALLOC(&(lines[line_num].syntax),
668 lines[line_num].syntax_arr_size, struct TextSyntax);
669 // Zero the new entry
670 const int index = lines[line_num].syntax_arr_size - 1;
671 struct TextSyntax *ts = &lines[line_num].syntax[index];
672 memset(ts, 0, sizeof(*ts));
673 }
674 }
675 i = lines[line_num].syntax_arr_size - 1;
676 pmatch[0].rm_so += offset;
677 pmatch[0].rm_eo += offset;
678 if (!found || (pmatch[0].rm_so < (lines[line_num].syntax)[i].first) ||
679 ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
680 (pmatch[0].rm_eo > (lines[line_num].syntax)[i].last)))
681 {
682 if (!(lines[line_num].syntax)[i].attr_color)
683 (lines[line_num].syntax)[i].attr_color = ac_attach;
684
685 (lines[line_num].syntax)[i].attr_color = merged_color_overlay(
686 (lines[line_num].syntax)[i].attr_color, &color_line->attr_color);
687 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
688 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
689 }
690 else if ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
691 (pmatch[0].rm_eo == (lines[line_num].syntax)[i].last))
692 {
693 (lines[line_num].syntax)[i].attr_color = merged_color_overlay(
694 (lines[line_num].syntax)[i].attr_color, &color_line->attr_color);
695 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
696 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
697 }
698 found = 1;
699 null_rx = false;
700 }
701 else
702 {
703 null_rx = true; /* empty regex; don't add it, but keep looking */
704 }
705 }
706
707 if (null_rx)
708 offset++; /* avoid degenerate cases */
709 else
710 offset = (lines[line_num].syntax)[i].last;
711 } while (found || null_rx);
712 if (nl > 0)
713 buf[nl] = '\n';
714 }
715}
716
730void buf_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
731{
732 const char *s = src;
733
734 buf_reset(dest);
735
736 if (!s)
737 return;
738
739 while (s[0] != '\0')
740 {
741 if ((s[0] == '\010') && (s > src))
742 {
743 if (s[1] == '_') /* underline */
744 {
745 s += 2;
746 }
747 else if (s[1] && buf_len(dest)) /* bold or overstrike */
748 {
749 dest->dptr--;
750 buf_addch(dest, s[1]);
751 s += 2;
752 }
753 else /* ^H */
754 {
755 buf_addch(dest, *s++);
756 }
757 continue;
758 }
759
760 int len = ansi_color_seq_length(s);
761 if (len > 0)
762 {
763 s += len;
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 {
774 buf_addch(dest, *s++);
775 }
776 }
777}
778
791static int fill_buffer(FILE *fp, LOFF_T *bytes_read, LOFF_T offset, unsigned char **buf,
792 unsigned char **fmt, size_t *blen, int *buf_ready)
793{
794 static int b_read = 0;
795
796 if (*buf_ready == 0)
797 {
798 if (offset != *bytes_read)
799 {
800 if (!mutt_file_seek(fp, offset, SEEK_SET))
801 {
802 return -1;
803 }
804 }
805
806 *buf = (unsigned char *) mutt_file_read_line((char *) *buf, blen, fp, NULL, MUTT_RL_EOL);
807 if (!*buf)
808 {
809 fmt[0] = NULL;
810 return -1;
811 }
812
813 *bytes_read = ftello(fp);
814 b_read = (int) (*bytes_read - offset);
815 *buf_ready = 1;
816
817 struct Buffer *stripped = buf_pool_get();
818 buf_alloc(stripped, *blen);
819 buf_strip_formatting(stripped, (const char *) *buf, 1);
820 /* This should be a noop, because *fmt should be NULL */
821 FREE(fmt);
822 *fmt = (unsigned char *) buf_strdup(stripped);
823 buf_pool_release(&stripped);
824 }
825
826 return b_read;
827}
828
846static int format_line(struct MuttWindow *win, struct Line **lines, int line_num,
847 unsigned char *buf, PagerFlags flags, struct AnsiColor *ansi,
848 int cnt, int *pspace, int *pvch, int *pcol,
849 int *pspecial, int width, struct AttrColorList *ansi_list)
850{
851 int space = -1; /* index of the last space or TAB */
852 const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
853 size_t col = c_markers ? (*lines)[line_num].cont_line : 0;
854 size_t k;
855 int ch, vch, last_special = -1, special = 0, t;
856 wchar_t wc = 0;
857 mbstate_t mbstate = { 0 }; // FIXME: this should come from lines
858 const size_t c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
859 size_t wrap_cols = mutt_window_wrap_cols(width, (flags & MUTT_PAGER_NOWRAP) ? 0 : c_wrap);
860
861 if (check_attachment_marker((char *) buf) == 0)
862 wrap_cols = width;
863
864 struct PagerPrivateData *priv = win->parent->wdata;
865 enum PagerMode mode = priv->pview->mode;
866 const bool c_allow_ansi = (mode == PAGER_MODE_OTHER) ||
867 cs_subset_bool(NeoMutt->sub, "allow_ansi");
868
869 for (ch = 0, vch = 0; ch < cnt; ch += k, vch += k)
870 {
871 /* Handle ANSI sequences */
872 if (buf[ch] == '\033') // Escape
873 {
874 int len = ansi_color_parse((const char *) buf + ch, ansi, ansi_list, !c_allow_ansi);
875 ch += len;
876 }
877
878 while ((cnt - ch >= 2) && (buf[ch] == '\033') && (buf[ch + 1] == ']') && // Escape
879 ((check_attachment_marker((char *) buf + ch) == 0) ||
880 (check_protected_header_marker((char *) buf + ch) == 0)))
881 {
882 while (buf[ch++] != '\a')
883 if (ch >= cnt)
884 break;
885 }
886
887 /* is anything left to do? */
888 if (ch >= cnt)
889 break;
890
891 k = mbrtowc(&wc, (char *) buf + ch, cnt - ch, &mbstate);
892 if ((k == ICONV_BUF_TOO_SMALL) || (k == ICONV_ILLEGAL_SEQ))
893 {
894 if (k == ICONV_ILLEGAL_SEQ)
895 memset(&mbstate, 0, sizeof(mbstate));
896 mutt_debug(LL_DEBUG1, "mbrtowc returned %zu; errno = %d\n", k, errno);
897 if ((col + 4) > wrap_cols)
898 break;
899 col += 4;
900 if (ansi)
901 mutt_window_printf(win, "\\%03o", buf[ch]);
902 k = 1;
903 continue;
904 }
905 if (k == 0)
906 k = 1;
907
908 if (CharsetIsUtf8)
909 {
910 /* zero width space, zero with non-joiner, zero width no-break space */
911 if ((wc == 0x200B) || (wc == 0x200C) || (wc == 0xFEFF))
912 {
913 mutt_debug(LL_DEBUG3, "skip zero-width character U+%04X\n", (unsigned short) wc);
914 continue;
915 }
917 {
918 mutt_debug(LL_DEBUG3, "filtered U+%04X\n", (unsigned short) wc);
919 continue;
920 }
921 }
922
923 /* Handle backspace */
924 special = 0;
925 if (IsWPrint(wc))
926 {
927 wchar_t wc1 = 0;
928 mbstate_t mbstate1 = mbstate;
929 size_t k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
930 while ((k1 != ICONV_BUF_TOO_SMALL) && (k1 != ICONV_ILLEGAL_SEQ) &&
931 (k1 > 0) && (wc1 == '\b'))
932 {
933 const size_t k2 = mbrtowc(&wc1, (char *) buf + ch + k + k1,
934 cnt - ch - k - k1, &mbstate1);
935 if ((k2 == ICONV_BUF_TOO_SMALL) || (k2 == ICONV_ILLEGAL_SEQ) ||
936 (k2 == 0) || (!IsWPrint(wc1)))
937 {
938 break;
939 }
940
941 if (wc == wc1)
942 {
943 special |= ((wc == '_') && (special & A_UNDERLINE)) ? A_UNDERLINE : A_BOLD;
944 }
945 else if ((wc == '_') || (wc1 == '_'))
946 {
947 special |= A_UNDERLINE;
948 wc = (wc1 == '_') ? wc : wc1;
949 }
950 else
951 {
952 /* special = 0; / * overstrike: nothing to do! */
953 wc = wc1;
954 }
955
956 ch += k + k1;
957 k = k2;
958 mbstate = mbstate1;
959 k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
960 }
961 }
962
963 if (ansi && ((flags & (MUTT_SHOWCOLOR | MUTT_SEARCH | MUTT_PAGER_MARKER)) ||
964 special || last_special || (ansi->attrs != A_NORMAL)))
965 {
966 resolve_color(win, *lines, line_num, vch, flags, special, ansi);
967 last_special = special;
968 }
969
970 /* no-break space, narrow no-break space */
971 if (IsWPrint(wc) || (CharsetIsUtf8 && ((wc == 0x00A0) || (wc == 0x202F))))
972 {
973 if (wc == ' ')
974 {
975 space = ch;
976 }
977 t = wcwidth(wc);
978 if (col + t > wrap_cols)
979 break;
980 col += t;
981 if (ansi)
982 mutt_addwch(win, wc);
983 }
984 else if (wc == '\n')
985 {
986 break;
987 }
988 else if (wc == '\t')
989 {
990 space = ch;
991 t = (col & ~7) + 8;
992 if (t > wrap_cols)
993 break;
994 if (ansi)
995 for (; col < t; col++)
996 mutt_window_addch(win, ' ');
997 else
998 col = t;
999 }
1000 else if ((wc < 0x20) || (wc == 0x7f))
1001 {
1002 if ((col + 2) > wrap_cols)
1003 break;
1004 col += 2;
1005 if (ansi)
1006 mutt_window_printf(win, "^%c", (char) (('@' + wc) & 0x7f));
1007 }
1008 else if (wc < 0x100)
1009 {
1010 if ((col + 4) > wrap_cols)
1011 break;
1012 col += 4;
1013 if (ansi)
1014 mutt_window_printf(win, "\\%03lo", (long) wc);
1015 }
1016 else
1017 {
1018 if ((col + 1) > wrap_cols)
1019 break;
1020 col += k;
1021 if (ansi)
1023 }
1024 }
1025 *pspace = space;
1026 *pcol = col;
1027 *pvch = vch;
1028 *pspecial = special;
1029 return ch;
1030}
1031
1051int display_line(FILE *fp, LOFF_T *bytes_read, struct Line **lines,
1052 int line_num, int *lines_used, int *lines_max,
1053 PagerFlags flags, struct QuoteStyle **quote_list, int *q_level,
1054 bool *force_redraw, regex_t *search_re,
1055 struct MuttWindow *win_pager, struct AttrColorList *ansi_list)
1056{
1057 unsigned char *buf = NULL, *fmt = NULL;
1058 size_t buflen = 0;
1059 unsigned char *buf_ptr = NULL;
1060 int ch, vch, col, cnt, b_read;
1061 int buf_ready = 0;
1062 bool change_last = false;
1063 int special;
1064 int offset;
1065 const struct AttrColor *def_color = NULL;
1066 int m;
1067 int rc = -1;
1068 struct AnsiColor ansi = { { COLOR_DEFAULT, 0, 0 }, { COLOR_DEFAULT, 0, 0 }, 0, NULL };
1069 regmatch_t pmatch[1] = { 0 };
1070
1071 struct PagerPrivateData *priv = win_pager->parent->wdata;
1072 enum PagerMode mode = priv->pview->mode;
1073
1074 if (line_num == *lines_used)
1075 {
1076 (*lines_used)++;
1077 change_last = true;
1078 }
1079
1080 if (*lines_used == *lines_max)
1081 {
1082 *lines_max += LINES;
1084 for (ch = *lines_used; ch < *lines_max; ch++)
1085 {
1086 memset(&((*lines)[ch]), 0, sizeof(struct Line));
1087 (*lines)[ch].cid = -1;
1088 (*lines)[ch].search_arr_size = -1;
1089 (*lines)[ch].syntax = MUTT_MEM_CALLOC(1, struct TextSyntax);
1090 ((*lines)[ch].syntax)[0].first = -1;
1091 ((*lines)[ch].syntax)[0].last = -1;
1092 }
1093 }
1094
1095 struct Line *const cur_line = &(*lines)[line_num];
1096
1097 if (flags & MUTT_PAGER_LOGS)
1098 {
1099 /* determine the line class */
1100 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1101 {
1102 if (change_last)
1103 (*lines_used)--;
1104 goto out;
1105 }
1106
1107 if ((cur_line->cont_line) && (line_num > 0))
1108 {
1109 struct Line *const old_line = &(*lines)[line_num - 1];
1110 cur_line->cid = old_line->cid;
1111 cur_line->syntax[0].attr_color = old_line->syntax[0].attr_color;
1112 }
1113 else
1114 {
1115 cur_line->cid = MT_COLOR_NORMAL;
1116 if (buf[11] == 'M')
1118 else if (buf[11] == 'W')
1120 else if (buf[11] == 'E')
1122 else
1124 }
1125 }
1126
1127 /* only do color highlighting if we are viewing a message */
1128 if (flags & (MUTT_SHOWCOLOR | MUTT_TYPES))
1129 {
1130 if (cur_line->cid == -1)
1131 {
1132 /* determine the line class */
1133 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1134 {
1135 if (change_last)
1136 (*lines_used)--;
1137 goto out;
1138 }
1139
1140 if (mode == PAGER_MODE_EMAIL)
1141 {
1142 resolve_types(win_pager, (char *) fmt, (char *) buf, *lines, line_num, *lines_used,
1143 quote_list, q_level, force_redraw, flags & MUTT_SHOWCOLOR);
1144 }
1145 else
1146 {
1147 (*lines)[line_num].cid = MT_COLOR_NORMAL;
1148 }
1149
1150 /* avoid race condition for continuation lines when scrolling up */
1151 for (m = line_num + 1;
1152 m < *lines_used && (*lines)[m].offset && (*lines)[m].cont_line; m++)
1153 {
1154 (*lines)[m].cid = cur_line->cid;
1155 }
1156 }
1157
1158 /* this also prevents searching through the hidden lines */
1159 const short c_toggle_quoted_show_levels = cs_subset_number(NeoMutt->sub, "toggle_quoted_show_levels");
1160 if ((flags & MUTT_HIDE) && COLOR_QUOTED(cur_line->cid) &&
1161 (!cur_line->quote || (cur_line->quote->quote_n >= c_toggle_quoted_show_levels)))
1162 {
1163 flags = 0; /* MUTT_NOSHOW */
1164 }
1165 }
1166
1167 /* At this point, (*lines[line_num]).quote may still be undefined. We
1168 * don't want to compute it every time MUTT_TYPES is set, since this
1169 * would slow down the "bottom" function unacceptably. A compromise
1170 * solution is hence to call regexec() again, just to find out the
1171 * length of the quote prefix. */
1172 if ((flags & MUTT_SHOWCOLOR) && !cur_line->cont_line &&
1173 COLOR_QUOTED(cur_line->cid) && !cur_line->quote)
1174 {
1175 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1176 {
1177 if (change_last)
1178 (*lines_used)--;
1179 goto out;
1180 }
1181
1182 const struct Regex *c_quote_regex = cs_subset_regex(NeoMutt->sub, "quote_regex");
1183 if (mutt_regex_capture(c_quote_regex, (char *) fmt, 1, pmatch))
1184 {
1185 cur_line->quote = qstyle_classify(quote_list, (char *) fmt + pmatch[0].rm_so,
1186 pmatch[0].rm_eo - pmatch[0].rm_so,
1187 force_redraw, q_level);
1188 }
1189 else
1190 {
1191 goto out;
1192 }
1193 }
1194
1195 if ((flags & MUTT_SEARCH) && !cur_line->cont_line && (cur_line->search_arr_size == -1))
1196 {
1197 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1198 {
1199 if (change_last)
1200 (*lines_used)--;
1201 goto out;
1202 }
1203
1204 offset = 0;
1205 cur_line->search_arr_size = 0;
1206 while (regexec(search_re, (char *) fmt + offset, 1, pmatch,
1207 (offset ? REG_NOTBOL : 0)) == 0)
1208 {
1209 if (++(cur_line->search_arr_size) > 1)
1210 {
1211 MUTT_MEM_REALLOC(&(cur_line->search), cur_line->search_arr_size, struct TextSyntax);
1212 // Zero the new entry
1213 const int index = cur_line->search_arr_size - 1;
1214 struct TextSyntax *ts = &cur_line->search[index];
1215 memset(ts, 0, sizeof(*ts));
1216 }
1217 else
1218 {
1219 cur_line->search = MUTT_MEM_CALLOC(1, struct TextSyntax);
1220 }
1221 pmatch[0].rm_so += offset;
1222 pmatch[0].rm_eo += offset;
1223 (cur_line->search)[cur_line->search_arr_size - 1].first = pmatch[0].rm_so;
1224 (cur_line->search)[cur_line->search_arr_size - 1].last = pmatch[0].rm_eo;
1225
1226 if (pmatch[0].rm_eo == pmatch[0].rm_so)
1227 offset++; /* avoid degenerate cases */
1228 else
1229 offset = pmatch[0].rm_eo;
1230 if (!fmt[offset])
1231 break;
1232 }
1233 }
1234
1235 if (!(flags & MUTT_SHOW) && ((*lines)[line_num + 1].offset > 0))
1236 {
1237 /* we've already scanned this line, so just exit */
1238 rc = 0;
1239 goto out;
1240 }
1241 if ((flags & MUTT_SHOWCOLOR) && *force_redraw && ((*lines)[line_num + 1].offset > 0))
1242 {
1243 /* no need to try to display this line... */
1244 rc = 1;
1245 goto out; /* fake display */
1246 }
1247
1248 b_read = fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready);
1249 if (b_read < 0)
1250 {
1251 if (change_last)
1252 (*lines_used)--;
1253 goto out;
1254 }
1255
1256 /* now chose a good place to break the line */
1257 cnt = format_line(win_pager, lines, line_num, buf, flags, NULL, b_read, &ch,
1258 &vch, &col, &special, win_pager->state.cols, ansi_list);
1259 buf_ptr = buf + cnt;
1260
1261 /* move the break point only if smart_wrap is set */
1262 const bool c_smart_wrap = cs_subset_bool(NeoMutt->sub, "smart_wrap");
1263 if (c_smart_wrap)
1264 {
1265 if ((cnt < b_read) && (ch != -1) && !color_is_header(cur_line->cid) &&
1266 !mutt_isspace(buf[cnt]))
1267 {
1268 buf_ptr = buf + ch;
1269 /* skip trailing blanks */
1270 while (ch && ((buf[ch] == ' ') || (buf[ch] == '\t') || (buf[ch] == '\r')))
1271 ch--;
1272
1273 /* A folded header with a single long word shouldn't be smartwrapped.
1274 * So just disable smart_wrap if it would wrap at the beginning of the line. */
1275 if (ch == 0)
1276 buf_ptr = buf + cnt;
1277 else
1278 cnt = ch + 1;
1279 }
1280
1281 /* skip leading blanks on the next line too */
1282 while ((*buf_ptr == ' ') || (*buf_ptr == '\t'))
1283 buf_ptr++;
1284 }
1285
1286 if (*buf_ptr == '\r')
1287 buf_ptr++;
1288 if (*buf_ptr == '\n')
1289 buf_ptr++;
1290
1291 if (((int) (buf_ptr - buf) < b_read) && !(*lines)[line_num + 1].cont_line)
1292 append_line(*lines, line_num, (int) (buf_ptr - buf));
1293 (*lines)[line_num + 1].offset = cur_line->offset + (long) (buf_ptr - buf);
1294
1295 /* if we don't need to display the line we are done */
1296 if (!(flags & MUTT_SHOW))
1297 {
1298 rc = 0;
1299 goto out;
1300 }
1301
1302 if (flags & MUTT_PAGER_STRIPES)
1303 {
1304 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1306 }
1307
1308 /* display the line */
1309 format_line(win_pager, lines, line_num, buf, flags, &ansi, cnt, &ch, &vch,
1310 &col, &special, win_pager->state.cols, ansi_list);
1311
1312 /* avoid a bug in ncurses... */
1313 if (col == 0)
1314 {
1315 if (flags & MUTT_PAGER_STRIPES)
1316 {
1317 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1319 }
1320 else
1321 {
1323 }
1324
1325 mutt_window_addch(win_pager, ' ');
1326 }
1327
1328 /* Fill the blank space at the end of the line with the prevailing color.
1329 * ncurses does an implicit clrtoeol() when you do mutt_window_addch('\n') so we have
1330 * to make sure to reset the color *after* that */
1331 if (flags & MUTT_SHOWCOLOR)
1332 {
1333 m = (cur_line->cont_line) ? (cur_line->syntax)[0].first : line_num;
1334 if ((*lines)[m].cid == MT_COLOR_HEADER)
1335 {
1336 def_color = ((*lines)[m].syntax)[0].attr_color;
1337 }
1338 else
1339 {
1340 def_color = simple_color_get((*lines)[m].cid);
1341 }
1342 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
1343 const struct AttrColor *ac_eol = NULL;
1344 if (def_color)
1345 ac_eol = merged_color_overlay(ac_normal, def_color);
1346 else
1347 ac_eol = ac_normal;
1348 mutt_curses_set_color(ac_eol);
1349 }
1350
1351 if (col < win_pager->state.cols)
1352 {
1353 if (flags & MUTT_PAGER_STRIPES)
1354 {
1355 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1356 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
1357 const struct AttrColor *stripe_color = simple_color_get(cid);
1358 const struct AttrColor *ac_eol = merged_color_overlay(ac_normal, stripe_color);
1359 mutt_curses_set_color(ac_eol);
1360 }
1361 else
1362 {
1363 // Colours may be disabled, but we may still need to end the "search" colour
1364 if (!(flags & MUTT_SHOWCOLOR))
1366 }
1367 mutt_window_clrtoeol(win_pager);
1368 }
1369
1370 /* reset the color back to normal. This *must* come after the
1371 * clrtoeol, otherwise the color for this line will not be
1372 * filled to the right margin. */
1373 if (flags & MUTT_SHOWCOLOR)
1375
1376 /* build a return code */
1377 if (!(flags & MUTT_SHOW))
1378 flags = 0;
1379
1380 rc = flags;
1381
1382out:
1383 FREE(&buf);
1384 FREE(&fmt);
1385 return rc;
1386}
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:118
bool attr_color_match(struct AttrColor *ac1, struct AttrColor *ac2)
Do the colours match?
Definition: attr.c:193
struct AttrColor attr_color_copy(const struct AttrColor *ac)
Copy a colour.
Definition: attr.c:166
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:491
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:76
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:241
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:571
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:337
Color and attribute parsing.
struct RegexColorList * regex_colors_get_list(enum ColorId cid)
Return the RegexColorList for a Colour ID.
Definition: regex.c:190
bool simple_color_is_set(enum ColorId cid)
Is the object coloured?
Definition: simple.c:116
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition: simple.c:95
#define COLOR_DEFAULT
Definition: color.h:103
ColorId
List of all coloured objects.
Definition: color.h:36
@ MT_COLOR_MARKERS
Pager: markers, line continuation.
Definition: color.h:52
@ MT_COLOR_MESSAGE
Informational message.
Definition: color.h:53
@ MT_COLOR_QUOTED0
Pager: quoted text, level 0.
Definition: color.h:58
@ MT_COLOR_HEADER
Message headers (takes a pattern)
Definition: color.h:49
@ MT_COLOR_STRIPE_EVEN
Stripes: even lines of the Help Page.
Definition: color.h:80
@ MT_COLOR_ERROR
Error message.
Definition: color.h:47
@ MT_COLOR_BOLD
Bold text.
Definition: color.h:41
@ MT_COLOR_BODY
Pager: highlight body of message (takes a pattern)
Definition: color.h:40
@ MT_COLOR_HDRDEFAULT
Header default colour.
Definition: color.h:48
@ MT_COLOR_NORMAL
Plain text.
Definition: color.h:54
@ MT_COLOR_ATTACH_HEADERS
MIME attachment test (takes a pattern)
Definition: color.h:39
@ MT_COLOR_SEARCH
Pager: search matches.
Definition: color.h:68
@ MT_COLOR_ITALIC
Italic text.
Definition: color.h:51
@ MT_COLOR_STRIPE_ODD
Stripes: odd lines of the Help Page.
Definition: color.h:81
@ MT_COLOR_ATTACHMENT
MIME attachments text (entire line)
Definition: color.h:38
@ MT_COLOR_WARNING
Warning messages.
Definition: color.h:85
@ MT_COLOR_UNDERLINE
Underlined text.
Definition: color.h:84
@ MT_COLOR_SIGNATURE
Pager: signature lines.
Definition: color.h:78
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:217
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:143
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:47
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
bool mutt_isspace(int arg)
Wrapper for isspace(3)
Definition: ctype.c:95
int mutt_addwch(struct MuttWindow *win, wchar_t wc)
Addwch would be provided by an up-to-date curses library.
Definition: curs_lib.c:320
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:846
static int check_protected_header_marker(const char *p)
Check that the unique marker is present.
Definition: display.c:308
bool mutt_is_quote_line(char *line, regmatch_t *pmatch)
Is a line of message text a quote?
Definition: display.c:322
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:791
static int check_sig(const char *s, struct Line *info, int offset)
Check for an email signature.
Definition: display.c:56
static void match_body_patterns(char *pat, struct Line *lines, int line_num)
Match body patterns, e.g.
Definition: display.c:363
void buf_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
Removes ANSI and backspace formatting.
Definition: display.c:730
static int comp_syntax_t(const void *m1, const void *m2)
Search for a Syntax using bsearch(3)
Definition: display.c:96
bool color_is_header(enum ColorId cid)
Colour is for an Email header.
Definition: display.c:486
static int check_attachment_marker(const char *p)
Check that the unique marker is present.
Definition: display.c:298
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:118
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:504
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:1051
static void append_line(struct Line *lines, int line_num, int cnt)
Add a new Line to the array.
Definition: display.c:257
static int check_marker(const char *q, const char *p)
Check that the unique marker is present.
Definition: display.c:282
Pager Display.
int BrailleRow
Braille display: row to leave the cursor.
Definition: dlg_pager.c:68
int BrailleCol
Braille display: column to leave the cursor.
Definition: dlg_pager.c:70
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:685
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:655
#define MUTT_RL_EOL
don't strip \n / \r\n
Definition: file.h:42
#define mutt_debug(LEVEL,...)
Definition: logging2.h:90
Convenience wrapper for the gui headers.
@ LL_DEBUG3
Log at debug level 3.
Definition: logging2.h:46
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:45
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:44
bool mutt_mb_is_display_corrupting_utf8(wchar_t wc)
Will this character corrupt the display?
Definition: mbyte.c:385
#define IsWPrint(wc)
Definition: mbyte.h:41
#define FREE(x)
Definition: memory.h:62
#define MUTT_MEM_CALLOC(n, type)
Definition: memory.h:47
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition: memory.h:50
static int search(struct Menu *menu, int op)
Search a menu.
Definition: functions.c:58
const struct AttrColor * merged_color_overlay(const struct AttrColor *base, const struct AttrColor *over)
Combine two colours.
Definition: merged.c:107
bool CharsetIsUtf8
Is the user's current character set utf-8?
Definition: charset.c:66
wchar_t ReplacementChar
When a Unicode character can't be displayed, use this instead.
Definition: charset.c:61
#define ICONV_BUF_TOO_SMALL
Error value for iconv() - Buffer too small.
Definition: charset.h:98
#define ICONV_ILLEGAL_SEQ
Error value for iconv() - Illegal sequence.
Definition: charset.h:96
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:597
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:659
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:231
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:497
const struct AttrColor * mutt_curses_set_normal_backed_color_by_id(enum ColorId cid)
Set the colour and attributes by the Colour ID.
Definition: mutt_curses.c:63
const struct AttrColor * mutt_curses_set_color_by_id(enum ColorId cid)
Set the colour and attributes by the Colour ID.
Definition: mutt_curses.c:79
void mutt_curses_set_color(const struct AttrColor *ac)
Set the colour and attributes for text.
Definition: mutt_curses.c:38
#define A_ITALIC
Definition: mutt_curses.h:49
int mutt_window_printf(struct MuttWindow *win, const char *fmt,...)
Write a formatted string to a Window.
Definition: mutt_window.c:396
void mutt_window_get_coords(struct MuttWindow *win, int *row, int *col)
Get the cursor position in the Window.
Definition: mutt_window.c:280
int mutt_window_wrap_cols(int width, short wrap)
Calculate the wrap column for a given screen width.
Definition: mutt_window.c:337
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:247
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
Definition: mutt_window.c:353
#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:63
#define MUTT_TYPES
Compute line's type.
Definition: lib.h:65
uint16_t PagerFlags
Flags for dlg_pager(), e.g. MUTT_SHOWFLAT.
Definition: lib.h:59
#define MUTT_PAGER_STRIPES
Striped highlighting.
Definition: lib.h:74
#define MUTT_SHOWCOLOR
Show characters in color otherwise don't show characters.
Definition: lib.h:62
#define MUTT_PAGER_MARKER
Use markers if option is set.
Definition: lib.h:69
#define MUTT_PAGER_LOGS
Logview mode.
Definition: lib.h:72
#define MUTT_SEARCH
Resolve search patterns.
Definition: lib.h:64
PagerMode
Determine the behaviour of the Pager.
Definition: lib.h:133
@ PAGER_MODE_OTHER
Pager is invoked via 3rd path. Non-email content is likely to be shown.
Definition: lib.h:140
@ PAGER_MODE_EMAIL
Pager is invoked via 1st path. The mime part is selected automatically.
Definition: lib.h:136
#define MUTT_SHOW
Definition: lib.h:66
Private state data for the Pager.
int ansi_color_seq_length(const char *str)
Is this an ANSI escape sequence?
Definition: parse_ansi.c:78
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
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: qstyle.c:136
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:390
#define COLOR_QUOTED(cid)
Definition: quoted.h:28
Key value store.
An ANSI escape sequence.
Definition: ansi.h:35
int attrs
Text attributes, e.g. A_BOLD.
Definition: ansi.h:38
const struct AttrColor * attr_color
Curses colour of text.
Definition: ansi.h:39
A curses colour and its attributes.
Definition: attr.h:66
int attrs
Text attributes, e.g. A_BOLD.
Definition: attr.h:69
struct CursesColor * curses_color
Underlying Curses colour.
Definition: attr.h:70
String manipulation buffer.
Definition: buffer.h:36
char * dptr
Current read/write position.
Definition: buffer.h:38
A line of text in the pager.
Definition: display.h:50
short search_arr_size
Number of items in search array.
Definition: display.h:59
struct TextSyntax * search
Array of search text in the line.
Definition: display.h:60
bool cont_line
Continuation of a previous line (wrapped by NeoMutt)
Definition: display.h:53
short cid
Default line colour, e.g. MT_COLOR_SIGNATURE.
Definition: display.h:52
struct QuoteStyle * quote
Quoting style for this line (pointer into PagerPrivateData->quote_list)
Definition: display.h:62
LOFF_T offset
Offset into Email file (PagerPrivateData->fp)
Definition: display.h:51
bool cont_header
Continuation of a header line (wrapped by MTA)
Definition: display.h:54
short syntax_arr_size
Number of items in syntax array.
Definition: display.h:56
struct TextSyntax * syntax
Array of coloured text in the line.
Definition: display.h:57
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
void * wdata
Private data.
Definition: mutt_window.h:145
struct MuttWindow * parent
Parent Window.
Definition: mutt_window.h:135
Container for Accounts, Notifications.
Definition: neomutt.h:43
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:47
Private state data for the Pager.
Definition: private_data.h:41
int q_level
Number of unique quoting levels.
Definition: private_data.h:59
int lines_used
Size of lines array (used entries)
Definition: private_data.h:49
int lines_max
Capacity of lines array (total entries)
Definition: private_data.h:50
bool force_redraw
Repaint is needed.
Definition: private_data.h:68
struct Line * lines
Array of text lines in pager.
Definition: private_data.h:48
LOFF_T bytes_read
Number of bytes read from file.
Definition: private_data.h:46
bool first
First time flag for toggle-new.
Definition: private_data.h:75
struct QuoteStyle * quote_list
Tree of quoting levels.
Definition: private_data.h:58
struct PagerView * pview
Object to view in the pager.
Definition: private_data.h:42
struct AttrColorList ansi_list
List of ANSI colours used in the Pager.
Definition: private_data.h:70
regex_t search_re
Compiled search string.
Definition: private_data.h:65
FILE * fp
File containing decrypted/decoded/weeded Email.
Definition: private_data.h:44
enum PagerMode mode
Pager mode.
Definition: lib.h:172
Style of quoted text.
Definition: qstyle.h:56
struct AttrColor * attr_color
Colour and attribute of the text.
Definition: qstyle.h:58
struct QuoteStyle * up
Definition: qstyle.h:62
size_t prefix_len
Length of the prefix string.
Definition: qstyle.h:60
int quote_n
The quoteN colour index for this level.
Definition: qstyle.h:57
A regular expression and a color to highlight a line.
Definition: regex4.h:36
regex_t regex
Compiled regex.
Definition: regex4.h:39
struct AttrColor attr_color
Colour and attributes to apply.
Definition: regex4.h:37
bool stop_matching
Used by the pager for body patterns, to prevent the color from being retried once it fails.
Definition: regex4.h:43
Cached regular expression.
Definition: regex3.h:86
Highlighting for a piece of text.
Definition: display.h:39
const struct AttrColor * attr_color
Curses colour of text.
Definition: display.h:40
int last
Last character in line to be coloured (not included)
Definition: display.h:42
int first
First character in line to be coloured.
Definition: display.h:41
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:61