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