NeoMutt  2022-04-29-249-gaae397
Teaching an old dog new tricks
DOXYGEN
display.c File Reference

Pager Display. More...

#include "config.h"
#include <errno.h>
#include <inttypes.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 54 of file display.c.

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

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

118{
119 struct AttrColor def_color = { 0 }; /* color without syntax highlight */
120 struct AttrColor color = { 0 }; /* final color */
121 static struct AttrColor last_color = { 0 }; /* last color set */
122 bool search = false;
123 int m;
124 struct TextSyntax *matching_chunk = NULL;
125
126 if (cnt == 0)
127 {
128 last_color.curses_color = NULL;
129 last_color.attrs = 0;
130 }
131
132 if (lines[line_num].cont_line)
133 {
134 const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
135 if (!cnt && c_markers)
136 {
138 mutt_window_addch(win, '+');
139 }
140 m = (lines[line_num].syntax)[0].first;
141 cnt += (lines[line_num].syntax)[0].last;
142 }
143 else
144 m = line_num;
145 if (flags & MUTT_PAGER_LOGS)
146 {
147 def_color = *(lines[line_num].syntax[0].attr_color);
148 }
149 else if (!(flags & MUTT_SHOWCOLOR))
150 {
151 def_color = *simple_color_get(MT_COLOR_NORMAL);
152 }
153 else if ((lines[m].cid == MT_COLOR_HEADER) && lines[m].syntax[0].attr_color)
154 {
155 def_color = *lines[m].syntax[0].attr_color;
156 }
157 else
158 {
159 def_color = *simple_color_get(lines[m].cid);
160 }
161
162 if ((flags & MUTT_SHOWCOLOR) && (lines[m].cid == MT_COLOR_QUOTED))
163 {
164 struct QuoteStyle *qc = lines[m].quote;
165
166 if (qc)
167 {
168 def_color = attr_color_copy(qc->attr_color);
169
170 while (qc && (qc->prefix_len > cnt))
171 {
172 def_color = attr_color_copy(qc->attr_color);
173 qc = qc->up;
174 }
175 }
176 }
177
178 color = def_color;
179 if ((flags & MUTT_SHOWCOLOR) && lines[m].syntax_arr_size)
180 {
181 matching_chunk = bsearch(&cnt, lines[m].syntax, lines[m].syntax_arr_size,
182 sizeof(struct TextSyntax), comp_syntax_t);
183 if (matching_chunk && (cnt >= matching_chunk->first) &&
184 (cnt < matching_chunk->last))
185 {
186 if (matching_chunk->attr_color)
187 color = *matching_chunk->attr_color;
188 }
189 }
190
191 if ((flags & MUTT_SEARCH) && lines[m].search_arr_size)
192 {
193 matching_chunk = bsearch(&cnt, lines[m].search, lines[m].search_arr_size,
194 sizeof(struct TextSyntax), comp_syntax_t);
195 if (matching_chunk && (cnt >= matching_chunk->first) &&
196 (cnt < matching_chunk->last))
197 {
199 search = true;
200 }
201 }
202
203 /* handle "special" bold & underlined characters */
204 if (special & A_BOLD)
205 {
208 else
209 color.attrs |= A_BOLD;
210 }
211 else if (special & A_UNDERLINE)
212 {
215 else
216 color.attrs |= A_UNDERLINE;
217 }
218 else if (special & A_ITALIC)
219 {
222 else
223 color.attrs |= A_ITALIC;
224 }
225 else if (ansi->attr_color)
226 {
227 color = *ansi->attr_color;
228 }
229
230 if (!attr_color_match(&color, &last_color))
231 {
233 &color);
234 mutt_curses_set_color(ac_merge);
235 last_color = color;
236 }
237}
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:51
@ MT_COLOR_QUOTED
Pager: quoted text.
Definition: color.h:58
@ MT_COLOR_HEADER
Message headers (takes a pattern)
Definition: color.h:48
@ MT_COLOR_BOLD
Bold text.
Definition: color.h:40
@ MT_COLOR_NORMAL
Plain text.
Definition: color.h:54
@ MT_COLOR_SEARCH
Pager: search matches.
Definition: color.h:59
@ MT_COLOR_ITALIC
Italic text.
Definition: color.h:50
@ MT_COLOR_UNDERLINE
Underlined text.
Definition: color.h:74
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:94
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:381
#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 245 of file display.c.

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

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

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

297{
299}
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 310 of file display.c.

311{
312 bool is_quote = false;
313 const struct Regex *c_smileys = cs_subset_regex(NeoMutt->sub, "smileys");
314 regmatch_t pmatch_internal[1], smatch[1];
315
316 if (!pmatch)
317 pmatch = pmatch_internal;
318
319 const struct Regex *c_quote_regex = cs_subset_regex(NeoMutt->sub, "quote_regex");
320 if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
321 {
322 if (mutt_regex_capture(c_smileys, line, 1, smatch))
323 {
324 if (smatch[0].rm_so > 0)
325 {
326 char c = line[smatch[0].rm_so];
327 line[smatch[0].rm_so] = 0;
328
329 if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
330 is_quote = true;
331
332 line[smatch[0].rm_so] = c;
333 }
334 }
335 else
336 is_quote = true;
337 }
338
339 return is_quote;
340}
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:614
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 355 of file display.c.

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

675{
676 const char *s = src;
677
678 mutt_buffer_reset(dest);
679
680 if (!s)
681 return;
682
683 while (s[0] != '\0')
684 {
685 if ((s[0] == '\010') && (s > src))
686 {
687 if (s[1] == '_') /* underline */
688 s += 2;
689 else if (s[1] && mutt_buffer_len(dest)) /* bold or overstrike */
690 {
691 dest->dptr--;
692 mutt_buffer_addch(dest, s[1]);
693 s += 2;
694 }
695 else /* ^H */
696 mutt_buffer_addch(dest, *s++);
697 continue;
698 }
699
700 int len = ansi_color_seq_length(s);
701 if (len > 0)
702 {
703 s += len;
704 }
705 else if (strip_markers && (s[0] == '\033') && (s[1] == ']') &&
707 {
708 mutt_debug(LL_DEBUG2, "Seen attachment marker\n");
709 while (*s++ != '\a')
710 ; /* skip pseudo-ANSI sequence */
711 }
712 else
713 mutt_buffer_addch(dest, *s++);
714 }
715}
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:371
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 729 of file display.c.

731{
732 static int b_read;
733 struct Buffer stripped;
734
735 if (*buf_ready == 0)
736 {
737 if (offset != *bytes_read)
738 {
739 if (!mutt_file_seek(fp, offset, SEEK_SET))
740 {
741 return -1;
742 }
743 }
744
745 *buf = (unsigned char *) mutt_file_read_line((char *) *buf, blen, fp, NULL, MUTT_RL_EOL);
746 if (!*buf)
747 {
748 fmt[0] = NULL;
749 return -1;
750 }
751
752 *bytes_read = ftello(fp);
753 b_read = (int) (*bytes_read - offset);
754 *buf_ready = 1;
755
756 mutt_buffer_init(&stripped);
757 mutt_buffer_alloc(&stripped, *blen);
758 mutt_buffer_strip_formatting(&stripped, (const char *) *buf, 1);
759 /* This should be a noop, because *fmt should be NULL */
760 FREE(fmt);
761 *fmt = (unsigned char *) stripped.data;
762 }
763
764 return b_read;
765}
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:275
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:674
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:720
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:690
#define MUTT_RL_EOL
don't strip \n / \r\n
Definition: file.h:40
#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 784 of file display.c.

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

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