NeoMutt  2021-10-29-43-g6b8931
Teaching an old dog new tricks
DOXYGEN
display.c File Reference

Pager Display. More...

#include "config.h"
#include <ctype.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 "protos.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 AnsiAttr *aa)
 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...
 
static bool is_ansi (const char *str)
 Is this an ANSI escape sequence? More...
 
static int grok_ansi (const unsigned char *buf, int pos, struct AnsiAttr *aa)
 Parse an ANSI escape sequence. 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 AnsiAttr *aa, int cnt, int *pspace, int *pvch, int *pcol, int *pspecial, int width)
 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)
 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 56 of file display.c.

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

97 {
98  const int *cnt = (const int *) m1;
99  const struct TextSyntax *stx = (const struct TextSyntax *) m2;
100 
101  if (*cnt < stx->first)
102  return -1;
103  if (*cnt >= stx->last)
104  return 1;
105  return 0;
106 }
Highlighting for a piece of text.
Definition: display.h:50
int last
Last character in line to be coloured.
Definition: display.h:53
int first
First character in line to be coloured.
Definition: display.h:52
+ 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 AnsiAttr aa 
)
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
aaANSI attributes

Definition at line 118 of file display.c.

120 {
121  int def_color; /* color without syntax highlight */
122  int color; /* final color */
123  static int last_color; /* last color set */
124  bool search = false;
125  int m;
126  struct TextSyntax *matching_chunk = NULL;
127 
128  if (cnt == 0)
129  last_color = -1;
130 
131  if (lines[line_num].cont_line)
132  {
133  const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
134  if (!cnt && c_markers)
135  {
137  mutt_window_addch(win, '+');
138  last_color = simple_colors_get(MT_COLOR_MARKERS);
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 = simple_colors_get(lines[line_num].syntax[0].color);
148  }
149  else if (!(flags & MUTT_SHOWCOLOR))
150  def_color = simple_colors_get(MT_COLOR_NORMAL);
151  else if (lines[m].color == MT_COLOR_HEADER)
152  def_color = lines[m].syntax[0].color;
153  else
154  def_color = simple_colors_get(lines[m].color);
155 
156  if ((flags & MUTT_SHOWCOLOR) && (lines[m].color == MT_COLOR_QUOTED))
157  {
158  struct QuoteStyle *qc = lines[m].quote;
159 
160  if (qc)
161  {
162  def_color = qc->color;
163 
164  while (qc && (qc->prefix_len > cnt))
165  {
166  def_color = qc->color;
167  qc = qc->up;
168  }
169  }
170  }
171 
172  color = def_color;
173  if ((flags & MUTT_SHOWCOLOR) && lines[m].syntax_arr_size)
174  {
175  matching_chunk = bsearch(&cnt, lines[m].syntax, lines[m].syntax_arr_size,
176  sizeof(struct TextSyntax), comp_syntax_t);
177  if (matching_chunk && (cnt >= matching_chunk->first) &&
178  (cnt < matching_chunk->last))
179  {
180  color = matching_chunk->color;
181  }
182  }
183 
184  if ((flags & MUTT_SEARCH) && lines[m].search_arr_size)
185  {
186  matching_chunk = bsearch(&cnt, lines[m].search, lines[m].search_arr_size,
187  sizeof(struct TextSyntax), comp_syntax_t);
188  if (matching_chunk && (cnt >= matching_chunk->first) &&
189  (cnt < matching_chunk->last))
190  {
192  search = 1;
193  }
194  }
195 
196  /* handle "special" bold & underlined characters */
197  if (special || aa->attr)
198  {
199  if ((aa->attr & ANSI_COLOR))
200  {
201  if (aa->pair == -1)
202  aa->pair = mutt_color_alloc(aa->fg, aa->bg);
203  color = aa->pair;
204  if (aa->attr & ANSI_BOLD)
205  color |= A_BOLD;
206  }
207  else if ((special & A_BOLD) || (aa->attr & ANSI_BOLD))
208  {
211  else
212  color ^= A_BOLD;
213  }
214  if ((special & A_UNDERLINE) || (aa->attr & ANSI_UNDERLINE))
215  {
218  else
219  color ^= A_UNDERLINE;
220  }
221  else if (aa->attr & ANSI_REVERSE)
222  {
223  color ^= A_REVERSE;
224  }
225  else if (aa->attr & ANSI_BLINK)
226  {
227  color ^= A_BLINK;
228  }
229  else if (aa->attr == ANSI_OFF)
230  {
231  aa->attr = 0;
232  }
233  }
234 
235  if (color != last_color)
236  {
238  last_color = color;
239  }
240 }
int simple_colors_get(enum ColorId id)
Get the colour of an object by its ID.
Definition: simple.c:68
bool simple_color_is_set(enum ColorId id)
Is the object coloured?
Definition: simple.c:89
int mutt_color_alloc(uint32_t fg, uint32_t bg)
Allocate a colour pair.
Definition: color.c:188
@ 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
static int comp_syntax_t(const void *m1, const void *m2)
Search for a Syntax using bsearch(3)
Definition: display.c:96
#define ANSI_BOLD
Bold text.
Definition: display.h:40
#define ANSI_BLINK
Blinking text.
Definition: display.h:39
#define ANSI_UNDERLINE
Underlined text.
Definition: display.h:41
#define ANSI_REVERSE
Reverse video.
Definition: display.h:42
#define ANSI_OFF
Turn off colours and attributes.
Definition: display.h:38
#define ANSI_COLOR
Use colours.
Definition: display.h:43
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
static int search(struct Menu *menu, int op)
Search a menu.
Definition: menu.c:142
void mutt_curses_set_attr(int attr)
Set the attributes for text.
Definition: mutt_curses.c:39
void mutt_curses_set_color_by_id(enum ColorId color)
Set the current colour for text.
Definition: mutt_curses.c:52
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
Definition: mutt_window.c:380
#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
AnsiFlags attr
Attributes, e.g. ANSI_BOLD.
Definition: display.h:61
int pair
Curses colour pair.
Definition: display.h:64
int fg
ANSI Foreground colour, e.g. 1 red, 2 green, etc.
Definition: display.h:62
int bg
ANSI Background colour, e.g. 1 red, 2 green, etc.
Definition: display.h:63
struct QuoteStyle * quote
Quoting style for this line (pointer into PagerPrivateData->quote_list)
Definition: display.h:83
struct TextSyntax * syntax
Array of coloured text in the line.
Definition: display.h:78
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:66
struct QuoteStyle * up
Definition: quoted.h:72
size_t prefix_len
Length of the prefix string.
Definition: quoted.h:70
int color
Curses colour pair.
Definition: quoted.h:68
int color
Curses colour of text.
Definition: display.h:51
+ 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 248 of file display.c.

249 {
250  int m;
251 
252  lines[line_num + 1].color = lines[line_num].color;
253  (lines[line_num + 1].syntax)[0].color = (lines[line_num].syntax)[0].color;
254  lines[line_num + 1].cont_line = 1;
255 
256  /* find the real start of the line */
257  for (m = line_num; m >= 0; m--)
258  if (lines[m].cont_line == 0)
259  break;
260 
261  (lines[line_num + 1].syntax)[0].first = m;
262  (lines[line_num + 1].syntax)[0].last =
263  (lines[line_num].cont_line) ? cnt + (lines[line_num].syntax)[0].last : cnt;
264 }
bool cont_line
Continuation of a previous line (wrapped by NeoMutt)
Definition: display.h:74
short color
Default line colour, e.g. MT_COLOR_QUOTED.
Definition: display.h:73
+ 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 272 of file display.c.

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

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

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

313 {
314  bool is_quote = false;
315  const struct Regex *c_smileys = cs_subset_regex(NeoMutt->sub, "smileys");
316  regmatch_t pmatch_internal[1], smatch[1];
317 
318  if (!pmatch)
319  pmatch = pmatch_internal;
320 
321  const struct Regex *c_quote_regex =
322  cs_subset_regex(NeoMutt->sub, "quote_regex");
323  if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
324  {
325  if (mutt_regex_capture(c_smileys, line, 1, smatch))
326  {
327  if (smatch[0].rm_so > 0)
328  {
329  char c = line[smatch[0].rm_so];
330  line[smatch[0].rm_so] = 0;
331 
332  if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
333  is_quote = true;
334 
335  line[smatch[0].rm_so] = c;
336  }
337  }
338  else
339  is_quote = true;
340  }
341 
342  return is_quote;
343 }
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:243
bool mutt_regex_capture(const struct Regex *regex, const char *str, size_t nmatch, regmatch_t matches[])
Match a regex against a string, with provided options.
Definition: regex.c:610
Cached regular expression.
Definition: regex3.h:90
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ resolve_types()

static void resolve_types ( struct MuttWindow win,
char *  buf,
char *  raw,
struct Line lines,
int  line_num,
int  lines_used,
struct QuoteStyle **  quote_list,
int *  q_level,
bool *  force_redraw,
bool  q_classify 
)
static

Determine the style for a line of text.

Parameters
[in]winWindow
[in]bufFormatted text
[in]rawRaw text
[in]linesLine info array
[in]line_numLine number (index into lines)
[in]lines_usedLast line
[out]quote_listList of quote colours
[out]q_levelQuote level
[out]force_redrawSet to true if a screen redraw is needed
[in]q_classifyIf true, style the text

Definition at line 358 of file display.c.

362 {
363  struct RegexColor *color_line = NULL;
364  struct RegexColorList *head = NULL;
365  regmatch_t pmatch[1];
366  bool found;
367  bool null_rx;
368  const bool c_header_color_partial =
369  cs_subset_bool(NeoMutt->sub, "header_color_partial");
370  int offset, i = 0;
371 
372  if ((line_num == 0) || simple_color_is_header(lines[line_num - 1].color) ||
373  (check_protected_header_marker(raw) == 0))
374  {
375  if (buf[0] == '\n') /* end of header */
376  {
377  lines[line_num].color = MT_COLOR_NORMAL;
379  }
380  else
381  {
382  /* if this is a continuation of the previous line, use the previous
383  * line's color as default. */
384  if ((line_num > 0) && ((buf[0] == ' ') || (buf[0] == '\t')))
385  {
386  lines[line_num].color = lines[line_num - 1].color; /* wrapped line */
387  if (!c_header_color_partial)
388  {
389  (lines[line_num].syntax)[0].color = (lines[line_num - 1].syntax)[0].color;
390  lines[line_num].cont_header = 1;
391  }
392  }
393  else
394  {
395  lines[line_num].color = MT_COLOR_HDRDEFAULT;
396  }
397 
398  /* When this option is unset, we color the entire header the
399  * same color. Otherwise, we handle the header patterns just
400  * like body patterns (further below). */
401  if (!c_header_color_partial)
402  {
404  {
405  if (regexec(&color_line->regex, buf, 0, NULL, 0) == 0)
406  {
407  lines[line_num].color = MT_COLOR_HEADER;
408  lines[line_num].syntax[0].color = color_line->pair;
409  if (lines[line_num].cont_header)
410  {
411  /* adjust the previous continuation lines to reflect the color of this continuation line */
412  int j;
413  for (j = line_num - 1; j >= 0 && lines[j].cont_header; --j)
414  {
415  lines[j].color = lines[line_num].color;
416  lines[j].syntax[0].color = lines[line_num].syntax[0].color;
417  }
418  /* now adjust the first line of this header field */
419  if (j >= 0)
420  {
421  lines[j].color = lines[line_num].color;
422  lines[j].syntax[0].color = lines[line_num].syntax[0].color;
423  }
424  *force_redraw = true; /* the previous lines have already been drawn on the screen */
425  }
426  break;
427  }
428  }
429  }
430  }
431  }
432  else if (mutt_str_startswith(raw, "\033[0m")) // Escape: a little hack...
433  lines[line_num].color = MT_COLOR_NORMAL;
434  else if (check_attachment_marker((char *) raw) == 0)
435  lines[line_num].color = MT_COLOR_ATTACHMENT;
436  else if (mutt_str_equal("-- \n", buf) || mutt_str_equal("-- \r\n", buf))
437  {
438  i = line_num + 1;
439 
440  lines[line_num].color = MT_COLOR_SIGNATURE;
441  while ((i < lines_used) && (check_sig(buf, lines, i - 1) == 0) &&
442  ((lines[i].color == MT_COLOR_NORMAL) || (lines[i].color == MT_COLOR_QUOTED) ||
443  (lines[i].color == MT_COLOR_HEADER)))
444  {
445  /* oops... */
446  if (lines[i].syntax_arr_size)
447  {
448  lines[i].syntax_arr_size = 0;
449  mutt_mem_realloc(&(lines[line_num].syntax), sizeof(struct TextSyntax));
450  }
451  lines[i++].color = MT_COLOR_SIGNATURE;
452  }
453  }
454  else if (check_sig(buf, lines, line_num - 1) == 0)
455  lines[line_num].color = MT_COLOR_SIGNATURE;
456  else if (mutt_is_quote_line(buf, pmatch))
457 
458  {
459  if (q_classify && (lines[line_num].quote == NULL))
460  {
461  lines[line_num].quote = qstyle_classify(quote_list, buf + pmatch[0].rm_so,
462  pmatch[0].rm_eo - pmatch[0].rm_so,
463  force_redraw, q_level);
464  }
465  lines[line_num].color = MT_COLOR_QUOTED;
466  }
467  else
468  lines[line_num].color = MT_COLOR_NORMAL;
469 
470  /* body patterns */
471  if ((lines[line_num].color == MT_COLOR_NORMAL) || (lines[line_num].color == MT_COLOR_QUOTED) ||
472  ((lines[line_num].color == MT_COLOR_HDRDEFAULT) && c_header_color_partial))
473  {
474  size_t nl;
475 
476  /* don't consider line endings part of the buffer
477  * for regex matching */
478  nl = mutt_str_len(buf);
479  if ((nl > 0) && (buf[nl - 1] == '\n'))
480  buf[nl - 1] = '\0';
481 
482  i = 0;
483  offset = 0;
484  lines[line_num].syntax_arr_size = 0;
485  if (lines[line_num].color == MT_COLOR_HDRDEFAULT)
487  else
489  STAILQ_FOREACH(color_line, head, entries)
490  {
491  color_line->stop_matching = false;
492  }
493  do
494 
495  {
496  if (!buf[offset])
497  break;
498 
499  found = false;
500  null_rx = false;
501  STAILQ_FOREACH(color_line, head, entries)
502  {
503  if (!color_line->stop_matching &&
504  (regexec(&color_line->regex, buf + offset, 1, pmatch,
505  ((offset != 0) ? REG_NOTBOL : 0)) == 0))
506  {
507  if (pmatch[0].rm_eo == pmatch[0].rm_so)
508  {
509  null_rx = true; /* empty regex; don't add it, but keep looking */
510  continue;
511  }
512 
513  if (!found)
514  {
515  /* Abort if we fill up chunks.
516  * Yes, this really happened. */
517  if (lines[line_num].syntax_arr_size == SHRT_MAX)
518  {
519  null_rx = false;
520  break;
521  }
522  if (++(lines[line_num].syntax_arr_size) > 1)
523  {
524  mutt_mem_realloc(&(lines[line_num].syntax),
525  (lines[line_num].syntax_arr_size) * sizeof(struct TextSyntax));
526  }
527  }
528  i = lines[line_num].syntax_arr_size - 1;
529  pmatch[0].rm_so += offset;
530  pmatch[0].rm_eo += offset;
531  if (!found || (pmatch[0].rm_so < (lines[line_num].syntax)[i].first) ||
532  ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
533  (pmatch[0].rm_eo > (lines[line_num].syntax)[i].last)))
534  {
535  (lines[line_num].syntax)[i].color = color_line->pair;
536  (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
537  (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
538  }
539  found = true;
540  null_rx = false;
541  }
542  else
543  {
544  /* Once a regexp fails to match, don't try matching it again.
545  * On very long lines this can cause a performance issue if there
546  * are other regexps that have many matches. */
547  color_line->stop_matching = true;
548  }
549  }
550 
551  if (null_rx)
552  offset++; /* avoid degenerate cases */
553  else
554  offset = (lines[line_num].syntax)[i].last;
555  } while (found || null_rx);
556  if (nl > 0)
557  buf[nl] = '\n';
558  }
559 
560  /* attachment patterns */
561  if (lines[line_num].color == MT_COLOR_ATTACHMENT)
562  {
563  size_t nl;
564 
565  /* don't consider line endings part of the buffer for regex matching */
566  nl = mutt_str_len(buf);
567  if ((nl > 0) && (buf[nl - 1] == '\n'))
568  buf[nl - 1] = '\0';
569 
570  i = 0;
571  offset = 0;
572  lines[line_num].syntax_arr_size = 0;
573  do
574  {
575  if (!buf[offset])
576  break;
577 
578  found = false;
579  null_rx = false;
581  {
582  if (regexec(&color_line->regex, buf + offset, 1, pmatch,
583  ((offset != 0) ? REG_NOTBOL : 0)) != 0)
584  {
585  continue;
586  }
587 
588  if (pmatch[0].rm_eo != pmatch[0].rm_so)
589  {
590  if (!found)
591  {
592  if (++(lines[line_num].syntax_arr_size) > 1)
593  {
594  mutt_mem_realloc(&(lines[line_num].syntax),
595  (lines[line_num].syntax_arr_size) * sizeof(struct TextSyntax));
596  }
597  }
598  i = lines[line_num].syntax_arr_size - 1;
599  pmatch[0].rm_so += offset;
600  pmatch[0].rm_eo += offset;
601  if (!found || (pmatch[0].rm_so < (lines[line_num].syntax)[i].first) ||
602  ((pmatch[0].rm_so == (lines[line_num].syntax)[i].first) &&
603  (pmatch[0].rm_eo > (lines[line_num].syntax)[i].last)))
604  {
605  (lines[line_num].syntax)[i].color = color_line->pair;
606  (lines[line_num].syntax)[i].first = pmatch[0].rm_so;
607  (lines[line_num].syntax)[i].last = pmatch[0].rm_eo;
608  }
609  found = 1;
610  null_rx = false;
611  }
612  else
613  null_rx = true; /* empty regex; don't add it, but keep looking */
614  }
615 
616  if (null_rx)
617  offset++; /* avoid degenerate cases */
618  else
619  offset = (lines[line_num].syntax)[i].last;
620  } while (found || null_rx);
621  if (nl > 0)
622  buf[nl] = '\n';
623  }
624 }
struct RegexColorList * regex_colors_get_list(enum ColorId id)
Return the RegexColorList for a colour id.
Definition: regex.c:129
bool simple_color_is_header(enum ColorId color_id)
Colour is for an Email header.
Definition: simple.c:101
@ 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:298
int mutt_is_quote_line(char *line, regmatch_t *pmatch)
Is a line of message text a quote?
Definition: display.c:312
static int check_sig(const char *s, struct Line *info, int offset)
Check for an email signature.
Definition: display.c:56
static int check_attachment_marker(const char *p)
Check that the unique marker is present.
Definition: display.c:288
int braille_col
Definition: dlg_pager.c:83
int braille_row
Definition: dlg_pager.c:82
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:715
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:158
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:475
void mutt_window_get_coords(struct MuttWindow *win, int *col, int *row)
Get the cursor position in the Window.
Definition: mutt_window.c:272
#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:223
bool cont_header
Continuation of a header line (wrapped by MTA)
Definition: display.h:75
short syntax_arr_size
Number of items in syntax array.
Definition: display.h:77
A regular expression and a color to highlight a line.
Definition: regex4.h:36
regex_t regex
Compiled regex.
Definition: regex4.h:38
int pair
Colour pair index.
Definition: regex4.h:43
bool stop_matching
Used by the pager for body patterns, to prevent the color from being retried once it fails.
Definition: regex4.h:45
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ is_ansi()

static bool is_ansi ( const char *  str)
static

Is this an ANSI escape sequence?

Parameters
strString to test
Return values
trueIt's an ANSI escape sequence

Definition at line 631 of file display.c.

632 {
633  while (*str && (isdigit(*str) || (*str == ';')))
634  str++;
635  return (*str == 'm');
636 }
+ Here is the caller graph for this function:

◆ grok_ansi()

static int grok_ansi ( const unsigned char *  buf,
int  pos,
struct AnsiAttr aa 
)
static

Parse an ANSI escape sequence.

Parameters
bufString to parse
posStarting position in string
aaAnsiAttr for the result
Return values
numIndex of first character after the escape sequence

Definition at line 645 of file display.c.

646 {
647  int x = pos;
648 
649  while (isdigit(buf[x]) || (buf[x] == ';'))
650  x++;
651 
652  /* Character Attributes */
653  const bool c_allow_ansi = cs_subset_bool(NeoMutt->sub, "allow_ansi");
654  if (c_allow_ansi && aa && (buf[x] == 'm'))
655  {
656  if (pos == x)
657  {
658  if (aa->pair != -1)
659  mutt_color_free(aa->fg, aa->bg);
660  aa->attr = ANSI_OFF;
661  aa->pair = -1;
662  }
663  while (pos < x)
664  {
665  if ((buf[pos] == '1') && (((pos + 1) == x) || (buf[pos + 1] == ';')))
666  {
667  aa->attr |= ANSI_BOLD;
668  pos += 2;
669  }
670  else if ((buf[pos] == '4') && (((pos + 1) == x) || (buf[pos + 1] == ';')))
671  {
672  aa->attr |= ANSI_UNDERLINE;
673  pos += 2;
674  }
675  else if ((buf[pos] == '5') && (((pos + 1) == x) || (buf[pos + 1] == ';')))
676  {
677  aa->attr |= ANSI_BLINK;
678  pos += 2;
679  }
680  else if ((buf[pos] == '7') && (((pos + 1) == x) || (buf[pos + 1] == ';')))
681  {
682  aa->attr |= ANSI_REVERSE;
683  pos += 2;
684  }
685  else if ((buf[pos] == '0') && (((pos + 1) == x) || (buf[pos + 1] == ';')))
686  {
687  if (aa->pair != -1)
688  mutt_color_free(aa->fg, aa->bg);
689  aa->attr = ANSI_OFF;
690  aa->pair = -1;
691  pos += 2;
692  }
693  else if ((buf[pos] == '3') && isdigit(buf[pos + 1]))
694  {
695  if (aa->pair != -1)
696  mutt_color_free(aa->fg, aa->bg);
697  aa->pair = -1;
698  aa->attr |= ANSI_COLOR;
699  aa->fg = buf[pos + 1] - '0';
700  pos += 3;
701  }
702  else if ((buf[pos] == '4') && isdigit(buf[pos + 1]))
703  {
704  if (aa->pair != -1)
705  mutt_color_free(aa->fg, aa->bg);
706  aa->pair = -1;
707  aa->attr |= ANSI_COLOR;
708  aa->bg = buf[pos + 1] - '0';
709  pos += 3;
710  }
711  else
712  {
713  while ((pos < x) && (buf[pos] != ';'))
714  pos++;
715  pos++;
716  }
717  }
718  }
719  pos = x;
720  return pos;
721 }
void mutt_color_free(uint32_t fg, uint32_t bg)
Free a colour.
Definition: color.c:109
+ 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 736 of file display.c.

737 {
738  const char *s = src;
739 
740  mutt_buffer_reset(dest);
741 
742  if (!s)
743  return;
744 
745  while (s[0] != '\0')
746  {
747  if ((s[0] == '\010') && (s > src))
748  {
749  if (s[1] == '_') /* underline */
750  s += 2;
751  else if (s[1] && mutt_buffer_len(dest)) /* bold or overstrike */
752  {
753  dest->dptr--;
754  mutt_buffer_addch(dest, s[1]);
755  s += 2;
756  }
757  else /* ^H */
758  mutt_buffer_addch(dest, *s++);
759  }
760  else if ((s[0] == '\033') && (s[1] == '[') && is_ansi(s + 2))
761  {
762  while (*s++ != 'm')
763  ; /* skip ANSI sequence */
764  }
765  else if (strip_markers && (s[0] == '\033') && (s[1] == ']') &&
767  {
768  mutt_debug(LL_DEBUG2, "Seen attachment marker\n");
769  while (*s++ != '\a')
770  ; /* skip pseudo-ANSI sequence */
771  }
772  else
773  mutt_buffer_addch(dest, *s++);
774  }
775 }
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
static bool is_ansi(const char *str)
Is this an ANSI escape sequence?
Definition: display.c:631
#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 789 of file display.c.

791 {
792  static int b_read;
793  struct Buffer stripped;
794 
795  if (*buf_ready == 0)
796  {
797  if (offset != *bytes_read)
798  {
799  if (!mutt_file_seek(fp, offset, SEEK_SET))
800  {
801  return -1;
802  }
803  }
804 
805  *buf = (unsigned char *) mutt_file_read_line((char *) *buf, blen, fp, NULL, MUTT_RL_EOL);
806  if (!*buf)
807  {
808  fmt[0] = NULL;
809  return -1;
810  }
811 
812  *bytes_read = ftello(fp);
813  b_read = (int) (*bytes_read - offset);
814  *buf_ready = 1;
815 
816  mutt_buffer_init(&stripped);
817  mutt_buffer_alloc(&stripped, *blen);
818  mutt_buffer_strip_formatting(&stripped, (const char *) *buf, 1);
819  /* This should be a noop, because *fmt should be NULL */
820  FREE(fmt);
821  *fmt = (unsigned char *) stripped.data;
822  }
823 
824  return b_read;
825 }
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:46
void mutt_buffer_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
Removes ANSI and backspace formatting.
Definition: display.c:736
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:695
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:665
#define MUTT_RL_EOL
don't strip \n / \r\n
Definition: file.h:40
#define FREE(x)
Definition: memory.h:40
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 AnsiAttr aa,
int  cnt,
int *  pspace,
int *  pvch,
int *  pcol,
int *  pspecial,
int  width 
)
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]aaANSI 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)
Return values
numNumber of characters displayed

Definition at line 843 of file display.c.

846 {
847  int space = -1; /* index of the last space or TAB */
848  const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
849  size_t col = c_markers ? (*lines)[line_num].cont_line : 0;
850  size_t k;
851  int ch, vch, last_special = -1, special = 0, t;
852  wchar_t wc;
853  mbstate_t mbstate;
854  const size_t c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
855  size_t wrap_cols = mutt_window_wrap_cols(width, (flags & MUTT_PAGER_NOWRAP) ? 0 : c_wrap);
856 
857  if (check_attachment_marker((char *) buf) == 0)
858  wrap_cols = width;
859 
860  /* FIXME: this should come from lines */
861  memset(&mbstate, 0, sizeof(mbstate));
862 
863  for (ch = 0, vch = 0; ch < cnt; ch += k, vch += k)
864  {
865  /* Handle ANSI sequences */
866  while ((cnt - ch >= 2) && (buf[ch] == '\033') && (buf[ch + 1] == '[') && // Escape
867  is_ansi((char *) buf + ch + 2))
868  {
869  ch = grok_ansi(buf, ch + 2, aa) + 1;
870  }
871 
872  while ((cnt - ch >= 2) && (buf[ch] == '\033') && (buf[ch + 1] == ']') && // Escape
873  ((check_attachment_marker((char *) buf + ch) == 0) ||
874  (check_protected_header_marker((char *) buf + ch) == 0)))
875  {
876  while (buf[ch++] != '\a')
877  if (ch >= cnt)
878  break;
879  }
880 
881  /* is anything left to do? */
882  if (ch >= cnt)
883  break;
884 
885  k = mbrtowc(&wc, (char *) buf + ch, cnt - ch, &mbstate);
886  if ((k == (size_t) (-2)) || (k == (size_t) (-1)))
887  {
888  if (k == (size_t) (-1))
889  memset(&mbstate, 0, sizeof(mbstate));
890  mutt_debug(LL_DEBUG1, "mbrtowc returned %lu; errno = %d\n", k, errno);
891  if (col + 4 > wrap_cols)
892  break;
893  col += 4;
894  if (aa)
895  mutt_window_printf(win, "\\%03o", buf[ch]);
896  k = 1;
897  continue;
898  }
899  if (k == 0)
900  k = 1;
901 
902  if (CharsetIsUtf8)
903  {
904  /* zero width space, zero width no-break space */
905  if ((wc == 0x200B) || (wc == 0xFEFF))
906  {
907  mutt_debug(LL_DEBUG3, "skip zero-width character U+%04X\n", (unsigned short) wc);
908  continue;
909  }
911  {
912  mutt_debug(LL_DEBUG3, "filtered U+%04X\n", (unsigned short) wc);
913  continue;
914  }
915  }
916 
917  /* Handle backspace */
918  special = 0;
919  if (IsWPrint(wc))
920  {
921  wchar_t wc1;
922  mbstate_t mbstate1 = mbstate;
923  size_t k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
924  while ((k1 != (size_t) (-2)) && (k1 != (size_t) (-1)) && (k1 > 0) && (wc1 == '\b'))
925  {
926  const size_t k2 =
927  mbrtowc(&wc1, (char *) buf + ch + k + k1, cnt - ch - k - k1, &mbstate1);
928  if ((k2 == (size_t) (-2)) || (k2 == (size_t) (-1)) || (k2 == 0) || (!IsWPrint(wc1)))
929  break;
930 
931  if (wc == wc1)
932  {
933  special |= ((wc == '_') && (special & A_UNDERLINE)) ? A_UNDERLINE : A_BOLD;
934  }
935  else if ((wc == '_') || (wc1 == '_'))
936  {
937  special |= A_UNDERLINE;
938  wc = (wc1 == '_') ? wc : wc1;
939  }
940  else
941  {
942  /* special = 0; / * overstrike: nothing to do! */
943  wc = wc1;
944  }
945 
946  ch += k + k1;
947  k = k2;
948  mbstate = mbstate1;
949  k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
950  }
951  }
952 
953  if (aa && ((flags & (MUTT_SHOWCOLOR | MUTT_SEARCH | MUTT_PAGER_MARKER)) ||
954  special || last_special || aa->attr))
955  {
956  resolve_color(win, *lines, line_num, vch, flags, special, aa);
957  last_special = special;
958  }
959 
960  /* no-break space, narrow no-break space */
961  if (IsWPrint(wc) || (CharsetIsUtf8 && ((wc == 0x00A0) || (wc == 0x202F))))
962  {
963  if (wc == ' ')
964  {
965  space = ch;
966  }
967  t = wcwidth(wc);
968  if (col + t > wrap_cols)
969  break;
970  col += t;
971  if (aa)
972  mutt_addwch(win, wc);
973  }
974  else if (wc == '\n')
975  break;
976  else if (wc == '\t')
977  {
978  space = ch;
979  t = (col & ~7) + 8;
980  if (t > wrap_cols)
981  break;
982  if (aa)
983  for (; col < t; col++)
984  mutt_window_addch(win, ' ');
985  else
986  col = t;
987  }
988  else if ((wc < 0x20) || (wc == 0x7f))
989  {
990  if (col + 2 > wrap_cols)
991  break;
992  col += 2;
993  if (aa)
994  mutt_window_printf(win, "^%c", ('@' + wc) & 0x7f);
995  }
996  else if (wc < 0x100)
997  {
998  if (col + 4 > wrap_cols)
999  break;
1000  col += 4;
1001  if (aa)
1002  mutt_window_printf(win, "\\%03o", wc);
1003  }
1004  else
1005  {
1006  if (col + 1 > wrap_cols)
1007  break;
1008  col += k;
1009  if (aa)
1011  }
1012  }
1013  *pspace = space;
1014  *pcol = col;
1015  *pvch = vch;
1016  *pspecial = special;
1017  return ch;
1018 }
int mutt_addwch(struct MuttWindow *win, wchar_t wc)
Addwch would be provided by an up-to-date curses library.
Definition: curs_lib.c:687
static int grok_ansi(const unsigned char *buf, int pos, struct AnsiAttr *aa)
Parse an ANSI escape sequence.
Definition: display.c:645
static void resolve_color(struct MuttWindow *win, struct Line *lines, int line_num, int cnt, PagerFlags flags, int special, struct AnsiAttr *aa)
Set the colour for a line of text.
Definition: display.c:118
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
@ LL_DEBUG3
Log at debug level 3.
Definition: logging.h:42
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
bool mutt_mb_is_display_corrupting_utf8(wchar_t wc)
Will this character corrupt the display?
Definition: mbyte.c:389
#define IsWPrint(wc)
Definition: mbyte.h:39
bool CharsetIsUtf8
Is the user's current character set utf-8?
Definition: charset.c: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:423
int mutt_window_wrap_cols(int width, short wrap)
Calculate the wrap column for a given screen width.
Definition: mutt_window.c:364
#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
+ 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 
)

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
Return values
-1EOF was reached
0normal exit, line was not displayed
>0normal exit, line was displayed

Definition at line 1038 of file display.c.

1042 {
1043  unsigned char *buf = NULL, *fmt = NULL;
1044  size_t buflen = 0;
1045  unsigned char *buf_ptr = NULL;
1046  int ch, vch, col, cnt, b_read;
1047  int buf_ready = 0;
1048  bool change_last = false;
1049  int special;
1050  int offset;
1051  int def_color;
1052  int m;
1053  int rc = -1;
1054  struct AnsiAttr aa = { 0, 0, 0, -1 };
1055  regmatch_t pmatch[1];
1056 
1057  if (line_num == *lines_used)
1058  {
1059  (*lines_used)++;
1060  change_last = true;
1061  }
1062 
1063  if (*lines_used == *lines_max)
1064  {
1065  mutt_mem_realloc(lines, sizeof(struct Line) * (*lines_max += LINES));
1066  for (ch = *lines_used; ch < *lines_max; ch++)
1067  {
1068  memset(&((*lines)[ch]), 0, sizeof(struct Line));
1069  (*lines)[ch].color = -1;
1070  (*lines)[ch].search_arr_size = -1;
1071  (*lines)[ch].syntax = mutt_mem_malloc(sizeof(struct TextSyntax));
1072  ((*lines)[ch].syntax)[0].first = -1;
1073  ((*lines)[ch].syntax)[0].last = -1;
1074  }
1075  }
1076 
1077  struct Line *const curr_line = &(*lines)[line_num];
1078 
1079  if (flags & MUTT_PAGER_LOGS)
1080  {
1081  /* determine the line class */
1082  if (fill_buffer(fp, bytes_read, curr_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1083  {
1084  if (change_last)
1085  (*lines_used)--;
1086  goto out;
1087  }
1088 
1089  curr_line->color = MT_COLOR_MESSAGE_LOG;
1090  if (buf[11] == 'M')
1091  curr_line->syntax[0].color = MT_COLOR_MESSAGE;
1092  else if (buf[11] == 'W')
1093  curr_line->syntax[0].color = MT_COLOR_WARNING;
1094  else if (buf[11] == 'E')
1095  curr_line->syntax[0].color = MT_COLOR_ERROR;
1096  else
1097  curr_line->syntax[0].color = MT_COLOR_NORMAL;
1098  }
1099 
1100  /* only do color highlighting if we are viewing a message */
1101  if (flags & (MUTT_SHOWCOLOR | MUTT_TYPES))
1102  {
1103  if (curr_line->color == -1)
1104  {
1105  /* determine the line class */
1106  if (fill_buffer(fp, bytes_read, curr_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1107  {
1108  if (change_last)
1109  (*lines_used)--;
1110  goto out;
1111  }
1112 
1113  resolve_types(win_pager, (char *) fmt, (char *) buf, *lines, line_num, *lines_used,
1114  quote_list, q_level, force_redraw, flags & MUTT_SHOWCOLOR);
1115 
1116  /* avoid race condition for continuation lines when scrolling up */
1117  for (m = line_num + 1;
1118  m < *lines_used && (*lines)[m].offset && (*lines)[m].cont_line; m++)
1119  (*lines)[m].color = curr_line->color;
1120  }
1121 
1122  /* this also prevents searching through the hidden lines */
1123  const short c_toggle_quoted_show_levels =
1124  cs_subset_number(NeoMutt->sub, "toggle_quoted_show_levels");
1125  if ((flags & MUTT_HIDE) && (curr_line->color == MT_COLOR_QUOTED) &&
1126  ((curr_line->quote == NULL) || (curr_line->quote->quote_n >= c_toggle_quoted_show_levels)))
1127  {
1128  flags = 0; /* MUTT_NOSHOW */
1129  }
1130  }
1131 
1132  /* At this point, (*lines[line_num]).quote may still be undefined. We
1133  * don't want to compute it every time MUTT_TYPES is set, since this
1134  * would slow down the "bottom" function unacceptably. A compromise
1135  * solution is hence to call regexec() again, just to find out the
1136  * length of the quote prefix. */
1137  if ((flags & MUTT_SHOWCOLOR) && !curr_line->cont_line &&
1138  (curr_line->color == MT_COLOR_QUOTED) && !curr_line->quote)
1139  {
1140  if (fill_buffer(fp, bytes_read, curr_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1141  {
1142  if (change_last)
1143  (*lines_used)--;
1144  goto out;
1145  }
1146 
1147  const struct Regex *c_quote_regex =
1148  cs_subset_regex(NeoMutt->sub, "quote_regex");
1149  if (mutt_regex_capture(c_quote_regex, (char *) fmt, 1, pmatch))
1150  {
1151  curr_line->quote =
1152  qstyle_classify(quote_list, (char *) fmt + pmatch[0].rm_so,
1153  pmatch[0].rm_eo - pmatch[0].rm_so, force_redraw, q_level);
1154  }
1155  else
1156  {
1157  goto out;
1158  }
1159  }
1160 
1161  if ((flags & MUTT_SEARCH) && !curr_line->cont_line && (curr_line->search_arr_size == -1))
1162  {
1163  if (fill_buffer(fp, bytes_read, curr_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1164  {
1165  if (change_last)
1166  (*lines_used)--;
1167  goto out;
1168  }
1169 
1170  offset = 0;
1171  curr_line->search_arr_size = 0;
1172  while (regexec(search_re, (char *) fmt + offset, 1, pmatch,
1173  (offset ? REG_NOTBOL : 0)) == 0)
1174  {
1175  if (++(curr_line->search_arr_size) > 1)
1176  {
1177  mutt_mem_realloc(&(curr_line->search),
1178  (curr_line->search_arr_size) * sizeof(struct TextSyntax));
1179  }
1180  else
1181  curr_line->search = mutt_mem_malloc(sizeof(struct TextSyntax));
1182  pmatch[0].rm_so += offset;
1183  pmatch[0].rm_eo += offset;
1184  (curr_line->search)[curr_line->search_arr_size - 1].first = pmatch[0].rm_so;
1185  (curr_line->search)[curr_line->search_arr_size - 1].last = pmatch[0].rm_eo;
1186 
1187  if (pmatch[0].rm_eo == pmatch[0].rm_so)
1188  offset++; /* avoid degenerate cases */
1189  else
1190  offset = pmatch[0].rm_eo;
1191  if (!fmt[offset])
1192  break;
1193  }
1194  }
1195 
1196  if (!(flags & MUTT_SHOW) && ((*lines)[line_num + 1].offset > 0))
1197  {
1198  /* we've already scanned this line, so just exit */
1199  rc = 0;
1200  goto out;
1201  }
1202  if ((flags & MUTT_SHOWCOLOR) && *force_redraw && ((*lines)[line_num + 1].offset > 0))
1203  {
1204  /* no need to try to display this line... */
1205  rc = 1;
1206  goto out; /* fake display */
1207  }
1208 
1209  b_read = fill_buffer(fp, bytes_read, curr_line->offset, &buf, &fmt, &buflen, &buf_ready);
1210  if (b_read < 0)
1211  {
1212  if (change_last)
1213  (*lines_used)--;
1214  goto out;
1215  }
1216 
1217  /* now chose a good place to break the line */
1218  cnt = format_line(win_pager, lines, line_num, buf, flags, NULL, b_read, &ch,
1219  &vch, &col, &special, win_pager->state.cols);
1220  buf_ptr = buf + cnt;
1221 
1222  /* move the break point only if smart_wrap is set */
1223  const bool c_smart_wrap = cs_subset_bool(NeoMutt->sub, "smart_wrap");
1224  if (c_smart_wrap)
1225  {
1226  if ((cnt < b_read) && (ch != -1) &&
1227  !simple_color_is_header(curr_line->color) && !IS_SPACE(buf[cnt]))
1228  {
1229  buf_ptr = buf + ch;
1230  /* skip trailing blanks */
1231  while (ch && ((buf[ch] == ' ') || (buf[ch] == '\t') || (buf[ch] == '\r')))
1232  ch--;
1233  /* A very long word with leading spaces causes infinite
1234  * wrapping when MUTT_PAGER_NSKIP is set. A folded header
1235  * with a single long word shouldn't be smartwrapped
1236  * either. So just disable smart_wrap if it would wrap at the
1237  * beginning of the line. */
1238  if (ch == 0)
1239  buf_ptr = buf + cnt;
1240  else
1241  cnt = ch + 1;
1242  }
1243  if (!(flags & MUTT_PAGER_NSKIP))
1244  {
1245  /* skip leading blanks on the next line too */
1246  while ((*buf_ptr == ' ') || (*buf_ptr == '\t'))
1247  buf_ptr++;
1248  }
1249  }
1250 
1251  if (*buf_ptr == '\r')
1252  buf_ptr++;
1253  if (*buf_ptr == '\n')
1254  buf_ptr++;
1255 
1256  if (((int) (buf_ptr - buf) < b_read) && !(*lines)[line_num + 1].cont_line)
1257  append_line(*lines, line_num, (int) (buf_ptr - buf));
1258  (*lines)[line_num + 1].offset = curr_line->offset + (long) (buf_ptr - buf);
1259 
1260  /* if we don't need to display the line we are done */
1261  if (!(flags & MUTT_SHOW))
1262  {
1263  rc = 0;
1264  goto out;
1265  }
1266 
1267  /* display the line */
1268  format_line(win_pager, lines, line_num, buf, flags, &aa, cnt, &ch, &vch, &col,
1269  &special, win_pager->state.cols);
1270 
1271  /* avoid a bug in ncurses... */
1272  if (col == 0)
1273  {
1275  mutt_window_addch(win_pager, ' ');
1276  }
1277 
1278  /* end the last color pattern (needed by S-Lang) */
1279  if (special || ((col != win_pager->state.cols) && (flags & (MUTT_SHOWCOLOR | MUTT_SEARCH))))
1280  resolve_color(win_pager, *lines, line_num, vch, flags, 0, &aa);
1281 
1282  /* Fill the blank space at the end of the line with the prevailing color.
1283  * ncurses does an implicit clrtoeol() when you do mutt_window_addch('\n') so we have
1284  * to make sure to reset the color *after* that */
1285  if (flags & MUTT_SHOWCOLOR)
1286  {
1287  m = (curr_line->cont_line) ? (curr_line->syntax)[0].first : line_num;
1288  if ((*lines)[m].color == MT_COLOR_HEADER)
1289  def_color = ((*lines)[m].syntax)[0].color;
1290  else
1291  def_color = simple_colors_get((*lines)[m].color);
1292 
1293  mutt_curses_set_attr(def_color);
1294  }
1295 
1296  if (col < win_pager->state.cols)
1297  mutt_window_clrtoeol(win_pager);
1298 
1299  /* reset the color back to normal. This *must* come after the
1300  * clrtoeol, otherwise the color for this line will not be
1301  * filled to the right margin. */
1302  if (flags & MUTT_SHOWCOLOR)
1304 
1305  /* build a return code */
1306  if (!(flags & MUTT_SHOW))
1307  flags = 0;
1308 
1309  rc = flags;
1310 
1311 out:
1312  FREE(&buf);
1313  FREE(&fmt);
1314  return rc;
1315 }
@ 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 AnsiAttr *aa, int cnt, int *pspace, int *pvch, int *pcol, int *pspecial, int width)
Display a line of text in the pager.
Definition: display.c:843
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:789
static void resolve_types(struct MuttWindow *win, char *buf, char *raw, struct Line *lines, int line_num, int lines_used, struct QuoteStyle **quote_list, int *q_level, bool *force_redraw, bool q_classify)
Determine the style for a line of text.
Definition: display.c:358
static void append_line(struct Line *lines, int line_num, int cnt)
Add a new Line to the array.
Definition: display.c:248
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:241
#define MUTT_PAGER_NSKIP
Preserve whitespace with smartwrap.
Definition: lib.h: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: display.h:60
A line of text in the pager.
Definition: display.h:71
short search_arr_size
Number of items in search array.
Definition: display.h:80
struct TextSyntax * search
Array of search text in the line.
Definition: display.h:81
LOFF_T offset
Offset into Email file (PagerPrivateData->fp)
Definition: display.h:72
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:67
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: