NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
display.c File Reference

Pager Display. More...

#include "config.h"
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "mutt/lib.h"
#include "config/lib.h"
#include "core/lib.h"
#include "gui/lib.h"
#include "display.h"
#include "lib.h"
#include "color/lib.h"
+ Include dependency graph for display.c:

Go to the source code of this file.

Functions

static int check_sig (const char *s, struct Line *info, int offset)
 Check for an email signature.
 
static int comp_syntax_t (const void *m1, const void *m2)
 Search for a Syntax using bsearch(3)
 
static void resolve_color (struct MuttWindow *win, struct Line *lines, int line_num, int cnt, PagerFlags flags, int special, struct AnsiColor *ansi)
 Set the colour for a line of text.
 
static void append_line (struct Line *lines, int line_num, int cnt)
 Add a new Line to the array.
 
static int check_marker (const char *q, const char *p)
 Check that the unique marker is present.
 
static int check_attachment_marker (const char *p)
 Check that the unique marker is present.
 
static int check_protected_header_marker (const char *p)
 Check that the unique marker is present.
 
bool mutt_is_quote_line (char *line, regmatch_t *pmatch)
 Is a line of message text a quote?
 
static void resolve_types (struct MuttWindow *win, char *buf, char *raw, struct Line *lines, int line_num, int lines_used, struct QuoteStyle **quote_list, int *q_level, bool *force_redraw, bool q_classify)
 Determine the style for a line of text.
 
void buf_strip_formatting (struct Buffer *dest, const char *src, bool strip_markers)
 Removes ANSI and backspace formatting.
 
static int fill_buffer (FILE *fp, LOFF_T *bytes_read, LOFF_T offset, unsigned char **buf, unsigned char **fmt, size_t *blen, int *buf_ready)
 Fill a buffer from a file.
 
static int format_line (struct MuttWindow *win, struct Line **lines, int line_num, unsigned char *buf, PagerFlags flags, struct AnsiColor *ansi, int cnt, int *pspace, int *pvch, int *pcol, int *pspecial, int width, struct AttrColorList *ansi_list)
 Display a line of text in the pager.
 
int display_line (FILE *fp, LOFF_T *bytes_read, struct Line **lines, int line_num, int *lines_used, int *lines_max, PagerFlags flags, struct QuoteStyle **quote_list, int *q_level, bool *force_redraw, regex_t *search_re, struct MuttWindow *win_pager, struct AttrColorList *ansi_list)
 Print a line on screen.
 

Detailed Description

Pager Display.

Authors
  • Richard Russon

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 (!isspace(*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:74
+ Here is the caller graph for this function:

◆ comp_syntax_t()

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

Search for a Syntax using bsearch(3)

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

Definition at line 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 {
145 m = line_num;
146 }
147 if (flags & MUTT_PAGER_LOGS)
148 {
149 def_color = *(lines[line_num].syntax[0].attr_color);
150 }
151 else if (!(flags & MUTT_SHOWCOLOR))
152 {
153 if (flags & MUTT_PAGER_STRIPES)
154 {
155 def_color = *simple_color_get(((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD :
157 }
158 else
159 {
160 def_color = *simple_color_get(MT_COLOR_NORMAL);
161 }
162 }
163 else if ((lines[m].cid == MT_COLOR_HEADER) && lines[m].syntax[0].attr_color)
164 {
165 def_color = *lines[m].syntax[0].attr_color;
166 }
167 else
168 {
169 def_color = *simple_color_get(lines[m].cid);
170 }
171
172 if ((flags & MUTT_SHOWCOLOR) && (lines[m].cid == MT_COLOR_QUOTED))
173 {
174 struct QuoteStyle *qc = lines[m].quote;
175
176 if (qc)
177 {
178 def_color = attr_color_copy(qc->attr_color);
179
180 while (qc && (qc->prefix_len > cnt))
181 {
182 def_color = attr_color_copy(qc->attr_color);
183 qc = qc->up;
184 }
185 }
186 }
187
188 color = def_color;
189 if ((flags & MUTT_SHOWCOLOR) && lines[m].syntax)
190 {
191 matching_chunk = bsearch(&cnt, lines[m].syntax, lines[m].syntax_arr_size,
192 sizeof(struct TextSyntax), comp_syntax_t);
193 if (matching_chunk && (cnt >= matching_chunk->first) &&
194 (cnt < matching_chunk->last))
195 {
196 if (matching_chunk->attr_color)
197 color = *matching_chunk->attr_color;
198 }
199 }
200
201 if ((flags & MUTT_SEARCH) && lines[m].search)
202 {
203 matching_chunk = bsearch(&cnt, lines[m].search, lines[m].search_arr_size,
204 sizeof(struct TextSyntax), comp_syntax_t);
205 if (matching_chunk && (cnt >= matching_chunk->first) &&
206 (cnt < matching_chunk->last))
207 {
209 search = true;
210 }
211 }
212
213 /* handle "special" bold & underlined characters */
214 if (special & A_BOLD)
215 {
218 else
219 color.attrs |= A_BOLD;
220 }
221 else if (special & A_UNDERLINE)
222 {
225 else
226 color.attrs |= A_UNDERLINE;
227 }
228 else if (special & A_ITALIC)
229 {
232 else
233 color.attrs |= A_ITALIC;
234 }
235 else if (ansi->attr_color)
236 {
237 color = *ansi->attr_color;
238 }
239
240 if (!attr_color_match(&color, &last_color))
241 {
243 &color);
244 mutt_curses_set_color(ac_merge);
245 last_color = color;
246 }
247}
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:102
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition: simple.c:81
@ 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_STRIPE_EVEN
Stripes: even lines of the Help Page.
Definition: color.h:76
@ 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_STRIPE_ODD
Stripes: odd lines of the Help Page.
Definition: color.h:77
@ MT_COLOR_UNDERLINE
Underlined text.
Definition: color.h:80
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
static int comp_syntax_t(const void *m1, const void *m2)
Search for a Syntax using bsearch(3)
Definition: display.c:94
static int search(struct Menu *menu, int op)
Search a menu.
Definition: functions.c:58
const struct AttrColor * merged_color_overlay(const struct AttrColor *base, const struct AttrColor *over)
Combine two colours.
Definition: merged.c:109
const struct AttrColor * mutt_curses_set_color_by_id(enum ColorId cid)
Set the colour and attributes by the colour id.
Definition: mutt_curses.c:81
void mutt_curses_set_color(const struct AttrColor *ac)
Set the colour and attributes for text.
Definition: mutt_curses.c:40
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
Definition: mutt_window.c:388
#define MUTT_PAGER_STRIPES
Striped highlighting.
Definition: lib.h:75
#define MUTT_SHOWCOLOR
Show characters in color otherwise don't show characters.
Definition: lib.h:61
#define MUTT_PAGER_LOGS
Logview mode.
Definition: lib.h:73
#define MUTT_SEARCH
Resolve search patterns.
Definition: lib.h:63
const 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:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
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
const struct AttrColor * attr_color
Curses colour of text.
Definition: display.h:41
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ append_line()

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

Add a new Line to the array.

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

Definition at line 255 of file display.c.

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

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

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

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

321{
322 bool is_quote = false;
323 const struct Regex *c_smileys = cs_subset_regex(NeoMutt->sub, "smileys");
324 regmatch_t pmatch_internal[1];
325
326 if (!pmatch)
327 pmatch = pmatch_internal;
328
329 const struct Regex *c_quote_regex = cs_subset_regex(NeoMutt->sub, "quote_regex");
330 if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
331 {
332 regmatch_t smatch[1];
333 if (mutt_regex_capture(c_smileys, line, 1, smatch))
334 {
335 if (smatch[0].rm_so > 0)
336 {
337 char c = line[smatch[0].rm_so];
338 line[smatch[0].rm_so] = 0;
339
340 if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
341 is_quote = true;
342
343 line[smatch[0].rm_so] = c;
344 }
345 }
346 else
347 {
348 is_quote = true;
349 }
350 }
351
352 return is_quote;
353}
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:218
bool mutt_regex_capture(const struct Regex *regex, const char *str, size_t nmatch, regmatch_t matches[])
Match a regex against a string, with provided options.
Definition: regex.c:619
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 368 of file display.c.

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

◆ buf_strip_formatting()

void buf_strip_formatting ( struct Buffer dest,
const char *  src,
bool  strip_markers 
)

Removes ANSI and backspace formatting.

Parameters
destBuffer for the result
srcString to strip
strip_markersRemove

Removes ANSI and backspace formatting, and optionally markers. This is separated out so that it can be used both by the pager and the autoview handler.

This logic is pulled from the pager fill_buffer() function, for use in stripping reply-quoted autoview output of ansi sequences.

Definition at line 696 of file display.c.

697{
698 const char *s = src;
699
700 buf_reset(dest);
701
702 if (!s)
703 return;
704
705 while (s[0] != '\0')
706 {
707 if ((s[0] == '\010') && (s > src))
708 {
709 if (s[1] == '_') /* underline */
710 {
711 s += 2;
712 }
713 else if (s[1] && buf_len(dest)) /* bold or overstrike */
714 {
715 dest->dptr--;
716 buf_addch(dest, s[1]);
717 s += 2;
718 }
719 else /* ^H */
720 {
721 buf_addch(dest, *s++);
722 }
723 continue;
724 }
725
726 int len = ansi_color_seq_length(s);
727 if (len > 0)
728 {
729 s += len;
730 }
731 else if (strip_markers && (s[0] == '\033') && (s[1] == ']') &&
733 {
734 mutt_debug(LL_DEBUG2, "Seen attachment marker\n");
735 while (*s++ != '\a')
736 ; /* skip pseudo-ANSI sequence */
737 }
738 else
739 {
740 buf_addch(dest, *s++);
741 }
742 }
743}
int ansi_color_seq_length(const char *str)
Is this an ANSI escape sequence?
Definition: ansi.c:82
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:466
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:253
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
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 757 of file display.c.

759{
760 static int b_read = 0;
761 struct Buffer stripped;
762
763 if (*buf_ready == 0)
764 {
765 if (offset != *bytes_read)
766 {
767 if (!mutt_file_seek(fp, offset, SEEK_SET))
768 {
769 return -1;
770 }
771 }
772
773 *buf = (unsigned char *) mutt_file_read_line((char *) *buf, blen, fp, NULL, MUTT_RL_EOL);
774 if (!*buf)
775 {
776 fmt[0] = NULL;
777 return -1;
778 }
779
780 *bytes_read = ftello(fp);
781 b_read = (int) (*bytes_read - offset);
782 *buf_ready = 1;
783
784 buf_init(&stripped);
785 buf_alloc(&stripped, *blen);
786 buf_strip_formatting(&stripped, (const char *) *buf, 1);
787 /* This should be a noop, because *fmt should be NULL */
788 FREE(fmt);
789 *fmt = (unsigned char *) stripped.data;
790 }
791
792 return b_read;
793}
struct Buffer * buf_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:55
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:349
void buf_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
Removes ANSI and backspace formatting.
Definition: display.c:696
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:763
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:733
#define MUTT_RL_EOL
don't strip \n / \r\n
Definition: file.h:41
#define FREE(x)
Definition: memory.h:45
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 812 of file display.c.

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

1018{
1019 unsigned char *buf = NULL, *fmt = NULL;
1020 size_t buflen = 0;
1021 unsigned char *buf_ptr = NULL;
1022 int ch, vch, col, cnt, b_read;
1023 int buf_ready = 0;
1024 bool change_last = false;
1025 int special;
1026 int offset;
1027 const struct AttrColor *def_color = NULL;
1028 int m;
1029 int rc = -1;
1030 struct AnsiColor ansi = { NULL, 0, COLOR_DEFAULT, COLOR_DEFAULT };
1031 regmatch_t pmatch[1];
1032
1033 if (line_num == *lines_used)
1034 {
1035 (*lines_used)++;
1036 change_last = true;
1037 }
1038
1039 if (*lines_used == *lines_max)
1040 {
1041 mutt_mem_realloc(lines, sizeof(struct Line) * (*lines_max += LINES));
1042 for (ch = *lines_used; ch < *lines_max; ch++)
1043 {
1044 memset(&((*lines)[ch]), 0, sizeof(struct Line));
1045 (*lines)[ch].cid = -1;
1046 (*lines)[ch].search_arr_size = -1;
1047 (*lines)[ch].syntax = mutt_mem_calloc(1, sizeof(struct TextSyntax));
1048 ((*lines)[ch].syntax)[0].first = -1;
1049 ((*lines)[ch].syntax)[0].last = -1;
1050 }
1051 }
1052
1053 struct Line *const cur_line = &(*lines)[line_num];
1054
1055 if (flags & MUTT_PAGER_LOGS)
1056 {
1057 /* determine the line class */
1058 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1059 {
1060 if (change_last)
1061 (*lines_used)--;
1062 goto out;
1063 }
1064
1065 cur_line->cid = MT_COLOR_MESSAGE_LOG;
1066 if (buf[11] == 'M')
1068 else if (buf[11] == 'W')
1070 else if (buf[11] == 'E')
1072 else
1074 }
1075
1076 /* only do color highlighting if we are viewing a message */
1077 if (flags & (MUTT_SHOWCOLOR | MUTT_TYPES))
1078 {
1079 if (cur_line->cid == -1)
1080 {
1081 /* determine the line class */
1082 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1083 {
1084 if (change_last)
1085 (*lines_used)--;
1086 goto out;
1087 }
1088
1089 resolve_types(win_pager, (char *) fmt, (char *) buf, *lines, line_num, *lines_used,
1090 quote_list, q_level, force_redraw, flags & MUTT_SHOWCOLOR);
1091
1092 /* avoid race condition for continuation lines when scrolling up */
1093 for (m = line_num + 1;
1094 m < *lines_used && (*lines)[m].offset && (*lines)[m].cont_line; m++)
1095 {
1096 (*lines)[m].cid = cur_line->cid;
1097 }
1098 }
1099
1100 /* this also prevents searching through the hidden lines */
1101 const short c_toggle_quoted_show_levels = cs_subset_number(NeoMutt->sub, "toggle_quoted_show_levels");
1102 if ((flags & MUTT_HIDE) && (cur_line->cid == MT_COLOR_QUOTED) &&
1103 (!cur_line->quote || (cur_line->quote->quote_n >= c_toggle_quoted_show_levels)))
1104 {
1105 flags = 0; /* MUTT_NOSHOW */
1106 }
1107 }
1108
1109 /* At this point, (*lines[line_num]).quote may still be undefined. We
1110 * don't want to compute it every time MUTT_TYPES is set, since this
1111 * would slow down the "bottom" function unacceptably. A compromise
1112 * solution is hence to call regexec() again, just to find out the
1113 * length of the quote prefix. */
1114 if ((flags & MUTT_SHOWCOLOR) && !cur_line->cont_line &&
1115 (cur_line->cid == MT_COLOR_QUOTED) && !cur_line->quote)
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 const struct Regex *c_quote_regex = cs_subset_regex(NeoMutt->sub, "quote_regex");
1125 if (mutt_regex_capture(c_quote_regex, (char *) fmt, 1, pmatch))
1126 {
1127 cur_line->quote = qstyle_classify(quote_list, (char *) fmt + pmatch[0].rm_so,
1128 pmatch[0].rm_eo - pmatch[0].rm_so,
1129 force_redraw, q_level);
1130 }
1131 else
1132 {
1133 goto out;
1134 }
1135 }
1136
1137 if ((flags & MUTT_SEARCH) && !cur_line->cont_line && (cur_line->search_arr_size == -1))
1138 {
1139 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1140 {
1141 if (change_last)
1142 (*lines_used)--;
1143 goto out;
1144 }
1145
1146 offset = 0;
1147 cur_line->search_arr_size = 0;
1148 while (regexec(search_re, (char *) fmt + offset, 1, pmatch,
1149 (offset ? REG_NOTBOL : 0)) == 0)
1150 {
1151 if (++(cur_line->search_arr_size) > 1)
1152 {
1153 mutt_mem_realloc(&(cur_line->search),
1154 (cur_line->search_arr_size) * sizeof(struct TextSyntax));
1155 // Zero the new entry
1156 const int index = cur_line->search_arr_size - 1;
1157 struct TextSyntax *ts = &cur_line->search[index];
1158 memset(ts, 0, sizeof(*ts));
1159 }
1160 else
1161 {
1162 cur_line->search = mutt_mem_calloc(1, sizeof(struct TextSyntax));
1163 }
1164 pmatch[0].rm_so += offset;
1165 pmatch[0].rm_eo += offset;
1166 (cur_line->search)[cur_line->search_arr_size - 1].first = pmatch[0].rm_so;
1167 (cur_line->search)[cur_line->search_arr_size - 1].last = pmatch[0].rm_eo;
1168
1169 if (pmatch[0].rm_eo == pmatch[0].rm_so)
1170 offset++; /* avoid degenerate cases */
1171 else
1172 offset = pmatch[0].rm_eo;
1173 if (!fmt[offset])
1174 break;
1175 }
1176 }
1177
1178 if (!(flags & MUTT_SHOW) && ((*lines)[line_num + 1].offset > 0))
1179 {
1180 /* we've already scanned this line, so just exit */
1181 rc = 0;
1182 goto out;
1183 }
1184 if ((flags & MUTT_SHOWCOLOR) && *force_redraw && ((*lines)[line_num + 1].offset > 0))
1185 {
1186 /* no need to try to display this line... */
1187 rc = 1;
1188 goto out; /* fake display */
1189 }
1190
1191 b_read = fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready);
1192 if (b_read < 0)
1193 {
1194 if (change_last)
1195 (*lines_used)--;
1196 goto out;
1197 }
1198
1199 /* now chose a good place to break the line */
1200 cnt = format_line(win_pager, lines, line_num, buf, flags, NULL, b_read, &ch,
1201 &vch, &col, &special, win_pager->state.cols, ansi_list);
1202 buf_ptr = buf + cnt;
1203
1204 /* move the break point only if smart_wrap is set */
1205 const bool c_smart_wrap = cs_subset_bool(NeoMutt->sub, "smart_wrap");
1206 if (c_smart_wrap)
1207 {
1208 if ((cnt < b_read) && (ch != -1) &&
1209 !simple_color_is_header(cur_line->cid) && !isspace(buf[cnt]))
1210 {
1211 buf_ptr = buf + ch;
1212 /* skip trailing blanks */
1213 while (ch && ((buf[ch] == ' ') || (buf[ch] == '\t') || (buf[ch] == '\r')))
1214 ch--;
1215 /* A very long word with leading spaces causes infinite
1216 * wrapping when MUTT_PAGER_NSKIP is set. A folded header
1217 * with a single long word shouldn't be smartwrapped
1218 * either. So just disable smart_wrap if it would wrap at the
1219 * beginning of the line. */
1220 if (ch == 0)
1221 buf_ptr = buf + cnt;
1222 else
1223 cnt = ch + 1;
1224 }
1225 if (!(flags & MUTT_PAGER_NSKIP))
1226 {
1227 /* skip leading blanks on the next line too */
1228 while ((*buf_ptr == ' ') || (*buf_ptr == '\t'))
1229 buf_ptr++;
1230 }
1231 }
1232
1233 if (*buf_ptr == '\r')
1234 buf_ptr++;
1235 if (*buf_ptr == '\n')
1236 buf_ptr++;
1237
1238 if (((int) (buf_ptr - buf) < b_read) && !(*lines)[line_num + 1].cont_line)
1239 append_line(*lines, line_num, (int) (buf_ptr - buf));
1240 (*lines)[line_num + 1].offset = cur_line->offset + (long) (buf_ptr - buf);
1241
1242 /* if we don't need to display the line we are done */
1243 if (!(flags & MUTT_SHOW))
1244 {
1245 rc = 0;
1246 goto out;
1247 }
1248
1249 if (flags & MUTT_PAGER_STRIPES)
1250 {
1251 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1253 }
1254
1255 /* display the line */
1256 format_line(win_pager, lines, line_num, buf, flags, &ansi, cnt, &ch, &vch,
1257 &col, &special, win_pager->state.cols, ansi_list);
1258
1259 /* avoid a bug in ncurses... */
1260 if (col == 0)
1261 {
1262 if (flags & MUTT_PAGER_STRIPES)
1263 {
1264 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1266 }
1267 else
1268 {
1270 }
1271
1272 mutt_window_addch(win_pager, ' ');
1273 }
1274
1275 /* Fill the blank space at the end of the line with the prevailing color.
1276 * ncurses does an implicit clrtoeol() when you do mutt_window_addch('\n') so we have
1277 * to make sure to reset the color *after* that */
1278 if (flags & MUTT_SHOWCOLOR)
1279 {
1280 m = (cur_line->cont_line) ? (cur_line->syntax)[0].first : line_num;
1281 if ((*lines)[m].cid == MT_COLOR_HEADER)
1282 {
1283 def_color = ((*lines)[m].syntax)[0].attr_color;
1284 }
1285 else
1286 {
1287 def_color = simple_color_get((*lines)[m].cid);
1288 }
1289 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
1290 const struct AttrColor *ac_eol = NULL;
1291 if (def_color)
1292 ac_eol = merged_color_overlay(ac_normal, def_color);
1293 else
1294 ac_eol = ac_normal;
1295 mutt_curses_set_color(ac_eol);
1296 }
1297
1298 if (col < win_pager->state.cols)
1299 {
1300 if (flags & MUTT_PAGER_STRIPES)
1301 {
1302 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1304 }
1305 else
1306 {
1308 }
1309 mutt_window_clrtoeol(win_pager);
1310 }
1311
1312 /* reset the color back to normal. This *must* come after the
1313 * clrtoeol, otherwise the color for this line will not be
1314 * filled to the right margin. */
1315 if (flags & MUTT_SHOWCOLOR)
1317
1318 /* build a return code */
1319 if (!(flags & MUTT_SHOW))
1320 flags = 0;
1321
1322 rc = flags;
1323
1324out:
1325 FREE(&buf);
1326 FREE(&fmt);
1327 return rc;
1328}
#define COLOR_DEFAULT
Definition: color.h:104
ColorId
List of all colored objects.
Definition: color.h:38
@ 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:81
static int format_line(struct MuttWindow *win, struct Line **lines, int line_num, unsigned char *buf, PagerFlags flags, struct AnsiColor *ansi, int cnt, int *pspace, int *pvch, int *pcol, int *pspecial, int width, struct AttrColorList *ansi_list)
Display a line of text in the pager.
Definition: display.c:812
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:757
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:368
static void append_line(struct Line *lines, int line_num, int cnt)
Add a new Line to the array.
Definition: display.c:255
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:244
#define MUTT_PAGER_NSKIP
Preserve whitespace with smartwrap.
Definition: lib.h:68
#define MUTT_HIDE
Don't show quoted text.
Definition: lib.h:62
#define MUTT_TYPES
Compute line's type.
Definition: lib.h:64
#define MUTT_SHOW
Definition: lib.h:65
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: