NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
display.c
Go to the documentation of this file.
1
29#include "config.h"
30#include <ctype.h>
31#include <errno.h>
32#include <limits.h>
33#include <stdbool.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <wchar.h>
38#include "mutt/lib.h"
39#include "config/lib.h"
40#include "core/lib.h"
41#include "gui/lib.h"
42#include "display.h"
43#include "lib.h"
44#include "color/lib.h"
45
54static int check_sig(const char *s, struct Line *info, int offset)
55{
56 const unsigned int NUM_SIG_LINES = 4; // The amount of lines a signature takes
57 unsigned int count = 0;
58
59 while ((offset > 0) && (count <= NUM_SIG_LINES))
60 {
61 if (info[offset].cid != MT_COLOR_SIGNATURE)
62 break;
63 count++;
64 offset--;
65 }
66
67 if (count == 0)
68 return -1;
69
70 if (count > NUM_SIG_LINES)
71 {
72 /* check for a blank line */
73 while (*s)
74 {
75 if (!isspace(*s))
76 return 0;
77 s++;
78 }
79
80 return -1;
81 }
82
83 return 0;
84}
85
94static int comp_syntax_t(const void *m1, const void *m2)
95{
96 const int *cnt = (const int *) m1;
97 const struct TextSyntax *stx = (const struct TextSyntax *) m2;
98
99 if (*cnt < stx->first)
100 return -1;
101 if (*cnt >= stx->last)
102 return 1;
103 return 0;
104}
105
116static void resolve_color(struct MuttWindow *win, struct Line *lines, int line_num,
117 int cnt, PagerFlags flags, int special, struct AnsiColor *ansi)
118{
119 struct AttrColor def_color = { 0 }; /* color without syntax highlight */
120 struct AttrColor color = { 0 }; /* final color */
121 static struct AttrColor last_color = { 0 }; /* last color set */
122 bool search = false;
123 int m;
124 struct TextSyntax *matching_chunk = NULL;
125
126 if (cnt == 0)
127 {
128 last_color.curses_color = NULL;
129 last_color.attrs = 0;
130 }
131
132 if (lines[line_num].cont_line)
133 {
134 const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
135 if (!cnt && c_markers)
136 {
138 mutt_window_addch(win, '+');
139 }
140 m = (lines[line_num].syntax)[0].first;
141 cnt += (lines[line_num].syntax)[0].last;
142 }
143 else
144 {
145 m = line_num;
146 }
147 if (flags & MUTT_PAGER_LOGS)
148 {
149 def_color = *(lines[line_num].syntax[0].attr_color);
150 }
151 else if (!(flags & MUTT_SHOWCOLOR))
152 {
153 if (flags & MUTT_PAGER_STRIPES)
154 {
155 def_color = *simple_color_get(((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD :
157 }
158 else
159 {
160 def_color = *simple_color_get(MT_COLOR_NORMAL);
161 }
162 }
163 else if ((lines[m].cid == MT_COLOR_HEADER) && lines[m].syntax[0].attr_color)
164 {
165 def_color = *lines[m].syntax[0].attr_color;
166 }
167 else
168 {
169 def_color = *simple_color_get(lines[m].cid);
170 }
171
172 if ((flags & MUTT_SHOWCOLOR) && (lines[m].cid == MT_COLOR_QUOTED))
173 {
174 struct QuoteStyle *qc = lines[m].quote;
175
176 if (qc)
177 {
178 def_color = attr_color_copy(qc->attr_color);
179
180 while (qc && (qc->prefix_len > cnt))
181 {
182 def_color = attr_color_copy(qc->attr_color);
183 qc = qc->up;
184 }
185 }
186 }
187
188 color = def_color;
189 if ((flags & MUTT_SHOWCOLOR) && lines[m].syntax)
190 {
191 matching_chunk = bsearch(&cnt, lines[m].syntax, lines[m].syntax_arr_size,
192 sizeof(struct TextSyntax), comp_syntax_t);
193 if (matching_chunk && (cnt >= matching_chunk->first) &&
194 (cnt < matching_chunk->last))
195 {
196 if (matching_chunk->attr_color)
197 color = *matching_chunk->attr_color;
198 }
199 }
200
201 if ((flags & MUTT_SEARCH) && lines[m].search)
202 {
203 matching_chunk = bsearch(&cnt, lines[m].search, lines[m].search_arr_size,
204 sizeof(struct TextSyntax), comp_syntax_t);
205 if (matching_chunk && (cnt >= matching_chunk->first) &&
206 (cnt < matching_chunk->last))
207 {
209 search = true;
210 }
211 }
212
213 /* handle "special" bold & underlined characters */
214 if (special & A_BOLD)
215 {
218 else
219 color.attrs |= A_BOLD;
220 }
221 else if (special & A_UNDERLINE)
222 {
225 else
226 color.attrs |= A_UNDERLINE;
227 }
228 else if (special & A_ITALIC)
229 {
232 else
233 color.attrs |= A_ITALIC;
234 }
235 else if (ansi->attr_color)
236 {
237 color = *ansi->attr_color;
238 }
239
240 if (!attr_color_match(&color, &last_color))
241 {
243 &color);
244 mutt_curses_set_color(ac_merge);
245 last_color = color;
246 }
247}
248
255static void append_line(struct Line *lines, int line_num, int cnt)
256{
257 int m;
258
259 lines[line_num + 1].cid = lines[line_num].cid;
260 (lines[line_num + 1].syntax)[0].attr_color = (lines[line_num].syntax)[0].attr_color;
261 lines[line_num + 1].cont_line = true;
262
263 /* find the real start of the line */
264 for (m = line_num; m >= 0; m--)
265 if (!lines[m].cont_line)
266 break;
267
268 (lines[line_num + 1].syntax)[0].first = m;
269 (lines[line_num + 1].syntax)[0].last = (lines[line_num].cont_line) ?
270 cnt + (lines[line_num].syntax)[0].last :
271 cnt;
272}
273
280static int check_marker(const char *q, const char *p)
281{
282 for (; (p[0] == q[0]) && (q[0] != '\0') && (p[0] != '\0') && (q[0] != '\a') &&
283 (p[0] != '\a');
284 p++, q++)
285 {
286 }
287
288 return (int) (*p - *q);
289}
290
296static int check_attachment_marker(const char *p)
297{
299}
300
306static int check_protected_header_marker(const char *p)
307{
309}
310
320bool mutt_is_quote_line(char *line, regmatch_t *pmatch)
321{
322 bool is_quote = false;
323 const struct Regex *c_smileys = cs_subset_regex(NeoMutt->sub, "smileys");
324 regmatch_t pmatch_internal[1];
325
326 if (!pmatch)
327 pmatch = pmatch_internal;
328
329 const struct Regex *c_quote_regex = cs_subset_regex(NeoMutt->sub, "quote_regex");
330 if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
331 {
332 regmatch_t smatch[1];
333 if (mutt_regex_capture(c_smileys, line, 1, smatch))
334 {
335 if (smatch[0].rm_so > 0)
336 {
337 char c = line[smatch[0].rm_so];
338 line[smatch[0].rm_so] = 0;
339
340 if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
341 is_quote = true;
342
343 line[smatch[0].rm_so] = c;
344 }
345 }
346 else
347 {
348 is_quote = true;
349 }
350 }
351
352 return is_quote;
353}
354
368static void resolve_types(struct MuttWindow *win, char *buf, char *raw,
369 struct Line *lines, int line_num, int lines_used,
370 struct QuoteStyle **quote_list, int *q_level,
371 bool *force_redraw, bool q_classify)
372{
373 struct RegexColor *color_line = NULL;
374 struct RegexColorList *head = NULL;
375 regmatch_t pmatch[1];
376 bool found;
377 bool null_rx;
378 const bool c_header_color_partial = cs_subset_bool(NeoMutt->sub, "header_color_partial");
379 int offset, i = 0;
380
381 if ((line_num == 0) || simple_color_is_header(lines[line_num - 1].cid) ||
383 {
384 if (buf[0] == '\n') /* end of header */
385 {
386 lines[line_num].cid = MT_COLOR_NORMAL;
388 }
389 else
390 {
391 /* if this is a continuation of the previous line, use the previous
392 * line's color as default. */
393 if ((line_num > 0) && ((buf[0] == ' ') || (buf[0] == '\t')))
394 {
395 lines[line_num].cid = lines[line_num - 1].cid; /* wrapped line */
396 if (!c_header_color_partial)
397 {
398 (lines[line_num].syntax)[0].attr_color =
399 (lines[line_num - 1].syntax)[0].attr_color;
400 lines[line_num].cont_header = true;
401 }
402 }
403 else
404 {
405 lines[line_num].cid = MT_COLOR_HDRDEFAULT;
406 }
407
408 /* When this option is unset, we color the entire header the
409 * same color. Otherwise, we handle the header patterns just
410 * like body patterns (further below). */
411 if (!c_header_color_partial)
412 {
414 {
415 if (regexec(&color_line->regex, buf, 0, NULL, 0) == 0)
416 {
417 lines[line_num].cid = MT_COLOR_HEADER;
418 lines[line_num].syntax[0].attr_color =
419 merged_color_overlay(lines[line_num].syntax[0].attr_color,
421 lines[line_num].syntax[0].attr_color = merged_color_overlay(
422 lines[line_num].syntax[0].attr_color, &color_line->attr_color);
423 if (lines[line_num].cont_header)
424 {
425 /* adjust the previous continuation lines to reflect the color of this continuation line */
426 int j;
427 for (j = line_num - 1; j >= 0 && lines[j].cont_header; --j)
428 {
429 lines[j].cid = lines[line_num].cid;
430 lines[j].syntax[0].attr_color = lines[line_num].syntax[0].attr_color;
431 }
432 /* now adjust the first line of this header field */
433 if (j >= 0)
434 {
435 lines[j].cid = lines[line_num].cid;
436 lines[j].syntax[0].attr_color = lines[line_num].syntax[0].attr_color;
437 }
438 *force_redraw = true; /* the previous lines have already been drawn on the screen */
439 }
440 }
441 }
442 }
443 }
444 }
445 else if (mutt_str_startswith(raw, "\033[0m")) // Escape: a little hack...
446 {
447 lines[line_num].cid = MT_COLOR_NORMAL;
448 }
449 else if (check_attachment_marker((char *) raw) == 0)
450 {
451 lines[line_num].cid = MT_COLOR_ATTACHMENT;
452 }
453 else if (mutt_str_equal("-- \n", buf) || mutt_str_equal("-- \r\n", buf))
454 {
455 i = line_num + 1;
456
457 lines[line_num].cid = MT_COLOR_SIGNATURE;
458 while ((i < lines_used) && (check_sig(buf, lines, i - 1) == 0) &&
459 ((lines[i].cid == MT_COLOR_NORMAL) || (lines[i].cid == MT_COLOR_QUOTED) ||
460 (lines[i].cid == MT_COLOR_HEADER)))
461 {
462 /* oops... */
463 if (lines[i].syntax_arr_size)
464 {
465 lines[i].syntax_arr_size = 0;
466 mutt_mem_realloc(&(lines[line_num].syntax), sizeof(struct TextSyntax));
467 }
468 lines[i++].cid = MT_COLOR_SIGNATURE;
469 }
470 }
471 else if (check_sig(buf, lines, line_num - 1) == 0)
472 {
473 lines[line_num].cid = MT_COLOR_SIGNATURE;
474 }
475 else if (mutt_is_quote_line(buf, pmatch))
476 {
477 if (q_classify && !lines[line_num].quote)
478 {
479 lines[line_num].quote = qstyle_classify(quote_list, buf + pmatch[0].rm_so,
480 pmatch[0].rm_eo - pmatch[0].rm_so,
481 force_redraw, q_level);
482 }
483 lines[line_num].cid = MT_COLOR_QUOTED;
484 }
485 else
486 {
487 lines[line_num].cid = MT_COLOR_NORMAL;
488 }
489
490 /* body patterns */
491 if ((lines[line_num].cid == MT_COLOR_NORMAL) || (lines[line_num].cid == MT_COLOR_QUOTED) ||
492 ((lines[line_num].cid == MT_COLOR_HDRDEFAULT) && c_header_color_partial))
493 {
494 size_t nl;
495
496 /* don't consider line endings part of the buffer
497 * for regex matching */
498 nl = mutt_str_len(buf);
499 if ((nl > 0) && (buf[nl - 1] == '\n'))
500 buf[nl - 1] = '\0';
501
502 i = 0;
503 offset = 0;
504 lines[line_num].syntax_arr_size = 0;
505 if (lines[line_num].cid == MT_COLOR_HDRDEFAULT)
506 {
508 }
509 else
510 {
512 }
513 STAILQ_FOREACH(color_line, head, entries)
514 {
515 color_line->stop_matching = false;
516 }
517
518 do
519 {
520 if (!buf[offset])
521 break;
522
523 found = false;
524 null_rx = false;
525 STAILQ_FOREACH(color_line, head, entries)
526 {
527 if (color_line->stop_matching)
528 continue;
529
530 if ((regexec(&color_line->regex, buf + offset, 1, pmatch,
531 ((offset != 0) ? REG_NOTBOL : 0)) != 0))
532 {
533 /* Once a regex fails to match, don't try matching it again.
534 * On very long lines this can cause a performance issue if there
535 * are other regexes that have many matches. */
536 color_line->stop_matching = true;
537 continue;
538 }
539
540 if (pmatch[0].rm_eo == pmatch[0].rm_so)
541 {
542 null_rx = true; /* empty regex; don't add it, but keep looking */
543 continue;
544 }
545
546 if (!found)
547 {
548 // Abort if we fill up chunks. Yes, this really happened.
549 if (lines[line_num].syntax_arr_size == SHRT_MAX)
550 {
551 null_rx = false;
552 break;
553 }
554 if (++(lines[line_num].syntax_arr_size) > 1)
555 {
556 mutt_mem_realloc(&(lines[line_num].syntax),
557 (lines[line_num].syntax_arr_size) * sizeof(struct TextSyntax));
558 // Zero the new entry
559 const int index = lines[line_num].syntax_arr_size - 1;
560 struct TextSyntax *ts = &lines[line_num].syntax[index];
561 memset(ts, 0, sizeof(*ts));
562 }
563 }
564 i = lines[line_num].syntax_arr_size - 1;
565 pmatch[0].rm_so += offset;
566 pmatch[0].rm_eo += offset;
567
568 if (!found || (pmatch[0].rm_so < (lines[line_num].syntax)[i].first) ||
569 ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
570 (pmatch[0].rm_eo > (lines[line_num].syntax)[i].last)))
571 {
572 (lines[line_num].syntax)[i].attr_color = &color_line->attr_color;
573 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
574 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
575 }
576 else if ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
577 (pmatch[0].rm_eo == (lines[line_num].syntax)[i].last))
578 {
579 (lines[line_num].syntax)[i].attr_color = merged_color_overlay(
580 (lines[line_num].syntax)[i].attr_color, &color_line->attr_color);
581 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
582 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
583 }
584
585 found = true;
586 null_rx = false;
587 }
588
589 if (null_rx)
590 offset++; /* avoid degenerate cases */
591 else
592 offset = (lines[line_num].syntax)[i].last;
593 } while (found || null_rx);
594 if (nl > 0)
595 buf[nl] = '\n';
596 }
597
598 /* attachment patterns */
599 if (lines[line_num].cid == MT_COLOR_ATTACHMENT)
600 {
601 size_t nl;
602
603 /* don't consider line endings part of the buffer for regex matching */
604 nl = mutt_str_len(buf);
605 if ((nl > 0) && (buf[nl - 1] == '\n'))
606 buf[nl - 1] = '\0';
607
608 i = 0;
609 offset = 0;
610 lines[line_num].syntax_arr_size = 0;
612 do
613 {
614 if (!buf[offset])
615 break;
616
617 found = false;
618 null_rx = false;
620 {
621 if (regexec(&color_line->regex, buf + offset, 1, pmatch,
622 ((offset != 0) ? REG_NOTBOL : 0)) != 0)
623 {
624 continue;
625 }
626
627 if (pmatch[0].rm_eo != pmatch[0].rm_so)
628 {
629 if (!found)
630 {
631 if (++(lines[line_num].syntax_arr_size) > 1)
632 {
633 mutt_mem_realloc(&(lines[line_num].syntax),
634 (lines[line_num].syntax_arr_size) * sizeof(struct TextSyntax));
635 // Zero the new entry
636 const int index = lines[line_num].syntax_arr_size - 1;
637 struct TextSyntax *ts = &lines[line_num].syntax[index];
638 memset(ts, 0, sizeof(*ts));
639 }
640 }
641 i = lines[line_num].syntax_arr_size - 1;
642 pmatch[0].rm_so += offset;
643 pmatch[0].rm_eo += offset;
644 if (!found || (pmatch[0].rm_so < (lines[line_num].syntax)[i].first) ||
645 ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
646 (pmatch[0].rm_eo > (lines[line_num].syntax)[i].last)))
647 {
648 if (!(lines[line_num].syntax)[i].attr_color)
649 (lines[line_num].syntax)[i].attr_color = ac_attach;
650
651 (lines[line_num].syntax)[i].attr_color = merged_color_overlay(
652 (lines[line_num].syntax)[i].attr_color, &color_line->attr_color);
653 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
654 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
655 }
656 else if ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
657 (pmatch[0].rm_eo == (lines[line_num].syntax)[i].last))
658 {
659 (lines[line_num].syntax)[i].attr_color = merged_color_overlay(
660 (lines[line_num].syntax)[i].attr_color, &color_line->attr_color);
661 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
662 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
663 }
664 found = 1;
665 null_rx = false;
666 }
667 else
668 {
669 null_rx = true; /* empty regex; don't add it, but keep looking */
670 }
671 }
672
673 if (null_rx)
674 offset++; /* avoid degenerate cases */
675 else
676 offset = (lines[line_num].syntax)[i].last;
677 } while (found || null_rx);
678 if (nl > 0)
679 buf[nl] = '\n';
680 }
681}
682
696void buf_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
697{
698 const char *s = src;
699
700 buf_reset(dest);
701
702 if (!s)
703 return;
704
705 while (s[0] != '\0')
706 {
707 if ((s[0] == '\010') && (s > src))
708 {
709 if (s[1] == '_') /* underline */
710 {
711 s += 2;
712 }
713 else if (s[1] && buf_len(dest)) /* bold or overstrike */
714 {
715 dest->dptr--;
716 buf_addch(dest, s[1]);
717 s += 2;
718 }
719 else /* ^H */
720 {
721 buf_addch(dest, *s++);
722 }
723 continue;
724 }
725
726 int len = ansi_color_seq_length(s);
727 if (len > 0)
728 {
729 s += len;
730 }
731 else if (strip_markers && (s[0] == '\033') && (s[1] == ']') &&
733 {
734 mutt_debug(LL_DEBUG2, "Seen attachment marker\n");
735 while (*s++ != '\a')
736 ; /* skip pseudo-ANSI sequence */
737 }
738 else
739 {
740 buf_addch(dest, *s++);
741 }
742 }
743}
744
757static int fill_buffer(FILE *fp, LOFF_T *bytes_read, LOFF_T offset, unsigned char **buf,
758 unsigned char **fmt, size_t *blen, int *buf_ready)
759{
760 static int b_read = 0;
761 struct Buffer stripped;
762
763 if (*buf_ready == 0)
764 {
765 if (offset != *bytes_read)
766 {
767 if (!mutt_file_seek(fp, offset, SEEK_SET))
768 {
769 return -1;
770 }
771 }
772
773 *buf = (unsigned char *) mutt_file_read_line((char *) *buf, blen, fp, NULL, MUTT_RL_EOL);
774 if (!*buf)
775 {
776 fmt[0] = NULL;
777 return -1;
778 }
779
780 *bytes_read = ftello(fp);
781 b_read = (int) (*bytes_read - offset);
782 *buf_ready = 1;
783
784 buf_init(&stripped);
785 buf_alloc(&stripped, *blen);
786 buf_strip_formatting(&stripped, (const char *) *buf, 1);
787 /* This should be a noop, because *fmt should be NULL */
788 FREE(fmt);
789 *fmt = (unsigned char *) stripped.data;
790 }
791
792 return b_read;
793}
794
812static int format_line(struct MuttWindow *win, struct Line **lines, int line_num,
813 unsigned char *buf, PagerFlags flags, struct AnsiColor *ansi,
814 int cnt, int *pspace, int *pvch, int *pcol,
815 int *pspecial, int width, struct AttrColorList *ansi_list)
816{
817 int space = -1; /* index of the last space or TAB */
818 const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
819 size_t col = c_markers ? (*lines)[line_num].cont_line : 0;
820 size_t k;
821 int ch, vch, last_special = -1, special = 0, t;
822 wchar_t wc = 0;
823 mbstate_t mbstate = { 0 }; // FIXME: this should come from lines
824 const size_t c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
825 size_t wrap_cols = mutt_window_wrap_cols(width, (flags & MUTT_PAGER_NOWRAP) ? 0 : c_wrap);
826
827 if (check_attachment_marker((char *) buf) == 0)
828 wrap_cols = width;
829
830 const bool c_allow_ansi = cs_subset_bool(NeoMutt->sub, "allow_ansi");
831 for (ch = 0, vch = 0; ch < cnt; ch += k, vch += k)
832 {
833 /* Handle ANSI sequences */
834 if (buf[ch] == '\033') // Escape
835 {
836 int len = ansi_color_parse((const char *) buf + ch, ansi, ansi_list, !c_allow_ansi);
837 ch += len;
838 }
839
840 while ((cnt - ch >= 2) && (buf[ch] == '\033') && (buf[ch + 1] == ']') && // Escape
841 ((check_attachment_marker((char *) buf + ch) == 0) ||
842 (check_protected_header_marker((char *) buf + ch) == 0)))
843 {
844 while (buf[ch++] != '\a')
845 if (ch >= cnt)
846 break;
847 }
848
849 /* is anything left to do? */
850 if (ch >= cnt)
851 break;
852
853 k = mbrtowc(&wc, (char *) buf + ch, cnt - ch, &mbstate);
854 if ((k == ICONV_BUF_TOO_SMALL) || (k == ICONV_ILLEGAL_SEQ))
855 {
856 if (k == ICONV_ILLEGAL_SEQ)
857 memset(&mbstate, 0, sizeof(mbstate));
858 mutt_debug(LL_DEBUG1, "mbrtowc returned %lu; errno = %d\n", k, errno);
859 if ((col + 4) > wrap_cols)
860 break;
861 col += 4;
862 if (ansi)
863 mutt_window_printf(win, "\\%03o", buf[ch]);
864 k = 1;
865 continue;
866 }
867 if (k == 0)
868 k = 1;
869
870 if (CharsetIsUtf8)
871 {
872 /* zero width space, zero with non-joiner, zero width no-break space */
873 if ((wc == 0x200B) || (wc == 0x200C) || (wc == 0xFEFF))
874 {
875 mutt_debug(LL_DEBUG3, "skip zero-width character U+%04X\n", (unsigned short) wc);
876 continue;
877 }
879 {
880 mutt_debug(LL_DEBUG3, "filtered U+%04X\n", (unsigned short) wc);
881 continue;
882 }
883 }
884
885 /* Handle backspace */
886 special = 0;
887 if (IsWPrint(wc))
888 {
889 wchar_t wc1 = 0;
890 mbstate_t mbstate1 = mbstate;
891 size_t k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
892 while ((k1 != ICONV_BUF_TOO_SMALL) && (k1 != ICONV_ILLEGAL_SEQ) &&
893 (k1 > 0) && (wc1 == '\b'))
894 {
895 const size_t k2 = mbrtowc(&wc1, (char *) buf + ch + k + k1,
896 cnt - ch - k - k1, &mbstate1);
897 if ((k2 == ICONV_BUF_TOO_SMALL) || (k2 == ICONV_ILLEGAL_SEQ) ||
898 (k2 == 0) || (!IsWPrint(wc1)))
899 {
900 break;
901 }
902
903 if (wc == wc1)
904 {
905 special |= ((wc == '_') && (special & A_UNDERLINE)) ? A_UNDERLINE : A_BOLD;
906 }
907 else if ((wc == '_') || (wc1 == '_'))
908 {
909 special |= A_UNDERLINE;
910 wc = (wc1 == '_') ? wc : wc1;
911 }
912 else
913 {
914 /* special = 0; / * overstrike: nothing to do! */
915 wc = wc1;
916 }
917
918 ch += k + k1;
919 k = k2;
920 mbstate = mbstate1;
921 k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
922 }
923 }
924
925 if (ansi && ((flags & (MUTT_SHOWCOLOR | MUTT_SEARCH | MUTT_PAGER_MARKER)) ||
926 special || last_special || ansi->attrs))
927 {
928 resolve_color(win, *lines, line_num, vch, flags, special, ansi);
929 last_special = special;
930 }
931
932 /* no-break space, narrow no-break space */
933 if (IsWPrint(wc) || (CharsetIsUtf8 && ((wc == 0x00A0) || (wc == 0x202F))))
934 {
935 if (wc == ' ')
936 {
937 space = ch;
938 }
939 t = wcwidth(wc);
940 if (col + t > wrap_cols)
941 break;
942 col += t;
943 if (ansi)
944 mutt_addwch(win, wc);
945 }
946 else if (wc == '\n')
947 {
948 break;
949 }
950 else if (wc == '\t')
951 {
952 space = ch;
953 t = (col & ~7) + 8;
954 if (t > wrap_cols)
955 break;
956 if (ansi)
957 for (; col < t; col++)
958 mutt_window_addch(win, ' ');
959 else
960 col = t;
961 }
962 else if ((wc < 0x20) || (wc == 0x7f))
963 {
964 if ((col + 2) > wrap_cols)
965 break;
966 col += 2;
967 if (ansi)
968 mutt_window_printf(win, "^%c", ('@' + wc) & 0x7f);
969 }
970 else if (wc < 0x100)
971 {
972 if ((col + 4) > wrap_cols)
973 break;
974 col += 4;
975 if (ansi)
976 mutt_window_printf(win, "\\%03o", wc);
977 }
978 else
979 {
980 if ((col + 1) > wrap_cols)
981 break;
982 col += k;
983 if (ansi)
985 }
986 }
987 *pspace = space;
988 *pcol = col;
989 *pvch = vch;
990 *pspecial = special;
991 return ch;
992}
993
1013int display_line(FILE *fp, LOFF_T *bytes_read, struct Line **lines,
1014 int line_num, int *lines_used, int *lines_max,
1015 PagerFlags flags, struct QuoteStyle **quote_list, int *q_level,
1016 bool *force_redraw, regex_t *search_re,
1017 struct MuttWindow *win_pager, struct AttrColorList *ansi_list)
1018{
1019 unsigned char *buf = NULL, *fmt = NULL;
1020 size_t buflen = 0;
1021 unsigned char *buf_ptr = NULL;
1022 int ch, vch, col, cnt, b_read;
1023 int buf_ready = 0;
1024 bool change_last = false;
1025 int special;
1026 int offset;
1027 const struct AttrColor *def_color = NULL;
1028 int m;
1029 int rc = -1;
1030 struct AnsiColor ansi = { NULL, 0, COLOR_DEFAULT, COLOR_DEFAULT };
1031 regmatch_t pmatch[1];
1032
1033 if (line_num == *lines_used)
1034 {
1035 (*lines_used)++;
1036 change_last = true;
1037 }
1038
1039 if (*lines_used == *lines_max)
1040 {
1041 mutt_mem_realloc(lines, sizeof(struct Line) * (*lines_max += LINES));
1042 for (ch = *lines_used; ch < *lines_max; ch++)
1043 {
1044 memset(&((*lines)[ch]), 0, sizeof(struct Line));
1045 (*lines)[ch].cid = -1;
1046 (*lines)[ch].search_arr_size = -1;
1047 (*lines)[ch].syntax = mutt_mem_calloc(1, sizeof(struct TextSyntax));
1048 ((*lines)[ch].syntax)[0].first = -1;
1049 ((*lines)[ch].syntax)[0].last = -1;
1050 }
1051 }
1052
1053 struct Line *const cur_line = &(*lines)[line_num];
1054
1055 if (flags & MUTT_PAGER_LOGS)
1056 {
1057 /* determine the line class */
1058 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1059 {
1060 if (change_last)
1061 (*lines_used)--;
1062 goto out;
1063 }
1064
1065 cur_line->cid = MT_COLOR_MESSAGE_LOG;
1066 if (buf[11] == 'M')
1068 else if (buf[11] == 'W')
1070 else if (buf[11] == 'E')
1072 else
1074 }
1075
1076 /* only do color highlighting if we are viewing a message */
1077 if (flags & (MUTT_SHOWCOLOR | MUTT_TYPES))
1078 {
1079 if (cur_line->cid == -1)
1080 {
1081 /* determine the line class */
1082 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1083 {
1084 if (change_last)
1085 (*lines_used)--;
1086 goto out;
1087 }
1088
1089 resolve_types(win_pager, (char *) fmt, (char *) buf, *lines, line_num, *lines_used,
1090 quote_list, q_level, force_redraw, flags & MUTT_SHOWCOLOR);
1091
1092 /* avoid race condition for continuation lines when scrolling up */
1093 for (m = line_num + 1;
1094 m < *lines_used && (*lines)[m].offset && (*lines)[m].cont_line; m++)
1095 {
1096 (*lines)[m].cid = cur_line->cid;
1097 }
1098 }
1099
1100 /* this also prevents searching through the hidden lines */
1101 const short c_toggle_quoted_show_levels = cs_subset_number(NeoMutt->sub, "toggle_quoted_show_levels");
1102 if ((flags & MUTT_HIDE) && (cur_line->cid == MT_COLOR_QUOTED) &&
1103 (!cur_line->quote || (cur_line->quote->quote_n >= c_toggle_quoted_show_levels)))
1104 {
1105 flags = 0; /* MUTT_NOSHOW */
1106 }
1107 }
1108
1109 /* At this point, (*lines[line_num]).quote may still be undefined. We
1110 * don't want to compute it every time MUTT_TYPES is set, since this
1111 * would slow down the "bottom" function unacceptably. A compromise
1112 * solution is hence to call regexec() again, just to find out the
1113 * length of the quote prefix. */
1114 if ((flags & MUTT_SHOWCOLOR) && !cur_line->cont_line &&
1115 (cur_line->cid == MT_COLOR_QUOTED) && !cur_line->quote)
1116 {
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 const struct Regex *c_quote_regex = cs_subset_regex(NeoMutt->sub, "quote_regex");
1125 if (mutt_regex_capture(c_quote_regex, (char *) fmt, 1, pmatch))
1126 {
1127 cur_line->quote = qstyle_classify(quote_list, (char *) fmt + pmatch[0].rm_so,
1128 pmatch[0].rm_eo - pmatch[0].rm_so,
1129 force_redraw, q_level);
1130 }
1131 else
1132 {
1133 goto out;
1134 }
1135 }
1136
1137 if ((flags & MUTT_SEARCH) && !cur_line->cont_line && (cur_line->search_arr_size == -1))
1138 {
1139 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1140 {
1141 if (change_last)
1142 (*lines_used)--;
1143 goto out;
1144 }
1145
1146 offset = 0;
1147 cur_line->search_arr_size = 0;
1148 while (regexec(search_re, (char *) fmt + offset, 1, pmatch,
1149 (offset ? REG_NOTBOL : 0)) == 0)
1150 {
1151 if (++(cur_line->search_arr_size) > 1)
1152 {
1153 mutt_mem_realloc(&(cur_line->search),
1154 (cur_line->search_arr_size) * sizeof(struct TextSyntax));
1155 // Zero the new entry
1156 const int index = cur_line->search_arr_size - 1;
1157 struct TextSyntax *ts = &cur_line->search[index];
1158 memset(ts, 0, sizeof(*ts));
1159 }
1160 else
1161 {
1162 cur_line->search = mutt_mem_calloc(1, sizeof(struct TextSyntax));
1163 }
1164 pmatch[0].rm_so += offset;
1165 pmatch[0].rm_eo += offset;
1166 (cur_line->search)[cur_line->search_arr_size - 1].first = pmatch[0].rm_so;
1167 (cur_line->search)[cur_line->search_arr_size - 1].last = pmatch[0].rm_eo;
1168
1169 if (pmatch[0].rm_eo == pmatch[0].rm_so)
1170 offset++; /* avoid degenerate cases */
1171 else
1172 offset = pmatch[0].rm_eo;
1173 if (!fmt[offset])
1174 break;
1175 }
1176 }
1177
1178 if (!(flags & MUTT_SHOW) && ((*lines)[line_num + 1].offset > 0))
1179 {
1180 /* we've already scanned this line, so just exit */
1181 rc = 0;
1182 goto out;
1183 }
1184 if ((flags & MUTT_SHOWCOLOR) && *force_redraw && ((*lines)[line_num + 1].offset > 0))
1185 {
1186 /* no need to try to display this line... */
1187 rc = 1;
1188 goto out; /* fake display */
1189 }
1190
1191 b_read = fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready);
1192 if (b_read < 0)
1193 {
1194 if (change_last)
1195 (*lines_used)--;
1196 goto out;
1197 }
1198
1199 /* now chose a good place to break the line */
1200 cnt = format_line(win_pager, lines, line_num, buf, flags, NULL, b_read, &ch,
1201 &vch, &col, &special, win_pager->state.cols, ansi_list);
1202 buf_ptr = buf + cnt;
1203
1204 /* move the break point only if smart_wrap is set */
1205 const bool c_smart_wrap = cs_subset_bool(NeoMutt->sub, "smart_wrap");
1206 if (c_smart_wrap)
1207 {
1208 if ((cnt < b_read) && (ch != -1) &&
1209 !simple_color_is_header(cur_line->cid) && !isspace(buf[cnt]))
1210 {
1211 buf_ptr = buf + ch;
1212 /* skip trailing blanks */
1213 while (ch && ((buf[ch] == ' ') || (buf[ch] == '\t') || (buf[ch] == '\r')))
1214 ch--;
1215 /* A very long word with leading spaces causes infinite
1216 * wrapping when MUTT_PAGER_NSKIP is set. A folded header
1217 * with a single long word shouldn't be smartwrapped
1218 * either. So just disable smart_wrap if it would wrap at the
1219 * beginning of the line. */
1220 if (ch == 0)
1221 buf_ptr = buf + cnt;
1222 else
1223 cnt = ch + 1;
1224 }
1225 if (!(flags & MUTT_PAGER_NSKIP))
1226 {
1227 /* skip leading blanks on the next line too */
1228 while ((*buf_ptr == ' ') || (*buf_ptr == '\t'))
1229 buf_ptr++;
1230 }
1231 }
1232
1233 if (*buf_ptr == '\r')
1234 buf_ptr++;
1235 if (*buf_ptr == '\n')
1236 buf_ptr++;
1237
1238 if (((int) (buf_ptr - buf) < b_read) && !(*lines)[line_num + 1].cont_line)
1239 append_line(*lines, line_num, (int) (buf_ptr - buf));
1240 (*lines)[line_num + 1].offset = cur_line->offset + (long) (buf_ptr - buf);
1241
1242 /* if we don't need to display the line we are done */
1243 if (!(flags & MUTT_SHOW))
1244 {
1245 rc = 0;
1246 goto out;
1247 }
1248
1249 if (flags & MUTT_PAGER_STRIPES)
1250 {
1251 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1253 }
1254
1255 /* display the line */
1256 format_line(win_pager, lines, line_num, buf, flags, &ansi, cnt, &ch, &vch,
1257 &col, &special, win_pager->state.cols, ansi_list);
1258
1259 /* avoid a bug in ncurses... */
1260 if (col == 0)
1261 {
1262 if (flags & MUTT_PAGER_STRIPES)
1263 {
1264 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1266 }
1267 else
1268 {
1270 }
1271
1272 mutt_window_addch(win_pager, ' ');
1273 }
1274
1275 /* Fill the blank space at the end of the line with the prevailing color.
1276 * ncurses does an implicit clrtoeol() when you do mutt_window_addch('\n') so we have
1277 * to make sure to reset the color *after* that */
1278 if (flags & MUTT_SHOWCOLOR)
1279 {
1280 m = (cur_line->cont_line) ? (cur_line->syntax)[0].first : line_num;
1281 if ((*lines)[m].cid == MT_COLOR_HEADER)
1282 {
1283 def_color = ((*lines)[m].syntax)[0].attr_color;
1284 }
1285 else
1286 {
1287 def_color = simple_color_get((*lines)[m].cid);
1288 }
1289 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
1290 const struct AttrColor *ac_eol = NULL;
1291 if (def_color)
1292 ac_eol = merged_color_overlay(ac_normal, def_color);
1293 else
1294 ac_eol = ac_normal;
1295 mutt_curses_set_color(ac_eol);
1296 }
1297
1298 if (col < win_pager->state.cols)
1299 {
1300 if (flags & MUTT_PAGER_STRIPES)
1301 {
1302 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1304 }
1305 else
1306 {
1308 }
1309 mutt_window_clrtoeol(win_pager);
1310 }
1311
1312 /* reset the color back to normal. This *must* come after the
1313 * clrtoeol, otherwise the color for this line will not be
1314 * filled to the right margin. */
1315 if (flags & MUTT_SHOWCOLOR)
1317
1318 /* build a return code */
1319 if (!(flags & MUTT_SHOW))
1320 flags = 0;
1321
1322 rc = flags;
1323
1324out:
1325 FREE(&buf);
1326 FREE(&fmt);
1327 return rc;
1328}
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:319
int ansi_color_seq_length(const char *str)
Is this an ANSI escape sequence?
Definition: ansi.c:82
bool attr_color_match(struct AttrColor *ac1, struct AttrColor *ac2)
Do the colours match?
Definition: attr.c:174
struct AttrColor attr_color_copy(const struct AttrColor *ac)
Copy a colour.
Definition: attr.c:146
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:466
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
struct Buffer * buf_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:55
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:253
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:349
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:112
bool simple_color_is_set(enum ColorId cid)
Is the object coloured?
Definition: simple.c:102
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition: simple.c:81
#define COLOR_DEFAULT
Definition: color.h:104
ColorId
List of all colored objects.
Definition: color.h:38
@ MT_COLOR_MARKERS
Pager: markers, line continuation.
Definition: color.h:54
@ MT_COLOR_MESSAGE
Informational message.
Definition: color.h:55
@ MT_COLOR_QUOTED
Pager: quoted text.
Definition: color.h:61
@ MT_COLOR_HEADER
Message headers (takes a pattern)
Definition: color.h:51
@ MT_COLOR_STRIPE_EVEN
Stripes: even lines of the Help Page.
Definition: color.h:76
@ MT_COLOR_ERROR
Error message.
Definition: color.h:49
@ MT_COLOR_BOLD
Bold text.
Definition: color.h:43
@ MT_COLOR_BODY
Pager: highlight body of message (takes a pattern)
Definition: color.h:42
@ MT_COLOR_HDRDEFAULT
Header default colour.
Definition: color.h:50
@ MT_COLOR_NORMAL
Plain text.
Definition: color.h:57
@ MT_COLOR_ATTACH_HEADERS
MIME attachment test (takes a pattern)
Definition: color.h:41
@ MT_COLOR_SEARCH
Pager: search matches.
Definition: color.h:62
@ MT_COLOR_MESSAGE_LOG
Menu showing log messages.
Definition: color.h:56
@ MT_COLOR_ITALIC
Italic text.
Definition: color.h:53
@ 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:40
@ 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:333
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:812
static int check_protected_header_marker(const char *p)
Check that the unique marker is present.
Definition: display.c:306
bool mutt_is_quote_line(char *line, regmatch_t *pmatch)
Is a line of message text a quote?
Definition: display.c:320
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:757
static int check_sig(const char *s, struct Line *info, int offset)
Check for an email signature.
Definition: display.c:54
void buf_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
Removes ANSI and backspace formatting.
Definition: display.c:696
static int comp_syntax_t(const void *m1, const void *m2)
Search for a Syntax using bsearch(3)
Definition: display.c:94
static int check_attachment_marker(const char *p)
Check that the unique marker is present.
Definition: display.c:296
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:116
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:368
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:1013
static void append_line(struct Line *lines, int line_num, int cnt)
Add a new Line to the array.
Definition: display.c:255
static int check_marker(const char *q, const char *p)
Check that the unique marker is present.
Definition: display.c:280
Pager Display.
int BrailleRow
Braille display: row to leave the cursor.
Definition: dlg_pager.c:69
int BrailleCol
Braille display: column to leave the cursor.
Definition: dlg_pager.c:71
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:763
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:733
#define MUTT_RL_EOL
don't strip \n / \r\n
Definition: file.h:41
#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:57
const struct AttrColor * merged_color_overlay(const struct AttrColor *base, const struct AttrColor *over)
Combine two colours.
Definition: merged.c:109
bool CharsetIsUtf8
Is the user's current character set utf-8?
Definition: charset.c:63
wchar_t ReplacementChar
When a Unicode character can't be displayed, use this instead.
Definition: charset.c:58
#define ICONV_BUF_TOO_SMALL
Error value for iconv() - Buffer too small.
Definition: charset.h:105
#define ICONV_ILLEGAL_SEQ
Error value for iconv() - Illegal sequence.
Definition: charset.h:103
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:619
const char * state_attachment_marker(void)
Get a unique (per-run) ANSI string to mark PGP messages in an email.
Definition: state.c:43
const char * state_protected_header_marker(void)
Get a unique (per-run) ANSI string to mark protected headers in an email.
Definition: state.c:57
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:228
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
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:81
void mutt_curses_set_color(const struct AttrColor *ac)
Set the colour and attributes for text.
Definition: mutt_curses.c:40
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:68
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition: lib.h:72
#define MUTT_HIDE
Don't show quoted text.
Definition: lib.h:62
#define MUTT_TYPES
Compute line's type.
Definition: lib.h:64
uint16_t PagerFlags
Flags for dlg_pager(), e.g. MUTT_SHOWFLAT.
Definition: lib.h:58
#define MUTT_PAGER_STRIPES
Striped highlighting.
Definition: lib.h:75
#define MUTT_SHOWCOLOR
Show characters in color otherwise don't show characters.
Definition: lib.h:61
#define MUTT_PAGER_MARKER
Use markers if option is set.
Definition: lib.h:69
#define MUTT_PAGER_LOGS
Logview mode.
Definition: lib.h:73
#define MUTT_SEARCH
Resolve search patterns.
Definition: lib.h:63
#define MUTT_SHOW
Definition: lib.h:65
#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:291
Key value store.
An ANSI escape sequence.
Definition: ansi.h:34
int attrs
Attributes, e.g. A_BOLD.
Definition: ansi.h:36
const struct AttrColor * attr_color
Curses colour of text.
Definition: ansi.h:35
A curses colour and its attributes.
Definition: attr.h:35
int attrs
Text attributes, e.g. A_BOLD.
Definition: attr.h:37
struct CursesColor * curses_color
Underlying Curses colour.
Definition: attr.h:36
String manipulation buffer.
Definition: buffer.h:34
char * dptr
Current read/write position.
Definition: buffer.h:36
char * data
Pointer to data.
Definition: buffer.h:35
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
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
Style of quoted text.
Definition: quoted.h:68
struct AttrColor * attr_color
Colour and attribute of the text.
Definition: quoted.h:70
struct QuoteStyle * up
Definition: quoted.h:74
size_t prefix_len
Length of the prefix string.
Definition: quoted.h:72
int quote_n
The quoteN colour index for this level.
Definition: quoted.h:69
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:89
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