NeoMutt  2025-12-11-980-ge38c27
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 "module_data.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 58 of file display.c.

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

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

122{
123 struct AttrColor def_color = { 0 }; /* color without syntax highlight */
124 struct AttrColor color = { 0 }; /* final color */
125 static struct AttrColor last_color = { 0 }; /* last color set */
126 bool search = false;
127 int m;
128 struct TextSyntax *matching_chunk = NULL;
129
130 if (cnt == 0)
131 {
132 last_color.curses_color = NULL;
133 last_color.attrs = A_NORMAL;
134 }
135
136 if (lines[line_num].cont_line)
137 {
138 const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
139 if (!cnt && c_markers)
140 {
142 mutt_window_addch(win, '+');
143 }
144 m = (lines[line_num].syntax)[0].first;
145 cnt += (lines[line_num].syntax)[0].last;
146 }
147 else
148 {
149 m = line_num;
150 }
151 if (flags & MUTT_PAGER_LOGS)
152 {
153 def_color = *(lines[line_num].syntax[0].attr_color);
154 }
155 else if (!(flags & MUTT_SHOWCOLOR))
156 {
157 if (flags & MUTT_PAGER_STRIPES)
158 {
159 def_color = *simple_color_get(((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD :
161 }
162 else
163 {
164 def_color = *simple_color_get(MT_COLOR_NORMAL);
165 }
166 }
167 else if ((lines[m].cid == MT_COLOR_HEADER) && lines[m].syntax[0].attr_color)
168 {
169 def_color = *lines[m].syntax[0].attr_color;
170 }
171 else
172 {
173 def_color = *simple_color_get(lines[m].cid);
174 }
175
176 if ((flags & MUTT_SHOWCOLOR) && COLOR_QUOTED(lines[m].cid))
177 {
178 struct QuoteStyle *qc = lines[m].quote;
179
180 if (qc)
181 {
182 def_color = attr_color_copy(qc->attr_color);
183
184 while (qc && (qc->prefix_len > cnt))
185 {
186 def_color = attr_color_copy(qc->attr_color);
187 qc = qc->up;
188 }
189 }
190 }
191
192 color = def_color;
193 if ((flags & MUTT_SHOWCOLOR) && lines[m].syntax)
194 {
195 matching_chunk = bsearch(&cnt, lines[m].syntax, lines[m].syntax_arr_size,
196 sizeof(struct TextSyntax), comp_syntax_t);
197 if (matching_chunk && (cnt >= matching_chunk->first) &&
198 (cnt < matching_chunk->last))
199 {
200 if (matching_chunk->attr_color)
201 color = *matching_chunk->attr_color;
202 }
203 }
204
205 if ((flags & MUTT_SEARCH) && lines[m].search)
206 {
207 matching_chunk = bsearch(&cnt, lines[m].search, lines[m].search_arr_size,
208 sizeof(struct TextSyntax), comp_syntax_t);
209 if (matching_chunk && (cnt >= matching_chunk->first) &&
210 (cnt < matching_chunk->last))
211 {
213 search = true;
214 }
215 }
216
217 /* handle "special" bold & underlined characters */
218 if (special & A_BOLD)
219 {
222 else
223 color.attrs |= A_BOLD;
224 }
225 else if (special & A_UNDERLINE)
226 {
229 else
230 color.attrs |= A_UNDERLINE;
231 }
232 else if (special & A_ITALIC)
233 {
236 else
237 color.attrs |= A_ITALIC;
238 }
239 else if (ansi->attr_color)
240 {
241 color = *ansi->attr_color;
242 }
243
244 if (!attr_color_match(&color, &last_color))
245 {
247 &color);
248 mutt_curses_set_color(ac_merge);
249 last_color = color;
250 }
251}
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:120
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition simple.c:98
@ 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:98
static int search(struct Menu *menu, int op, int *match)
Search a menu.
Definition functions.c:59
const struct AttrColor * merged_color_overlay(const struct AttrColor *base, const struct AttrColor *over)
Combine two colours.
Definition merged.c:110
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:77
#define MUTT_SHOWCOLOR
Show characters in color otherwise don't show characters.
Definition lib.h:65
#define MUTT_PAGER_LOGS
Logview mode.
Definition lib.h:75
#define MUTT_SEARCH
Resolve search patterns.
Definition lib.h:67
#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 259 of file display.c.

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

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

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

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

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

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

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

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

738{
739 const char *s = src;
740
741 buf_reset(dest);
742
743 if (!s)
744 return;
745
746 while (s[0] != '\0')
747 {
748 if ((s[0] == '\010') && (s > src))
749 {
750 if (s[1] == '_') /* underline */
751 {
752 s += 2;
753 }
754 else if (s[1] && buf_len(dest)) /* bold or overstrike */
755 {
756 dest->dptr--;
757 buf_addch(dest, s[1]);
758 s += 2;
759 }
760 else /* ^H */
761 {
762 buf_addch(dest, *s++);
763 }
764 continue;
765 }
766
767 int len = ansi_color_seq_length(s);
768 if (len > 0)
769 {
770 s += len;
771 }
772 else if (strip_markers && (s[0] == '\033') && (s[1] == ']') &&
774 {
775 mutt_debug(LL_DEBUG2, "Seen attachment marker\n");
776 while (*s++ != '\a')
777 ; /* skip pseudo-ANSI sequence */
778 }
779 else
780 {
781 buf_addch(dest, *s++);
782 }
783 }
784}
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition buffer.c:497
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:248
#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 798 of file display.c.

800{
801 static int b_read = 0;
802
803 if (*buf_ready == 0)
804 {
805 if (offset != *bytes_read)
806 {
807 if (!mutt_file_seek(fp, offset, SEEK_SET))
808 {
809 return -1;
810 }
811 }
812
813 *buf = (unsigned char *) mutt_file_read_line((char *) *buf, blen, fp, NULL, MUTT_RL_EOL);
814 if (!*buf)
815 {
816 fmt[0] = NULL;
817 return -1;
818 }
819
820 *bytes_read = ftello(fp);
821 LOFF_T diff = *bytes_read - offset;
822 b_read = (diff > INT_MAX) ? INT_MAX : (int) diff;
823 *buf_ready = 1;
824
825 struct Buffer *stripped = buf_pool_get();
826 buf_alloc(stripped, *blen);
827 buf_strip_formatting(stripped, (const char *) *buf, 1);
828 /* This should be a noop, because *fmt should be NULL */
829 FREE(fmt);
830 *fmt = (unsigned char *) buf_strdup(stripped);
831 buf_pool_release(&stripped);
832 }
833
834 return b_read;
835}
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:577
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition buffer.c:342
void buf_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
Removes ANSI and backspace formatting.
Definition display.c:737
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
Read a line from a file.
Definition file.c:678
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition file.c:648
@ MUTT_RL_EOL
don't strip \n / \r\n
Definition file.h:45
#define 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 854 of file display.c.

858{
859 int space = -1; /* index of the last space or TAB */
860 const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
861 size_t col = c_markers ? (*lines)[line_num].cont_line : 0;
862 size_t k;
863 int ch;
864 int vch;
865 int last_special = -1;
866 int special = 0;
867 int t;
868 wchar_t wc = 0;
869 mbstate_t mbstate = { 0 }; // FIXME: this should come from lines
870 const size_t c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
871 size_t wrap_cols = mutt_window_wrap_cols(width, (flags & MUTT_PAGER_NOWRAP) ? 0 : c_wrap);
872
873 if (check_attachment_marker((char *) buf) == 0)
874 wrap_cols = width;
875
876 struct PagerPrivateData *priv = win->parent->wdata;
877 enum PagerMode mode = priv->pview->mode;
878 const bool c_allow_ansi = (mode == PAGER_MODE_OTHER) ||
879 cs_subset_bool(NeoMutt->sub, "allow_ansi");
880
881 for (ch = 0, vch = 0; ch < cnt; ch += k, vch += k)
882 {
883 /* Handle ANSI sequences */
884 if (buf[ch] == '\033') // Escape
885 {
886 int len = ansi_color_parse((const char *) buf + ch, ansi, ansi_list, !c_allow_ansi);
887 ch += len;
888 }
889
890 while ((cnt - ch >= 2) && (buf[ch] == '\033') && (buf[ch + 1] == ']') && // Escape
891 ((check_attachment_marker((char *) buf + ch) == 0) ||
892 (check_protected_header_marker((char *) buf + ch) == 0)))
893 {
894 while (buf[ch++] != '\a')
895 if (ch >= cnt)
896 break;
897 }
898
899 /* is anything left to do? */
900 if (ch >= cnt)
901 break;
902
903 k = mbrtowc(&wc, (char *) buf + ch, cnt - ch, &mbstate);
904 if ((k == ICONV_BUF_TOO_SMALL) || (k == ICONV_ILLEGAL_SEQ))
905 {
906 if (k == ICONV_ILLEGAL_SEQ)
907 memset(&mbstate, 0, sizeof(mbstate));
908 mutt_debug(LL_DEBUG1, "mbrtowc returned %zu; errno = %d\n", k, errno);
909 if ((col + 4) > wrap_cols)
910 break;
911 col += 4;
912 if (ansi)
913 mutt_window_printf(win, "\\%03o", buf[ch]);
914 k = 1;
915 continue;
916 }
917 if (k == 0)
918 k = 1;
919
920 if (CharsetIsUtf8)
921 {
922 /* zero width space, zero with non-joiner, zero width no-break space */
923 if ((wc == 0x200B) || (wc == 0x200C) || (wc == 0xFEFF))
924 {
925 mutt_debug(LL_DEBUG3, "skip zero-width character U+%04X\n", (unsigned short) wc);
926 continue;
927 }
929 {
930 mutt_debug(LL_DEBUG3, "filtered U+%04X\n", (unsigned short) wc);
931 continue;
932 }
933 }
934
935 /* Handle backspace */
936 special = 0;
937 if (IsWPrint(wc))
938 {
939 wchar_t wc1 = 0;
940 mbstate_t mbstate1 = mbstate;
941 size_t k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
942 while ((k1 != ICONV_BUF_TOO_SMALL) && (k1 != ICONV_ILLEGAL_SEQ) &&
943 (k1 > 0) && (wc1 == '\b'))
944 {
945 const size_t k2 = mbrtowc(&wc1, (char *) buf + ch + k + k1,
946 cnt - ch - k - k1, &mbstate1);
947 if ((k2 == ICONV_BUF_TOO_SMALL) || (k2 == ICONV_ILLEGAL_SEQ) ||
948 (k2 == 0) || (!IsWPrint(wc1)))
949 {
950 break;
951 }
952
953 if (wc == wc1)
954 {
955 special |= ((wc == '_') && (special & A_UNDERLINE)) ? A_UNDERLINE : A_BOLD;
956 }
957 else if ((wc == '_') || (wc1 == '_'))
958 {
959 special |= A_UNDERLINE;
960 wc = (wc1 == '_') ? wc : wc1;
961 }
962 else
963 {
964 /* special = 0; / * overstrike: nothing to do! */
965 wc = wc1;
966 }
967
968 ch += k + k1;
969 k = k2;
970 mbstate = mbstate1;
971 k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
972 }
973 }
974
975 if (ansi && ((flags & (MUTT_SHOWCOLOR | MUTT_SEARCH | MUTT_PAGER_MARKER)) ||
976 special || last_special || (ansi->attrs != A_NORMAL)))
977 {
978 resolve_color(win, *lines, line_num, vch, flags, special, ansi);
979 last_special = special;
980 }
981
982 /* no-break space, narrow no-break space */
983 if (IsWPrint(wc) || (CharsetIsUtf8 && ((wc == 0x00A0) || (wc == 0x202F))))
984 {
985 if (wc == ' ')
986 {
987 space = ch;
988 }
989 t = wcwidth(wc);
990 if (col + t > wrap_cols)
991 break;
992 col += t;
993 if (ansi)
994 mutt_addwch(win, wc);
995 }
996 else if (wc == '\n')
997 {
998 break;
999 }
1000 else if (wc == '\t')
1001 {
1002 space = ch;
1003 t = (col & ~7) + 8;
1004 if (t > wrap_cols)
1005 break;
1006 if (ansi)
1007 for (; col < t; col++)
1008 mutt_window_addch(win, ' ');
1009 else
1010 col = t;
1011 }
1012 else if ((wc < 0x20) || (wc == 0x7f))
1013 {
1014 if ((col + 2) > wrap_cols)
1015 break;
1016 col += 2;
1017 if (ansi)
1018 mutt_window_printf(win, "^%c", (char) (('@' + wc) & 0x7f));
1019 }
1020 else if (wc < 0x100)
1021 {
1022 if ((col + 4) > wrap_cols)
1023 break;
1024 col += 4;
1025 if (ansi)
1026 mutt_window_printf(win, "\\%03lo", (long) wc);
1027 }
1028 else
1029 {
1030 if ((col + 1) > wrap_cols)
1031 break;
1032 col += k;
1033 if (ansi)
1035 }
1036 }
1037 *pspace = space;
1038 *pcol = col;
1039 *pvch = vch;
1040 *pspecial = special;
1041 return ch;
1042}
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:120
@ 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:67
wchar_t ReplacementChar
When a Unicode character can't be displayed, use this instead.
Definition charset.c:62
#define ICONV_BUF_TOO_SMALL
Error value for iconv() - Buffer too small.
Definition charset.h:116
#define ICONV_ILLEGAL_SEQ
Error value for iconv() - Illegal sequence.
Definition charset.h:114
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:74
#define MUTT_PAGER_MARKER
Use markers if option is set.
Definition lib.h:72
PagerMode
Determine the behaviour of the Pager.
Definition lib.h:136
@ PAGER_MODE_OTHER
Pager is invoked via 3rd path. Non-email content is likely to be shown.
Definition lib.h:143
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:175
+ 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 1063 of file display.c.

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