NeoMutt  2024-12-12-14-g7b49f7
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 <ctype.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 (!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[line_num].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_QUOTED;
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] = '\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 b_read = (int) (*bytes_read - offset);
816 *buf_ready = 1;
817
818 struct Buffer *stripped = buf_pool_get();
819 buf_alloc(stripped, *blen);
820 buf_strip_formatting(stripped, (const char *) *buf, 1);
821 /* This should be a noop, because *fmt should be NULL */
822 FREE(fmt);
823 *fmt = (unsigned char *) buf_strdup(stripped);
824 buf_pool_release(&stripped);
825 }
826
827 return b_read;
828}
829
847static int format_line(struct MuttWindow *win, struct Line **lines, int line_num,
848 unsigned char *buf, PagerFlags flags, struct AnsiColor *ansi,
849 int cnt, int *pspace, int *pvch, int *pcol,
850 int *pspecial, int width, struct AttrColorList *ansi_list)
851{
852 int space = -1; /* index of the last space or TAB */
853 const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
854 size_t col = c_markers ? (*lines)[line_num].cont_line : 0;
855 size_t k;
856 int ch, vch, last_special = -1, special = 0, t;
857 wchar_t wc = 0;
858 mbstate_t mbstate = { 0 }; // FIXME: this should come from lines
859 const size_t c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
860 size_t wrap_cols = mutt_window_wrap_cols(width, (flags & MUTT_PAGER_NOWRAP) ? 0 : c_wrap);
861
862 if (check_attachment_marker((char *) buf) == 0)
863 wrap_cols = width;
864
865 struct PagerPrivateData *priv = win->parent->wdata;
866 enum PagerMode mode = priv->pview->mode;
867 const bool c_allow_ansi = (mode == PAGER_MODE_OTHER) ||
868 cs_subset_bool(NeoMutt->sub, "allow_ansi");
869
870 for (ch = 0, vch = 0; ch < cnt; ch += k, vch += k)
871 {
872 /* Handle ANSI sequences */
873 if (buf[ch] == '\033') // Escape
874 {
875 int len = ansi_color_parse((const char *) buf + ch, ansi, ansi_list, !c_allow_ansi);
876 ch += len;
877 }
878
879 while ((cnt - ch >= 2) && (buf[ch] == '\033') && (buf[ch + 1] == ']') && // Escape
880 ((check_attachment_marker((char *) buf + ch) == 0) ||
881 (check_protected_header_marker((char *) buf + ch) == 0)))
882 {
883 while (buf[ch++] != '\a')
884 if (ch >= cnt)
885 break;
886 }
887
888 /* is anything left to do? */
889 if (ch >= cnt)
890 break;
891
892 k = mbrtowc(&wc, (char *) buf + ch, cnt - ch, &mbstate);
893 if ((k == ICONV_BUF_TOO_SMALL) || (k == ICONV_ILLEGAL_SEQ))
894 {
895 if (k == ICONV_ILLEGAL_SEQ)
896 memset(&mbstate, 0, sizeof(mbstate));
897 mutt_debug(LL_DEBUG1, "mbrtowc returned %zu; errno = %d\n", k, errno);
898 if ((col + 4) > wrap_cols)
899 break;
900 col += 4;
901 if (ansi)
902 mutt_window_printf(win, "\\%03o", buf[ch]);
903 k = 1;
904 continue;
905 }
906 if (k == 0)
907 k = 1;
908
909 if (CharsetIsUtf8)
910 {
911 /* zero width space, zero with non-joiner, zero width no-break space */
912 if ((wc == 0x200B) || (wc == 0x200C) || (wc == 0xFEFF))
913 {
914 mutt_debug(LL_DEBUG3, "skip zero-width character U+%04X\n", (unsigned short) wc);
915 continue;
916 }
918 {
919 mutt_debug(LL_DEBUG3, "filtered U+%04X\n", (unsigned short) wc);
920 continue;
921 }
922 }
923
924 /* Handle backspace */
925 special = 0;
926 if (IsWPrint(wc))
927 {
928 wchar_t wc1 = 0;
929 mbstate_t mbstate1 = mbstate;
930 size_t k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
931 while ((k1 != ICONV_BUF_TOO_SMALL) && (k1 != ICONV_ILLEGAL_SEQ) &&
932 (k1 > 0) && (wc1 == '\b'))
933 {
934 const size_t k2 = mbrtowc(&wc1, (char *) buf + ch + k + k1,
935 cnt - ch - k - k1, &mbstate1);
936 if ((k2 == ICONV_BUF_TOO_SMALL) || (k2 == ICONV_ILLEGAL_SEQ) ||
937 (k2 == 0) || (!IsWPrint(wc1)))
938 {
939 break;
940 }
941
942 if (wc == wc1)
943 {
944 special |= ((wc == '_') && (special & A_UNDERLINE)) ? A_UNDERLINE : A_BOLD;
945 }
946 else if ((wc == '_') || (wc1 == '_'))
947 {
948 special |= A_UNDERLINE;
949 wc = (wc1 == '_') ? wc : wc1;
950 }
951 else
952 {
953 /* special = 0; / * overstrike: nothing to do! */
954 wc = wc1;
955 }
956
957 ch += k + k1;
958 k = k2;
959 mbstate = mbstate1;
960 k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
961 }
962 }
963
964 if (ansi && ((flags & (MUTT_SHOWCOLOR | MUTT_SEARCH | MUTT_PAGER_MARKER)) ||
965 special || last_special || (ansi->attrs != A_NORMAL)))
966 {
967 resolve_color(win, *lines, line_num, vch, flags, special, ansi);
968 last_special = special;
969 }
970
971 /* no-break space, narrow no-break space */
972 if (IsWPrint(wc) || (CharsetIsUtf8 && ((wc == 0x00A0) || (wc == 0x202F))))
973 {
974 if (wc == ' ')
975 {
976 space = ch;
977 }
978 t = wcwidth(wc);
979 if (col + t > wrap_cols)
980 break;
981 col += t;
982 if (ansi)
983 mutt_addwch(win, wc);
984 }
985 else if (wc == '\n')
986 {
987 break;
988 }
989 else if (wc == '\t')
990 {
991 space = ch;
992 t = (col & ~7) + 8;
993 if (t > wrap_cols)
994 break;
995 if (ansi)
996 for (; col < t; col++)
997 mutt_window_addch(win, ' ');
998 else
999 col = t;
1000 }
1001 else if ((wc < 0x20) || (wc == 0x7f))
1002 {
1003 if ((col + 2) > wrap_cols)
1004 break;
1005 col += 2;
1006 if (ansi)
1007 mutt_window_printf(win, "^%c", (char) (('@' + wc) & 0x7f));
1008 }
1009 else if (wc < 0x100)
1010 {
1011 if ((col + 4) > wrap_cols)
1012 break;
1013 col += 4;
1014 if (ansi)
1015 mutt_window_printf(win, "\\%03lo", (long) wc);
1016 }
1017 else
1018 {
1019 if ((col + 1) > wrap_cols)
1020 break;
1021 col += k;
1022 if (ansi)
1024 }
1025 }
1026 *pspace = space;
1027 *pcol = col;
1028 *pvch = vch;
1029 *pspecial = special;
1030 return ch;
1031}
1032
1052int display_line(FILE *fp, LOFF_T *bytes_read, struct Line **lines,
1053 int line_num, int *lines_used, int *lines_max,
1054 PagerFlags flags, struct QuoteStyle **quote_list, int *q_level,
1055 bool *force_redraw, regex_t *search_re,
1056 struct MuttWindow *win_pager, struct AttrColorList *ansi_list)
1057{
1058 unsigned char *buf = NULL, *fmt = NULL;
1059 size_t buflen = 0;
1060 unsigned char *buf_ptr = NULL;
1061 int ch, vch, col, cnt, b_read;
1062 int buf_ready = 0;
1063 bool change_last = false;
1064 int special;
1065 int offset;
1066 const struct AttrColor *def_color = NULL;
1067 int m;
1068 int rc = -1;
1069 struct AnsiColor ansi = { { COLOR_DEFAULT, 0, 0 }, { COLOR_DEFAULT, 0, 0 }, 0, NULL };
1070 regmatch_t pmatch[1] = { 0 };
1071
1072 struct PagerPrivateData *priv = win_pager->parent->wdata;
1073 enum PagerMode mode = priv->pview->mode;
1074
1075 if (line_num == *lines_used)
1076 {
1077 (*lines_used)++;
1078 change_last = true;
1079 }
1080
1081 if (*lines_used == *lines_max)
1082 {
1083 *lines_max += LINES;
1085 for (ch = *lines_used; ch < *lines_max; ch++)
1086 {
1087 memset(&((*lines)[ch]), 0, sizeof(struct Line));
1088 (*lines)[ch].cid = -1;
1089 (*lines)[ch].search_arr_size = -1;
1090 (*lines)[ch].syntax = MUTT_MEM_CALLOC(1, struct TextSyntax);
1091 ((*lines)[ch].syntax)[0].first = -1;
1092 ((*lines)[ch].syntax)[0].last = -1;
1093 }
1094 }
1095
1096 struct Line *const cur_line = &(*lines)[line_num];
1097
1098 if (flags & MUTT_PAGER_LOGS)
1099 {
1100 /* determine the line class */
1101 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1102 {
1103 if (change_last)
1104 (*lines_used)--;
1105 goto out;
1106 }
1107
1108 if ((cur_line->cont_line) && (line_num > 0))
1109 {
1110 struct Line *const old_line = &(*lines)[line_num - 1];
1111 cur_line->cid = old_line->cid;
1112 cur_line->syntax[0].attr_color = old_line->syntax[0].attr_color;
1113 }
1114 else
1115 {
1116 cur_line->cid = MT_COLOR_MESSAGE_LOG;
1117 if (buf[11] == 'M')
1119 else if (buf[11] == 'W')
1121 else if (buf[11] == 'E')
1123 else
1125 }
1126 }
1127
1128 /* only do color highlighting if we are viewing a message */
1129 if (flags & (MUTT_SHOWCOLOR | MUTT_TYPES))
1130 {
1131 if (cur_line->cid == -1)
1132 {
1133 /* determine the line class */
1134 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1135 {
1136 if (change_last)
1137 (*lines_used)--;
1138 goto out;
1139 }
1140
1141 if (mode == PAGER_MODE_EMAIL)
1142 {
1143 resolve_types(win_pager, (char *) fmt, (char *) buf, *lines, line_num, *lines_used,
1144 quote_list, q_level, force_redraw, flags & MUTT_SHOWCOLOR);
1145 }
1146 else
1147 {
1148 (*lines)[line_num].cid = MT_COLOR_NORMAL;
1149 }
1150
1151 /* avoid race condition for continuation lines when scrolling up */
1152 for (m = line_num + 1;
1153 m < *lines_used && (*lines)[m].offset && (*lines)[m].cont_line; m++)
1154 {
1155 (*lines)[m].cid = cur_line->cid;
1156 }
1157 }
1158
1159 /* this also prevents searching through the hidden lines */
1160 const short c_toggle_quoted_show_levels = cs_subset_number(NeoMutt->sub, "toggle_quoted_show_levels");
1161 if ((flags & MUTT_HIDE) && COLOR_QUOTED(cur_line->cid) &&
1162 (!cur_line->quote || (cur_line->quote->quote_n >= c_toggle_quoted_show_levels)))
1163 {
1164 flags = 0; /* MUTT_NOSHOW */
1165 }
1166 }
1167
1168 /* At this point, (*lines[line_num]).quote may still be undefined. We
1169 * don't want to compute it every time MUTT_TYPES is set, since this
1170 * would slow down the "bottom" function unacceptably. A compromise
1171 * solution is hence to call regexec() again, just to find out the
1172 * length of the quote prefix. */
1173 if ((flags & MUTT_SHOWCOLOR) && !cur_line->cont_line &&
1174 COLOR_QUOTED(cur_line->cid) && !cur_line->quote)
1175 {
1176 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1177 {
1178 if (change_last)
1179 (*lines_used)--;
1180 goto out;
1181 }
1182
1183 const struct Regex *c_quote_regex = cs_subset_regex(NeoMutt->sub, "quote_regex");
1184 if (mutt_regex_capture(c_quote_regex, (char *) fmt, 1, pmatch))
1185 {
1186 cur_line->quote = qstyle_classify(quote_list, (char *) fmt + pmatch[0].rm_so,
1187 pmatch[0].rm_eo - pmatch[0].rm_so,
1188 force_redraw, q_level);
1189 }
1190 else
1191 {
1192 goto out;
1193 }
1194 }
1195
1196 if ((flags & MUTT_SEARCH) && !cur_line->cont_line && (cur_line->search_arr_size == -1))
1197 {
1198 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1199 {
1200 if (change_last)
1201 (*lines_used)--;
1202 goto out;
1203 }
1204
1205 offset = 0;
1206 cur_line->search_arr_size = 0;
1207 while (regexec(search_re, (char *) fmt + offset, 1, pmatch,
1208 (offset ? REG_NOTBOL : 0)) == 0)
1209 {
1210 if (++(cur_line->search_arr_size) > 1)
1211 {
1212 MUTT_MEM_REALLOC(&(cur_line->search), cur_line->search_arr_size, struct TextSyntax);
1213 // Zero the new entry
1214 const int index = cur_line->search_arr_size - 1;
1215 struct TextSyntax *ts = &cur_line->search[index];
1216 memset(ts, 0, sizeof(*ts));
1217 }
1218 else
1219 {
1220 cur_line->search = MUTT_MEM_CALLOC(1, struct TextSyntax);
1221 }
1222 pmatch[0].rm_so += offset;
1223 pmatch[0].rm_eo += offset;
1224 (cur_line->search)[cur_line->search_arr_size - 1].first = pmatch[0].rm_so;
1225 (cur_line->search)[cur_line->search_arr_size - 1].last = pmatch[0].rm_eo;
1226
1227 if (pmatch[0].rm_eo == pmatch[0].rm_so)
1228 offset++; /* avoid degenerate cases */
1229 else
1230 offset = pmatch[0].rm_eo;
1231 if (!fmt[offset])
1232 break;
1233 }
1234 }
1235
1236 if (!(flags & MUTT_SHOW) && ((*lines)[line_num + 1].offset > 0))
1237 {
1238 /* we've already scanned this line, so just exit */
1239 rc = 0;
1240 goto out;
1241 }
1242 if ((flags & MUTT_SHOWCOLOR) && *force_redraw && ((*lines)[line_num + 1].offset > 0))
1243 {
1244 /* no need to try to display this line... */
1245 rc = 1;
1246 goto out; /* fake display */
1247 }
1248
1249 b_read = fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready);
1250 if (b_read < 0)
1251 {
1252 if (change_last)
1253 (*lines_used)--;
1254 goto out;
1255 }
1256
1257 /* now chose a good place to break the line */
1258 cnt = format_line(win_pager, lines, line_num, buf, flags, NULL, b_read, &ch,
1259 &vch, &col, &special, win_pager->state.cols, ansi_list);
1260 buf_ptr = buf + cnt;
1261
1262 /* move the break point only if smart_wrap is set */
1263 const bool c_smart_wrap = cs_subset_bool(NeoMutt->sub, "smart_wrap");
1264 if (c_smart_wrap)
1265 {
1266 if ((cnt < b_read) && (ch != -1) && !color_is_header(cur_line->cid) &&
1267 !isspace(buf[cnt]))
1268 {
1269 buf_ptr = buf + ch;
1270 /* skip trailing blanks */
1271 while (ch && ((buf[ch] == ' ') || (buf[ch] == '\t') || (buf[ch] == '\r')))
1272 ch--;
1273 /* A very long word with leading spaces causes infinite
1274 * wrapping when MUTT_PAGER_NSKIP is set. A folded header
1275 * with a single long word shouldn't be smartwrapped
1276 * either. So just disable smart_wrap if it would wrap at the
1277 * beginning of the line. */
1278 if (ch == 0)
1279 buf_ptr = buf + cnt;
1280 else
1281 cnt = ch + 1;
1282 }
1283 if (!(flags & MUTT_PAGER_NSKIP))
1284 {
1285 /* skip leading blanks on the next line too */
1286 while ((*buf_ptr == ' ') || (*buf_ptr == '\t'))
1287 buf_ptr++;
1288 }
1289 }
1290
1291 if (*buf_ptr == '\r')
1292 buf_ptr++;
1293 if (*buf_ptr == '\n')
1294 buf_ptr++;
1295
1296 if (((int) (buf_ptr - buf) < b_read) && !(*lines)[line_num + 1].cont_line)
1297 append_line(*lines, line_num, (int) (buf_ptr - buf));
1298 (*lines)[line_num + 1].offset = cur_line->offset + (long) (buf_ptr - buf);
1299
1300 /* if we don't need to display the line we are done */
1301 if (!(flags & MUTT_SHOW))
1302 {
1303 rc = 0;
1304 goto out;
1305 }
1306
1307 if (flags & MUTT_PAGER_STRIPES)
1308 {
1309 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1311 }
1312
1313 /* display the line */
1314 format_line(win_pager, lines, line_num, buf, flags, &ansi, cnt, &ch, &vch,
1315 &col, &special, win_pager->state.cols, ansi_list);
1316
1317 /* avoid a bug in ncurses... */
1318 if (col == 0)
1319 {
1320 if (flags & MUTT_PAGER_STRIPES)
1321 {
1322 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1324 }
1325 else
1326 {
1328 }
1329
1330 mutt_window_addch(win_pager, ' ');
1331 }
1332
1333 /* Fill the blank space at the end of the line with the prevailing color.
1334 * ncurses does an implicit clrtoeol() when you do mutt_window_addch('\n') so we have
1335 * to make sure to reset the color *after* that */
1336 if (flags & MUTT_SHOWCOLOR)
1337 {
1338 m = (cur_line->cont_line) ? (cur_line->syntax)[0].first : line_num;
1339 if ((*lines)[m].cid == MT_COLOR_HEADER)
1340 {
1341 def_color = ((*lines)[m].syntax)[0].attr_color;
1342 }
1343 else
1344 {
1345 def_color = simple_color_get((*lines)[m].cid);
1346 }
1347 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
1348 const struct AttrColor *ac_eol = NULL;
1349 if (def_color)
1350 ac_eol = merged_color_overlay(ac_normal, def_color);
1351 else
1352 ac_eol = ac_normal;
1353 mutt_curses_set_color(ac_eol);
1354 }
1355
1356 if (col < win_pager->state.cols)
1357 {
1358 if (flags & MUTT_PAGER_STRIPES)
1359 {
1360 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1361 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
1362 const struct AttrColor *stripe_color = simple_color_get(cid);
1363 const struct AttrColor *ac_eol = merged_color_overlay(ac_normal, stripe_color);
1364 mutt_curses_set_color(ac_eol);
1365 }
1366 mutt_window_clrtoeol(win_pager);
1367 }
1368
1369 /* reset the color back to normal. This *must* come after the
1370 * clrtoeol, otherwise the color for this line will not be
1371 * filled to the right margin. */
1372 if (flags & MUTT_SHOWCOLOR)
1374
1375 /* build a return code */
1376 if (!(flags & MUTT_SHOW))
1377 flags = 0;
1378
1379 rc = flags;
1380
1381out:
1382 FREE(&buf);
1383 FREE(&fmt);
1384 return rc;
1385}
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:96
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_QUOTED
Pager: quoted text.
Definition: color.h:59
@ 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:72
@ 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:55
@ MT_COLOR_ATTACH_HEADERS
MIME attachment test (takes a pattern)
Definition: color.h:39
@ MT_COLOR_SEARCH
Pager: search matches.
Definition: color.h:60
@ MT_COLOR_MESSAGE_LOG
Menu showing log messages.
Definition: color.h:54
@ MT_COLOR_ITALIC
Italic text.
Definition: color.h:51
@ MT_COLOR_STRIPE_ODD
Stripes: odd lines of the Help Page.
Definition: color.h:73
@ MT_COLOR_ATTACHMENT
MIME attachments text (entire line)
Definition: color.h:38
@ MT_COLOR_WARNING
Warning messages.
Definition: color.h:77
@ MT_COLOR_UNDERLINE
Underlined text.
Definition: color.h:76
@ MT_COLOR_SIGNATURE
Pager: signature lines.
Definition: color.h:70
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c: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.
int mutt_addwch(struct MuttWindow *win, wchar_t wc)
Addwch would be provided by an up-to-date curses library.
Definition: curs_lib.c:318
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:847
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:1052
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:67
int BrailleCol
Braille display: column to leave the cursor.
Definition: dlg_pager.c:69
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:808
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:778
#define MUTT_RL_EOL
don't strip \n / \r\n
Definition: file.h:42
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
Convenience wrapper for the gui headers.
@ LL_DEBUG3
Log at debug level 3.
Definition: logging2.h:45
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
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:55
#define MUTT_MEM_CALLOC(n, type)
Definition: memory.h:40
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition: memory.h:43
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:660
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:230
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:496
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:431
void mutt_window_get_coords(struct MuttWindow *win, int *col, int *row)
Get the cursor position in the Window.
Definition: mutt_window.c:277
int mutt_window_wrap_cols(int width, short wrap)
Calculate the wrap column for a given screen width.
Definition: mutt_window.c:372
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:244
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
Definition: mutt_window.c:388
#define MUTT_PAGER_NSKIP
Preserve whitespace with smartwrap.
Definition: lib.h:69
#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: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:76
#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:70
#define MUTT_PAGER_LOGS
Logview mode.
Definition: lib.h:74
#define MUTT_SEARCH
Resolve search patterns.
Definition: lib.h:64
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: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:79
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:352
#define COLOR_QUOTED(cid)
Definition: quoted.h:39
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:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
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:174
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