NeoMutt  2025-09-05-7-geaa2bd
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

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 56 of file display.c.

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

97{
98 const int *cnt = (const int *) m1;
99 const struct TextSyntax *stx = (const struct TextSyntax *) m2;
100
101 if (*cnt < stx->first)
102 return -1;
103 if (*cnt >= stx->last)
104 return 1;
105 return 0;
106}
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 118 of file display.c.

120{
121 struct AttrColor def_color = { 0 }; /* color without syntax highlight */
122 struct AttrColor color = { 0 }; /* final color */
123 static struct AttrColor last_color = { 0 }; /* last color set */
124 bool search = false;
125 int m;
126 struct TextSyntax *matching_chunk = NULL;
127
128 if (cnt == 0)
129 {
130 last_color.curses_color = NULL;
131 last_color.attrs = A_NORMAL;
132 }
133
134 if (lines[line_num].cont_line)
135 {
136 const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
137 if (!cnt && c_markers)
138 {
140 mutt_window_addch(win, '+');
141 }
142 m = (lines[line_num].syntax)[0].first;
143 cnt += (lines[line_num].syntax)[0].last;
144 }
145 else
146 {
147 m = line_num;
148 }
149 if (flags & MUTT_PAGER_LOGS)
150 {
151 def_color = *(lines[line_num].syntax[0].attr_color);
152 }
153 else if (!(flags & MUTT_SHOWCOLOR))
154 {
155 if (flags & MUTT_PAGER_STRIPES)
156 {
157 def_color = *simple_color_get(((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD :
159 }
160 else
161 {
162 def_color = *simple_color_get(MT_COLOR_NORMAL);
163 }
164 }
165 else if ((lines[m].cid == MT_COLOR_HEADER) && lines[m].syntax[0].attr_color)
166 {
167 def_color = *lines[m].syntax[0].attr_color;
168 }
169 else
170 {
171 def_color = *simple_color_get(lines[m].cid);
172 }
173
174 if ((flags & MUTT_SHOWCOLOR) && COLOR_QUOTED(lines[m].cid))
175 {
176 struct QuoteStyle *qc = lines[m].quote;
177
178 if (qc)
179 {
180 def_color = attr_color_copy(qc->attr_color);
181
182 while (qc && (qc->prefix_len > cnt))
183 {
184 def_color = attr_color_copy(qc->attr_color);
185 qc = qc->up;
186 }
187 }
188 }
189
190 color = def_color;
191 if ((flags & MUTT_SHOWCOLOR) && lines[m].syntax)
192 {
193 matching_chunk = bsearch(&cnt, lines[m].syntax, lines[m].syntax_arr_size,
194 sizeof(struct TextSyntax), comp_syntax_t);
195 if (matching_chunk && (cnt >= matching_chunk->first) &&
196 (cnt < matching_chunk->last))
197 {
198 if (matching_chunk->attr_color)
199 color = *matching_chunk->attr_color;
200 }
201 }
202
203 if ((flags & MUTT_SEARCH) && lines[m].search)
204 {
205 matching_chunk = bsearch(&cnt, lines[m].search, lines[m].search_arr_size,
206 sizeof(struct TextSyntax), comp_syntax_t);
207 if (matching_chunk && (cnt >= matching_chunk->first) &&
208 (cnt < matching_chunk->last))
209 {
211 search = true;
212 }
213 }
214
215 /* handle "special" bold & underlined characters */
216 if (special & A_BOLD)
217 {
220 else
221 color.attrs |= A_BOLD;
222 }
223 else if (special & A_UNDERLINE)
224 {
227 else
228 color.attrs |= A_UNDERLINE;
229 }
230 else if (special & A_ITALIC)
231 {
234 else
235 color.attrs |= A_ITALIC;
236 }
237 else if (ansi->attr_color)
238 {
239 color = *ansi->attr_color;
240 }
241
242 if (!attr_color_match(&color, &last_color))
243 {
245 &color);
246 mutt_curses_set_color(ac_merge);
247 last_color = color;
248 }
249}
bool attr_color_match(struct AttrColor *ac1, struct AttrColor *ac2)
Do the colours match?
Definition: attr.c:193
struct AttrColor attr_color_copy(const struct AttrColor *ac)
Copy a colour.
Definition: attr.c:166
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:52
@ MT_COLOR_HEADER
Message headers (takes a pattern)
Definition: color.h:49
@ MT_COLOR_STRIPE_EVEN
Stripes: even lines of the Help Page.
Definition: color.h:80
@ MT_COLOR_BOLD
Bold text.
Definition: color.h:41
@ MT_COLOR_NORMAL
Plain text.
Definition: color.h:54
@ MT_COLOR_SEARCH
Pager: search matches.
Definition: color.h:68
@ MT_COLOR_ITALIC
Italic text.
Definition: color.h:51
@ MT_COLOR_STRIPE_ODD
Stripes: odd lines of the Help Page.
Definition: color.h:81
@ MT_COLOR_UNDERLINE
Underlined text.
Definition: color.h:84
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:96
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:49
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
Definition: mutt_window.c:353
#define MUTT_PAGER_STRIPES
Striped highlighting.
Definition: lib.h:74
#define MUTT_SHOWCOLOR
Show characters in color otherwise don't show characters.
Definition: lib.h:62
#define MUTT_PAGER_LOGS
Logview mode.
Definition: lib.h:72
#define MUTT_SEARCH
Resolve search patterns.
Definition: lib.h:64
#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:66
int attrs
Text attributes, e.g. A_BOLD.
Definition: attr.h:69
struct CursesColor * curses_color
Underlying Curses colour.
Definition: attr.h:70
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:43
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:47
Style of quoted text.
Definition: qstyle.h:56
struct AttrColor * attr_color
Colour and attribute of the text.
Definition: qstyle.h:58
struct QuoteStyle * up
Definition: qstyle.h:62
size_t prefix_len
Length of the prefix string.
Definition: qstyle.h:60
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 257 of file display.c.

258{
259 int m;
260
261 lines[line_num + 1].cid = lines[line_num].cid;
262 (lines[line_num + 1].syntax)[0].attr_color = (lines[line_num].syntax)[0].attr_color;
263 lines[line_num + 1].cont_line = true;
264
265 /* find the real start of the line */
266 for (m = line_num; m >= 0; m--)
267 if (!lines[m].cont_line)
268 break;
269
270 (lines[line_num + 1].syntax)[0].first = m;
271 (lines[line_num + 1].syntax)[0].last = (lines[line_num].cont_line) ?
272 cnt + (lines[line_num].syntax)[0].last :
273 cnt;
274}
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 282 of file display.c.

283{
284 for (; (p[0] == q[0]) && (q[0] != '\0') && (p[0] != '\0') && (q[0] != '\a') &&
285 (p[0] != '\a');
286 p++, q++)
287 {
288 }
289
290 return (int) (*p - *q);
291}
+ 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 298 of file display.c.

299{
301}
static int check_marker(const char *q, const char *p)
Check that the unique marker is present.
Definition: display.c:282
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 308 of file display.c.

309{
311}
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 322 of file display.c.

323{
324 bool is_quote = false;
325 const struct Regex *c_smileys = cs_subset_regex(NeoMutt->sub, "smileys");
326 regmatch_t pmatch_internal[1] = { 0 };
327
328 if (!pmatch)
329 pmatch = pmatch_internal;
330
331 const struct Regex *c_quote_regex = cs_subset_regex(NeoMutt->sub, "quote_regex");
332 if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
333 {
334 regmatch_t smatch[1] = { 0 };
335 if (mutt_regex_capture(c_smileys, line, 1, smatch))
336 {
337 if (smatch[0].rm_so > 0)
338 {
339 char c = line[smatch[0].rm_so];
340 line[smatch[0].rm_so] = 0;
341
342 if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
343 is_quote = true;
344
345 line[smatch[0].rm_so] = c;
346 }
347 }
348 else
349 {
350 is_quote = true;
351 }
352 }
353
354 return is_quote;
355}
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:86
+ 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 363 of file display.c.

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

487{
488 return (cid == MT_COLOR_HEADER) || (cid == MT_COLOR_HDRDEFAULT);
489}
+ 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 504 of file display.c.

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

731{
732 const char *s = src;
733
734 buf_reset(dest);
735
736 if (!s)
737 return;
738
739 while (s[0] != '\0')
740 {
741 if ((s[0] == '\010') && (s > src))
742 {
743 if (s[1] == '_') /* underline */
744 {
745 s += 2;
746 }
747 else if (s[1] && buf_len(dest)) /* bold or overstrike */
748 {
749 dest->dptr--;
750 buf_addch(dest, s[1]);
751 s += 2;
752 }
753 else /* ^H */
754 {
755 buf_addch(dest, *s++);
756 }
757 continue;
758 }
759
760 int len = ansi_color_seq_length(s);
761 if (len > 0)
762 {
763 s += len;
764 }
765 else if (strip_markers && (s[0] == '\033') && (s[1] == ']') &&
767 {
768 mutt_debug(LL_DEBUG2, "Seen attachment marker\n");
769 while (*s++ != '\a')
770 ; /* skip pseudo-ANSI sequence */
771 }
772 else
773 {
774 buf_addch(dest, *s++);
775 }
776 }
777}
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:90
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:45
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 791 of file display.c.

793{
794 static int b_read = 0;
795
796 if (*buf_ready == 0)
797 {
798 if (offset != *bytes_read)
799 {
800 if (!mutt_file_seek(fp, offset, SEEK_SET))
801 {
802 return -1;
803 }
804 }
805
806 *buf = (unsigned char *) mutt_file_read_line((char *) *buf, blen, fp, NULL, MUTT_RL_EOL);
807 if (!*buf)
808 {
809 fmt[0] = NULL;
810 return -1;
811 }
812
813 *bytes_read = ftello(fp);
814 b_read = (int) (*bytes_read - offset);
815 *buf_ready = 1;
816
817 struct Buffer *stripped = buf_pool_get();
818 buf_alloc(stripped, *blen);
819 buf_strip_formatting(stripped, (const char *) *buf, 1);
820 /* This should be a noop, because *fmt should be NULL */
821 FREE(fmt);
822 *fmt = (unsigned char *) buf_strdup(stripped);
823 buf_pool_release(&stripped);
824 }
825
826 return b_read;
827}
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:730
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:685
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:655
#define MUTT_RL_EOL
don't strip \n / \r\n
Definition: file.h:42
#define FREE(x)
Definition: memory.h:62
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
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 846 of file display.c.

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

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