NeoMutt  2024-03-23-147-g885fbc
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) && (lines[m].cid == MT_COLOR_QUOTED))
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) * sizeof(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
495static void resolve_types(struct MuttWindow *win, char *buf, char *raw,
496 struct Line *lines, int line_num, int lines_used,
497 struct QuoteStyle **quote_list, int *q_level,
498 bool *force_redraw, bool q_classify)
499{
500 struct RegexColor *color_line = NULL;
501 regmatch_t pmatch[1] = { 0 };
502 const bool c_header_color_partial = cs_subset_bool(NeoMutt->sub, "header_color_partial");
503 int offset, i = 0;
504
505 if ((line_num == 0) || simple_color_is_header(lines[line_num - 1].cid) ||
507 {
508 if (buf[0] == '\n') /* end of header */
509 {
510 lines[line_num].cid = MT_COLOR_NORMAL;
512 }
513 else
514 {
515 /* if this is a continuation of the previous line, use the previous
516 * line's color as default. */
517 if ((line_num > 0) && ((buf[0] == ' ') || (buf[0] == '\t')))
518 {
519 lines[line_num].cid = lines[line_num - 1].cid; /* wrapped line */
520 if (!c_header_color_partial)
521 {
522 (lines[line_num].syntax)[0].attr_color =
523 (lines[line_num - 1].syntax)[0].attr_color;
524 lines[line_num].cont_header = true;
525 }
526 }
527 else
528 {
529 lines[line_num].cid = MT_COLOR_HDRDEFAULT;
530 }
531
532 /* When this option is unset, we color the entire header the
533 * same color. Otherwise, we handle the header patterns just
534 * like body patterns (further below). */
535 if (!c_header_color_partial)
536 {
538 {
539 if (regexec(&color_line->regex, buf, 0, NULL, 0) == 0)
540 {
541 lines[line_num].cid = MT_COLOR_HEADER;
542 lines[line_num].syntax[0].attr_color =
543 merged_color_overlay(lines[line_num].syntax[0].attr_color,
545 lines[line_num].syntax[0].attr_color = merged_color_overlay(
546 lines[line_num].syntax[0].attr_color, &color_line->attr_color);
547 if (lines[line_num].cont_header)
548 {
549 /* adjust the previous continuation lines to reflect the color of this continuation line */
550 int j;
551 for (j = line_num - 1; j >= 0 && lines[j].cont_header; --j)
552 {
553 lines[j].cid = lines[line_num].cid;
554 lines[j].syntax[0].attr_color = lines[line_num].syntax[0].attr_color;
555 }
556 /* now adjust the first line of this header field */
557 if (j >= 0)
558 {
559 lines[j].cid = lines[line_num].cid;
560 lines[j].syntax[0].attr_color = lines[line_num].syntax[0].attr_color;
561 }
562 *force_redraw = true; /* the previous lines have already been drawn on the screen */
563 }
564 }
565 }
566 }
567 }
568 }
569 else if (mutt_str_startswith(raw, "\033[0m")) // Escape: a little hack...
570 {
571 lines[line_num].cid = MT_COLOR_NORMAL;
572 }
573 else if (check_attachment_marker((char *) raw) == 0)
574 {
575 lines[line_num].cid = MT_COLOR_ATTACHMENT;
576 }
577 else if (mutt_str_equal("-- \n", buf) || mutt_str_equal("-- \r\n", buf))
578 {
579 i = line_num + 1;
580
581 lines[line_num].cid = MT_COLOR_SIGNATURE;
582 while ((i < lines_used) && (check_sig(buf, lines, i - 1) == 0) &&
583 ((lines[i].cid == MT_COLOR_NORMAL) || (lines[i].cid == MT_COLOR_QUOTED) ||
584 (lines[i].cid == MT_COLOR_HEADER)))
585 {
586 /* oops... */
587 if (lines[i].syntax_arr_size)
588 {
589 lines[i].syntax_arr_size = 0;
590 mutt_mem_realloc(&(lines[line_num].syntax), sizeof(struct TextSyntax));
591 }
592 lines[i++].cid = MT_COLOR_SIGNATURE;
593 }
594 }
595 else if (check_sig(buf, lines, line_num - 1) == 0)
596 {
597 lines[line_num].cid = MT_COLOR_SIGNATURE;
598 }
599 else if (mutt_is_quote_line(buf, pmatch))
600 {
601 if (q_classify && !lines[line_num].quote)
602 {
603 lines[line_num].quote = qstyle_classify(quote_list, buf + pmatch[0].rm_so,
604 pmatch[0].rm_eo - pmatch[0].rm_so,
605 force_redraw, q_level);
606 }
607 lines[line_num].cid = MT_COLOR_QUOTED;
608 }
609 else
610 {
611 lines[line_num].cid = MT_COLOR_NORMAL;
612 }
613
614 /* body patterns */
615 if ((lines[line_num].cid == MT_COLOR_NORMAL) || (lines[line_num].cid == MT_COLOR_QUOTED) ||
616 ((lines[line_num].cid == MT_COLOR_HDRDEFAULT) && c_header_color_partial))
617 {
618 match_body_patterns(buf, lines, line_num);
619 }
620
621 /* attachment patterns */
622 if (lines[line_num].cid == MT_COLOR_ATTACHMENT)
623 {
624 size_t nl;
625
626 /* don't consider line endings part of the buffer for regex matching */
627 nl = mutt_str_len(buf);
628 if ((nl > 0) && (buf[nl - 1] == '\n'))
629 buf[nl - 1] = '\0';
630
631 i = 0;
632 offset = 0;
633 lines[line_num].syntax_arr_size = 0;
635 bool found = false;
636 bool null_rx = false;
637 do
638 {
639 if (!buf[offset])
640 break;
641
642 found = false;
643 null_rx = false;
645 {
646 if (regexec(&color_line->regex, buf + offset, 1, pmatch,
647 ((offset != 0) ? REG_NOTBOL : 0)) != 0)
648 {
649 continue;
650 }
651
652 if (pmatch[0].rm_eo != pmatch[0].rm_so)
653 {
654 if (!found)
655 {
656 if (++(lines[line_num].syntax_arr_size) > 1)
657 {
658 mutt_mem_realloc(&(lines[line_num].syntax),
659 (lines[line_num].syntax_arr_size) * sizeof(struct TextSyntax));
660 // Zero the new entry
661 const int index = lines[line_num].syntax_arr_size - 1;
662 struct TextSyntax *ts = &lines[line_num].syntax[index];
663 memset(ts, 0, sizeof(*ts));
664 }
665 }
666 i = lines[line_num].syntax_arr_size - 1;
667 pmatch[0].rm_so += offset;
668 pmatch[0].rm_eo += offset;
669 if (!found || (pmatch[0].rm_so < (lines[line_num].syntax)[i].first) ||
670 ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
671 (pmatch[0].rm_eo > (lines[line_num].syntax)[i].last)))
672 {
673 if (!(lines[line_num].syntax)[i].attr_color)
674 (lines[line_num].syntax)[i].attr_color = ac_attach;
675
676 (lines[line_num].syntax)[i].attr_color = merged_color_overlay(
677 (lines[line_num].syntax)[i].attr_color, &color_line->attr_color);
678 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
679 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
680 }
681 else if ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
682 (pmatch[0].rm_eo == (lines[line_num].syntax)[i].last))
683 {
684 (lines[line_num].syntax)[i].attr_color = merged_color_overlay(
685 (lines[line_num].syntax)[i].attr_color, &color_line->attr_color);
686 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
687 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
688 }
689 found = 1;
690 null_rx = false;
691 }
692 else
693 {
694 null_rx = true; /* empty regex; don't add it, but keep looking */
695 }
696 }
697
698 if (null_rx)
699 offset++; /* avoid degenerate cases */
700 else
701 offset = (lines[line_num].syntax)[i].last;
702 } while (found || null_rx);
703 if (nl > 0)
704 buf[nl] = '\n';
705 }
706}
707
721void buf_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
722{
723 const char *s = src;
724
725 buf_reset(dest);
726
727 if (!s)
728 return;
729
730 while (s[0] != '\0')
731 {
732 if ((s[0] == '\010') && (s > src))
733 {
734 if (s[1] == '_') /* underline */
735 {
736 s += 2;
737 }
738 else if (s[1] && buf_len(dest)) /* bold or overstrike */
739 {
740 dest->dptr--;
741 buf_addch(dest, s[1]);
742 s += 2;
743 }
744 else /* ^H */
745 {
746 buf_addch(dest, *s++);
747 }
748 continue;
749 }
750
751 int len = ansi_color_seq_length(s);
752 if (len > 0)
753 {
754 s += len;
755 }
756 else if (strip_markers && (s[0] == '\033') && (s[1] == ']') &&
758 {
759 mutt_debug(LL_DEBUG2, "Seen attachment marker\n");
760 while (*s++ != '\a')
761 ; /* skip pseudo-ANSI sequence */
762 }
763 else
764 {
765 buf_addch(dest, *s++);
766 }
767 }
768}
769
782static int fill_buffer(FILE *fp, LOFF_T *bytes_read, LOFF_T offset, unsigned char **buf,
783 unsigned char **fmt, size_t *blen, int *buf_ready)
784{
785 static int b_read = 0;
786
787 if (*buf_ready == 0)
788 {
789 if (offset != *bytes_read)
790 {
791 if (!mutt_file_seek(fp, offset, SEEK_SET))
792 {
793 return -1;
794 }
795 }
796
797 *buf = (unsigned char *) mutt_file_read_line((char *) *buf, blen, fp, NULL, MUTT_RL_EOL);
798 if (!*buf)
799 {
800 fmt[0] = NULL;
801 return -1;
802 }
803
804 *bytes_read = ftello(fp);
805 b_read = (int) (*bytes_read - offset);
806 *buf_ready = 1;
807
808 struct Buffer *stripped = buf_pool_get();
809 buf_alloc(stripped, *blen);
810 buf_strip_formatting(stripped, (const char *) *buf, 1);
811 /* This should be a noop, because *fmt should be NULL */
812 FREE(fmt);
813 *fmt = (unsigned char *) buf_strdup(stripped);
814 buf_pool_release(&stripped);
815 }
816
817 return b_read;
818}
819
837static int format_line(struct MuttWindow *win, struct Line **lines, int line_num,
838 unsigned char *buf, PagerFlags flags, struct AnsiColor *ansi,
839 int cnt, int *pspace, int *pvch, int *pcol,
840 int *pspecial, int width, struct AttrColorList *ansi_list)
841{
842 int space = -1; /* index of the last space or TAB */
843 const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
844 size_t col = c_markers ? (*lines)[line_num].cont_line : 0;
845 size_t k;
846 int ch, vch, last_special = -1, special = 0, t;
847 wchar_t wc = 0;
848 mbstate_t mbstate = { 0 }; // FIXME: this should come from lines
849 const size_t c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
850 size_t wrap_cols = mutt_window_wrap_cols(width, (flags & MUTT_PAGER_NOWRAP) ? 0 : c_wrap);
851
852 if (check_attachment_marker((char *) buf) == 0)
853 wrap_cols = width;
854
855 struct PagerPrivateData *priv = win->parent->wdata;
856 enum PagerMode mode = priv->pview->mode;
857 const bool c_allow_ansi = (mode == PAGER_MODE_OTHER) ||
858 cs_subset_bool(NeoMutt->sub, "allow_ansi");
859
860 for (ch = 0, vch = 0; ch < cnt; ch += k, vch += k)
861 {
862 /* Handle ANSI sequences */
863 if (buf[ch] == '\033') // Escape
864 {
865 int len = ansi_color_parse((const char *) buf + ch, ansi, ansi_list, !c_allow_ansi);
866 ch += len;
867 }
868
869 while ((cnt - ch >= 2) && (buf[ch] == '\033') && (buf[ch + 1] == ']') && // Escape
870 ((check_attachment_marker((char *) buf + ch) == 0) ||
871 (check_protected_header_marker((char *) buf + ch) == 0)))
872 {
873 while (buf[ch++] != '\a')
874 if (ch >= cnt)
875 break;
876 }
877
878 /* is anything left to do? */
879 if (ch >= cnt)
880 break;
881
882 k = mbrtowc(&wc, (char *) buf + ch, cnt - ch, &mbstate);
883 if ((k == ICONV_BUF_TOO_SMALL) || (k == ICONV_ILLEGAL_SEQ))
884 {
885 if (k == ICONV_ILLEGAL_SEQ)
886 memset(&mbstate, 0, sizeof(mbstate));
887 mutt_debug(LL_DEBUG1, "mbrtowc returned %zu; errno = %d\n", k, errno);
888 if ((col + 4) > wrap_cols)
889 break;
890 col += 4;
891 if (ansi)
892 mutt_window_printf(win, "\\%03o", buf[ch]);
893 k = 1;
894 continue;
895 }
896 if (k == 0)
897 k = 1;
898
899 if (CharsetIsUtf8)
900 {
901 /* zero width space, zero with non-joiner, zero width no-break space */
902 if ((wc == 0x200B) || (wc == 0x200C) || (wc == 0xFEFF))
903 {
904 mutt_debug(LL_DEBUG3, "skip zero-width character U+%04X\n", (unsigned short) wc);
905 continue;
906 }
908 {
909 mutt_debug(LL_DEBUG3, "filtered U+%04X\n", (unsigned short) wc);
910 continue;
911 }
912 }
913
914 /* Handle backspace */
915 special = 0;
916 if (IsWPrint(wc))
917 {
918 wchar_t wc1 = 0;
919 mbstate_t mbstate1 = mbstate;
920 size_t k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
921 while ((k1 != ICONV_BUF_TOO_SMALL) && (k1 != ICONV_ILLEGAL_SEQ) &&
922 (k1 > 0) && (wc1 == '\b'))
923 {
924 const size_t k2 = mbrtowc(&wc1, (char *) buf + ch + k + k1,
925 cnt - ch - k - k1, &mbstate1);
926 if ((k2 == ICONV_BUF_TOO_SMALL) || (k2 == ICONV_ILLEGAL_SEQ) ||
927 (k2 == 0) || (!IsWPrint(wc1)))
928 {
929 break;
930 }
931
932 if (wc == wc1)
933 {
934 special |= ((wc == '_') && (special & A_UNDERLINE)) ? A_UNDERLINE : A_BOLD;
935 }
936 else if ((wc == '_') || (wc1 == '_'))
937 {
938 special |= A_UNDERLINE;
939 wc = (wc1 == '_') ? wc : wc1;
940 }
941 else
942 {
943 /* special = 0; / * overstrike: nothing to do! */
944 wc = wc1;
945 }
946
947 ch += k + k1;
948 k = k2;
949 mbstate = mbstate1;
950 k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
951 }
952 }
953
954 if (ansi && ((flags & (MUTT_SHOWCOLOR | MUTT_SEARCH | MUTT_PAGER_MARKER)) ||
955 special || last_special || (ansi->attrs != A_NORMAL)))
956 {
957 resolve_color(win, *lines, line_num, vch, flags, special, ansi);
958 last_special = special;
959 }
960
961 /* no-break space, narrow no-break space */
962 if (IsWPrint(wc) || (CharsetIsUtf8 && ((wc == 0x00A0) || (wc == 0x202F))))
963 {
964 if (wc == ' ')
965 {
966 space = ch;
967 }
968 t = wcwidth(wc);
969 if (col + t > wrap_cols)
970 break;
971 col += t;
972 if (ansi)
973 mutt_addwch(win, wc);
974 }
975 else if (wc == '\n')
976 {
977 break;
978 }
979 else if (wc == '\t')
980 {
981 space = ch;
982 t = (col & ~7) + 8;
983 if (t > wrap_cols)
984 break;
985 if (ansi)
986 for (; col < t; col++)
987 mutt_window_addch(win, ' ');
988 else
989 col = t;
990 }
991 else if ((wc < 0x20) || (wc == 0x7f))
992 {
993 if ((col + 2) > wrap_cols)
994 break;
995 col += 2;
996 if (ansi)
997 mutt_window_printf(win, "^%c", (char) (('@' + wc) & 0x7f));
998 }
999 else if (wc < 0x100)
1000 {
1001 if ((col + 4) > wrap_cols)
1002 break;
1003 col += 4;
1004 if (ansi)
1005 mutt_window_printf(win, "\\%03lo", (long) wc);
1006 }
1007 else
1008 {
1009 if ((col + 1) > wrap_cols)
1010 break;
1011 col += k;
1012 if (ansi)
1014 }
1015 }
1016 *pspace = space;
1017 *pcol = col;
1018 *pvch = vch;
1019 *pspecial = special;
1020 return ch;
1021}
1022
1042int display_line(FILE *fp, LOFF_T *bytes_read, struct Line **lines,
1043 int line_num, int *lines_used, int *lines_max,
1044 PagerFlags flags, struct QuoteStyle **quote_list, int *q_level,
1045 bool *force_redraw, regex_t *search_re,
1046 struct MuttWindow *win_pager, struct AttrColorList *ansi_list)
1047{
1048 unsigned char *buf = NULL, *fmt = NULL;
1049 size_t buflen = 0;
1050 unsigned char *buf_ptr = NULL;
1051 int ch, vch, col, cnt, b_read;
1052 int buf_ready = 0;
1053 bool change_last = false;
1054 int special;
1055 int offset;
1056 const struct AttrColor *def_color = NULL;
1057 int m;
1058 int rc = -1;
1059 struct AnsiColor ansi = { { COLOR_DEFAULT, 0, 0 }, { COLOR_DEFAULT, 0, 0 }, 0, NULL };
1060 regmatch_t pmatch[1] = { 0 };
1061
1062 struct PagerPrivateData *priv = win_pager->parent->wdata;
1063 enum PagerMode mode = priv->pview->mode;
1064
1065 if (line_num == *lines_used)
1066 {
1067 (*lines_used)++;
1068 change_last = true;
1069 }
1070
1071 if (*lines_used == *lines_max)
1072 {
1073 mutt_mem_realloc(lines, sizeof(struct Line) * (*lines_max += LINES));
1074 for (ch = *lines_used; ch < *lines_max; ch++)
1075 {
1076 memset(&((*lines)[ch]), 0, sizeof(struct Line));
1077 (*lines)[ch].cid = -1;
1078 (*lines)[ch].search_arr_size = -1;
1079 (*lines)[ch].syntax = mutt_mem_calloc(1, sizeof(struct TextSyntax));
1080 ((*lines)[ch].syntax)[0].first = -1;
1081 ((*lines)[ch].syntax)[0].last = -1;
1082 }
1083 }
1084
1085 struct Line *const cur_line = &(*lines)[line_num];
1086
1087 if (flags & MUTT_PAGER_LOGS)
1088 {
1089 /* determine the line class */
1090 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1091 {
1092 if (change_last)
1093 (*lines_used)--;
1094 goto out;
1095 }
1096
1097 if ((cur_line->cont_line) && (line_num > 0))
1098 {
1099 struct Line *const old_line = &(*lines)[line_num - 1];
1100 cur_line->cid = old_line->cid;
1101 cur_line->syntax[0].attr_color = old_line->syntax[0].attr_color;
1102 }
1103 else
1104 {
1105 cur_line->cid = MT_COLOR_MESSAGE_LOG;
1106 if (buf[11] == 'M')
1108 else if (buf[11] == 'W')
1110 else if (buf[11] == 'E')
1112 else
1114 }
1115 }
1116
1117 /* only do color highlighting if we are viewing a message */
1118 if (flags & (MUTT_SHOWCOLOR | MUTT_TYPES))
1119 {
1120 if (cur_line->cid == -1)
1121 {
1122 /* determine the line class */
1123 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1124 {
1125 if (change_last)
1126 (*lines_used)--;
1127 goto out;
1128 }
1129
1130 if (mode == PAGER_MODE_EMAIL)
1131 {
1132 resolve_types(win_pager, (char *) fmt, (char *) buf, *lines, line_num, *lines_used,
1133 quote_list, q_level, force_redraw, flags & MUTT_SHOWCOLOR);
1134 }
1135 else
1136 {
1137 (*lines)[line_num].cid = MT_COLOR_NORMAL;
1138 }
1139
1140 /* avoid race condition for continuation lines when scrolling up */
1141 for (m = line_num + 1;
1142 m < *lines_used && (*lines)[m].offset && (*lines)[m].cont_line; m++)
1143 {
1144 (*lines)[m].cid = cur_line->cid;
1145 }
1146 }
1147
1148 /* this also prevents searching through the hidden lines */
1149 const short c_toggle_quoted_show_levels = cs_subset_number(NeoMutt->sub, "toggle_quoted_show_levels");
1150 if ((flags & MUTT_HIDE) && (cur_line->cid == MT_COLOR_QUOTED) &&
1151 (!cur_line->quote || (cur_line->quote->quote_n >= c_toggle_quoted_show_levels)))
1152 {
1153 flags = 0; /* MUTT_NOSHOW */
1154 }
1155 }
1156
1157 /* At this point, (*lines[line_num]).quote may still be undefined. We
1158 * don't want to compute it every time MUTT_TYPES is set, since this
1159 * would slow down the "bottom" function unacceptably. A compromise
1160 * solution is hence to call regexec() again, just to find out the
1161 * length of the quote prefix. */
1162 if ((flags & MUTT_SHOWCOLOR) && !cur_line->cont_line &&
1163 (cur_line->cid == MT_COLOR_QUOTED) && !cur_line->quote)
1164 {
1165 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1166 {
1167 if (change_last)
1168 (*lines_used)--;
1169 goto out;
1170 }
1171
1172 const struct Regex *c_quote_regex = cs_subset_regex(NeoMutt->sub, "quote_regex");
1173 if (mutt_regex_capture(c_quote_regex, (char *) fmt, 1, pmatch))
1174 {
1175 cur_line->quote = qstyle_classify(quote_list, (char *) fmt + pmatch[0].rm_so,
1176 pmatch[0].rm_eo - pmatch[0].rm_so,
1177 force_redraw, q_level);
1178 }
1179 else
1180 {
1181 goto out;
1182 }
1183 }
1184
1185 if ((flags & MUTT_SEARCH) && !cur_line->cont_line && (cur_line->search_arr_size == -1))
1186 {
1187 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1188 {
1189 if (change_last)
1190 (*lines_used)--;
1191 goto out;
1192 }
1193
1194 offset = 0;
1195 cur_line->search_arr_size = 0;
1196 while (regexec(search_re, (char *) fmt + offset, 1, pmatch,
1197 (offset ? REG_NOTBOL : 0)) == 0)
1198 {
1199 if (++(cur_line->search_arr_size) > 1)
1200 {
1201 mutt_mem_realloc(&(cur_line->search),
1202 (cur_line->search_arr_size) * sizeof(struct TextSyntax));
1203 // Zero the new entry
1204 const int index = cur_line->search_arr_size - 1;
1205 struct TextSyntax *ts = &cur_line->search[index];
1206 memset(ts, 0, sizeof(*ts));
1207 }
1208 else
1209 {
1210 cur_line->search = mutt_mem_calloc(1, sizeof(struct TextSyntax));
1211 }
1212 pmatch[0].rm_so += offset;
1213 pmatch[0].rm_eo += offset;
1214 (cur_line->search)[cur_line->search_arr_size - 1].first = pmatch[0].rm_so;
1215 (cur_line->search)[cur_line->search_arr_size - 1].last = pmatch[0].rm_eo;
1216
1217 if (pmatch[0].rm_eo == pmatch[0].rm_so)
1218 offset++; /* avoid degenerate cases */
1219 else
1220 offset = pmatch[0].rm_eo;
1221 if (!fmt[offset])
1222 break;
1223 }
1224 }
1225
1226 if (!(flags & MUTT_SHOW) && ((*lines)[line_num + 1].offset > 0))
1227 {
1228 /* we've already scanned this line, so just exit */
1229 rc = 0;
1230 goto out;
1231 }
1232 if ((flags & MUTT_SHOWCOLOR) && *force_redraw && ((*lines)[line_num + 1].offset > 0))
1233 {
1234 /* no need to try to display this line... */
1235 rc = 1;
1236 goto out; /* fake display */
1237 }
1238
1239 b_read = fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready);
1240 if (b_read < 0)
1241 {
1242 if (change_last)
1243 (*lines_used)--;
1244 goto out;
1245 }
1246
1247 /* now chose a good place to break the line */
1248 cnt = format_line(win_pager, lines, line_num, buf, flags, NULL, b_read, &ch,
1249 &vch, &col, &special, win_pager->state.cols, ansi_list);
1250 buf_ptr = buf + cnt;
1251
1252 /* move the break point only if smart_wrap is set */
1253 const bool c_smart_wrap = cs_subset_bool(NeoMutt->sub, "smart_wrap");
1254 if (c_smart_wrap)
1255 {
1256 if ((cnt < b_read) && (ch != -1) &&
1257 !simple_color_is_header(cur_line->cid) && !isspace(buf[cnt]))
1258 {
1259 buf_ptr = buf + ch;
1260 /* skip trailing blanks */
1261 while (ch && ((buf[ch] == ' ') || (buf[ch] == '\t') || (buf[ch] == '\r')))
1262 ch--;
1263 /* A very long word with leading spaces causes infinite
1264 * wrapping when MUTT_PAGER_NSKIP is set. A folded header
1265 * with a single long word shouldn't be smartwrapped
1266 * either. So just disable smart_wrap if it would wrap at the
1267 * beginning of the line. */
1268 if (ch == 0)
1269 buf_ptr = buf + cnt;
1270 else
1271 cnt = ch + 1;
1272 }
1273 if (!(flags & MUTT_PAGER_NSKIP))
1274 {
1275 /* skip leading blanks on the next line too */
1276 while ((*buf_ptr == ' ') || (*buf_ptr == '\t'))
1277 buf_ptr++;
1278 }
1279 }
1280
1281 if (*buf_ptr == '\r')
1282 buf_ptr++;
1283 if (*buf_ptr == '\n')
1284 buf_ptr++;
1285
1286 if (((int) (buf_ptr - buf) < b_read) && !(*lines)[line_num + 1].cont_line)
1287 append_line(*lines, line_num, (int) (buf_ptr - buf));
1288 (*lines)[line_num + 1].offset = cur_line->offset + (long) (buf_ptr - buf);
1289
1290 /* if we don't need to display the line we are done */
1291 if (!(flags & MUTT_SHOW))
1292 {
1293 rc = 0;
1294 goto out;
1295 }
1296
1297 if (flags & MUTT_PAGER_STRIPES)
1298 {
1299 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1301 }
1302
1303 /* display the line */
1304 format_line(win_pager, lines, line_num, buf, flags, &ansi, cnt, &ch, &vch,
1305 &col, &special, win_pager->state.cols, ansi_list);
1306
1307 /* avoid a bug in ncurses... */
1308 if (col == 0)
1309 {
1310 if (flags & MUTT_PAGER_STRIPES)
1311 {
1312 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1314 }
1315 else
1316 {
1318 }
1319
1320 mutt_window_addch(win_pager, ' ');
1321 }
1322
1323 /* Fill the blank space at the end of the line with the prevailing color.
1324 * ncurses does an implicit clrtoeol() when you do mutt_window_addch('\n') so we have
1325 * to make sure to reset the color *after* that */
1326 if (flags & MUTT_SHOWCOLOR)
1327 {
1328 m = (cur_line->cont_line) ? (cur_line->syntax)[0].first : line_num;
1329 if ((*lines)[m].cid == MT_COLOR_HEADER)
1330 {
1331 def_color = ((*lines)[m].syntax)[0].attr_color;
1332 }
1333 else
1334 {
1335 def_color = simple_color_get((*lines)[m].cid);
1336 }
1337 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
1338 const struct AttrColor *ac_eol = NULL;
1339 if (def_color)
1340 ac_eol = merged_color_overlay(ac_normal, def_color);
1341 else
1342 ac_eol = ac_normal;
1343 mutt_curses_set_color(ac_eol);
1344 }
1345
1346 if (col < win_pager->state.cols)
1347 {
1348 if (flags & MUTT_PAGER_STRIPES)
1349 {
1350 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1351 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
1352 const struct AttrColor *stripe_color = simple_color_get(cid);
1353 const struct AttrColor *ac_eol = merged_color_overlay(ac_normal, stripe_color);
1354 mutt_curses_set_color(ac_eol);
1355 }
1356 mutt_window_clrtoeol(win_pager);
1357 }
1358
1359 /* reset the color back to normal. This *must* come after the
1360 * clrtoeol, otherwise the color for this line will not be
1361 * filled to the right margin. */
1362 if (flags & MUTT_SHOWCOLOR)
1364
1365 /* build a return code */
1366 if (!(flags & MUTT_SHOW))
1367 flags = 0;
1368
1369 rc = flags;
1370
1371out:
1372 FREE(&buf);
1373 FREE(&fmt);
1374 return rc;
1375}
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:194
struct AttrColor attr_color_copy(const struct AttrColor *ac)
Copy a colour.
Definition: attr.c:167
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:490
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:75
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:570
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:336
Color and attribute parsing.
struct RegexColorList * regex_colors_get_list(enum ColorId cid)
Return the RegexColorList for a colour id.
Definition: regex.c:184
bool simple_color_is_header(enum ColorId cid)
Colour is for an Email header.
Definition: simple.c:119
bool simple_color_is_set(enum ColorId cid)
Is the object coloured?
Definition: simple.c:109
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition: simple.c:88
#define COLOR_DEFAULT
Definition: color.h:100
ColorId
List of all colored objects.
Definition: color.h:40
@ MT_COLOR_MARKERS
Pager: markers, line continuation.
Definition: color.h:56
@ MT_COLOR_MESSAGE
Informational message.
Definition: color.h:57
@ MT_COLOR_QUOTED
Pager: quoted text.
Definition: color.h:63
@ MT_COLOR_HEADER
Message headers (takes a pattern)
Definition: color.h:53
@ MT_COLOR_STRIPE_EVEN
Stripes: even lines of the Help Page.
Definition: color.h:76
@ MT_COLOR_ERROR
Error message.
Definition: color.h:51
@ MT_COLOR_BOLD
Bold text.
Definition: color.h:45
@ MT_COLOR_BODY
Pager: highlight body of message (takes a pattern)
Definition: color.h:44
@ MT_COLOR_HDRDEFAULT
Header default colour.
Definition: color.h:52
@ MT_COLOR_NORMAL
Plain text.
Definition: color.h:59
@ MT_COLOR_ATTACH_HEADERS
MIME attachment test (takes a pattern)
Definition: color.h:43
@ MT_COLOR_SEARCH
Pager: search matches.
Definition: color.h:64
@ MT_COLOR_MESSAGE_LOG
Menu showing log messages.
Definition: color.h:58
@ MT_COLOR_ITALIC
Italic text.
Definition: color.h:55
@ MT_COLOR_STRIPE_ODD
Stripes: odd lines of the Help Page.
Definition: color.h:77
@ MT_COLOR_ATTACHMENT
MIME attachments text (entire line)
Definition: color.h:42
@ MT_COLOR_WARNING
Warning messages.
Definition: color.h:81
@ MT_COLOR_UNDERLINE
Underlined text.
Definition: color.h:80
@ MT_COLOR_SIGNATURE
Pager: signature lines.
Definition: color.h:74
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:218
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:144
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
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:837
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:782
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:721
static int comp_syntax_t(const void *m1, const void *m2)
Search for a Syntax using bsearch(3)
Definition: display.c:97
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:495
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:1042
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:68
int BrailleCol
Braille display: column to leave the cursor.
Definition: dlg_pager.c:70
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
Read a line from a file.
Definition: file.c:805
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:775
#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
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define FREE(x)
Definition: memory.h:45
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:106
#define ICONV_ILLEGAL_SEQ
Error value for iconv() - Illegal sequence.
Definition: charset.h:104
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:44
const char * state_protected_header_marker(void)
Get a unique (per-run) ANSI string to mark protected headers in an email.
Definition: state.c:58
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:709
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:545
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:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
struct QuoteStyle * qstyle_classify(struct QuoteStyle **quote_list, const char *qptr, size_t length, bool *force_redraw, int *q_level)
Find a style for a string.
Definition: quoted.c:293
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:51
short search_arr_size
Number of items in search array.
Definition: display.h:60
struct TextSyntax * search
Array of search text in the line.
Definition: display.h:61
bool cont_line
Continuation of a previous line (wrapped by NeoMutt)
Definition: display.h:54
short cid
Default line colour, e.g. MT_COLOR_QUOTED.
Definition: display.h:53
struct QuoteStyle * quote
Quoting style for this line (pointer into PagerPrivateData->quote_list)
Definition: display.h:63
LOFF_T offset
Offset into Email file (PagerPrivateData->fp)
Definition: display.h:52
bool cont_header
Continuation of a header line (wrapped by MTA)
Definition: display.h:55
short syntax_arr_size
Number of items in syntax array.
Definition: display.h:57
struct TextSyntax * syntax
Array of coloured text in the line.
Definition: display.h:58
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
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:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
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: quoted.h:67
struct AttrColor * attr_color
Colour and attribute of the text.
Definition: quoted.h:69
struct QuoteStyle * up
Definition: quoted.h:73
size_t prefix_len
Length of the prefix string.
Definition: quoted.h:71
int quote_n
The quoteN colour index for this level.
Definition: quoted.h:68
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:85
Highlighting for a piece of text.
Definition: display.h:40
const struct AttrColor * attr_color
Curses colour of text.
Definition: display.h:41
int last
Last character in line to be coloured (not included)
Definition: display.h:43
int first
First character in line to be coloured.
Definition: display.h:42
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:60