NeoMutt  2024-04-16-36-g75b6fb
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
display.c File Reference

Pager Display. More...

#include "config.h"
#include <ctype.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.
 
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 57 of file display.c.

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

◆ comp_syntax_t()

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

Search for a Syntax using bsearch(3)

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

Definition at line 97 of file display.c.

98{
99 const int *cnt = (const int *) m1;
100 const struct TextSyntax *stx = (const struct TextSyntax *) m2;
101
102 if (*cnt < stx->first)
103 return -1;
104 if (*cnt >= stx->last)
105 return 1;
106 return 0;
107}
Highlighting for a piece of text.
Definition: display.h:40
int last
Last character in line to be coloured (not included)
Definition: display.h:43
int first
First character in line to be coloured.
Definition: display.h:42
+ Here is the caller graph for this function:

◆ resolve_color()

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

Set the colour for a line of text.

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

Definition at line 119 of file display.c.

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

◆ append_line()

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

Add a new Line to the array.

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

Definition at line 258 of file display.c.

259{
260 int m;
261
262 lines[line_num + 1].cid = lines[line_num].cid;
263 (lines[line_num + 1].syntax)[0].attr_color = (lines[line_num].syntax)[0].attr_color;
264 lines[line_num + 1].cont_line = true;
265
266 /* find the real start of the line */
267 for (m = line_num; m >= 0; m--)
268 if (!lines[m].cont_line)
269 break;
270
271 (lines[line_num + 1].syntax)[0].first = m;
272 (lines[line_num + 1].syntax)[0].last = (lines[line_num].cont_line) ?
273 cnt + (lines[line_num].syntax)[0].last :
274 cnt;
275}
bool cont_line
Continuation of a previous line (wrapped by NeoMutt)
Definition: display.h:54
short cid
Default line colour, e.g. MT_COLOR_QUOTED.
Definition: display.h:53
+ Here is the caller graph for this function:

◆ check_marker()

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

Check that the unique marker is present.

Parameters
qMarker string
pString to check
Return values
numOffset of marker

Definition at line 283 of file display.c.

284{
285 for (; (p[0] == q[0]) && (q[0] != '\0') && (p[0] != '\0') && (q[0] != '\a') &&
286 (p[0] != '\a');
287 p++, q++)
288 {
289 }
290
291 return (int) (*p - *q);
292}
+ Here is the caller graph for this function:

◆ check_attachment_marker()

static int check_attachment_marker ( const char *  p)
static

Check that the unique marker is present.

Parameters
pString to check
Return values
numOffset of marker

Definition at line 299 of file display.c.

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

◆ check_protected_header_marker()

static int check_protected_header_marker ( const char *  p)
static

Check that the unique marker is present.

Parameters
pString to check
Return values
numOffset of marker

Definition at line 309 of file display.c.

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

◆ mutt_is_quote_line()

bool mutt_is_quote_line ( char *  line,
regmatch_t *  pmatch 
)

Is a line of message text a quote?

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

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

Definition at line 323 of file display.c.

324{
325 bool is_quote = false;
326 const struct Regex *c_smileys = cs_subset_regex(NeoMutt->sub, "smileys");
327 regmatch_t pmatch_internal[1] = { 0 };
328
329 if (!pmatch)
330 pmatch = pmatch_internal;
331
332 const struct Regex *c_quote_regex = cs_subset_regex(NeoMutt->sub, "quote_regex");
333 if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
334 {
335 regmatch_t smatch[1] = { 0 };
336 if (mutt_regex_capture(c_smileys, line, 1, smatch))
337 {
338 if (smatch[0].rm_so > 0)
339 {
340 char c = line[smatch[0].rm_so];
341 line[smatch[0].rm_so] = 0;
342
343 if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
344 is_quote = true;
345
346 line[smatch[0].rm_so] = c;
347 }
348 }
349 else
350 {
351 is_quote = true;
352 }
353 }
354
355 return is_quote;
356}
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:218
bool mutt_regex_capture(const struct Regex *regex, const char *str, size_t nmatch, regmatch_t matches[])
Match a regex against a string, with provided options.
Definition: regex.c:597
Cached regular expression.
Definition: regex3.h:85
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ match_body_patterns()

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

Match body patterns, e.g.

color quoted

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

Definition at line 364 of file display.c.

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

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

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

722{
723 const char *s = src;
724
725 buf_reset(dest);
726
727 if (!s)
728 return;
729
730 while (s[0] != '\0')
731 {
732 if ((s[0] == '\010') && (s > src))
733 {
734 if (s[1] == '_') /* underline */
735 {
736 s += 2;
737 }
738 else if (s[1] && buf_len(dest)) /* bold or overstrike */
739 {
740 dest->dptr--;
741 buf_addch(dest, s[1]);
742 s += 2;
743 }
744 else /* ^H */
745 {
746 buf_addch(dest, *s++);
747 }
748 continue;
749 }
750
751 int len = ansi_color_seq_length(s);
752 if (len > 0)
753 {
754 s += len;
755 }
756 else if (strip_markers && (s[0] == '\033') && (s[1] == ']') &&
758 {
759 mutt_debug(LL_DEBUG2, "Seen attachment marker\n");
760 while (*s++ != '\a')
761 ; /* skip pseudo-ANSI sequence */
762 }
763 else
764 {
765 buf_addch(dest, *s++);
766 }
767 }
768}
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:490
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:75
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
int ansi_color_seq_length(const char *str)
Is this an ANSI escape sequence?
Definition: parse_ansi.c:79
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 782 of file display.c.

784{
785 static int b_read = 0;
786
787 if (*buf_ready == 0)
788 {
789 if (offset != *bytes_read)
790 {
791 if (!mutt_file_seek(fp, offset, SEEK_SET))
792 {
793 return -1;
794 }
795 }
796
797 *buf = (unsigned char *) mutt_file_read_line((char *) *buf, blen, fp, NULL, MUTT_RL_EOL);
798 if (!*buf)
799 {
800 fmt[0] = NULL;
801 return -1;
802 }
803
804 *bytes_read = ftello(fp);
805 b_read = (int) (*bytes_read - offset);
806 *buf_ready = 1;
807
808 struct Buffer *stripped = buf_pool_get();
809 buf_alloc(stripped, *blen);
810 buf_strip_formatting(stripped, (const char *) *buf, 1);
811 /* This should be a noop, because *fmt should be NULL */
812 FREE(fmt);
813 *fmt = (unsigned char *) buf_strdup(stripped);
814 buf_pool_release(&stripped);
815 }
816
817 return b_read;
818}
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:570
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:336
void buf_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
Removes ANSI and backspace formatting.
Definition: display.c:721
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
Read a line from a file.
Definition: file.c:805
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:775
#define MUTT_RL_EOL
don't strip \n / \r\n
Definition: file.h:42
#define FREE(x)
Definition: memory.h:45
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
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 837 of file display.c.

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

◆ display_line()

int display_line ( FILE *  fp,
LOFF_T *  bytes_read,
struct Line **  lines,
int  line_num,
int *  lines_used,
int *  lines_max,
PagerFlags  flags,
struct QuoteStyle **  quote_list,
int *  q_level,
bool *  force_redraw,
regex_t *  search_re,
struct MuttWindow win_pager,
struct AttrColorList *  ansi_list 
)

Print a line on screen.

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

Definition at line 1042 of file display.c.

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