NeoMutt  2022-04-29-145-g9b6a0e
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...
 
int 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:69
#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 (ansi->attr_color)
219  {
220  color = *ansi->attr_color;
221  }
222 
223  if (!attr_color_match(&color, &last_color))
224  {
226  &color);
227  mutt_curses_set_color(ac_merge);
228  last_color = color;
229  }
230 }
struct AttrColor attr_color_copy(struct AttrColor *ac)
Copy a colour.
Definition: attr.c:146
bool attr_color_match(struct AttrColor *ac1, struct AttrColor *ac2)
Do the colours match?
Definition: attr.c:174
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition: simple.c:74
bool simple_color_is_set(enum ColorId cid)
Is the object coloured?
Definition: simple.c:95
@ MT_COLOR_MARKERS
Pager: markers, line continuation.
Definition: color.h:50
@ MT_COLOR_QUOTED
Pager: quoted text.
Definition: color.h:57
@ 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:53
@ MT_COLOR_SEARCH
Pager: search matches.
Definition: color.h:58
@ MT_COLOR_UNDERLINE
Underlined text.
Definition: color.h:73
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 238 of file display.c.

239 {
240  int m;
241 
242  lines[line_num + 1].cid = lines[line_num].cid;
243  (lines[line_num + 1].syntax)[0].attr_color = (lines[line_num].syntax)[0].attr_color;
244  lines[line_num + 1].cont_line = 1;
245 
246  /* find the real start of the line */
247  for (m = line_num; m >= 0; m--)
248  if (lines[m].cont_line == 0)
249  break;
250 
251  (lines[line_num + 1].syntax)[0].first = m;
252  (lines[line_num + 1].syntax)[0].last = (lines[line_num].cont_line) ?
253  cnt + (lines[line_num].syntax)[0].last :
254  cnt;
255 }
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 263 of file display.c.

264 {
265  for (; (p[0] == q[0]) && (q[0] != '\0') && (p[0] != '\0') && (q[0] != '\a') &&
266  (p[0] != '\a');
267  p++, q++)
268  {
269  }
270 
271  return (int) (*p - *q);
272 }
+ 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 279 of file display.c.

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

290 {
292 }
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()

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

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

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

668 {
669  const char *s = src;
670 
671  mutt_buffer_reset(dest);
672 
673  if (!s)
674  return;
675 
676  while (s[0] != '\0')
677  {
678  if ((s[0] == '\010') && (s > src))
679  {
680  if (s[1] == '_') /* underline */
681  s += 2;
682  else if (s[1] && mutt_buffer_len(dest)) /* bold or overstrike */
683  {
684  dest->dptr--;
685  mutt_buffer_addch(dest, s[1]);
686  s += 2;
687  }
688  else /* ^H */
689  mutt_buffer_addch(dest, *s++);
690  continue;
691  }
692 
693  int len = ansi_color_seq_length(s);
694  if (len > 0)
695  {
696  s += len;
697  }
698  else if (strip_markers && (s[0] == '\033') && (s[1] == ']') &&
700  {
701  mutt_debug(LL_DEBUG2, "Seen attachment marker\n");
702  while (*s++ != '\a')
703  ; /* skip pseudo-ANSI sequence */
704  }
705  else
706  mutt_buffer_addch(dest, *s++);
707  }
708 }
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:354
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:238
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:81
#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 722 of file display.c.

724 {
725  static int b_read;
726  struct Buffer stripped;
727 
728  if (*buf_ready == 0)
729  {
730  if (offset != *bytes_read)
731  {
732  if (!mutt_file_seek(fp, offset, SEEK_SET))
733  {
734  return -1;
735  }
736  }
737 
738  *buf = (unsigned char *) mutt_file_read_line((char *) *buf, blen, fp, NULL, MUTT_RL_EOL);
739  if (!*buf)
740  {
741  fmt[0] = NULL;
742  return -1;
743  }
744 
745  *bytes_read = ftello(fp);
746  b_read = (int) (*bytes_read - offset);
747  *buf_ready = 1;
748 
749  mutt_buffer_init(&stripped);
750  mutt_buffer_alloc(&stripped, *blen);
751  mutt_buffer_strip_formatting(&stripped, (const char *) *buf, 1);
752  /* This should be a noop, because *fmt should be NULL */
753  FREE(fmt);
754  *fmt = (unsigned char *) stripped.data;
755  }
756 
757  return b_read;
758 }
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:265
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:48
void mutt_buffer_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
Removes ANSI and backspace formatting.
Definition: display.c:667
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 777 of file display.c.

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

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