NeoMutt  2023-03-22-27-g3cb248
Teaching an old dog new tricks
DOXYGEN
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 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. More...
 
static int comp_syntax_t (const void *m1, const void *m2)
 Search for a Syntax using bsearch(3) More...
 
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. More...
 
static void append_line (struct Line *lines, int line_num, int cnt)
 Add a new Line to the array. More...
 
static int check_marker (const char *q, const char *p)
 Check that the unique marker is present. More...
 
static int check_attachment_marker (const char *p)
 Check that the unique marker is present. More...
 
static int check_protected_header_marker (const char *p)
 Check that the unique marker is present. More...
 
bool mutt_is_quote_line (char *line, regmatch_t *pmatch)
 Is a line of message text a quote? More...
 
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. More...
 
void mutt_buffer_strip_formatting (struct Buffer *dest, const char *src, bool strip_markers)
 Removes ANSI and backspace formatting. More...
 
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. More...
 
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. More...
 
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. More...
 

Detailed Description

Pager Display.

Authors
  • Richard Russon

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

54{
55 const unsigned int NUM_SIG_LINES = 4; // The amount of lines a signature takes
56 unsigned int count = 0;
57
58 while ((offset > 0) && (count <= NUM_SIG_LINES))
59 {
60 if (info[offset].cid != MT_COLOR_SIGNATURE)
61 break;
62 count++;
63 offset--;
64 }
65
66 if (count == 0)
67 return -1;
68
69 if (count > NUM_SIG_LINES)
70 {
71 /* check for a blank line */
72 while (*s)
73 {
74 if (!IS_SPACE(*s))
75 return 0;
76 s++;
77 }
78
79 return -1;
80 }
81
82 return 0;
83}
@ MT_COLOR_SIGNATURE
Pager: signature lines.
Definition: color.h:74
#define IS_SPACE(ch)
Definition: string2.h:38
+ 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 93 of file display.c.

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

117{
118 struct AttrColor def_color = { 0 }; /* color without syntax highlight */
119 struct AttrColor color = { 0 }; /* final color */
120 static struct AttrColor last_color = { 0 }; /* last color set */
121 bool search = false;
122 int m;
123 struct TextSyntax *matching_chunk = NULL;
124
125 if (cnt == 0)
126 {
127 last_color.curses_color = NULL;
128 last_color.attrs = 0;
129 }
130
131 if (lines[line_num].cont_line)
132 {
133 const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
134 if (!cnt && c_markers)
135 {
137 mutt_window_addch(win, '+');
138 }
139 m = (lines[line_num].syntax)[0].first;
140 cnt += (lines[line_num].syntax)[0].last;
141 }
142 else
143 {
144 m = line_num;
145 }
146 if (flags & MUTT_PAGER_LOGS)
147 {
148 def_color = *(lines[line_num].syntax[0].attr_color);
149 }
150 else if (!(flags & MUTT_SHOWCOLOR))
151 {
152 def_color = *simple_color_get(MT_COLOR_NORMAL);
153 }
154 else if ((lines[m].cid == MT_COLOR_HEADER) && lines[m].syntax[0].attr_color)
155 {
156 def_color = *lines[m].syntax[0].attr_color;
157 }
158 else
159 {
160 def_color = *simple_color_get(lines[m].cid);
161 }
162
163 if ((flags & MUTT_SHOWCOLOR) && (lines[m].cid == MT_COLOR_QUOTED))
164 {
165 struct QuoteStyle *qc = lines[m].quote;
166
167 if (qc)
168 {
169 def_color = attr_color_copy(qc->attr_color);
170
171 while (qc && (qc->prefix_len > cnt))
172 {
173 def_color = attr_color_copy(qc->attr_color);
174 qc = qc->up;
175 }
176 }
177 }
178
179 color = def_color;
180 if ((flags & MUTT_SHOWCOLOR) && lines[m].syntax_arr_size)
181 {
182 matching_chunk = bsearch(&cnt, lines[m].syntax, lines[m].syntax_arr_size,
183 sizeof(struct TextSyntax), comp_syntax_t);
184 if (matching_chunk && (cnt >= matching_chunk->first) &&
185 (cnt < matching_chunk->last))
186 {
187 if (matching_chunk->attr_color)
188 color = *matching_chunk->attr_color;
189 }
190 }
191
192 if ((flags & MUTT_SEARCH) && lines[m].search_arr_size)
193 {
194 matching_chunk = bsearch(&cnt, lines[m].search, lines[m].search_arr_size,
195 sizeof(struct TextSyntax), comp_syntax_t);
196 if (matching_chunk && (cnt >= matching_chunk->first) &&
197 (cnt < matching_chunk->last))
198 {
200 search = true;
201 }
202 }
203
204 /* handle "special" bold & underlined characters */
205 if (special & A_BOLD)
206 {
209 else
210 color.attrs |= A_BOLD;
211 }
212 else if (special & A_UNDERLINE)
213 {
216 else
217 color.attrs |= A_UNDERLINE;
218 }
219 else if (special & A_ITALIC)
220 {
223 else
224 color.attrs |= A_ITALIC;
225 }
226 else if (ansi->attr_color)
227 {
228 color = *ansi->attr_color;
229 }
230
231 if (!attr_color_match(&color, &last_color))
232 {
234 &color);
235 mutt_curses_set_color(ac_merge);
236 last_color = color;
237 }
238}
bool attr_color_match(struct AttrColor *ac1, struct AttrColor *ac2)
Do the colours match?
Definition: attr.c:174
struct AttrColor attr_color_copy(const struct AttrColor *ac)
Copy a colour.
Definition: attr.c:146
bool simple_color_is_set(enum ColorId cid)
Is the object coloured?
Definition: simple.c:95
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition: simple.c:74
@ MT_COLOR_MARKERS
Pager: markers, line continuation.
Definition: color.h:54
@ MT_COLOR_QUOTED
Pager: quoted text.
Definition: color.h:61
@ MT_COLOR_HEADER
Message headers (takes a pattern)
Definition: color.h:51
@ MT_COLOR_BOLD
Bold text.
Definition: color.h:43
@ MT_COLOR_NORMAL
Plain text.
Definition: color.h:57
@ MT_COLOR_SEARCH
Pager: search matches.
Definition: color.h:62
@ MT_COLOR_ITALIC
Italic text.
Definition: color.h:53
@ MT_COLOR_UNDERLINE
Underlined text.
Definition: color.h:78
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
static int comp_syntax_t(const void *m1, const void *m2)
Search for a Syntax using bsearch(3)
Definition: display.c:93
static int search(struct Menu *menu, int op)
Search a menu.
Definition: functions.c:57
struct AttrColor * merged_color_overlay(struct AttrColor *base, struct AttrColor *over)
Combine two colours.
Definition: merged.c:109
void mutt_curses_set_color(struct AttrColor *ac)
Set the colour and attributes for text.
Definition: mutt_curses.c:40
struct AttrColor * mutt_curses_set_color_by_id(enum ColorId cid)
Set the colour and attributes by the colour id.
Definition: mutt_curses.c:81
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
Definition: mutt_window.c:382
#define MUTT_SHOWCOLOR
Show characters in color otherwise don't show characters.
Definition: lib.h:60
#define MUTT_PAGER_LOGS
Logview mode.
Definition: lib.h:72
#define MUTT_SEARCH
Resolve search patterns.
Definition: lib.h:62
struct AttrColor * attr_color
Curses colour of text.
Definition: ansi.h:35
A curses colour and its attributes.
Definition: attr.h:35
int attrs
Text attributes, e.g. A_BOLD.
Definition: attr.h:37
struct CursesColor * curses_color
Underlying Curses colour.
Definition: attr.h:36
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:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
Style of quoted text.
Definition: quoted.h:68
struct AttrColor * attr_color
Colour and attribute of the text.
Definition: quoted.h:70
struct QuoteStyle * up
Definition: quoted.h:74
size_t prefix_len
Length of the prefix string.
Definition: quoted.h:72
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 246 of file display.c.

247{
248 int m;
249
250 lines[line_num + 1].cid = lines[line_num].cid;
251 (lines[line_num + 1].syntax)[0].attr_color = (lines[line_num].syntax)[0].attr_color;
252 lines[line_num + 1].cont_line = 1;
253
254 /* find the real start of the line */
255 for (m = line_num; m >= 0; m--)
256 if (lines[m].cont_line == 0)
257 break;
258
259 (lines[line_num + 1].syntax)[0].first = m;
260 (lines[line_num + 1].syntax)[0].last = (lines[line_num].cont_line) ?
261 cnt + (lines[line_num].syntax)[0].last :
262 cnt;
263}
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 271 of file display.c.

272{
273 for (; (p[0] == q[0]) && (q[0] != '\0') && (p[0] != '\0') && (q[0] != '\a') &&
274 (p[0] != '\a');
275 p++, q++)
276 {
277 }
278
279 return (int) (*p - *q);
280}
+ 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 287 of file display.c.

288{
290}
static int check_marker(const char *q, const char *p)
Check that the unique marker is present.
Definition: display.c:271
const char * state_attachment_marker(void)
Get a unique (per-run) ANSI string to mark PGP messages in an email.
Definition: state.c:43
+ 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 297 of file display.c.

298{
300}
const char * state_protected_header_marker(void)
Get a unique (per-run) ANSI string to mark protected headers in an email.
Definition: state.c:57
+ 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 311 of file display.c.

312{
313 bool is_quote = false;
314 const struct Regex *c_smileys = cs_subset_regex(NeoMutt->sub, "smileys");
315 regmatch_t pmatch_internal[1], smatch[1];
316
317 if (!pmatch)
318 pmatch = pmatch_internal;
319
320 const struct Regex *c_quote_regex = cs_subset_regex(NeoMutt->sub, "quote_regex");
321 if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
322 {
323 if (mutt_regex_capture(c_smileys, line, 1, smatch))
324 {
325 if (smatch[0].rm_so > 0)
326 {
327 char c = line[smatch[0].rm_so];
328 line[smatch[0].rm_so] = 0;
329
330 if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
331 is_quote = true;
332
333 line[smatch[0].rm_so] = c;
334 }
335 }
336 else
337 {
338 is_quote = true;
339 }
340 }
341
342 return is_quote;
343}
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:243
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:618
Cached regular expression.
Definition: regex3.h:89
+ 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 358 of file display.c.

362{
363 struct RegexColor *color_line = NULL;
364 struct RegexColorList *head = NULL;
365 regmatch_t pmatch[1];
366 bool found;
367 bool null_rx;
368 const bool c_header_color_partial = cs_subset_bool(NeoMutt->sub, "header_color_partial");
369 int offset, i = 0;
370
371 if ((line_num == 0) || simple_color_is_header(lines[line_num - 1].cid) ||
373 {
374 if (buf[0] == '\n') /* end of header */
375 {
376 lines[line_num].cid = MT_COLOR_NORMAL;
378 }
379 else
380 {
381 /* if this is a continuation of the previous line, use the previous
382 * line's color as default. */
383 if ((line_num > 0) && ((buf[0] == ' ') || (buf[0] == '\t')))
384 {
385 lines[line_num].cid = lines[line_num - 1].cid; /* wrapped line */
386 if (!c_header_color_partial)
387 {
388 (lines[line_num].syntax)[0].attr_color =
389 (lines[line_num - 1].syntax)[0].attr_color;
390 lines[line_num].cont_header = 1;
391 }
392 }
393 else
394 {
395 lines[line_num].cid = MT_COLOR_HDRDEFAULT;
396 }
397
398 /* When this option is unset, we color the entire header the
399 * same color. Otherwise, we handle the header patterns just
400 * like body patterns (further below). */
401 if (!c_header_color_partial)
402 {
404 {
405 if (regexec(&color_line->regex, buf, 0, NULL, 0) == 0)
406 {
407 lines[line_num].cid = MT_COLOR_HEADER;
408 lines[line_num].syntax[0].attr_color =
409 merged_color_overlay(lines[line_num].syntax[0].attr_color,
411 lines[line_num].syntax[0].attr_color = merged_color_overlay(
412 lines[line_num].syntax[0].attr_color, &color_line->attr_color);
413 if (lines[line_num].cont_header)
414 {
415 /* adjust the previous continuation lines to reflect the color of this continuation line */
416 int j;
417 for (j = line_num - 1; j >= 0 && lines[j].cont_header; --j)
418 {
419 lines[j].cid = lines[line_num].cid;
420 lines[j].syntax[0].attr_color = lines[line_num].syntax[0].attr_color;
421 }
422 /* now adjust the first line of this header field */
423 if (j >= 0)
424 {
425 lines[j].cid = lines[line_num].cid;
426 lines[j].syntax[0].attr_color = lines[line_num].syntax[0].attr_color;
427 }
428 *force_redraw = true; /* the previous lines have already been drawn on the screen */
429 }
430 }
431 }
432 }
433 }
434 }
435 else if (mutt_str_startswith(raw, "\033[0m")) // Escape: a little hack...
436 lines[line_num].cid = MT_COLOR_NORMAL;
437 else if (check_attachment_marker((char *) raw) == 0)
438 lines[line_num].cid = MT_COLOR_ATTACHMENT;
439 else if (mutt_str_equal("-- \n", buf) || mutt_str_equal("-- \r\n", buf))
440 {
441 i = line_num + 1;
442
443 lines[line_num].cid = MT_COLOR_SIGNATURE;
444 while ((i < lines_used) && (check_sig(buf, lines, i - 1) == 0) &&
445 ((lines[i].cid == MT_COLOR_NORMAL) || (lines[i].cid == MT_COLOR_QUOTED) ||
446 (lines[i].cid == MT_COLOR_HEADER)))
447 {
448 /* oops... */
449 if (lines[i].syntax_arr_size)
450 {
451 lines[i].syntax_arr_size = 0;
452 mutt_mem_realloc(&(lines[line_num].syntax), sizeof(struct TextSyntax));
453 }
454 lines[i++].cid = MT_COLOR_SIGNATURE;
455 }
456 }
457 else if (check_sig(buf, lines, line_num - 1) == 0)
458 lines[line_num].cid = MT_COLOR_SIGNATURE;
459 else if (mutt_is_quote_line(buf, pmatch))
460
461 {
462 if (q_classify && (lines[line_num].quote == NULL))
463 {
464 lines[line_num].quote = qstyle_classify(quote_list, buf + pmatch[0].rm_so,
465 pmatch[0].rm_eo - pmatch[0].rm_so,
466 force_redraw, q_level);
467 }
468 lines[line_num].cid = MT_COLOR_QUOTED;
469 }
470 else
471 {
472 lines[line_num].cid = MT_COLOR_NORMAL;
473 }
474
475 /* body patterns */
476 if ((lines[line_num].cid == MT_COLOR_NORMAL) || (lines[line_num].cid == MT_COLOR_QUOTED) ||
477 ((lines[line_num].cid == MT_COLOR_HDRDEFAULT) && c_header_color_partial))
478 {
479 size_t nl;
480
481 /* don't consider line endings part of the buffer
482 * for regex matching */
483 nl = mutt_str_len(buf);
484 if ((nl > 0) && (buf[nl - 1] == '\n'))
485 buf[nl - 1] = '\0';
486
487 i = 0;
488 offset = 0;
489 lines[line_num].syntax_arr_size = 0;
490 if (lines[line_num].cid == MT_COLOR_HDRDEFAULT)
491 {
493 }
494 else
495 {
497 }
498 STAILQ_FOREACH(color_line, head, entries)
499 {
500 color_line->stop_matching = false;
501 }
502
503 do
504 {
505 if (!buf[offset])
506 break;
507
508 found = false;
509 null_rx = false;
510 STAILQ_FOREACH(color_line, head, entries)
511 {
512 if (color_line->stop_matching)
513 continue;
514
515 if ((regexec(&color_line->regex, buf + offset, 1, pmatch,
516 ((offset != 0) ? REG_NOTBOL : 0)) != 0))
517 {
518 /* Once a regex fails to match, don't try matching it again.
519 * On very long lines this can cause a performance issue if there
520 * are other regexes that have many matches. */
521 color_line->stop_matching = true;
522 continue;
523 }
524
525 if (pmatch[0].rm_eo == pmatch[0].rm_so)
526 {
527 null_rx = true; /* empty regex; don't add it, but keep looking */
528 continue;
529 }
530
531 if (!found)
532 {
533 // Abort if we fill up chunks. Yes, this really happened.
534 if (lines[line_num].syntax_arr_size == SHRT_MAX)
535 {
536 null_rx = false;
537 break;
538 }
539 if (++(lines[line_num].syntax_arr_size) > 1)
540 {
541 mutt_mem_realloc(&(lines[line_num].syntax),
542 (lines[line_num].syntax_arr_size) * sizeof(struct TextSyntax));
543 // Zero the new entry
544 const int index = lines[line_num].syntax_arr_size - 1;
545 struct TextSyntax *ts = &lines[line_num].syntax[index];
546 memset(ts, 0, sizeof(*ts));
547 }
548 }
549 i = lines[line_num].syntax_arr_size - 1;
550 pmatch[0].rm_so += offset;
551 pmatch[0].rm_eo += offset;
552
553 if (!found || (pmatch[0].rm_so < (lines[line_num].syntax)[i].first) ||
554 ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
555 (pmatch[0].rm_eo > (lines[line_num].syntax)[i].last)))
556 {
557 (lines[line_num].syntax)[i].attr_color = &color_line->attr_color;
558 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
559 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
560 }
561 else if ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
562 (pmatch[0].rm_eo == (lines[line_num].syntax)[i].last))
563 {
564 (lines[line_num].syntax)[i].attr_color = merged_color_overlay(
565 (lines[line_num].syntax)[i].attr_color, &color_line->attr_color);
566 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
567 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
568 }
569
570 found = true;
571 null_rx = false;
572 }
573
574 if (null_rx)
575 offset++; /* avoid degenerate cases */
576 else
577 offset = (lines[line_num].syntax)[i].last;
578 } while (found || null_rx);
579 if (nl > 0)
580 buf[nl] = '\n';
581 }
582
583 /* attachment patterns */
584 if (lines[line_num].cid == MT_COLOR_ATTACHMENT)
585 {
586 size_t nl;
587
588 /* don't consider line endings part of the buffer for regex matching */
589 nl = mutt_str_len(buf);
590 if ((nl > 0) && (buf[nl - 1] == '\n'))
591 buf[nl - 1] = '\0';
592
593 i = 0;
594 offset = 0;
595 lines[line_num].syntax_arr_size = 0;
597 do
598 {
599 if (!buf[offset])
600 break;
601
602 found = false;
603 null_rx = false;
605 {
606 if (regexec(&color_line->regex, buf + offset, 1, pmatch,
607 ((offset != 0) ? REG_NOTBOL : 0)) != 0)
608 {
609 continue;
610 }
611
612 if (pmatch[0].rm_eo != pmatch[0].rm_so)
613 {
614 if (!found)
615 {
616 if (++(lines[line_num].syntax_arr_size) > 1)
617 {
618 mutt_mem_realloc(&(lines[line_num].syntax),
619 (lines[line_num].syntax_arr_size) * sizeof(struct TextSyntax));
620 // Zero the new entry
621 const int index = lines[line_num].syntax_arr_size - 1;
622 struct TextSyntax *ts = &lines[line_num].syntax[index];
623 memset(ts, 0, sizeof(*ts));
624 }
625 }
626 i = lines[line_num].syntax_arr_size - 1;
627 pmatch[0].rm_so += offset;
628 pmatch[0].rm_eo += offset;
629 if (!found || (pmatch[0].rm_so < (lines[line_num].syntax)[i].first) ||
630 ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
631 (pmatch[0].rm_eo > (lines[line_num].syntax)[i].last)))
632 {
633 if (!(lines[line_num].syntax)[i].attr_color)
634 (lines[line_num].syntax)[i].attr_color = ac_attach;
635
636 (lines[line_num].syntax)[i].attr_color = merged_color_overlay(
637 (lines[line_num].syntax)[i].attr_color, &color_line->attr_color);
638 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
639 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
640 }
641 else if ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
642 (pmatch[0].rm_eo == (lines[line_num].syntax)[i].last))
643 {
644 (lines[line_num].syntax)[i].attr_color = merged_color_overlay(
645 (lines[line_num].syntax)[i].attr_color, &color_line->attr_color);
646 (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
647 (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
648 }
649 found = 1;
650 null_rx = false;
651 }
652 else
653 {
654 null_rx = true; /* empty regex; don't add it, but keep looking */
655 }
656 }
657
658 if (null_rx)
659 offset++; /* avoid degenerate cases */
660 else
661 offset = (lines[line_num].syntax)[i].last;
662 } while (found || null_rx);
663 if (nl > 0)
664 buf[nl] = '\n';
665 }
666}
struct RegexColorList * regex_colors_get_list(enum ColorId cid)
Return the RegexColorList for a colour id.
Definition: regex.c:184
bool simple_color_is_header(enum ColorId cid)
Colour is for an Email header.
Definition: simple.c:105
@ MT_COLOR_BODY
Pager: highlight body of message (takes a pattern)
Definition: color.h:42
@ MT_COLOR_HDRDEFAULT
Header default colour.
Definition: color.h:50
@ MT_COLOR_ATTACH_HEADERS
MIME attachment test (takes a pattern)
Definition: color.h:41
@ MT_COLOR_ATTACHMENT
MIME attachments text (entire line)
Definition: color.h:40
static int check_protected_header_marker(const char *p)
Check that the unique marker is present.
Definition: display.c:297
bool mutt_is_quote_line(char *line, regmatch_t *pmatch)
Is a line of message text a quote?
Definition: display.c:311
static int check_sig(const char *s, struct Line *info, int offset)
Check for an email signature.
Definition: display.c:53
static int check_attachment_marker(const char *p)
Check that the unique marker is present.
Definition: display.c:287
int braille_col
Definition: dlg_pager.c:80
int braille_row
Definition: dlg_pager.c:79
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
void mutt_window_get_coords(struct MuttWindow *win, int *col, int *row)
Get the cursor position in the Window.
Definition: mutt_window.c:274
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
struct QuoteStyle * qstyle_classify(struct QuoteStyle **quote_list, const char *qptr, size_t length, bool *force_redraw, int *q_level)
Find a style for a string.
Definition: quoted.c:280
bool cont_header
Continuation of a header line (wrapped by MTA)
Definition: display.h:55
short syntax_arr_size
Number of items in syntax array.
Definition: display.h:57
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:

◆ mutt_buffer_strip_formatting()

void mutt_buffer_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 681 of file display.c.

682{
683 const char *s = src;
684
685 mutt_buffer_reset(dest);
686
687 if (!s)
688 return;
689
690 while (s[0] != '\0')
691 {
692 if ((s[0] == '\010') && (s > src))
693 {
694 if (s[1] == '_') /* underline */
695 {
696 s += 2;
697 }
698 else if (s[1] && mutt_buffer_len(dest)) /* bold or overstrike */
699 {
700 dest->dptr--;
701 mutt_buffer_addch(dest, s[1]);
702 s += 2;
703 }
704 else /* ^H */
705 mutt_buffer_addch(dest, *s++);
706 continue;
707 }
708
709 int len = ansi_color_seq_length(s);
710 if (len > 0)
711 {
712 s += len;
713 }
714 else if (strip_markers && (s[0] == '\033') && (s[1] == ']') &&
716 {
717 mutt_debug(LL_DEBUG2, "Seen attachment marker\n");
718 while (*s++ != '\a')
719 ; /* skip pseudo-ANSI sequence */
720 }
721 else
722 {
723 mutt_buffer_addch(dest, *s++);
724 }
725 }
726}
int ansi_color_seq_length(const char *str)
Is this an ANSI escape sequence?
Definition: ansi.c:78
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:409
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:248
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:85
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
char * dptr
Current read/write position.
Definition: buffer.h:36
+ 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 740 of file display.c.

742{
743 static int b_read;
744 struct Buffer stripped;
745
746 if (*buf_ready == 0)
747 {
748 if (offset != *bytes_read)
749 {
750 if (!mutt_file_seek(fp, offset, SEEK_SET))
751 {
752 return -1;
753 }
754 }
755
756 *buf = (unsigned char *) mutt_file_read_line((char *) *buf, blen, fp, NULL, MUTT_RL_EOL);
757 if (!*buf)
758 {
759 fmt[0] = NULL;
760 return -1;
761 }
762
763 *bytes_read = ftello(fp);
764 b_read = (int) (*bytes_read - offset);
765 *buf_ready = 1;
766
767 mutt_buffer_init(&stripped);
768 mutt_buffer_alloc(&stripped, *blen);
769 mutt_buffer_strip_formatting(&stripped, (const char *) *buf, 1);
770 /* This should be a noop, because *fmt should be NULL */
771 FREE(fmt);
772 *fmt = (unsigned char *) stripped.data;
773 }
774
775 return b_read;
776}
void mutt_buffer_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:313
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:52
void mutt_buffer_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
Removes ANSI and backspace formatting.
Definition: display.c:681
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:738
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:708
#define MUTT_RL_EOL
don't strip \n / \r\n
Definition: file.h:41
#define FREE(x)
Definition: memory.h:43
String manipulation buffer.
Definition: buffer.h:34
+ 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 795 of file display.c.

799{
800 int space = -1; /* index of the last space or TAB */
801 const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
802 size_t col = c_markers ? (*lines)[line_num].cont_line : 0;
803 size_t k;
804 int ch, vch, last_special = -1, special = 0, t;
805 wchar_t wc = 0;
806 mbstate_t mbstate = { 0 }; // FIXME: this should come from lines
807 const size_t c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
808 size_t wrap_cols = mutt_window_wrap_cols(width, (flags & MUTT_PAGER_NOWRAP) ? 0 : c_wrap);
809
810 if (check_attachment_marker((char *) buf) == 0)
811 wrap_cols = width;
812
813 const bool c_allow_ansi = cs_subset_bool(NeoMutt->sub, "allow_ansi");
814 for (ch = 0, vch = 0; ch < cnt; ch += k, vch += k)
815 {
816 /* Handle ANSI sequences */
817 if (buf[ch] == '\033') // Escape
818 {
819 int len = ansi_color_parse((const char *) buf + ch, ansi, ansi_list, !c_allow_ansi);
820 ch += len;
821 }
822
823 while ((cnt - ch >= 2) && (buf[ch] == '\033') && (buf[ch + 1] == ']') && // Escape
824 ((check_attachment_marker((char *) buf + ch) == 0) ||
825 (check_protected_header_marker((char *) buf + ch) == 0)))
826 {
827 while (buf[ch++] != '\a')
828 if (ch >= cnt)
829 break;
830 }
831
832 /* is anything left to do? */
833 if (ch >= cnt)
834 break;
835
836 k = mbrtowc(&wc, (char *) buf + ch, cnt - ch, &mbstate);
837 if ((k == (size_t) (-2)) || (k == (size_t) (-1)))
838 {
839 if (k == (size_t) (-1))
840 memset(&mbstate, 0, sizeof(mbstate));
841 mutt_debug(LL_DEBUG1, "mbrtowc returned %lu; errno = %d\n", k, errno);
842 if ((col + 4) > wrap_cols)
843 break;
844 col += 4;
845 if (ansi)
846 mutt_window_printf(win, "\\%03o", buf[ch]);
847 k = 1;
848 continue;
849 }
850 if (k == 0)
851 k = 1;
852
853 if (CharsetIsUtf8)
854 {
855 /* zero width space, zero with non-joiner, zero width no-break space */
856 if ((wc == 0x200B) || (wc == 0x200C) || (wc == 0xFEFF))
857 {
858 mutt_debug(LL_DEBUG3, "skip zero-width character U+%04X\n", (unsigned short) wc);
859 continue;
860 }
862 {
863 mutt_debug(LL_DEBUG3, "filtered U+%04X\n", (unsigned short) wc);
864 continue;
865 }
866 }
867
868 /* Handle backspace */
869 special = 0;
870 if (IsWPrint(wc))
871 {
872 wchar_t wc1 = 0;
873 mbstate_t mbstate1 = mbstate;
874 size_t k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
875 while ((k1 != (size_t) (-2)) && (k1 != (size_t) (-1)) && (k1 > 0) && (wc1 == '\b'))
876 {
877 const size_t k2 = mbrtowc(&wc1, (char *) buf + ch + k + k1,
878 cnt - ch - k - k1, &mbstate1);
879 if ((k2 == (size_t) (-2)) || (k2 == (size_t) (-1)) || (k2 == 0) || (!IsWPrint(wc1)))
880 break;
881
882 if (wc == wc1)
883 {
884 special |= ((wc == '_') && (special & A_UNDERLINE)) ? A_UNDERLINE : A_BOLD;
885 }
886 else if ((wc == '_') || (wc1 == '_'))
887 {
888 special |= A_UNDERLINE;
889 wc = (wc1 == '_') ? wc : wc1;
890 }
891 else
892 {
893 /* special = 0; / * overstrike: nothing to do! */
894 wc = wc1;
895 }
896
897 ch += k + k1;
898 k = k2;
899 mbstate = mbstate1;
900 k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
901 }
902 }
903
904 if (ansi && ((flags & (MUTT_SHOWCOLOR | MUTT_SEARCH | MUTT_PAGER_MARKER)) ||
905 special || last_special || ansi->attrs))
906 {
907 resolve_color(win, *lines, line_num, vch, flags, special, ansi);
908 last_special = special;
909 // memset(ansi, 0, sizeof(*ansi));
910 }
911
912 /* no-break space, narrow no-break space */
913 if (IsWPrint(wc) || (CharsetIsUtf8 && ((wc == 0x00A0) || (wc == 0x202F))))
914 {
915 if (wc == ' ')
916 {
917 space = ch;
918 }
919 t = wcwidth(wc);
920 if (col + t > wrap_cols)
921 break;
922 col += t;
923 if (ansi)
924 mutt_addwch(win, wc);
925 }
926 else if (wc == '\n')
927 break;
928 else if (wc == '\t')
929 {
930 space = ch;
931 t = (col & ~7) + 8;
932 if (t > wrap_cols)
933 break;
934 if (ansi)
935 for (; col < t; col++)
936 mutt_window_addch(win, ' ');
937 else
938 col = t;
939 }
940 else if ((wc < 0x20) || (wc == 0x7f))
941 {
942 if (col + 2 > wrap_cols)
943 break;
944 col += 2;
945 if (ansi)
946 mutt_window_printf(win, "^%c", ('@' + wc) & 0x7f);
947 }
948 else if (wc < 0x100)
949 {
950 if (col + 4 > wrap_cols)
951 break;
952 col += 4;
953 if (ansi)
954 mutt_window_printf(win, "\\%03o", wc);
955 }
956 else
957 {
958 if (col + 1 > wrap_cols)
959 break;
960 col += k;
961 if (ansi)
963 }
964 }
965 *pspace = space;
966 *pcol = col;
967 *pvch = vch;
968 *pspecial = special;
969 return ch;
970}
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:311
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
int mutt_addwch(struct MuttWindow *win, wchar_t wc)
Addwch would be provided by an up-to-date curses library.
Definition: curs_lib.c:605
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:115
@ LL_DEBUG3
Log at debug level 3.
Definition: logging.h:42
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
bool mutt_mb_is_display_corrupting_utf8(wchar_t wc)
Will this character corrupt the display?
Definition: mbyte.c:389
#define IsWPrint(wc)
Definition: mbyte.h:39
bool CharsetIsUtf8
Is the user's current character set utf-8?
Definition: charset.c:60
wchar_t ReplacementChar
When a Unicode character can't be displayed, use this instead.
Definition: charset.c:55
int mutt_window_printf(struct MuttWindow *win, const char *fmt,...)
Write a formatted string to a Window.
Definition: mutt_window.c:425
int mutt_window_wrap_cols(int width, short wrap)
Calculate the wrap column for a given screen width.
Definition: mutt_window.c:366
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition: lib.h:71
#define MUTT_PAGER_MARKER
Use markers if option is set.
Definition: lib.h:68
int attrs
Attributes, e.g. A_BOLD.
Definition: ansi.h:36
+ 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 991 of file display.c.

996{
997 unsigned char *buf = NULL, *fmt = NULL;
998 size_t buflen = 0;
999 unsigned char *buf_ptr = NULL;
1000 int ch, vch, col, cnt, b_read;
1001 int buf_ready = 0;
1002 bool change_last = false;
1003 int special;
1004 int offset;
1005 struct AttrColor *def_color = NULL;
1006 int m;
1007 int rc = -1;
1008 struct AnsiColor ansi = { NULL, 0, COLOR_DEFAULT, COLOR_DEFAULT };
1009 regmatch_t pmatch[1];
1010
1011 if (line_num == *lines_used)
1012 {
1013 (*lines_used)++;
1014 change_last = true;
1015 }
1016
1017 if (*lines_used == *lines_max)
1018 {
1019 mutt_mem_realloc(lines, sizeof(struct Line) * (*lines_max += LINES));
1020 for (ch = *lines_used; ch < *lines_max; ch++)
1021 {
1022 memset(&((*lines)[ch]), 0, sizeof(struct Line));
1023 (*lines)[ch].cid = -1;
1024 (*lines)[ch].search_arr_size = -1;
1025 (*lines)[ch].syntax = mutt_mem_calloc(1, sizeof(struct TextSyntax));
1026 ((*lines)[ch].syntax)[0].first = -1;
1027 ((*lines)[ch].syntax)[0].last = -1;
1028 }
1029 }
1030
1031 struct Line *const cur_line = &(*lines)[line_num];
1032
1033 if (flags & MUTT_PAGER_LOGS)
1034 {
1035 /* determine the line class */
1036 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1037 {
1038 if (change_last)
1039 (*lines_used)--;
1040 goto out;
1041 }
1042
1043 cur_line->cid = MT_COLOR_MESSAGE_LOG;
1044 if (buf[11] == 'M')
1046 else if (buf[11] == 'W')
1048 else if (buf[11] == 'E')
1050 else
1052 }
1053
1054 /* only do color highlighting if we are viewing a message */
1055 if (flags & (MUTT_SHOWCOLOR | MUTT_TYPES))
1056 {
1057 if (cur_line->cid == -1)
1058 {
1059 /* determine the line class */
1060 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1061 {
1062 if (change_last)
1063 (*lines_used)--;
1064 goto out;
1065 }
1066
1067 resolve_types(win_pager, (char *) fmt, (char *) buf, *lines, line_num, *lines_used,
1068 quote_list, q_level, force_redraw, flags & MUTT_SHOWCOLOR);
1069
1070 /* avoid race condition for continuation lines when scrolling up */
1071 for (m = line_num + 1;
1072 m < *lines_used && (*lines)[m].offset && (*lines)[m].cont_line; m++)
1073 {
1074 (*lines)[m].cid = cur_line->cid;
1075 }
1076 }
1077
1078 /* this also prevents searching through the hidden lines */
1079 const short c_toggle_quoted_show_levels = cs_subset_number(NeoMutt->sub, "toggle_quoted_show_levels");
1080 if ((flags & MUTT_HIDE) && (cur_line->cid == MT_COLOR_QUOTED) &&
1081 ((cur_line->quote == NULL) || (cur_line->quote->quote_n >= c_toggle_quoted_show_levels)))
1082 {
1083 flags = 0; /* MUTT_NOSHOW */
1084 }
1085 }
1086
1087 /* At this point, (*lines[line_num]).quote may still be undefined. We
1088 * don't want to compute it every time MUTT_TYPES is set, since this
1089 * would slow down the "bottom" function unacceptably. A compromise
1090 * solution is hence to call regexec() again, just to find out the
1091 * length of the quote prefix. */
1092 if ((flags & MUTT_SHOWCOLOR) && !cur_line->cont_line &&
1093 (cur_line->cid == MT_COLOR_QUOTED) && !cur_line->quote)
1094 {
1095 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1096 {
1097 if (change_last)
1098 (*lines_used)--;
1099 goto out;
1100 }
1101
1102 const struct Regex *c_quote_regex = cs_subset_regex(NeoMutt->sub, "quote_regex");
1103 if (mutt_regex_capture(c_quote_regex, (char *) fmt, 1, pmatch))
1104 {
1105 cur_line->quote = qstyle_classify(quote_list, (char *) fmt + pmatch[0].rm_so,
1106 pmatch[0].rm_eo - pmatch[0].rm_so,
1107 force_redraw, q_level);
1108 }
1109 else
1110 {
1111 goto out;
1112 }
1113 }
1114
1115 if ((flags & MUTT_SEARCH) && !cur_line->cont_line && (cur_line->search_arr_size == -1))
1116 {
1117 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1118 {
1119 if (change_last)
1120 (*lines_used)--;
1121 goto out;
1122 }
1123
1124 offset = 0;
1125 cur_line->search_arr_size = 0;
1126 while (regexec(search_re, (char *) fmt + offset, 1, pmatch,
1127 (offset ? REG_NOTBOL : 0)) == 0)
1128 {
1129 if (++(cur_line->search_arr_size) > 1)
1130 {
1131 mutt_mem_realloc(&(cur_line->search),
1132 (cur_line->search_arr_size) * sizeof(struct TextSyntax));
1133 // Zero the new entry
1134 const int index = cur_line->search_arr_size - 1;
1135 struct TextSyntax *ts = &cur_line->search[index];
1136 memset(ts, 0, sizeof(*ts));
1137 }
1138 else
1139 {
1140 cur_line->search = mutt_mem_calloc(1, sizeof(struct TextSyntax));
1141 }
1142 pmatch[0].rm_so += offset;
1143 pmatch[0].rm_eo += offset;
1144 (cur_line->search)[cur_line->search_arr_size - 1].first = pmatch[0].rm_so;
1145 (cur_line->search)[cur_line->search_arr_size - 1].last = pmatch[0].rm_eo;
1146
1147 if (pmatch[0].rm_eo == pmatch[0].rm_so)
1148 offset++; /* avoid degenerate cases */
1149 else
1150 offset = pmatch[0].rm_eo;
1151 if (!fmt[offset])
1152 break;
1153 }
1154 }
1155
1156 if (!(flags & MUTT_SHOW) && ((*lines)[line_num + 1].offset > 0))
1157 {
1158 /* we've already scanned this line, so just exit */
1159 rc = 0;
1160 goto out;
1161 }
1162 if ((flags & MUTT_SHOWCOLOR) && *force_redraw && ((*lines)[line_num + 1].offset > 0))
1163 {
1164 /* no need to try to display this line... */
1165 rc = 1;
1166 goto out; /* fake display */
1167 }
1168
1169 b_read = fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready);
1170 if (b_read < 0)
1171 {
1172 if (change_last)
1173 (*lines_used)--;
1174 goto out;
1175 }
1176
1177 /* now chose a good place to break the line */
1178 cnt = format_line(win_pager, lines, line_num, buf, flags, NULL, b_read, &ch,
1179 &vch, &col, &special, win_pager->state.cols, ansi_list);
1180 buf_ptr = buf + cnt;
1181
1182 /* move the break point only if smart_wrap is set */
1183 const bool c_smart_wrap = cs_subset_bool(NeoMutt->sub, "smart_wrap");
1184 if (c_smart_wrap)
1185 {
1186 if ((cnt < b_read) && (ch != -1) &&
1187 !simple_color_is_header(cur_line->cid) && !IS_SPACE(buf[cnt]))
1188 {
1189 buf_ptr = buf + ch;
1190 /* skip trailing blanks */
1191 while (ch && ((buf[ch] == ' ') || (buf[ch] == '\t') || (buf[ch] == '\r')))
1192 ch--;
1193 /* A very long word with leading spaces causes infinite
1194 * wrapping when MUTT_PAGER_NSKIP is set. A folded header
1195 * with a single long word shouldn't be smartwrapped
1196 * either. So just disable smart_wrap if it would wrap at the
1197 * beginning of the line. */
1198 if (ch == 0)
1199 buf_ptr = buf + cnt;
1200 else
1201 cnt = ch + 1;
1202 }
1203 if (!(flags & MUTT_PAGER_NSKIP))
1204 {
1205 /* skip leading blanks on the next line too */
1206 while ((*buf_ptr == ' ') || (*buf_ptr == '\t'))
1207 buf_ptr++;
1208 }
1209 }
1210
1211 if (*buf_ptr == '\r')
1212 buf_ptr++;
1213 if (*buf_ptr == '\n')
1214 buf_ptr++;
1215
1216 if (((int) (buf_ptr - buf) < b_read) && !(*lines)[line_num + 1].cont_line)
1217 append_line(*lines, line_num, (int) (buf_ptr - buf));
1218 (*lines)[line_num + 1].offset = cur_line->offset + (long) (buf_ptr - buf);
1219
1220 /* if we don't need to display the line we are done */
1221 if (!(flags & MUTT_SHOW))
1222 {
1223 rc = 0;
1224 goto out;
1225 }
1226
1227 /* display the line */
1228 format_line(win_pager, lines, line_num, buf, flags, &ansi, cnt, &ch, &vch,
1229 &col, &special, win_pager->state.cols, ansi_list);
1230
1231 /* avoid a bug in ncurses... */
1232 if (col == 0)
1233 {
1235 mutt_window_addch(win_pager, ' ');
1236 }
1237
1238 /* end the last color pattern (needed by S-Lang) */
1239 if (special || ((col != win_pager->state.cols) && (flags & (MUTT_SHOWCOLOR | MUTT_SEARCH))))
1240 resolve_color(win_pager, *lines, line_num, vch, flags, 0, &ansi);
1241
1242 /* Fill the blank space at the end of the line with the prevailing color.
1243 * ncurses does an implicit clrtoeol() when you do mutt_window_addch('\n') so we have
1244 * to make sure to reset the color *after* that */
1245 if (flags & MUTT_SHOWCOLOR)
1246 {
1247 m = (cur_line->cont_line) ? (cur_line->syntax)[0].first : line_num;
1248 if ((*lines)[m].cid == MT_COLOR_HEADER)
1249 {
1250 def_color = ((*lines)[m].syntax)[0].attr_color;
1251 }
1252 else
1253 {
1254 def_color = simple_color_get((*lines)[m].cid);
1255 }
1256 struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
1257 struct AttrColor *ac_eol = NULL;
1258 if (def_color)
1259 ac_eol = merged_color_overlay(ac_normal, def_color);
1260 else
1261 ac_eol = ac_normal;
1262 mutt_curses_set_color(ac_eol);
1263 }
1264
1265 if (col < win_pager->state.cols)
1266 mutt_window_clrtoeol(win_pager);
1267
1268 /* reset the color back to normal. This *must* come after the
1269 * clrtoeol, otherwise the color for this line will not be
1270 * filled to the right margin. */
1271 if (flags & MUTT_SHOWCOLOR)
1273
1274 /* build a return code */
1275 if (!(flags & MUTT_SHOW))
1276 flags = 0;
1277
1278 rc = flags;
1279
1280out:
1281 FREE(&buf);
1282 FREE(&fmt);
1283 return rc;
1284}
#define COLOR_DEFAULT
Definition: color.h:102
@ MT_COLOR_MESSAGE
Informational message.
Definition: color.h:55
@ MT_COLOR_ERROR
Error message.
Definition: color.h:49
@ MT_COLOR_MESSAGE_LOG
Menu showing log messages.
Definition: color.h:56
@ MT_COLOR_WARNING
Warning messages.
Definition: color.h:79
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:795
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:740
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:358
static void append_line(struct Line *lines, int line_num, int cnt)
Add a new Line to the array.
Definition: display.c:246
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:241
#define MUTT_PAGER_NSKIP
Preserve whitespace with smartwrap.
Definition: lib.h:67
#define MUTT_HIDE
Don't show quoted text.
Definition: lib.h:61
#define MUTT_TYPES
Compute line's type.
Definition: lib.h:63
#define MUTT_SHOW
Definition: lib.h:64
An ANSI escape sequence.
Definition: ansi.h:34
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 quote_n
The quoteN colour index for this level.
Definition: quoted.h:69
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: