NeoMutt  2025-12-11-435-g4ac674
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
display.c File Reference

Pager Display. More...

#include "config.h"
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "mutt/lib.h"
#include "config/lib.h"
#include "core/lib.h"
#include "gui/lib.h"
#include "display.h"
#include "lib.h"
#include "color/lib.h"
#include "private_data.h"
+ Include dependency graph for display.c:

Go to the source code of this file.

Functions

static int check_sig (const char *s, struct Line *info, int offset)
 Check for an email signature.
 
static int comp_syntax_t (const void *m1, const void *m2)
 Search for a Syntax using bsearch(3)
 
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.
 
static void append_line (struct Line *lines, int line_num, int cnt)
 Add a new Line to the array.
 
static int check_marker (const char *q, const char *p)
 Check that the unique marker is present.
 
static int check_attachment_marker (const char *p)
 Check that the unique marker is present.
 
static int check_protected_header_marker (const char *p)
 Check that the unique marker is present.
 
bool mutt_is_quote_line (char *line, regmatch_t *pmatch)
 Is a line of message text a quote?
 
static void match_body_patterns (char *pat, struct Line *lines, int line_num)
 Match body patterns, e.g.
 
bool color_is_header (enum ColorId cid)
 Colour is for an Email header.
 
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.
 
void buf_strip_formatting (struct Buffer *dest, const char *src, bool strip_markers)
 Removes ANSI and backspace formatting.
 
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.
 
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.
 
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.
 

Detailed Description

Pager Display.

Authors
  • Richard Russon
  • Pietro Cerutti
  • Tóth János
  • Thomas Klausner

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file display.c.

Function Documentation

◆ check_sig()

static int check_sig ( const char * s,
struct Line * info,
int offset )
static

Check for an email signature.

Parameters
sText to examine
infoLine info array to update
offsetAn offset line to start the check from
Return values
0Success
-1Error

Definition at line 57 of file display.c.

58{
59 const unsigned int NUM_SIG_LINES = 4; // The amount of lines a signature takes
60 unsigned int count = 0;
61
62 while ((offset > 0) && (count <= NUM_SIG_LINES))
63 {
64 if (info[offset].cid != MT_COLOR_SIGNATURE)
65 break;
66 count++;
67 offset--;
68 }
69
70 if (count == 0)
71 return -1;
72
73 if (count > NUM_SIG_LINES)
74 {
75 /* check for a blank line */
76 while (*s)
77 {
78 if (!mutt_isspace(*s))
79 return 0;
80 s++;
81 }
82
83 return -1;
84 }
85
86 return 0;
87}
@ MT_COLOR_SIGNATURE
Pager: signature lines.
Definition color.h:77
bool mutt_isspace(int arg)
Wrapper for isspace(3)
Definition ctype.c:96
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ comp_syntax_t()

static int comp_syntax_t ( const void * m1,
const void * m2 )
static

Search for a Syntax using bsearch(3)

Parameters
m1Search key
m2Array member
Return values
-1m1 precedes m2
0m1 matches m2
1m2 precedes m1

Definition at line 97 of file display.c.

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}
Highlighting for a piece of text.
Definition display.h:39
int last
Last character in line to be coloured (not included)
Definition display.h:42
int first
First character in line to be coloured.
Definition display.h:41
+ Here is the caller graph for this function:

◆ resolve_color()

static void resolve_color ( struct MuttWindow * win,
struct Line * lines,
int line_num,
int cnt,
PagerFlags flags,
int special,
struct AnsiColor * ansi )
static

Set the colour for a line of text.

Parameters
winWindow
linesLine info array
line_numLine Number (index into lines)
cntIf true, this is a continuation line
flagsFlags, see PagerFlags
specialFlags, e.g. A_BOLD
ansiANSI attributes

Definition at line 119 of file display.c.

121{
122 struct AttrColor def_color = { 0 }; /* color without syntax highlight */
123 struct AttrColor color = { 0 }; /* final color */
124 static struct AttrColor last_color = { 0 }; /* last color set */
125 bool search = false;
126 int m;
127 struct TextSyntax *matching_chunk = NULL;
128
129 if (cnt == 0)
130 {
131 last_color.curses_color = NULL;
132 last_color.attrs = A_NORMAL;
133 }
134
135 if (lines[line_num].cont_line)
136 {
137 const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
138 if (!cnt && c_markers)
139 {
141 mutt_window_addch(win, '+');
142 }
143 m = (lines[line_num].syntax)[0].first;
144 cnt += (lines[line_num].syntax)[0].last;
145 }
146 else
147 {
148 m = line_num;
149 }
150 if (flags & MUTT_PAGER_LOGS)
151 {
152 def_color = *(lines[line_num].syntax[0].attr_color);
153 }
154 else if (!(flags & MUTT_SHOWCOLOR))
155 {
156 if (flags & MUTT_PAGER_STRIPES)
157 {
158 def_color = *simple_color_get(((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD :
160 }
161 else
162 {
163 def_color = *simple_color_get(MT_COLOR_NORMAL);
164 }
165 }
166 else if ((lines[m].cid == MT_COLOR_HEADER) && lines[m].syntax[0].attr_color)
167 {
168 def_color = *lines[m].syntax[0].attr_color;
169 }
170 else
171 {
172 def_color = *simple_color_get(lines[m].cid);
173 }
174
175 if ((flags & MUTT_SHOWCOLOR) && COLOR_QUOTED(lines[m].cid))
176 {
177 struct QuoteStyle *qc = lines[m].quote;
178
179 if (qc)
180 {
181 def_color = attr_color_copy(qc->attr_color);
182
183 while (qc && (qc->prefix_len > cnt))
184 {
185 def_color = attr_color_copy(qc->attr_color);
186 qc = qc->up;
187 }
188 }
189 }
190
191 color = def_color;
192 if ((flags & MUTT_SHOWCOLOR) && lines[m].syntax)
193 {
194 matching_chunk = bsearch(&cnt, lines[m].syntax, lines[m].syntax_arr_size,
195 sizeof(struct TextSyntax), comp_syntax_t);
196 if (matching_chunk && (cnt >= matching_chunk->first) &&
197 (cnt < matching_chunk->last))
198 {
199 if (matching_chunk->attr_color)
200 color = *matching_chunk->attr_color;
201 }
202 }
203
204 if ((flags & MUTT_SEARCH) && lines[m].search)
205 {
206 matching_chunk = bsearch(&cnt, lines[m].search, lines[m].search_arr_size,
207 sizeof(struct TextSyntax), comp_syntax_t);
208 if (matching_chunk && (cnt >= matching_chunk->first) &&
209 (cnt < matching_chunk->last))
210 {
212 search = true;
213 }
214 }
215
216 /* handle "special" bold & underlined characters */
217 if (special & A_BOLD)
218 {
221 else
222 color.attrs |= A_BOLD;
223 }
224 else if (special & A_UNDERLINE)
225 {
228 else
229 color.attrs |= A_UNDERLINE;
230 }
231 else if (special & A_ITALIC)
232 {
235 else
236 color.attrs |= A_ITALIC;
237 }
238 else if (ansi->attr_color)
239 {
240 color = *ansi->attr_color;
241 }
242
243 if (!attr_color_match(&color, &last_color))
244 {
246 &color);
247 mutt_curses_set_color(ac_merge);
248 last_color = color;
249 }
250}
bool attr_color_match(struct AttrColor *ac1, struct AttrColor *ac2)
Do the colours match?
Definition attr.c:192
struct AttrColor attr_color_copy(const struct AttrColor *ac)
Copy a colour.
Definition attr.c:165
bool simple_color_is_set(enum ColorId cid)
Is the object coloured?
Definition simple.c:116
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition simple.c:95
@ MT_COLOR_MARKERS
Pager: markers, line continuation.
Definition color.h:51
@ MT_COLOR_HEADER
Message headers (takes a pattern)
Definition color.h:48
@ MT_COLOR_STRIPE_EVEN
Stripes: even lines of the Help Page.
Definition color.h:79
@ MT_COLOR_BOLD
Bold text.
Definition color.h:40
@ MT_COLOR_NORMAL
Plain text.
Definition color.h:53
@ MT_COLOR_SEARCH
Pager: search matches.
Definition color.h:67
@ MT_COLOR_ITALIC
Italic text.
Definition color.h:50
@ MT_COLOR_STRIPE_ODD
Stripes: odd lines of the Help Page.
Definition color.h:80
@ MT_COLOR_UNDERLINE
Underlined text.
Definition color.h:83
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
static int comp_syntax_t(const void *m1, const void *m2)
Search for a Syntax using bsearch(3)
Definition display.c:97
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
const struct AttrColor * mutt_curses_set_color_by_id(enum ColorId cid)
Set the colour and attributes by the Colour ID.
Definition mutt_curses.c:79
void mutt_curses_set_color(const struct AttrColor *ac)
Set the colour and attributes for text.
Definition mutt_curses.c:38
#define A_ITALIC
Definition mutt_curses.h:48
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
#define MUTT_PAGER_STRIPES
Striped highlighting.
Definition lib.h:76
#define MUTT_SHOWCOLOR
Show characters in color otherwise don't show characters.
Definition lib.h:64
#define MUTT_PAGER_LOGS
Logview mode.
Definition lib.h:74
#define MUTT_SEARCH
Resolve search patterns.
Definition lib.h:66
#define COLOR_QUOTED(cid)
Definition quoted.h:28
const struct AttrColor * attr_color
Curses colour of text.
Definition ansi.h:39
A curses colour and its attributes.
Definition attr.h:65
int attrs
Text attributes, e.g. A_BOLD.
Definition attr.h:68
struct CursesColor * curses_color
Underlying Curses colour.
Definition attr.h:69
struct QuoteStyle * quote
Quoting style for this line (pointer into PagerPrivateData->quote_list)
Definition display.h:62
struct TextSyntax * syntax
Array of coloured text in the line.
Definition display.h:57
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
Style of quoted text.
Definition qstyle.h:55
struct AttrColor * attr_color
Colour and attribute of the text.
Definition qstyle.h:57
struct QuoteStyle * up
Definition qstyle.h:61
size_t prefix_len
Length of the prefix string.
Definition qstyle.h:59
const struct AttrColor * attr_color
Curses colour of text.
Definition display.h:40
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ append_line()

static void append_line ( struct Line * lines,
int line_num,
int cnt )
static

Add a new Line to the array.

Parameters
linesArray of Line info
line_numLine number to add
cntOffset of the overflow if line is a continuation

Definition at line 258 of file display.c.

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}
bool cont_line
Continuation of a previous line (wrapped by NeoMutt)
Definition display.h:53
short cid
Default line colour, e.g. MT_COLOR_SIGNATURE.
Definition display.h:52
+ Here is the caller graph for this function:

◆ check_marker()

static int check_marker ( const char * q,
const char * p )
static

Check that the unique marker is present.

Parameters
qMarker string
pString to check
Return values
numOffset of marker

Definition at line 283 of file display.c.

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}
+ Here is the caller graph for this function:

◆ check_attachment_marker()

static int check_attachment_marker ( const char * p)
static

Check that the unique marker is present.

Parameters
pString to check
Return values
numOffset of marker

Definition at line 299 of file display.c.

300{
302}
static int check_marker(const char *q, const char *p)
Check that the unique marker is present.
Definition display.c:283
const char * state_attachment_marker(void)
Get a unique (per-run) ANSI string to mark PGP messages in an email.
Definition state.c:45
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ check_protected_header_marker()

static int check_protected_header_marker ( const char * p)
static

Check that the unique marker is present.

Parameters
pString to check
Return values
numOffset of marker

Definition at line 309 of file display.c.

310{
312}
const char * state_protected_header_marker(void)
Get a unique (per-run) ANSI string to mark protected headers in an email.
Definition state.c:59
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_is_quote_line()

bool mutt_is_quote_line ( char * line,
regmatch_t * pmatch )

Is a line of message text a quote?

Parameters
[in]lineLine to test
[out]pmatchRegex sub-matches
Return values
trueLine is quoted

Checks if line matches the $quote_regex and doesn't match $smileys. This is used by the pager for calling qstyle_classify.

Definition at line 323 of file display.c.

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}
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition helpers.c:217
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
Cached regular expression.
Definition regex3.h:85
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ match_body_patterns()

static void match_body_patterns ( char * pat,
struct Line * lines,
int line_num )
static

Match body patterns, e.g.

color quoted

Parameters
patPattern to match
linesLines of text in the pager
line_numCurrent line number

Definition at line 364 of file display.c.

365{
366 // don't consider line endings part of the buffer for regex matching
367 bool has_nl = false;
368 size_t buflen = mutt_str_len(pat);
369 if ((buflen > 0) && (pat[buflen - 1] == '\n'))
370 {
371 has_nl = true;
372 pat[buflen - 1] = '\0';
373 }
374
375 int i = 0;
376 int offset = 0;
377 struct RegexColor *color_line = NULL;
378 struct RegexColorList *head = NULL;
379 bool found = false;
380 bool null_rx = false;
381 regmatch_t pmatch[1] = { 0 };
382
383 lines[line_num].syntax_arr_size = 0;
384 if (lines[line_num].cid == MT_COLOR_HDRDEFAULT)
385 {
387 }
388 else
389 {
391 }
392 STAILQ_FOREACH(color_line, head, entries)
393 {
394 color_line->stop_matching = false;
395 }
396
397 do
398 {
399 /* if has_nl, we've stripped off a trailing newline */
400 if (offset >= (buflen - has_nl))
401 break;
402
403 if (!pat[offset])
404 break;
405
406 found = false;
407 null_rx = false;
408 STAILQ_FOREACH(color_line, head, entries)
409 {
410 if (color_line->stop_matching)
411 continue;
412
413 if ((regexec(&color_line->regex, pat + offset, 1, pmatch,
414 ((offset != 0) ? REG_NOTBOL : 0)) != 0))
415 {
416 /* Once a regex fails to match, don't try matching it again.
417 * On very long lines this can cause a performance issue if there
418 * are other regexes that have many matches. */
419 color_line->stop_matching = true;
420 continue;
421 }
422
423 if (pmatch[0].rm_eo == pmatch[0].rm_so)
424 {
425 null_rx = true; // empty regex; don't add it, but keep looking
426 continue;
427 }
428
429 if (!found)
430 {
431 // Abort if we fill up chunks. Yes, this really happened.
432 if (lines[line_num].syntax_arr_size == SHRT_MAX)
433 {
434 null_rx = false;
435 break;
436 }
437 if (++(lines[line_num].syntax_arr_size) > 1)
438 {
439 MUTT_MEM_REALLOC(&(lines[line_num].syntax),
440 lines[line_num].syntax_arr_size, struct TextSyntax);
441 // Zero the new entry
442 const int index = lines[line_num].syntax_arr_size - 1;
443 struct TextSyntax *ts = &lines[line_num].syntax[index];
444 memset(ts, 0, sizeof(*ts));
445 }
446 }
447 i = lines[line_num].syntax_arr_size - 1;
448 pmatch[0].rm_so += offset;
449 pmatch[0].rm_eo += offset;
450
451 if (!found || (pmatch[0].rm_so < (lines[line_num].syntax)[i].first) ||
452 ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
453 (pmatch[0].rm_eo > (lines[line_num].syntax)[i].last)))
454 {
455 (lines[line_num].syntax)[i].attr_color = &color_line->attr_color;
456 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
457 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
458 }
459 else if ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
460 (pmatch[0].rm_eo == (lines[line_num].syntax)[i].last))
461 {
462 (lines[line_num].syntax)[i].attr_color = merged_color_overlay(
463 (lines[line_num].syntax)[i].attr_color, &color_line->attr_color);
464 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
465 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
466 }
467
468 found = true;
469 null_rx = false;
470 }
471
472 if (null_rx)
473 offset++; /* avoid degenerate cases */
474 else
475 offset = (lines[line_num].syntax)[i].last;
476 } while (found || null_rx);
477
478 if (has_nl)
479 pat[buflen - 1] = '\n';
480}
struct RegexColorList * regex_colors_get_list(enum ColorId cid)
Return the RegexColorList for a Colour ID.
Definition regex.c:205
@ MT_COLOR_BODY
Pager: highlight body of message (takes a pattern)
Definition color.h:39
@ MT_COLOR_HDRDEFAULT
Header default colour.
Definition color.h:47
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition memory.h:55
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:500
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
short syntax_arr_size
Number of items in syntax array.
Definition display.h:56
A regular expression and a color to highlight a line.
Definition regex4.h:35
regex_t regex
Compiled regex.
Definition regex4.h:38
struct AttrColor attr_color
Colour and attributes to apply.
Definition regex4.h:36
bool stop_matching
Used by the pager for body patterns, to prevent the color from being retried once it fails.
Definition regex4.h:42
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ color_is_header()

bool color_is_header ( enum ColorId cid)

Colour is for an Email header.

Parameters
cidColour ID, e.g. MT_COLOR_HEADER
Return values
trueColour is for an Email header

Definition at line 487 of file display.c.

488{
489 return (cid == MT_COLOR_HEADER) || (cid == MT_COLOR_HDRDEFAULT);
490}
+ Here is the caller graph for this function:

◆ resolve_types()

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 )
static

Determine the style for a line of text.

Parameters
[in]winWindow
[in]bufFormatted text
[in]rawRaw text
[in]linesLine info array
[in]line_numLine number (index into lines)
[in]lines_usedLast line
[out]quote_listList of quote colours
[out]q_levelQuote level
[out]force_redrawSet to true if a screen redraw is needed
[in]q_classifyIf true, style the text

Definition at line 505 of file display.c.

509{
510 struct RegexColor *color_line = NULL;
511 regmatch_t pmatch[1] = { 0 };
512 const bool c_header_color_partial = cs_subset_bool(NeoMutt->sub, "header_color_partial");
513 int offset, i = 0;
514
515 if ((line_num == 0) || color_is_header(lines[line_num - 1].cid) ||
517 {
518 if (buf[0] == '\n') /* end of header */
519 {
520 lines[line_num].cid = MT_COLOR_NORMAL;
522 }
523 else
524 {
525 /* if this is a continuation of the previous line, use the previous
526 * line's color as default. */
527 if ((line_num > 0) && ((buf[0] == ' ') || (buf[0] == '\t')))
528 {
529 lines[line_num].cid = lines[line_num - 1].cid; /* wrapped line */
530 if (!c_header_color_partial)
531 {
532 (lines[line_num].syntax)[0].attr_color =
533 (lines[line_num - 1].syntax)[0].attr_color;
534 lines[line_num].cont_header = true;
535 }
536 }
537 else
538 {
539 lines[line_num].cid = MT_COLOR_HDRDEFAULT;
540 }
541
542 /* When this option is unset, we color the entire header the
543 * same color. Otherwise, we handle the header patterns just
544 * like body patterns (further below). */
545 if (!c_header_color_partial)
546 {
548 {
549 if (regexec(&color_line->regex, buf, 0, NULL, 0) == 0)
550 {
551 lines[line_num].cid = MT_COLOR_HEADER;
552 lines[line_num].syntax[0].attr_color =
553 merged_color_overlay(lines[line_num].syntax[0].attr_color,
555 lines[line_num].syntax[0].attr_color = merged_color_overlay(
556 lines[line_num].syntax[0].attr_color, &color_line->attr_color);
557 if (lines[line_num].cont_header)
558 {
559 /* adjust the previous continuation lines to reflect the color of this continuation line */
560 int j;
561 for (j = line_num - 1; j >= 0 && lines[j].cont_header; j--)
562 {
563 lines[j].cid = lines[line_num].cid;
564 lines[j].syntax[0].attr_color = lines[line_num].syntax[0].attr_color;
565 }
566 /* now adjust the first line of this header field */
567 if (j >= 0)
568 {
569 lines[j].cid = lines[line_num].cid;
570 lines[j].syntax[0].attr_color = lines[line_num].syntax[0].attr_color;
571 }
572 *force_redraw = true; /* the previous lines have already been drawn on the screen */
573 }
574 }
575 }
576 }
577 }
578 }
579 else if (mutt_str_startswith(raw, "\033[0m")) // Escape: a little hack...
580 {
581 lines[line_num].cid = MT_COLOR_NORMAL;
582 }
583 else if (check_attachment_marker((char *) raw) == 0)
584 {
585 lines[line_num].cid = MT_COLOR_ATTACHMENT;
586 }
587 else if (mutt_str_equal("-- \n", buf) || mutt_str_equal("-- \r\n", buf))
588 {
589 i = line_num + 1;
590
591 lines[line_num].cid = MT_COLOR_SIGNATURE;
592 while ((i < lines_used) && (check_sig(buf, lines, i - 1) == 0) &&
593 ((lines[i].cid == MT_COLOR_NORMAL) || COLOR_QUOTED(lines[i].cid) ||
594 (lines[i].cid == MT_COLOR_HEADER)))
595 {
596 /* oops... */
597 if (lines[i].syntax_arr_size)
598 {
599 lines[i].syntax_arr_size = 0;
600 MUTT_MEM_REALLOC(&(lines[i].syntax), 1, struct TextSyntax);
601 }
602 lines[i++].cid = MT_COLOR_SIGNATURE;
603 }
604 }
605 else if (check_sig(buf, lines, line_num - 1) == 0)
606 {
607 lines[line_num].cid = MT_COLOR_SIGNATURE;
608 }
609 else if (mutt_is_quote_line(buf, pmatch))
610 {
611 if (q_classify && !lines[line_num].quote)
612 {
613 lines[line_num].quote = qstyle_classify(quote_list, buf + pmatch[0].rm_so,
614 pmatch[0].rm_eo - pmatch[0].rm_so,
615 force_redraw, q_level);
616 }
617 lines[line_num].cid = MT_COLOR_QUOTED0;
618 }
619 else
620 {
621 lines[line_num].cid = MT_COLOR_NORMAL;
622 }
623
624 /* body patterns */
625 if ((lines[line_num].cid == MT_COLOR_NORMAL) || COLOR_QUOTED(lines[line_num].cid) ||
626 ((lines[line_num].cid == MT_COLOR_HDRDEFAULT) && c_header_color_partial))
627 {
628 match_body_patterns(buf, lines, line_num);
629 }
630
631 /* attachment patterns */
632 if (lines[line_num].cid == MT_COLOR_ATTACHMENT)
633 {
634 size_t nl;
635
636 /* don't consider line endings part of the buffer for regex matching */
637 nl = mutt_str_len(buf);
638 if ((nl > 0) && (buf[nl - 1] == '\n'))
639 buf[nl - 1] = '\0';
640
641 i = 0;
642 offset = 0;
643 lines[line_num].syntax_arr_size = 0;
645 bool found = false;
646 bool null_rx = false;
647 do
648 {
649 if (!buf[offset])
650 break;
651
652 found = false;
653 null_rx = false;
655 {
656 if (regexec(&color_line->regex, buf + offset, 1, pmatch,
657 ((offset != 0) ? REG_NOTBOL : 0)) != 0)
658 {
659 continue;
660 }
661
662 if (pmatch[0].rm_eo != pmatch[0].rm_so)
663 {
664 if (!found)
665 {
666 if (++(lines[line_num].syntax_arr_size) > 1)
667 {
668 MUTT_MEM_REALLOC(&(lines[line_num].syntax),
669 lines[line_num].syntax_arr_size, struct TextSyntax);
670 // Zero the new entry
671 const int index = lines[line_num].syntax_arr_size - 1;
672 struct TextSyntax *ts = &lines[line_num].syntax[index];
673 memset(ts, 0, sizeof(*ts));
674 }
675 }
676 i = lines[line_num].syntax_arr_size - 1;
677 pmatch[0].rm_so += offset;
678 pmatch[0].rm_eo += offset;
679 if (!found || (pmatch[0].rm_so < (lines[line_num].syntax)[i].first) ||
680 ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
681 (pmatch[0].rm_eo > (lines[line_num].syntax)[i].last)))
682 {
683 if (!(lines[line_num].syntax)[i].attr_color)
684 (lines[line_num].syntax)[i].attr_color = ac_attach;
685
686 (lines[line_num].syntax)[i].attr_color = merged_color_overlay(
687 (lines[line_num].syntax)[i].attr_color, &color_line->attr_color);
688 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
689 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
690 }
691 else if ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
692 (pmatch[0].rm_eo == (lines[line_num].syntax)[i].last))
693 {
694 (lines[line_num].syntax)[i].attr_color = merged_color_overlay(
695 (lines[line_num].syntax)[i].attr_color, &color_line->attr_color);
696 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
697 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
698 }
699 found = 1;
700 null_rx = false;
701 }
702 else
703 {
704 null_rx = true; /* empty regex; don't add it, but keep looking */
705 }
706 }
707
708 if (null_rx)
709 offset++; /* avoid degenerate cases */
710 else
711 offset = (lines[line_num].syntax)[i].last;
712 } while (found || null_rx);
713 if (nl > 0)
714 buf[nl - 1] = '\n';
715 }
716}
@ MT_COLOR_QUOTED0
Pager: quoted text, level 0.
Definition color.h:57
@ MT_COLOR_ATTACH_HEADERS
MIME attachment test (takes a pattern)
Definition color.h:38
@ MT_COLOR_ATTACHMENT
MIME attachments text (entire line)
Definition color.h:37
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 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
bool color_is_header(enum ColorId cid)
Colour is for an Email header.
Definition display.c:487
static int check_attachment_marker(const char *p)
Check that the unique marker is present.
Definition display.c:299
int BrailleRow
Braille display: row to leave the cursor.
Definition dlg_pager.c:65
int BrailleCol
Braille display: column to leave the cursor.
Definition dlg_pager.c:67
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:662
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
void mutt_window_get_coords(struct MuttWindow *win, int *row, int *col)
Get the cursor position in the Window.
struct QuoteStyle * qstyle_classify(struct QuoteStyle **quote_list, const char *qptr, size_t length, bool *force_redraw, int *q_level)
Find a style for a string.
Definition qstyle.c:136
bool cont_header
Continuation of a header line (wrapped by MTA)
Definition display.h:54
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ buf_strip_formatting()

void buf_strip_formatting ( struct Buffer * dest,
const char * src,
bool strip_markers )

Removes ANSI and backspace formatting.

Parameters
destBuffer for the result
srcString to strip
strip_markersRemove

Removes ANSI and backspace formatting, and optionally markers. This is separated out so that it can be used both by the pager and the autoview handler.

This logic is pulled from the pager fill_buffer() function, for use in stripping reply-quoted autoview output of ansi sequences.

Definition at line 731 of file display.c.

732{
733 const char *s = src;
734
735 buf_reset(dest);
736
737 if (!s)
738 return;
739
740 while (s[0] != '\0')
741 {
742 if ((s[0] == '\010') && (s > src))
743 {
744 if (s[1] == '_') /* underline */
745 {
746 s += 2;
747 }
748 else if (s[1] && buf_len(dest)) /* bold or overstrike */
749 {
750 dest->dptr--;
751 buf_addch(dest, s[1]);
752 s += 2;
753 }
754 else /* ^H */
755 {
756 buf_addch(dest, *s++);
757 }
758 continue;
759 }
760
761 int len = ansi_color_seq_length(s);
762 if (len > 0)
763 {
764 s += len;
765 }
766 else if (strip_markers && (s[0] == '\033') && (s[1] == ']') &&
768 {
769 mutt_debug(LL_DEBUG2, "Seen attachment marker\n");
770 while (*s++ != '\a')
771 ; /* skip pseudo-ANSI sequence */
772 }
773 else
774 {
775 buf_addch(dest, *s++);
776 }
777 }
778}
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition buffer.c:491
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:241
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
int ansi_color_seq_length(const char *str)
Is this an ANSI escape sequence?
Definition parse_ansi.c:78
char * dptr
Current read/write position.
Definition buffer.h:38
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ fill_buffer()

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 )
static

Fill a buffer from a file.

Parameters
[in]fpFile to read from
[in,out]bytes_readEnd of last read
[in]offsetPosition start reading from
[out]bufBuffer to fill
[out]fmtCopy of buffer, stripped of attributes
[out]blenLength of the buffer
[in,out]buf_readytrue if the buffer already has data in it
Return values
>=0Bytes read
-1Error

Definition at line 792 of file display.c.

794{
795 static int b_read = 0;
796
797 if (*buf_ready == 0)
798 {
799 if (offset != *bytes_read)
800 {
801 if (!mutt_file_seek(fp, offset, SEEK_SET))
802 {
803 return -1;
804 }
805 }
806
807 *buf = (unsigned char *) mutt_file_read_line((char *) *buf, blen, fp, NULL, MUTT_RL_EOL);
808 if (!*buf)
809 {
810 fmt[0] = NULL;
811 return -1;
812 }
813
814 *bytes_read = ftello(fp);
815 LOFF_T diff = *bytes_read - offset;
816 b_read = (diff > INT_MAX) ? INT_MAX : (int) diff;
817 *buf_ready = 1;
818
819 struct Buffer *stripped = buf_pool_get();
820 buf_alloc(stripped, *blen);
821 buf_strip_formatting(stripped, (const char *) *buf, 1);
822 /* This should be a noop, because *fmt should be NULL */
823 FREE(fmt);
824 *fmt = (unsigned char *) buf_strdup(stripped);
825 buf_pool_release(&stripped);
826 }
827
828 return b_read;
829}
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition buffer.c:337
void buf_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
Removes ANSI and backspace formatting.
Definition display.c:731
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:682
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition file.c:652
#define MUTT_RL_EOL
don't strip \n / \r\n
Definition file.h:42
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
String manipulation buffer.
Definition buffer.h:36
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ format_line()

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 )
static

Display a line of text in the pager.

Parameters
[in]winWindow
[out]linesLine info
[in]line_numLine number (index into lines)
[in]bufText to display
[in]flagsFlags, see PagerFlags
[out]ansiANSI attributes used
[in]cntLength of text buffer
[out]pspaceIndex of last whitespace character
[out]pvchNumber of bytes read
[out]pcolNumber of columns used
[out]pspecialAttribute flags, e.g. A_UNDERLINE
[in]widthWidth of screen (to wrap to)
[out]ansi_listList of unique Ansi colours
Return values
numNumber of characters displayed

Definition at line 848 of file display.c.

852{
853 int space = -1; /* index of the last space or TAB */
854 const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
855 size_t col = c_markers ? (*lines)[line_num].cont_line : 0;
856 size_t k;
857 int ch, vch, last_special = -1, special = 0, t;
858 wchar_t wc = 0;
859 mbstate_t mbstate = { 0 }; // FIXME: this should come from lines
860 const size_t c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
861 size_t wrap_cols = mutt_window_wrap_cols(width, (flags & MUTT_PAGER_NOWRAP) ? 0 : c_wrap);
862
863 if (check_attachment_marker((char *) buf) == 0)
864 wrap_cols = width;
865
866 struct PagerPrivateData *priv = win->parent->wdata;
867 enum PagerMode mode = priv->pview->mode;
868 const bool c_allow_ansi = (mode == PAGER_MODE_OTHER) ||
869 cs_subset_bool(NeoMutt->sub, "allow_ansi");
870
871 for (ch = 0, vch = 0; ch < cnt; ch += k, vch += k)
872 {
873 /* Handle ANSI sequences */
874 if (buf[ch] == '\033') // Escape
875 {
876 int len = ansi_color_parse((const char *) buf + ch, ansi, ansi_list, !c_allow_ansi);
877 ch += len;
878 }
879
880 while ((cnt - ch >= 2) && (buf[ch] == '\033') && (buf[ch + 1] == ']') && // Escape
881 ((check_attachment_marker((char *) buf + ch) == 0) ||
882 (check_protected_header_marker((char *) buf + ch) == 0)))
883 {
884 while (buf[ch++] != '\a')
885 if (ch >= cnt)
886 break;
887 }
888
889 /* is anything left to do? */
890 if (ch >= cnt)
891 break;
892
893 k = mbrtowc(&wc, (char *) buf + ch, cnt - ch, &mbstate);
894 if ((k == ICONV_BUF_TOO_SMALL) || (k == ICONV_ILLEGAL_SEQ))
895 {
896 if (k == ICONV_ILLEGAL_SEQ)
897 memset(&mbstate, 0, sizeof(mbstate));
898 mutt_debug(LL_DEBUG1, "mbrtowc returned %zu; errno = %d\n", k, errno);
899 if ((col + 4) > wrap_cols)
900 break;
901 col += 4;
902 if (ansi)
903 mutt_window_printf(win, "\\%03o", buf[ch]);
904 k = 1;
905 continue;
906 }
907 if (k == 0)
908 k = 1;
909
910 if (CharsetIsUtf8)
911 {
912 /* zero width space, zero with non-joiner, zero width no-break space */
913 if ((wc == 0x200B) || (wc == 0x200C) || (wc == 0xFEFF))
914 {
915 mutt_debug(LL_DEBUG3, "skip zero-width character U+%04X\n", (unsigned short) wc);
916 continue;
917 }
919 {
920 mutt_debug(LL_DEBUG3, "filtered U+%04X\n", (unsigned short) wc);
921 continue;
922 }
923 }
924
925 /* Handle backspace */
926 special = 0;
927 if (IsWPrint(wc))
928 {
929 wchar_t wc1 = 0;
930 mbstate_t mbstate1 = mbstate;
931 size_t k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
932 while ((k1 != ICONV_BUF_TOO_SMALL) && (k1 != ICONV_ILLEGAL_SEQ) &&
933 (k1 > 0) && (wc1 == '\b'))
934 {
935 const size_t k2 = mbrtowc(&wc1, (char *) buf + ch + k + k1,
936 cnt - ch - k - k1, &mbstate1);
937 if ((k2 == ICONV_BUF_TOO_SMALL) || (k2 == ICONV_ILLEGAL_SEQ) ||
938 (k2 == 0) || (!IsWPrint(wc1)))
939 {
940 break;
941 }
942
943 if (wc == wc1)
944 {
945 special |= ((wc == '_') && (special & A_UNDERLINE)) ? A_UNDERLINE : A_BOLD;
946 }
947 else if ((wc == '_') || (wc1 == '_'))
948 {
949 special |= A_UNDERLINE;
950 wc = (wc1 == '_') ? wc : wc1;
951 }
952 else
953 {
954 /* special = 0; / * overstrike: nothing to do! */
955 wc = wc1;
956 }
957
958 ch += k + k1;
959 k = k2;
960 mbstate = mbstate1;
961 k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
962 }
963 }
964
965 if (ansi && ((flags & (MUTT_SHOWCOLOR | MUTT_SEARCH | MUTT_PAGER_MARKER)) ||
966 special || last_special || (ansi->attrs != A_NORMAL)))
967 {
968 resolve_color(win, *lines, line_num, vch, flags, special, ansi);
969 last_special = special;
970 }
971
972 /* no-break space, narrow no-break space */
973 if (IsWPrint(wc) || (CharsetIsUtf8 && ((wc == 0x00A0) || (wc == 0x202F))))
974 {
975 if (wc == ' ')
976 {
977 space = ch;
978 }
979 t = wcwidth(wc);
980 if (col + t > wrap_cols)
981 break;
982 col += t;
983 if (ansi)
984 mutt_addwch(win, wc);
985 }
986 else if (wc == '\n')
987 {
988 break;
989 }
990 else if (wc == '\t')
991 {
992 space = ch;
993 t = (col & ~7) + 8;
994 if (t > wrap_cols)
995 break;
996 if (ansi)
997 for (; col < t; col++)
998 mutt_window_addch(win, ' ');
999 else
1000 col = t;
1001 }
1002 else if ((wc < 0x20) || (wc == 0x7f))
1003 {
1004 if ((col + 2) > wrap_cols)
1005 break;
1006 col += 2;
1007 if (ansi)
1008 mutt_window_printf(win, "^%c", (char) (('@' + wc) & 0x7f));
1009 }
1010 else if (wc < 0x100)
1011 {
1012 if ((col + 4) > wrap_cols)
1013 break;
1014 col += 4;
1015 if (ansi)
1016 mutt_window_printf(win, "\\%03lo", (long) wc);
1017 }
1018 else
1019 {
1020 if ((col + 1) > wrap_cols)
1021 break;
1022 col += k;
1023 if (ansi)
1025 }
1026 }
1027 *pspace = space;
1028 *pcol = col;
1029 *pvch = vch;
1030 *pspecial = special;
1031 return ch;
1032}
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
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition helpers.c:143
int mutt_addwch(struct MuttWindow *win, wchar_t wc)
Addwch would be provided by an up-to-date curses library.
Definition curs_lib.c:319
static 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
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:47
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
bool mutt_mb_is_display_corrupting_utf8(wchar_t wc)
Will this character corrupt the display?
Definition mbyte.c:386
#define IsWPrint(wc)
Definition mbyte.h:40
bool CharsetIsUtf8
Is the user's current character set utf-8?
Definition charset.c:66
wchar_t ReplacementChar
When a Unicode character can't be displayed, use this instead.
Definition charset.c:61
#define ICONV_BUF_TOO_SMALL
Error value for iconv() - Buffer too small.
Definition charset.h:116
#define ICONV_ILLEGAL_SEQ
Error value for iconv() - Illegal sequence.
Definition charset.h:114
int mutt_window_printf(struct MuttWindow *win, const char *fmt,...)
Write a formatted string to a Window.
int mutt_window_wrap_cols(int width, short wrap)
Calculate the wrap column for a given screen width.
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition lib.h:73
#define MUTT_PAGER_MARKER
Use markers if option is set.
Definition lib.h:71
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
int attrs
Text attributes, e.g. A_BOLD.
Definition ansi.h:38
void * wdata
Private data.
struct MuttWindow * parent
Parent Window.
Private state data for the Pager.
struct Line * lines
Array of text lines in pager.
struct PagerView * pview
Object to view in the pager.
struct AttrColorList ansi_list
List of ANSI colours used in the Pager.
enum PagerMode mode
Pager mode.
Definition lib.h:174
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ display_line()

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.

Parameters
[in]fpFile to read from
[out]bytes_readOffset into file
[out]linesLine attributes
[in]line_numLine number
[out]lines_usedLast line
[out]lines_maxMaximum number of lines
[in]flagsFlags, see PagerFlags
[out]quote_listEmail quoting style
[out]q_levelLevel of quoting
[out]force_redrawForce a repaint
[out]search_reRegex to highlight
[in]win_pagerWindow to draw into
[in]ansi_listList of ANSI colours/attributes
Return values
-1EOF was reached
0normal exit, line was not displayed
>0normal exit, line was displayed

Definition at line 1053 of file display.c.

1058{
1059 unsigned char *buf = NULL, *fmt = NULL;
1060 size_t buflen = 0;
1061 unsigned char *buf_ptr = NULL;
1062 int ch, vch, col, cnt, b_read;
1063 int buf_ready = 0;
1064 bool change_last = false;
1065 int special;
1066 int offset;
1067 const struct AttrColor *def_color = NULL;
1068 int m;
1069 int rc = -1;
1070 struct AnsiColor ansi = { { COLOR_DEFAULT, 0, 0 }, { COLOR_DEFAULT, 0, 0 }, 0, NULL };
1071 regmatch_t pmatch[1] = { 0 };
1072
1073 struct PagerPrivateData *priv = win_pager->parent->wdata;
1074 enum PagerMode mode = priv->pview->mode;
1075
1076 if (line_num == *lines_used)
1077 {
1078 (*lines_used)++;
1079 change_last = true;
1080 }
1081
1082 if (*lines_used == *lines_max)
1083 {
1084 *lines_max += LINES;
1086 for (ch = *lines_used; ch < *lines_max; ch++)
1087 {
1088 memset(&((*lines)[ch]), 0, sizeof(struct Line));
1089 (*lines)[ch].cid = -1;
1090 (*lines)[ch].search_arr_size = -1;
1091 (*lines)[ch].syntax = MUTT_MEM_CALLOC(1, struct TextSyntax);
1092 ((*lines)[ch].syntax)[0].first = -1;
1093 ((*lines)[ch].syntax)[0].last = -1;
1094 }
1095 }
1096
1097 struct Line *const cur_line = &(*lines)[line_num];
1098
1099 if (flags & MUTT_PAGER_LOGS)
1100 {
1101 /* determine the line class */
1102 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1103 {
1104 if (change_last)
1105 (*lines_used)--;
1106 goto out;
1107 }
1108
1109 if ((cur_line->cont_line) && (line_num > 0))
1110 {
1111 struct Line *const old_line = &(*lines)[line_num - 1];
1112 cur_line->cid = old_line->cid;
1113 cur_line->syntax[0].attr_color = old_line->syntax[0].attr_color;
1114 }
1115 else
1116 {
1117 cur_line->cid = MT_COLOR_NORMAL;
1118 size_t blen = mutt_str_len((const char *) buf);
1119 if ((blen > 11) && (buf[11] == 'M'))
1121 else if ((blen > 11) && (buf[11] == 'W'))
1123 else if ((blen > 11) && (buf[11] == 'E'))
1125 else
1127 }
1128 }
1129
1130 /* only do color highlighting if we are viewing a message */
1131 if (flags & (MUTT_SHOWCOLOR | MUTT_TYPES))
1132 {
1133 if (cur_line->cid == -1)
1134 {
1135 /* determine the line class */
1136 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1137 {
1138 if (change_last)
1139 (*lines_used)--;
1140 goto out;
1141 }
1142
1143 if (mode == PAGER_MODE_EMAIL)
1144 {
1145 resolve_types(win_pager, (char *) fmt, (char *) buf, *lines, line_num, *lines_used,
1146 quote_list, q_level, force_redraw, flags & MUTT_SHOWCOLOR);
1147 }
1148 else
1149 {
1150 (*lines)[line_num].cid = MT_COLOR_NORMAL;
1151 }
1152
1153 /* avoid race condition for continuation lines when scrolling up */
1154 for (m = line_num + 1;
1155 m < *lines_used && (*lines)[m].offset && (*lines)[m].cont_line; m++)
1156 {
1157 (*lines)[m].cid = cur_line->cid;
1158 }
1159 }
1160
1161 /* this also prevents searching through the hidden lines */
1162 const short c_toggle_quoted_show_levels = cs_subset_number(NeoMutt->sub, "toggle_quoted_show_levels");
1163 if ((flags & MUTT_HIDE) && COLOR_QUOTED(cur_line->cid) &&
1164 (!cur_line->quote || (cur_line->quote->quote_n >= c_toggle_quoted_show_levels)))
1165 {
1166 flags = 0; /* MUTT_NOSHOW */
1167 }
1168 }
1169
1170 /* At this point, (*lines[line_num]).quote may still be undefined. We
1171 * don't want to compute it every time MUTT_TYPES is set, since this
1172 * would slow down the "bottom" function unacceptably. A compromise
1173 * solution is hence to call regexec() again, just to find out the
1174 * length of the quote prefix. */
1175 if ((flags & MUTT_SHOWCOLOR) && !cur_line->cont_line &&
1176 COLOR_QUOTED(cur_line->cid) && !cur_line->quote)
1177 {
1178 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1179 {
1180 if (change_last)
1181 (*lines_used)--;
1182 goto out;
1183 }
1184
1185 const struct Regex *c_quote_regex = cs_subset_regex(NeoMutt->sub, "quote_regex");
1186 if (mutt_regex_capture(c_quote_regex, (char *) fmt, 1, pmatch))
1187 {
1188 cur_line->quote = qstyle_classify(quote_list, (char *) fmt + pmatch[0].rm_so,
1189 pmatch[0].rm_eo - pmatch[0].rm_so,
1190 force_redraw, q_level);
1191 }
1192 else
1193 {
1194 goto out;
1195 }
1196 }
1197
1198 if ((flags & MUTT_SEARCH) && !cur_line->cont_line && (cur_line->search_arr_size == -1))
1199 {
1200 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1201 {
1202 if (change_last)
1203 (*lines_used)--;
1204 goto out;
1205 }
1206
1207 offset = 0;
1208 cur_line->search_arr_size = 0;
1209 while (regexec(search_re, (char *) fmt + offset, 1, pmatch,
1210 (offset ? REG_NOTBOL : 0)) == 0)
1211 {
1212 if (++(cur_line->search_arr_size) > 1)
1213 {
1214 MUTT_MEM_REALLOC(&(cur_line->search), cur_line->search_arr_size, struct TextSyntax);
1215 // Zero the new entry
1216 const int index = cur_line->search_arr_size - 1;
1217 struct TextSyntax *ts = &cur_line->search[index];
1218 memset(ts, 0, sizeof(*ts));
1219 }
1220 else
1221 {
1222 cur_line->search = MUTT_MEM_CALLOC(1, struct TextSyntax);
1223 }
1224 pmatch[0].rm_so += offset;
1225 pmatch[0].rm_eo += offset;
1226 (cur_line->search)[cur_line->search_arr_size - 1].first = pmatch[0].rm_so;
1227 (cur_line->search)[cur_line->search_arr_size - 1].last = pmatch[0].rm_eo;
1228
1229 if (pmatch[0].rm_eo == pmatch[0].rm_so)
1230 offset++; /* avoid degenerate cases */
1231 else
1232 offset = pmatch[0].rm_eo;
1233 if (!fmt[offset])
1234 break;
1235 }
1236 }
1237
1238 if (!(flags & MUTT_SHOW) && ((*lines)[line_num + 1].offset > 0))
1239 {
1240 /* we've already scanned this line, so just exit */
1241 rc = 0;
1242 goto out;
1243 }
1244 if ((flags & MUTT_SHOWCOLOR) && *force_redraw && ((*lines)[line_num + 1].offset > 0))
1245 {
1246 /* no need to try to display this line... */
1247 rc = 1;
1248 goto out; /* fake display */
1249 }
1250
1251 b_read = fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready);
1252 if (b_read < 0)
1253 {
1254 if (change_last)
1255 (*lines_used)--;
1256 goto out;
1257 }
1258
1259 /* now chose a good place to break the line */
1260 cnt = format_line(win_pager, lines, line_num, buf, flags, NULL, b_read, &ch,
1261 &vch, &col, &special, win_pager->state.cols, ansi_list);
1262 buf_ptr = buf + cnt;
1263
1264 /* move the break point only if smart_wrap is set */
1265 const bool c_smart_wrap = cs_subset_bool(NeoMutt->sub, "smart_wrap");
1266 if (c_smart_wrap)
1267 {
1268 if ((cnt < b_read) && (ch != -1) && !color_is_header(cur_line->cid) &&
1269 !mutt_isspace(buf[cnt]))
1270 {
1271 buf_ptr = buf + ch;
1272 /* skip trailing blanks */
1273 while (ch && ((buf[ch] == ' ') || (buf[ch] == '\t') || (buf[ch] == '\r')))
1274 ch--;
1275
1276 /* A folded header with a single long word shouldn't be smartwrapped.
1277 * So just disable smart_wrap if it would wrap at the beginning of the line. */
1278 if (ch == 0)
1279 buf_ptr = buf + cnt;
1280 else
1281 cnt = ch + 1;
1282 }
1283
1284 /* skip leading blanks on the next line too */
1285 while ((*buf_ptr == ' ') || (*buf_ptr == '\t'))
1286 buf_ptr++;
1287 }
1288
1289 if (*buf_ptr == '\r')
1290 buf_ptr++;
1291 if (*buf_ptr == '\n')
1292 buf_ptr++;
1293
1294 if (((int) (buf_ptr - buf) < b_read) && !(*lines)[line_num + 1].cont_line)
1295 append_line(*lines, line_num, (int) (buf_ptr - buf));
1296 (*lines)[line_num + 1].offset = cur_line->offset + (long) (buf_ptr - buf);
1297
1298 /* if we don't need to display the line we are done */
1299 if (!(flags & MUTT_SHOW))
1300 {
1301 rc = 0;
1302 goto out;
1303 }
1304
1305 if (flags & MUTT_PAGER_STRIPES)
1306 {
1307 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1309 }
1310
1311 /* display the line */
1312 format_line(win_pager, lines, line_num, buf, flags, &ansi, cnt, &ch, &vch,
1313 &col, &special, win_pager->state.cols, ansi_list);
1314
1315 /* avoid a bug in ncurses... */
1316 if (col == 0)
1317 {
1318 if (flags & MUTT_PAGER_STRIPES)
1319 {
1320 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1322 }
1323 else
1324 {
1326 }
1327
1328 mutt_window_addch(win_pager, ' ');
1329 }
1330
1331 /* Fill the blank space at the end of the line with the prevailing color.
1332 * ncurses does an implicit clrtoeol() when you do mutt_window_addch('\n') so we have
1333 * to make sure to reset the color *after* that */
1334 if (flags & MUTT_SHOWCOLOR)
1335 {
1336 m = (cur_line->cont_line) ? (cur_line->syntax)[0].first : line_num;
1337 if ((*lines)[m].cid == MT_COLOR_HEADER)
1338 {
1339 def_color = ((*lines)[m].syntax)[0].attr_color;
1340 }
1341 else
1342 {
1343 def_color = simple_color_get((*lines)[m].cid);
1344 }
1345 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
1346 const struct AttrColor *ac_eol = NULL;
1347 if (def_color)
1348 ac_eol = merged_color_overlay(ac_normal, def_color);
1349 else
1350 ac_eol = ac_normal;
1351 mutt_curses_set_color(ac_eol);
1352 }
1353
1354 if (col < win_pager->state.cols)
1355 {
1356 if (flags & MUTT_PAGER_STRIPES)
1357 {
1358 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1359 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
1360 const struct AttrColor *stripe_color = simple_color_get(cid);
1361 const struct AttrColor *ac_eol = merged_color_overlay(ac_normal, stripe_color);
1362 mutt_curses_set_color(ac_eol);
1363 }
1364 else
1365 {
1366 // Colours may be disabled, but we may still need to end the "search" colour
1367 if (!(flags & MUTT_SHOWCOLOR))
1369 }
1370 mutt_window_clrtoeol(win_pager);
1371 }
1372
1373 /* reset the color back to normal. This *must* come after the
1374 * clrtoeol, otherwise the color for this line will not be
1375 * filled to the right margin. */
1376 if (flags & MUTT_SHOWCOLOR)
1378
1379 /* build a return code */
1380 if (!(flags & MUTT_SHOW))
1381 flags = 0;
1382
1383 rc = flags;
1384
1385out:
1386 FREE(&buf);
1387 FREE(&fmt);
1388 return rc;
1389}
#define COLOR_DEFAULT
Definition color.h:102
ColorId
List of all coloured objects.
Definition color.h:35
@ MT_COLOR_MESSAGE
Informational message.
Definition color.h:52
@ MT_COLOR_ERROR
Error message.
Definition color.h:46
@ MT_COLOR_WARNING
Warning messages.
Definition color.h:84
static int format_line(struct MuttWindow *win, struct Line **lines, int line_num, unsigned char *buf, PagerFlags flags, struct AnsiColor *ansi, int cnt, int *pspace, int *pvch, int *pcol, int *pspecial, int width, struct AttrColorList *ansi_list)
Display a line of text in the pager.
Definition display.c:848
static int fill_buffer(FILE *fp, LOFF_T *bytes_read, LOFF_T offset, unsigned char **buf, unsigned char **fmt, size_t *blen, int *buf_ready)
Fill a buffer from a file.
Definition display.c:792
static void resolve_types(struct MuttWindow *win, char *buf, char *raw, struct Line *lines, int line_num, int lines_used, struct QuoteStyle **quote_list, int *q_level, bool *force_redraw, bool q_classify)
Determine the style for a line of text.
Definition display.c:505
static void append_line(struct Line *lines, int line_num, int cnt)
Add a new Line to the array.
Definition display.c:258
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
const struct AttrColor * mutt_curses_set_normal_backed_color_by_id(enum ColorId cid)
Set the colour and attributes by the Colour ID.
Definition mutt_curses.c:63
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
#define MUTT_HIDE
Don't show quoted text.
Definition lib.h:65
#define MUTT_TYPES
Compute line's type.
Definition lib.h:67
@ PAGER_MODE_EMAIL
Pager is invoked via 1st path. The mime part is selected automatically.
Definition lib.h:138
#define MUTT_SHOW
Definition lib.h:68
An ANSI escape sequence.
Definition ansi.h:35
A line of text in the pager.
Definition display.h:50
short search_arr_size
Number of items in search array.
Definition display.h:59
struct TextSyntax * search
Array of search text in the line.
Definition display.h:60
LOFF_T offset
Offset into Email file (PagerPrivateData->fp)
Definition display.h:51
struct WindowState state
Current state of the Window.
int lines_used
Size of lines array (used entries)
int lines_max
Capacity of lines array (total entries)
bool first
First time flag for toggle-new.
int quote_n
The quoteN colour index for this level.
Definition qstyle.h:56
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition mutt_window.h:60
+ Here is the call graph for this function:
+ Here is the caller graph for this function: