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