NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
dlg_pager.c File Reference

Pager Dialog. More...

#include "config.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <wchar.h>
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "alias/lib.h"
#include "gui/lib.h"
#include "mutt.h"
#include "lib.h"
#include "index/lib.h"
#include "menu/lib.h"
#include "ncrypt/lib.h"
#include "question/lib.h"
#include "send/lib.h"
#include "commands.h"
#include "context.h"
#include "format_flags.h"
#include "hdrline.h"
#include "hook.h"
#include "keymap.h"
#include "mutt_attach.h"
#include "mutt_globals.h"
#include "mutt_header.h"
#include "mutt_logging.h"
#include "mutt_mailbox.h"
#include "muttlib.h"
#include "mx.h"
#include "opcodes.h"
#include "options.h"
#include "private_data.h"
#include "protos.h"
#include "recvattach.h"
#include "recvcmd.h"
#include "status.h"
#include "sidebar/lib.h"
#include "nntp/lib.h"
#include "nntp/mdata.h"
#include <libintl.h>

Go to the source code of this file.

Data Structures

struct  QClass
 Style of quoted text. More...
 
struct  TextSyntax
 Highlighting for a line of text. More...
 
struct  Line
 A line of text in the pager. More...
 
struct  AnsiAttr
 An ANSI escape sequence. More...
 
struct  Resize
 Keep track of screen resizing. More...
 

Macros

#define ANSI_NO_FLAGS   0
 No flags are set. More...
 
#define ANSI_OFF   (1 << 0)
 Turn off colours and attributes. More...
 
#define ANSI_BLINK   (1 << 1)
 Blinking text. More...
 
#define ANSI_BOLD   (1 << 2)
 Bold text. More...
 
#define ANSI_UNDERLINE   (1 << 3)
 Underlined text. More...
 
#define ANSI_REVERSE   (1 << 4)
 Reverse video. More...
 
#define ANSI_COLOR   (1 << 5)
 Use colours. More...
 

Typedefs

typedef uint8_t AnsiFlags
 Flags, e.g. ANSI_OFF. More...
 

Functions

static bool assert_pager_mode (bool test)
 Check that pager is in correct mode. More...
 
static bool assert_mailbox_writable (struct Mailbox *mailbox)
 checks that mailbox is writable More...
 
static bool assert_attach_msg_mode (bool attach_msg)
 Check that attach message mode is on. More...
 
static bool assert_mailbox_permissions (struct Mailbox *m, AclFlags acl, char *action)
 checks that mailbox is has requested acl flags set More...
 
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. More...
 
static void resolve_color (struct MuttWindow *win, struct Line *line_info, int n, int cnt, PagerFlags flags, int special, struct AnsiAttr *a)
 Set the colour for a line of text. More...
 
static void append_line (struct Line *line_info, int n, int cnt)
 Add a new Line to the array. More...
 
static void class_color_new (struct QClass *qc, int *q_level)
 Create a new quoting colour. More...
 
static void shift_class_colors (struct QClass *quote_list, struct QClass *new_class, int index, int *q_level)
 Insert a new quote colour class into a list. More...
 
static void cleanup_quote (struct QClass **quote_list)
 Free a quote list. More...
 
static struct QClassclassify_quote (struct QClass **quote_list, const char *qptr, size_t length, bool *force_redraw, int *q_level)
 Find a style for a string. 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 *line_info, int n, int last, struct QClass **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 *a)
 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 *last_pos, 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 **line_info, int n, unsigned char *buf, PagerFlags flags, struct AnsiAttr *pa, int cnt, int *pspace, int *pvch, int *pcol, int *pspecial, int width)
 Display a line of text in the pager. More...
 
static int display_line (FILE *fp, LOFF_T *last_pos, struct Line **line_info, int n, int *last, int *max, PagerFlags flags, struct QClass **quote_list, int *q_level, bool *force_redraw, regex_t *search_re, struct MuttWindow *win_pager)
 Print a line on screen. More...
 
static int up_n_lines (int nlines, struct Line *info, int cur, bool hiding)
 Reposition the pager's view up by n lines. More...
 
void mutt_clear_pager_position (void)
 Reset the pager's viewing position. More...
 
void pager_queue_redraw (struct PagerPrivateData *priv, MenuRedrawFlags redraw)
 Queue a request for a redraw. More...
 
static void pager_custom_redraw (struct PagerPrivateData *priv)
 Redraw the pager window. More...
 
static const struct Mappingpager_resolve_help_mapping (enum PagerMode mode, enum MailboxType type)
 determine help mapping based on pager mode and mailbox type More...
 
static bool jump_to_bottom (struct PagerPrivateData *priv, struct PagerView *pview)
 make sure the bottom line is displayed More...
 
static bool check_read_delay (uint64_t *timestamp)
 Is it time to mark the message read? More...
 
int mutt_pager (struct PagerView *pview)
 Display an email, attachment, or help, in a window. More...
 

Variables

static int TopLine = 0
 
static struct EmailOldEmail = NULL
 
static int braille_row = -1
 
static int braille_col = -1
 
static struct ResizeResize = NULL
 
static const char * Not_available_in_this_menu
 
static const char * Mailbox_is_read_only = N_("Mailbox is read-only")
 
static const char * Function_not_permitted_in_attach_message_mode
 
static const struct Mapping PagerHelp []
 Help Bar for the Pager's Help Page. More...
 
static const struct Mapping PagerHelpHelp []
 Help Bar for the Help Page itself. More...
 
static const struct Mapping PagerNormalHelp []
 Help Bar for the Pager of a normal Mailbox. More...
 
static const struct Mapping PagerNewsHelp []
 Help Bar for the Pager of an NNTP Mailbox. More...
 

Detailed Description

Pager Dialog.

Authors
  • Michael R. Elkins
  • R Primus

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 dlg_pager.c.

Macro Definition Documentation

◆ ANSI_NO_FLAGS

#define ANSI_NO_FLAGS   0

No flags are set.

Definition at line 96 of file dlg_pager.c.

◆ ANSI_OFF

#define ANSI_OFF   (1 << 0)

Turn off colours and attributes.

Definition at line 97 of file dlg_pager.c.

◆ ANSI_BLINK

#define ANSI_BLINK   (1 << 1)

Blinking text.

Definition at line 98 of file dlg_pager.c.

◆ ANSI_BOLD

#define ANSI_BOLD   (1 << 2)

Bold text.

Definition at line 99 of file dlg_pager.c.

◆ ANSI_UNDERLINE

#define ANSI_UNDERLINE   (1 << 3)

Underlined text.

Definition at line 100 of file dlg_pager.c.

◆ ANSI_REVERSE

#define ANSI_REVERSE   (1 << 4)

Reverse video.

Definition at line 101 of file dlg_pager.c.

◆ ANSI_COLOR

#define ANSI_COLOR   (1 << 5)

Use colours.

Definition at line 102 of file dlg_pager.c.

Typedef Documentation

◆ AnsiFlags

typedef uint8_t AnsiFlags

Flags, e.g. ANSI_OFF.

Definition at line 95 of file dlg_pager.c.

Function Documentation

◆ assert_pager_mode()

static bool assert_pager_mode ( bool  test)
inlinestatic

Check that pager is in correct mode.

Parameters
testTest condition
Return values
trueExpected mode is set
falsePager is is some other mode
Note
On failure, the input will be flushed and an error message displayed

Definition at line 241 of file dlg_pager.c.

242 {
243  if (test)
244  return true;
245 
246  mutt_flushinp();
248  return false;
249 }
#define mutt_error(...)
Definition: logging.h:88
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:668
#define _(a)
Definition: message.h:28
static const char * Not_available_in_this_menu
Definition: dlg_pager.c:174
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ assert_mailbox_writable()

static bool assert_mailbox_writable ( struct Mailbox mailbox)
inlinestatic

checks that mailbox is writable

Parameters
mailboxmailbox to check
Return values
trueMailbox is writable
falseMailbox is not writable
Note
On failure, the input will be flushed and an error message displayed

Definition at line 259 of file dlg_pager.c.

260 {
261  assert(mailbox);
262  if (mailbox->readonly)
263  {
264  mutt_flushinp();
266  return false;
267  }
268  return true;
269 }
#define mutt_error(...)
Definition: logging.h:88
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:668
#define _(a)
Definition: message.h:28
static const char * Mailbox_is_read_only
Definition: dlg_pager.c:176
bool readonly
Don&#39;t allow changes to the mailbox.
Definition: mailbox.h:119
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ assert_attach_msg_mode()

static bool assert_attach_msg_mode ( bool  attach_msg)
inlinestatic

Check that attach message mode is on.

Parameters
attach_msgGlobally-named boolean pseudo-option
Return values
trueAttach message mode in on
falseAttach message mode is off
Note
On true, the input will be flushed and an error message displayed

Definition at line 279 of file dlg_pager.c.

280 {
281  if (attach_msg)
282  {
283  mutt_flushinp();
285  return true;
286  }
287  return false;
288 }
#define mutt_error(...)
Definition: logging.h:88
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:668
#define _(a)
Definition: message.h:28
static const char * Function_not_permitted_in_attach_message_mode
Definition: dlg_pager.c:177
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ assert_mailbox_permissions()

static bool assert_mailbox_permissions ( struct Mailbox m,
AclFlags  acl,
char *  action 
)
inlinestatic

checks that mailbox is has requested acl flags set

Parameters
mMailbox to check
aclAclFlags required to be set on a given mailbox
actionString to augment error message
Return values
trueMailbox has necessary flags set
falseMailbox does not have necessary flags set
Note
On failure, the input will be flushed and an error message displayed

Definition at line 300 of file dlg_pager.c.

301 {
302  assert(m);
303  assert(action);
304  if (m->rights & acl)
305  {
306  return true;
307  }
308  mutt_flushinp();
309  /* L10N: %s is one of the CHECK_ACL entries below. */
310  mutt_error(_("%s: Operation not permitted by ACL"), action);
311  return false;
312 }
#define mutt_error(...)
Definition: logging.h:88
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:668
#define _(a)
Definition: message.h:28
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:121
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ 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 322 of file dlg_pager.c.

323 {
324  const unsigned int NUM_SIG_LINES = 4; // The amount of lines a signature takes
325  unsigned int count = 0;
326 
327  while ((offset > 0) && (count <= NUM_SIG_LINES))
328  {
329  if (info[offset].type != MT_COLOR_SIGNATURE)
330  break;
331  count++;
332  offset--;
333  }
334 
335  if (count == 0)
336  return -1;
337 
338  if (count > NUM_SIG_LINES)
339  {
340  /* check for a blank line */
341  while (*s)
342  {
343  if (!IS_SPACE(*s))
344  return 0;
345  s++;
346  }
347 
348  return -1;
349  }
350 
351  return 0;
352 }
Pager: signature lines.
Definition: color.h:74
#define IS_SPACE(ch)
Definition: string2.h:38
+ Here is the caller graph for this function:

◆ comp_syntax_t()

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

Search for a Syntax using bsearch.

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

Definition at line 362 of file dlg_pager.c.

363 {
364  const int *cnt = (const int *) m1;
365  const struct TextSyntax *stx = (const struct TextSyntax *) m2;
366 
367  if (*cnt < stx->first)
368  return -1;
369  if (*cnt >= stx->last)
370  return 1;
371  return 0;
372 }
Highlighting for a line of text.
Definition: dlg_pager.c:121
+ Here is the caller graph for this function:

◆ resolve_color()

static void resolve_color ( struct MuttWindow win,
struct Line line_info,
int  n,
int  cnt,
PagerFlags  flags,
int  special,
struct AnsiAttr a 
)
static

Set the colour for a line of text.

Parameters
winWindow
line_infoLine info array
nLine Number (index into line_info)
cntIf true, this is a continuation line
flagsFlags, see PagerFlags
specialFlags, e.g. A_BOLD
aANSI attributes

Definition at line 384 of file dlg_pager.c.

386 {
387  int def_color; /* color without syntax highlight */
388  int color; /* final color */
389  static int last_color; /* last color set */
390  bool search = false;
391  int m;
392  struct TextSyntax *matching_chunk = NULL;
393 
394  if (cnt == 0)
395  last_color = -1; /* force attrset() */
396 
397  if (line_info[n].continuation)
398  {
399  const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
400  if (!cnt && c_markers)
401  {
403  mutt_window_addch(win, '+');
404  last_color = mutt_color(MT_COLOR_MARKERS);
405  }
406  m = (line_info[n].syntax)[0].first;
407  cnt += (line_info[n].syntax)[0].last;
408  }
409  else
410  m = n;
411  if (flags & MUTT_PAGER_LOGS)
412  {
413  def_color = mutt_color(line_info[n].syntax[0].color);
414  }
415  else if (!(flags & MUTT_SHOWCOLOR))
416  def_color = mutt_color(MT_COLOR_NORMAL);
417  else if (line_info[m].type == MT_COLOR_HEADER)
418  def_color = line_info[m].syntax[0].color;
419  else
420  def_color = mutt_color(line_info[m].type);
421 
422  if ((flags & MUTT_SHOWCOLOR) && (line_info[m].type == MT_COLOR_QUOTED))
423  {
424  struct QClass *qc = line_info[m].quote;
425 
426  if (qc)
427  {
428  def_color = qc->color;
429 
430  while (qc && (qc->length > cnt))
431  {
432  def_color = qc->color;
433  qc = qc->up;
434  }
435  }
436  }
437 
438  color = def_color;
439  if ((flags & MUTT_SHOWCOLOR) && line_info[m].chunks)
440  {
441  matching_chunk = bsearch(&cnt, line_info[m].syntax, line_info[m].chunks,
442  sizeof(struct TextSyntax), comp_syntax_t);
443  if (matching_chunk && (cnt >= matching_chunk->first) &&
444  (cnt < matching_chunk->last))
445  {
446  color = matching_chunk->color;
447  }
448  }
449 
450  if ((flags & MUTT_SEARCH) && line_info[m].search_cnt)
451  {
452  matching_chunk = bsearch(&cnt, line_info[m].search, line_info[m].search_cnt,
453  sizeof(struct TextSyntax), comp_syntax_t);
454  if (matching_chunk && (cnt >= matching_chunk->first) &&
455  (cnt < matching_chunk->last))
456  {
457  color = mutt_color(MT_COLOR_SEARCH);
458  search = 1;
459  }
460  }
461 
462  /* handle "special" bold & underlined characters */
463  if (special || a->attr)
464  {
465 #ifdef HAVE_COLOR
466  if ((a->attr & ANSI_COLOR))
467  {
468  if (a->pair == -1)
469  a->pair = mutt_color_alloc(a->fg, a->bg);
470  color = a->pair;
471  if (a->attr & ANSI_BOLD)
472  color |= A_BOLD;
473  }
474  else
475 #endif
476  if ((special & A_BOLD) || (a->attr & ANSI_BOLD))
477  {
478  if (mutt_color(MT_COLOR_BOLD) && !search)
479  color = mutt_color(MT_COLOR_BOLD);
480  else
481  color ^= A_BOLD;
482  }
483  if ((special & A_UNDERLINE) || (a->attr & ANSI_UNDERLINE))
484  {
485  if (mutt_color(MT_COLOR_UNDERLINE) && !search)
487  else
488  color ^= A_UNDERLINE;
489  }
490  else if (a->attr & ANSI_REVERSE)
491  {
492  color ^= A_REVERSE;
493  }
494  else if (a->attr & ANSI_BLINK)
495  {
496  color ^= A_BLINK;
497  }
498  else if (a->attr == ANSI_OFF)
499  {
500  a->attr = 0;
501  }
502  }
503 
504  if (color != last_color)
505  {
506  mutt_curses_set_attr(color);
507  last_color = color;
508  }
509 }
#define ANSI_BOLD
Bold text.
Definition: dlg_pager.c:99
Bold text.
Definition: color.h:45
Underlined text.
Definition: color.h:78
#define ANSI_BLINK
Blinking text.
Definition: dlg_pager.c:98
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
#define ANSI_UNDERLINE
Underlined text.
Definition: dlg_pager.c:100
int mutt_color_alloc(uint32_t fg, uint32_t bg)
Allocate a colour pair.
Definition: color.c:478
#define ANSI_OFF
Turn off colours and attributes.
Definition: dlg_pager.c:97
void mutt_curses_set_color(enum ColorId color)
Set the current colour for text.
Definition: mutt_curses.c:56
size_t length
Definition: dlg_pager.c:110
int pair
Curses colour pair.
Definition: dlg_pager.c:152
Pager: quoted text.
Definition: color.h:62
Container for Accounts, Notifications.
Definition: neomutt.h:36
#define MUTT_SEARCH
Resolve search patterns.
Definition: lib.h:58
#define ANSI_COLOR
Use colours.
Definition: dlg_pager.c:102
AnsiFlags attr
Attributes, e.g. underline, bold, etc.
Definition: dlg_pager.c:149
Message headers (takes a pattern)
Definition: color.h:53
Pager: markers, line continuation.
Definition: color.h:55
Plain text.
Definition: color.h:58
struct QClass * quote
Definition: dlg_pager.c:140
int bg
Background colour.
Definition: dlg_pager.c:151
#define MUTT_SHOWCOLOR
Show characters in color otherwise don&#39;t show characters.
Definition: lib.h:56
#define MUTT_PAGER_LOGS
Logview mode.
Definition: lib.h:68
int mutt_color(enum ColorId id)
Return the color of an object.
Definition: color.c:1427
#define ANSI_REVERSE
Reverse video.
Definition: dlg_pager.c:101
int fg
Foreground colour.
Definition: dlg_pager.c:150
Highlighting for a line of text.
Definition: dlg_pager.c:121
int color
Definition: dlg_pager.c:112
static int comp_syntax_t(const void *m1, const void *m2)
Search for a Syntax using bsearch.
Definition: dlg_pager.c:362
Pager: search matches.
Definition: color.h:63
struct TextSyntax * syntax
Definition: dlg_pager.c:138
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
void mutt_curses_set_attr(int attr)
Set the attributes for text.
Definition: mutt_curses.c:39
struct QClass * up
Definition: dlg_pager.c:115
Style of quoted text.
Definition: dlg_pager.c:108
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
Definition: mutt_window.c:402
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ append_line()

static void append_line ( struct Line line_info,
int  n,
int  cnt 
)
static

Add a new Line to the array.

Parameters
line_infoArray of Line info
nLine number to add
cnttrue, if line is a continuation

Definition at line 517 of file dlg_pager.c.

518 {
519  int m;
520 
521  line_info[n + 1].type = line_info[n].type;
522  (line_info[n + 1].syntax)[0].color = (line_info[n].syntax)[0].color;
523  line_info[n + 1].continuation = 1;
524 
525  /* find the real start of the line */
526  for (m = n; m >= 0; m--)
527  if (line_info[m].continuation == 0)
528  break;
529 
530  (line_info[n + 1].syntax)[0].first = m;
531  (line_info[n + 1].syntax)[0].last =
532  (line_info[n].continuation) ? cnt + (line_info[n].syntax)[0].last : cnt;
533 }
short type
Definition: dlg_pager.c:134
short continuation
Definition: dlg_pager.c:135
int color
Definition: dlg_pager.c:112
struct TextSyntax * syntax
Definition: dlg_pager.c:138
+ Here is the caller graph for this function:

◆ class_color_new()

static void class_color_new ( struct QClass qc,
int *  q_level 
)
static

Create a new quoting colour.

Parameters
[in]qcClass of quoted text
[in,out]q_levelQuote level

Definition at line 540 of file dlg_pager.c.

541 {
542  qc->index = (*q_level)++;
543  qc->color = mutt_color_quote(qc->index);
544 }
int mutt_color_quote(int q)
Return the color of a quote, cycling through the used quotes.
Definition: color.c:1518
int color
Definition: dlg_pager.c:112
int index
Definition: dlg_pager.c:111
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ shift_class_colors()

static void shift_class_colors ( struct QClass quote_list,
struct QClass new_class,
int  index,
int *  q_level 
)
static

Insert a new quote colour class into a list.

Parameters
[in]quote_listList of quote colours
[in]new_classNew quote colour to inset
[in]indexIndex to insert at
[in,out]q_levelQuote level

Definition at line 553 of file dlg_pager.c.

555 {
556  struct QClass *q_list = quote_list;
557  new_class->index = -1;
558 
559  while (q_list)
560  {
561  if (q_list->index >= index)
562  {
563  q_list->index++;
564  q_list->color = mutt_color_quote(q_list->index);
565  }
566  if (q_list->down)
567  q_list = q_list->down;
568  else if (q_list->next)
569  q_list = q_list->next;
570  else
571  {
572  while (!q_list->next)
573  {
574  q_list = q_list->up;
575  if (!q_list)
576  break;
577  }
578  if (q_list)
579  q_list = q_list->next;
580  }
581  }
582 
583  new_class->index = index;
584  new_class->color = mutt_color_quote(index);
585  (*q_level)++;
586 }
int mutt_color_quote(int q)
Return the color of a quote, cycling through the used quotes.
Definition: color.c:1518
struct QClass * down
Definition: dlg_pager.c:115
struct QClass * next
Definition: dlg_pager.c:114
int color
Definition: dlg_pager.c:112
int index
Definition: dlg_pager.c:111
struct QClass * up
Definition: dlg_pager.c:115
Style of quoted text.
Definition: dlg_pager.c:108
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cleanup_quote()

static void cleanup_quote ( struct QClass **  quote_list)
static

Free a quote list.

Parameters
[out]quote_listQuote list to free

Definition at line 592 of file dlg_pager.c.

593 {
594  struct QClass *ptr = NULL;
595 
596  while (*quote_list)
597  {
598  if ((*quote_list)->down)
599  cleanup_quote(&((*quote_list)->down));
600  ptr = (*quote_list)->next;
601  FREE(&(*quote_list)->prefix);
602  FREE(quote_list);
603  *quote_list = ptr;
604  }
605 }
static void cleanup_quote(struct QClass **quote_list)
Free a quote list.
Definition: dlg_pager.c:592
struct QClass * next
Definition: dlg_pager.c:114
#define FREE(x)
Definition: memory.h:40
Style of quoted text.
Definition: dlg_pager.c:108
+ Here is the caller graph for this function:

◆ classify_quote()

static struct QClass* classify_quote ( struct QClass **  quote_list,
const char *  qptr,
size_t  length,
bool *  force_redraw,
int *  q_level 
)
static

Find a style for a string.

Parameters
[out]quote_listList of quote colours
[in]qptrString to classify
[in]lengthLength of string
[out]force_redrawSet to true if a screen redraw is needed
[out]q_levelQuoting level
Return values
ptrQuoting style

Definition at line 616 of file dlg_pager.c.

618 {
619  struct QClass *q_list = *quote_list;
620  struct QClass *qc = NULL, *tmp = NULL, *ptr = NULL, *save = NULL;
621  const char *tail_qptr = NULL;
622  size_t offset, tail_lng;
623  int index = -1;
624 
625  if (mutt_color_quotes_used() <= 1)
626  {
627  /* not much point in classifying quotes... */
628 
629  if (!*quote_list)
630  {
631  qc = mutt_mem_calloc(1, sizeof(struct QClass));
632  qc->color = mutt_color_quote(0);
633  *quote_list = qc;
634  }
635  return *quote_list;
636  }
637 
638  /* classify quoting prefix */
639  while (q_list)
640  {
641  if (length <= q_list->length)
642  {
643  /* case 1: check the top level nodes */
644 
645  if (mutt_strn_equal(qptr, q_list->prefix, length))
646  {
647  if (length == q_list->length)
648  return q_list; /* same prefix: return the current class */
649 
650  /* found shorter prefix */
651  if (!tmp)
652  {
653  /* add a node above q_list */
654  tmp = mutt_mem_calloc(1, sizeof(struct QClass));
655  tmp->prefix = mutt_mem_calloc(1, length + 1);
656  strncpy(tmp->prefix, qptr, length);
657  tmp->length = length;
658 
659  /* replace q_list by tmp in the top level list */
660  if (q_list->next)
661  {
662  tmp->next = q_list->next;
663  q_list->next->prev = tmp;
664  }
665  if (q_list->prev)
666  {
667  tmp->prev = q_list->prev;
668  q_list->prev->next = tmp;
669  }
670 
671  /* make q_list a child of tmp */
672  tmp->down = q_list;
673  q_list->up = tmp;
674 
675  /* q_list has no siblings for now */
676  q_list->next = NULL;
677  q_list->prev = NULL;
678 
679  /* update the root if necessary */
680  if (q_list == *quote_list)
681  *quote_list = tmp;
682 
683  index = q_list->index;
684 
685  /* tmp should be the return class too */
686  qc = tmp;
687 
688  /* next class to test; if tmp is a shorter prefix for another
689  * node, that node can only be in the top level list, so don't
690  * go down after this point */
691  q_list = tmp->next;
692  }
693  else
694  {
695  /* found another branch for which tmp is a shorter prefix */
696 
697  /* save the next sibling for later */
698  save = q_list->next;
699 
700  /* unlink q_list from the top level list */
701  if (q_list->next)
702  q_list->next->prev = q_list->prev;
703  if (q_list->prev)
704  q_list->prev->next = q_list->next;
705 
706  /* at this point, we have a tmp->down; link q_list to it */
707  ptr = tmp->down;
708  /* sibling order is important here, q_list should be linked last */
709  while (ptr->next)
710  ptr = ptr->next;
711  ptr->next = q_list;
712  q_list->next = NULL;
713  q_list->prev = ptr;
714  q_list->up = tmp;
715 
716  index = q_list->index;
717 
718  /* next class to test; as above, we shouldn't go down */
719  q_list = save;
720  }
721 
722  /* we found a shorter prefix, so certain quotes have changed classes */
723  *force_redraw = true;
724  continue;
725  }
726  else
727  {
728  /* shorter, but not a substring of the current class: try next */
729  q_list = q_list->next;
730  continue;
731  }
732  }
733  else
734  {
735  /* case 2: try subclassing the current top level node */
736 
737  /* tmp != NULL means we already found a shorter prefix at case 1 */
738  if (!tmp && mutt_strn_equal(qptr, q_list->prefix, q_list->length))
739  {
740  /* ok, it's a subclass somewhere on this branch */
741 
742  ptr = q_list;
743  offset = q_list->length;
744 
745  q_list = q_list->down;
746  tail_lng = length - offset;
747  tail_qptr = qptr + offset;
748 
749  while (q_list)
750  {
751  if (length <= q_list->length)
752  {
753  if (mutt_strn_equal(tail_qptr, (q_list->prefix) + offset, tail_lng))
754  {
755  /* same prefix: return the current class */
756  if (length == q_list->length)
757  return q_list;
758 
759  /* found shorter common prefix */
760  if (!tmp)
761  {
762  /* add a node above q_list */
763  tmp = mutt_mem_calloc(1, sizeof(struct QClass));
764  tmp->prefix = mutt_mem_calloc(1, length + 1);
765  strncpy(tmp->prefix, qptr, length);
766  tmp->length = length;
767 
768  /* replace q_list by tmp */
769  if (q_list->next)
770  {
771  tmp->next = q_list->next;
772  q_list->next->prev = tmp;
773  }
774  if (q_list->prev)
775  {
776  tmp->prev = q_list->prev;
777  q_list->prev->next = tmp;
778  }
779 
780  /* make q_list a child of tmp */
781  tmp->down = q_list;
782  tmp->up = q_list->up;
783  q_list->up = tmp;
784  if (tmp->up->down == q_list)
785  tmp->up->down = tmp;
786 
787  /* q_list has no siblings */
788  q_list->next = NULL;
789  q_list->prev = NULL;
790 
791  index = q_list->index;
792 
793  /* tmp should be the return class too */
794  qc = tmp;
795 
796  /* next class to test */
797  q_list = tmp->next;
798  }
799  else
800  {
801  /* found another branch for which tmp is a shorter prefix */
802 
803  /* save the next sibling for later */
804  save = q_list->next;
805 
806  /* unlink q_list from the top level list */
807  if (q_list->next)
808  q_list->next->prev = q_list->prev;
809  if (q_list->prev)
810  q_list->prev->next = q_list->next;
811 
812  /* at this point, we have a tmp->down; link q_list to it */
813  ptr = tmp->down;
814  while (ptr->next)
815  ptr = ptr->next;
816  ptr->next = q_list;
817  q_list->next = NULL;
818  q_list->prev = ptr;
819  q_list->up = tmp;
820 
821  index = q_list->index;
822 
823  /* next class to test */
824  q_list = save;
825  }
826 
827  /* we found a shorter prefix, so we need a redraw */
828  *force_redraw = true;
829  continue;
830  }
831  else
832  {
833  q_list = q_list->next;
834  continue;
835  }
836  }
837  else
838  {
839  /* longer than the current prefix: try subclassing it */
840  if (!tmp && mutt_strn_equal(tail_qptr, (q_list->prefix) + offset,
841  q_list->length - offset))
842  {
843  /* still a subclass: go down one level */
844  ptr = q_list;
845  offset = q_list->length;
846 
847  q_list = q_list->down;
848  tail_lng = length - offset;
849  tail_qptr = qptr + offset;
850 
851  continue;
852  }
853  else
854  {
855  /* nope, try the next prefix */
856  q_list = q_list->next;
857  continue;
858  }
859  }
860  }
861 
862  /* still not found so far: add it as a sibling to the current node */
863  if (!qc)
864  {
865  tmp = mutt_mem_calloc(1, sizeof(struct QClass));
866  tmp->prefix = mutt_mem_calloc(1, length + 1);
867  strncpy(tmp->prefix, qptr, length);
868  tmp->length = length;
869 
870  if (ptr->down)
871  {
872  tmp->next = ptr->down;
873  ptr->down->prev = tmp;
874  }
875  ptr->down = tmp;
876  tmp->up = ptr;
877 
878  class_color_new(tmp, q_level);
879 
880  return tmp;
881  }
882  else
883  {
884  if (index != -1)
885  shift_class_colors(*quote_list, tmp, index, q_level);
886 
887  return qc;
888  }
889  }
890  else
891  {
892  /* nope, try the next prefix */
893  q_list = q_list->next;
894  continue;
895  }
896  }
897  }
898 
899  if (!qc)
900  {
901  /* not found so far: add it as a top level class */
902  qc = mutt_mem_calloc(1, sizeof(struct QClass));
903  qc->prefix = mutt_mem_calloc(1, length + 1);
904  strncpy(qc->prefix, qptr, length);
905  qc->length = length;
906  class_color_new(qc, q_level);
907 
908  if (*quote_list)
909  {
910  qc->next = *quote_list;
911  (*quote_list)->prev = qc;
912  }
913  *quote_list = qc;
914  }
915 
916  if (index != -1)
917  shift_class_colors(*quote_list, tmp, index, q_level);
918 
919  return qc;
920 }
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
static void class_color_new(struct QClass *qc, int *q_level)
Create a new quoting colour.
Definition: dlg_pager.c:540
size_t length
Definition: dlg_pager.c:110
int mutt_color_quote(int q)
Return the color of a quote, cycling through the used quotes.
Definition: color.c:1518
struct QClass * down
Definition: dlg_pager.c:115
char * prefix
Definition: dlg_pager.c:113
struct QClass * next
Definition: dlg_pager.c:114
int mutt_color_quotes_used(void)
Return the number of used quotes.
Definition: color.c:1530
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:593
int color
Definition: dlg_pager.c:112
int index
Definition: dlg_pager.c:111
struct QClass * prev
Definition: dlg_pager.c:114
static void shift_class_colors(struct QClass *quote_list, struct QClass *new_class, int index, int *q_level)
Insert a new quote colour class into a list.
Definition: dlg_pager.c:553
struct QClass * up
Definition: dlg_pager.c:115
Style of quoted text.
Definition: dlg_pager.c:108
+ Here is the call graph for this function:
+ 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 928 of file dlg_pager.c.

929 {
930  for (; (p[0] == q[0]) && (q[0] != '\0') && (p[0] != '\0') && (q[0] != '\a') &&
931  (p[0] != '\a');
932  p++, q++)
933  {
934  }
935 
936  return (int) (*p - *q);
937 }
+ 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 944 of file dlg_pager.c.

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

955 {
957 }
const char * state_protected_header_marker(void)
Get a unique (per-run) ANSI string to mark protected headers in an email.
Definition: state.c:59
static int check_marker(const char *q, const char *p)
Check that the unique marker is present.
Definition: dlg_pager.c:928
+ 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 classify_quote.

Definition at line 968 of file dlg_pager.c.

969 {
970  bool is_quote = false;
971  const struct Regex *c_smileys = cs_subset_regex(NeoMutt->sub, "smileys");
972  regmatch_t pmatch_internal[1], smatch[1];
973 
974  if (!pmatch)
975  pmatch = pmatch_internal;
976 
977  const struct Regex *c_quote_regex =
978  cs_subset_regex(NeoMutt->sub, "quote_regex");
979  if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
980  {
981  if (mutt_regex_capture(c_smileys, line, 1, smatch))
982  {
983  if (smatch[0].rm_so > 0)
984  {
985  char c = line[smatch[0].rm_so];
986  line[smatch[0].rm_so] = 0;
987 
988  if (mutt_regex_capture(c_quote_regex, line, 1, pmatch))
989  is_quote = true;
990 
991  line[smatch[0].rm_so] = c;
992  }
993  }
994  else
995  is_quote = true;
996  }
997 
998  return is_quote;
999 }
Container for Accounts, Notifications.
Definition: neomutt.h:36
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:596
Cached regular expression.
Definition: regex3.h:89
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ 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 line_info,
int  n,
int  last,
struct QClass **  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]line_infoLine info array
[in]nLine number (index into line_info)
[in]lastLast 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 1014 of file dlg_pager.c.

1017 {
1018  struct ColorLine *color_line = NULL;
1019  struct ColorLineList *head = NULL;
1020  regmatch_t pmatch[1];
1021  bool found;
1022  bool null_rx;
1023  const bool c_header_color_partial =
1024  cs_subset_bool(NeoMutt->sub, "header_color_partial");
1025  int offset, i = 0;
1026 
1027  if ((n == 0) || mutt_color_is_header(line_info[n - 1].type) ||
1028  (check_protected_header_marker(raw) == 0))
1029  {
1030  if (buf[0] == '\n') /* end of header */
1031  {
1032  line_info[n].type = MT_COLOR_NORMAL;
1034  }
1035  else
1036  {
1037  /* if this is a continuation of the previous line, use the previous
1038  * line's color as default. */
1039  if ((n > 0) && ((buf[0] == ' ') || (buf[0] == '\t')))
1040  {
1041  line_info[n].type = line_info[n - 1].type; /* wrapped line */
1042  if (!c_header_color_partial)
1043  {
1044  (line_info[n].syntax)[0].color = (line_info[n - 1].syntax)[0].color;
1045  line_info[n].is_cont_hdr = 1;
1046  }
1047  }
1048  else
1049  {
1050  line_info[n].type = MT_COLOR_HDRDEFAULT;
1051  }
1052 
1053  /* When this option is unset, we color the entire header the
1054  * same color. Otherwise, we handle the header patterns just
1055  * like body patterns (further below). */
1056  if (!c_header_color_partial)
1057  {
1058  STAILQ_FOREACH(color_line, mutt_color_headers(), entries)
1059  {
1060  if (regexec(&color_line->regex, buf, 0, NULL, 0) == 0)
1061  {
1062  line_info[n].type = MT_COLOR_HEADER;
1063  line_info[n].syntax[0].color = color_line->pair;
1064  if (line_info[n].is_cont_hdr)
1065  {
1066  /* adjust the previous continuation lines to reflect the color of this continuation line */
1067  int j;
1068  for (j = n - 1; j >= 0 && line_info[j].is_cont_hdr; --j)
1069  {
1070  line_info[j].type = line_info[n].type;
1071  line_info[j].syntax[0].color = line_info[n].syntax[0].color;
1072  }
1073  /* now adjust the first line of this header field */
1074  if (j >= 0)
1075  {
1076  line_info[j].type = line_info[n].type;
1077  line_info[j].syntax[0].color = line_info[n].syntax[0].color;
1078  }
1079  *force_redraw = true; /* the previous lines have already been drawn on the screen */
1080  }
1081  break;
1082  }
1083  }
1084  }
1085  }
1086  }
1087  else if (mutt_str_startswith(raw, "\033[0m")) // Escape: a little hack...
1088  line_info[n].type = MT_COLOR_NORMAL;
1089  else if (check_attachment_marker((char *) raw) == 0)
1090  line_info[n].type = MT_COLOR_ATTACHMENT;
1091  else if (mutt_str_equal("-- \n", buf) || mutt_str_equal("-- \r\n", buf))
1092  {
1093  i = n + 1;
1094 
1095  line_info[n].type = MT_COLOR_SIGNATURE;
1096  while ((i < last) && (check_sig(buf, line_info, i - 1) == 0) &&
1097  ((line_info[i].type == MT_COLOR_NORMAL) || (line_info[i].type == MT_COLOR_QUOTED) ||
1098  (line_info[i].type == MT_COLOR_HEADER)))
1099  {
1100  /* oops... */
1101  if (line_info[i].chunks)
1102  {
1103  line_info[i].chunks = 0;
1104  mutt_mem_realloc(&(line_info[n].syntax), sizeof(struct TextSyntax));
1105  }
1106  line_info[i++].type = MT_COLOR_SIGNATURE;
1107  }
1108  }
1109  else if (check_sig(buf, line_info, n - 1) == 0)
1110  line_info[n].type = MT_COLOR_SIGNATURE;
1111  else if (mutt_is_quote_line(buf, pmatch))
1112 
1113  {
1114  if (q_classify && (line_info[n].quote == NULL))
1115  {
1116  line_info[n].quote = classify_quote(quote_list, buf + pmatch[0].rm_so,
1117  pmatch[0].rm_eo - pmatch[0].rm_so,
1118  force_redraw, q_level);
1119  }
1120  line_info[n].type = MT_COLOR_QUOTED;
1121  }
1122  else
1123  line_info[n].type = MT_COLOR_NORMAL;
1124 
1125  /* body patterns */
1126  if ((line_info[n].type == MT_COLOR_NORMAL) || (line_info[n].type == MT_COLOR_QUOTED) ||
1127  ((line_info[n].type == MT_COLOR_HDRDEFAULT) && c_header_color_partial))
1128  {
1129  size_t nl;
1130 
1131  /* don't consider line endings part of the buffer
1132  * for regex matching */
1133  nl = mutt_str_len(buf);
1134  if ((nl > 0) && (buf[nl - 1] == '\n'))
1135  buf[nl - 1] = '\0';
1136 
1137  i = 0;
1138  offset = 0;
1139  line_info[n].chunks = 0;
1140  if (line_info[n].type == MT_COLOR_HDRDEFAULT)
1141  head = mutt_color_headers();
1142  else
1143  head = mutt_color_body();
1144  STAILQ_FOREACH(color_line, head, entries)
1145  {
1146  color_line->stop_matching = false;
1147  }
1148  do
1149  {
1150  if (!buf[offset])
1151  break;
1152 
1153  found = false;
1154  null_rx = false;
1155  STAILQ_FOREACH(color_line, head, entries)
1156  {
1157  if (!color_line->stop_matching &&
1158  (regexec(&color_line->regex, buf + offset, 1, pmatch,
1159  ((offset != 0) ? REG_NOTBOL : 0)) == 0))
1160  {
1161  if (pmatch[0].rm_eo != pmatch[0].rm_so)
1162  {
1163  if (!found)
1164  {
1165  /* Abort if we fill up chunks.
1166  * Yes, this really happened. */
1167  if (line_info[n].chunks == SHRT_MAX)
1168  {
1169  null_rx = false;
1170  break;
1171  }
1172  if (++(line_info[n].chunks) > 1)
1173  {
1174  mutt_mem_realloc(&(line_info[n].syntax),
1175  (line_info[n].chunks) * sizeof(struct TextSyntax));
1176  }
1177  }
1178  i = line_info[n].chunks - 1;
1179  pmatch[0].rm_so += offset;
1180  pmatch[0].rm_eo += offset;
1181  if (!found || (pmatch[0].rm_so < (line_info[n].syntax)[i].first) ||
1182  ((pmatch[0].rm_so == (line_info[n].syntax)[i].first) &&
1183  (pmatch[0].rm_eo > (line_info[n].syntax)[i].last)))
1184  {
1185  (line_info[n].syntax)[i].color = color_line->pair;
1186  (line_info[n].syntax)[i].first = pmatch[0].rm_so;
1187  (line_info[n].syntax)[i].last = pmatch[0].rm_eo;
1188  }
1189  found = true;
1190  null_rx = false;
1191  }
1192  else
1193  null_rx = true; /* empty regex; don't add it, but keep looking */
1194  }
1195  else
1196  {
1197  /* Once a regexp fails to match, don't try matching it again.
1198  * On very long lines this can cause a performance issue if there
1199  * are other regexps that have many matches. */
1200  color_line->stop_matching = true;
1201  }
1202  }
1203 
1204  if (null_rx)
1205  offset++; /* avoid degenerate cases */
1206  else
1207  offset = (line_info[n].syntax)[i].last;
1208  } while (found || null_rx);
1209  if (nl > 0)
1210  buf[nl] = '\n';
1211  }
1212 
1213  /* attachment patterns */
1214  if (line_info[n].type == MT_COLOR_ATTACHMENT)
1215  {
1216  size_t nl;
1217 
1218  /* don't consider line endings part of the buffer for regex matching */
1219  nl = mutt_str_len(buf);
1220  if ((nl > 0) && (buf[nl - 1] == '\n'))
1221  buf[nl - 1] = '\0';
1222 
1223  i = 0;
1224  offset = 0;
1225  line_info[n].chunks = 0;
1226  do
1227  {
1228  if (!buf[offset])
1229  break;
1230 
1231  found = false;
1232  null_rx = false;
1233  STAILQ_FOREACH(color_line, mutt_color_attachments(), entries)
1234  {
1235  if (regexec(&color_line->regex, buf + offset, 1, pmatch,
1236  ((offset != 0) ? REG_NOTBOL : 0)) == 0)
1237  {
1238  if (pmatch[0].rm_eo != pmatch[0].rm_so)
1239  {
1240  if (!found)
1241  {
1242  if (++(line_info[n].chunks) > 1)
1243  {
1244  mutt_mem_realloc(&(line_info[n].syntax),
1245  (line_info[n].chunks) * sizeof(struct TextSyntax));
1246  }
1247  }
1248  i = line_info[n].chunks - 1;
1249  pmatch[0].rm_so += offset;
1250  pmatch[0].rm_eo += offset;
1251  if (!found || (pmatch[0].rm_so < (line_info[n].syntax)[i].first) ||
1252  ((pmatch[0].rm_so == (line_info[n].syntax)[i].first) &&
1253  (pmatch[0].rm_eo > (line_info[n].syntax)[i].last)))
1254  {
1255  (line_info[n].syntax)[i].color = color_line->pair;
1256  (line_info[n].syntax)[i].first = pmatch[0].rm_so;
1257  (line_info[n].syntax)[i].last = pmatch[0].rm_eo;
1258  }
1259  found = 1;
1260  null_rx = 0;
1261  }
1262  else
1263  null_rx = 1; /* empty regex; don't add it, but keep looking */
1264  }
1265  }
1266 
1267  if (null_rx)
1268  offset++; /* avoid degenerate cases */
1269  else
1270  offset = (line_info[n].syntax)[i].last;
1271  } while (found || null_rx);
1272  if (nl > 0)
1273  buf[nl] = '\n';
1274  }
1275 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
Header default colour.
Definition: color.h:52
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
static int braille_col
Definition: dlg_pager.c:170
short chunks
Definition: dlg_pager.c:136
bool mutt_color_is_header(enum ColorId color_id)
Colour is for an Email header.
Definition: color.c:1560
static struct QClass * classify_quote(struct QClass **quote_list, const char *qptr, size_t length, bool *force_redraw, int *q_level)
Find a style for a string.
Definition: dlg_pager.c:616
int pair
Colour pair index.
Definition: color.h:107
Pager: signature lines.
Definition: color.h:74
static int braille_row
Definition: dlg_pager.c:169
short type
Definition: dlg_pager.c:134
Pager: quoted text.
Definition: color.h:62
Container for Accounts, Notifications.
Definition: neomutt.h:36
int mutt_is_quote_line(char *line, regmatch_t *pmatch)
Is a line of message text a quote?
Definition: dlg_pager.c:968
Message headers (takes a pattern)
Definition: color.h:53
static int check_sig(const char *s, struct Line *info, int offset)
Check for an email signature.
Definition: dlg_pager.c:322
Plain text.
Definition: color.h:58
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
struct ColorLineList * mutt_color_attachments(void)
Return the ColorLineList for the attachments.
Definition: color.c:1472
struct QClass * quote
Definition: dlg_pager.c:140
void mutt_window_get_coords(struct MuttWindow *win, int *col, int *row)
Get the cursor position in the Window.
Definition: mutt_window.c:290
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
bool stop_matching
Used by the pager for body patterns, to prevent the color from being retried once it fails...
Definition: color.h:109
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
struct ColorLineList * mutt_color_body(void)
Return the ColorLineList for the body.
Definition: color.c:1463
static int check_attachment_marker(const char *p)
Check that the unique marker is present.
Definition: dlg_pager.c:944
regex_t regex
Compiled regex.
Definition: color.h:101
Highlighting for a line of text.
Definition: dlg_pager.c:121
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
MIME attachments text (entire line)
Definition: color.h:42
struct TextSyntax * syntax
Definition: dlg_pager.c:138
static int check_protected_header_marker(const char *p)
Check that the unique marker is present.
Definition: dlg_pager.c:954
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
struct ColorLineList * mutt_color_headers(void)
Return the ColorLineList for headers.
Definition: color.c:1454
unsigned int is_cont_hdr
this line is a continuation of the previous header line
Definition: dlg_pager.c:141
A regular expression and a color to highlight a line.
Definition: color.h:99
+ 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 1282 of file dlg_pager.c.

1283 {
1284  while (*str && (isdigit(*str) || (*str == ';')))
1285  str++;
1286  return (*str == 'm');
1287 }
+ Here is the caller graph for this function:

◆ grok_ansi()

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

Parse an ANSI escape sequence.

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

Definition at line 1296 of file dlg_pager.c.

1297 {
1298  int x = pos;
1299 
1300  while (isdigit(buf[x]) || (buf[x] == ';'))
1301  x++;
1302 
1303  /* Character Attributes */
1304  const bool c_allow_ansi = cs_subset_bool(NeoMutt->sub, "allow_ansi");
1305  if (c_allow_ansi && a && (buf[x] == 'm'))
1306  {
1307  if (pos == x)
1308  {
1309 #ifdef HAVE_COLOR
1310  if (a->pair != -1)
1311  mutt_color_free(a->fg, a->bg);
1312 #endif
1313  a->attr = ANSI_OFF;
1314  a->pair = -1;
1315  }
1316  while (pos < x)
1317  {
1318  if ((buf[pos] == '1') && ((pos + 1 == x) || (buf[pos + 1] == ';')))
1319  {
1320  a->attr |= ANSI_BOLD;
1321  pos += 2;
1322  }
1323  else if ((buf[pos] == '4') && ((pos + 1 == x) || (buf[pos + 1] == ';')))
1324  {
1325  a->attr |= ANSI_UNDERLINE;
1326  pos += 2;
1327  }
1328  else if ((buf[pos] == '5') && ((pos + 1 == x) || (buf[pos + 1] == ';')))
1329  {
1330  a->attr |= ANSI_BLINK;
1331  pos += 2;
1332  }
1333  else if ((buf[pos] == '7') && ((pos + 1 == x) || (buf[pos + 1] == ';')))
1334  {
1335  a->attr |= ANSI_REVERSE;
1336  pos += 2;
1337  }
1338  else if ((buf[pos] == '0') && ((pos + 1 == x) || (buf[pos + 1] == ';')))
1339  {
1340 #ifdef HAVE_COLOR
1341  if (a->pair != -1)
1342  mutt_color_free(a->fg, a->bg);
1343 #endif
1344  a->attr = ANSI_OFF;
1345  a->pair = -1;
1346  pos += 2;
1347  }
1348  else if ((buf[pos] == '3') && isdigit(buf[pos + 1]))
1349  {
1350 #ifdef HAVE_COLOR
1351  if (a->pair != -1)
1352  mutt_color_free(a->fg, a->bg);
1353 #endif
1354  a->pair = -1;
1355  a->attr |= ANSI_COLOR;
1356  a->fg = buf[pos + 1] - '0';
1357  pos += 3;
1358  }
1359  else if ((buf[pos] == '4') && isdigit(buf[pos + 1]))
1360  {
1361 #ifdef HAVE_COLOR
1362  if (a->pair != -1)
1363  mutt_color_free(a->fg, a->bg);
1364 #endif
1365  a->pair = -1;
1366  a->attr |= ANSI_COLOR;
1367  a->bg = buf[pos + 1] - '0';
1368  pos += 3;
1369  }
1370  else
1371  {
1372  while ((pos < x) && (buf[pos] != ';'))
1373  pos++;
1374  pos++;
1375  }
1376  }
1377  }
1378  pos = x;
1379  return pos;
1380 }
#define ANSI_BOLD
Bold text.
Definition: dlg_pager.c:99
#define ANSI_BLINK
Blinking text.
Definition: dlg_pager.c:98
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
#define ANSI_UNDERLINE
Underlined text.
Definition: dlg_pager.c:100
#define ANSI_OFF
Turn off colours and attributes.
Definition: dlg_pager.c:97
int pair
Curses colour pair.
Definition: dlg_pager.c:152
Container for Accounts, Notifications.
Definition: neomutt.h:36
#define ANSI_COLOR
Use colours.
Definition: dlg_pager.c:102
AnsiFlags attr
Attributes, e.g. underline, bold, etc.
Definition: dlg_pager.c:149
int bg
Background colour.
Definition: dlg_pager.c:151
#define ANSI_REVERSE
Reverse video.
Definition: dlg_pager.c:101
int fg
Foreground colour.
Definition: dlg_pager.c:150
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
void mutt_color_free(uint32_t fg, uint32_t bg)
Free a colour.
Definition: color.c:284
+ 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 1395 of file dlg_pager.c.

1396 {
1397  const char *s = src;
1398 
1399  mutt_buffer_reset(dest);
1400 
1401  if (!s)
1402  return;
1403 
1404  while (s[0] != '\0')
1405  {
1406  if ((s[0] == '\010') && (s > src))
1407  {
1408  if (s[1] == '_') /* underline */
1409  s += 2;
1410  else if (s[1] && mutt_buffer_len(dest)) /* bold or overstrike */
1411  {
1412  dest->dptr--;
1413  mutt_buffer_addch(dest, s[1]);
1414  s += 2;
1415  }
1416  else /* ^H */
1417  mutt_buffer_addch(dest, *s++);
1418  }
1419  else if ((s[0] == '\033') && (s[1] == '[') && is_ansi(s + 2))
1420  {
1421  while (*s++ != 'm')
1422  ; /* skip ANSI sequence */
1423  }
1424  else if (strip_markers && (s[0] == '\033') && (s[1] == ']') &&
1425  ((check_attachment_marker(s) == 0) || (check_protected_header_marker(s) == 0)))
1426  {
1427  mutt_debug(LL_DEBUG2, "Seen attachment marker\n");
1428  while (*s++ != '\a')
1429  ; /* skip pseudo-ANSI sequence */
1430  }
1431  else
1432  mutt_buffer_addch(dest, *s++);
1433  }
1434 }
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: dlg_pager.c:1282
Log at debug level 2.
Definition: logging.h:41
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
char * dptr
Current read/write position.
Definition: buffer.h:36
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
static int check_attachment_marker(const char *p)
Check that the unique marker is present.
Definition: dlg_pager.c:944
static int check_protected_header_marker(const char *p)
Check that the unique marker is present.
Definition: dlg_pager.c:954
+ 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 *  last_pos,
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]last_posEnd 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 1448 of file dlg_pager.c.

1450 {
1451  static int b_read;
1452  struct Buffer stripped;
1453 
1454  if (*buf_ready == 0)
1455  {
1456  if (offset != *last_pos)
1457  fseeko(fp, offset, SEEK_SET);
1458 
1459  *buf = (unsigned char *) mutt_file_read_line((char *) *buf, blen, fp, NULL, MUTT_RL_EOL);
1460  if (!*buf)
1461  {
1462  fmt[0] = NULL;
1463  return -1;
1464  }
1465 
1466  *last_pos = ftello(fp);
1467  b_read = (int) (*last_pos - offset);
1468  *buf_ready = 1;
1469 
1470  mutt_buffer_init(&stripped);
1471  mutt_buffer_alloc(&stripped, *blen);
1472  mutt_buffer_strip_formatting(&stripped, (const char *) *buf, 1);
1473  /* This should be a noop, because *fmt should be NULL */
1474  FREE(fmt);
1475  *fmt = (unsigned char *) stripped.data;
1476  }
1477 
1478  return b_read;
1479 }
void mutt_buffer_strip_formatting(struct Buffer *dest, const char *src, bool strip_markers)
Removes ANSI and backspace formatting.
Definition: dlg_pager.c:1395
String manipulation buffer.
Definition: buffer.h:33
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:667
#define MUTT_RL_EOL
don&#39;t strip \n / \r\n
Definition: file.h:40
#define FREE(x)
Definition: memory.h:40
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:46
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
+ 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 **  line_info,
int  n,
unsigned char *  buf,
PagerFlags  flags,
struct AnsiAttr pa,
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]line_infoLine info
[in]nLine number (index into line_info)
[in]bufText to display
[in]flagsFlags, see PagerFlags
[out]paANSI 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 1497 of file dlg_pager.c.

1500 {
1501  int space = -1; /* index of the last space or TAB */
1502  const bool c_markers = cs_subset_bool(NeoMutt->sub, "markers");
1503  size_t col = c_markers ? (*line_info)[n].continuation : 0;
1504  size_t k;
1505  int ch, vch, last_special = -1, special = 0, t;
1506  wchar_t wc;
1507  mbstate_t mbstate;
1508  const size_t c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
1509  size_t wrap_cols = mutt_window_wrap_cols(width, (flags & MUTT_PAGER_NOWRAP) ? 0 : c_wrap);
1510 
1511  if (check_attachment_marker((char *) buf) == 0)
1512  wrap_cols = width;
1513 
1514  /* FIXME: this should come from line_info */
1515  memset(&mbstate, 0, sizeof(mbstate));
1516 
1517  for (ch = 0, vch = 0; ch < cnt; ch += k, vch += k)
1518  {
1519  /* Handle ANSI sequences */
1520  while ((cnt - ch >= 2) && (buf[ch] == '\033') && (buf[ch + 1] == '[') && // Escape
1521  is_ansi((char *) buf + ch + 2))
1522  {
1523  ch = grok_ansi(buf, ch + 2, pa) + 1;
1524  }
1525 
1526  while ((cnt - ch >= 2) && (buf[ch] == '\033') && (buf[ch + 1] == ']') && // Escape
1527  ((check_attachment_marker((char *) buf + ch) == 0) ||
1528  (check_protected_header_marker((char *) buf + ch) == 0)))
1529  {
1530  while (buf[ch++] != '\a')
1531  if (ch >= cnt)
1532  break;
1533  }
1534 
1535  /* is anything left to do? */
1536  if (ch >= cnt)
1537  break;
1538 
1539  k = mbrtowc(&wc, (char *) buf + ch, cnt - ch, &mbstate);
1540  if ((k == (size_t) (-2)) || (k == (size_t) (-1)))
1541  {
1542  if (k == (size_t) (-1))
1543  memset(&mbstate, 0, sizeof(mbstate));
1544  mutt_debug(LL_DEBUG1, "mbrtowc returned %lu; errno = %d\n", k, errno);
1545  if (col + 4 > wrap_cols)
1546  break;
1547  col += 4;
1548  if (pa)
1549  mutt_window_printf(win, "\\%03o", buf[ch]);
1550  k = 1;
1551  continue;
1552  }
1553  if (k == 0)
1554  k = 1;
1555 
1556  if (CharsetIsUtf8)
1557  {
1558  /* zero width space, zero width no-break space */
1559  if ((wc == 0x200B) || (wc == 0xFEFF))
1560  {
1561  mutt_debug(LL_DEBUG3, "skip zero-width character U+%04X\n", (unsigned short) wc);
1562  continue;
1563  }
1565  {
1566  mutt_debug(LL_DEBUG3, "filtered U+%04X\n", (unsigned short) wc);
1567  continue;
1568  }
1569  }
1570 
1571  /* Handle backspace */
1572  special = 0;
1573  if (IsWPrint(wc))
1574  {
1575  wchar_t wc1;
1576  mbstate_t mbstate1 = mbstate;
1577  size_t k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
1578  while ((k1 != (size_t) (-2)) && (k1 != (size_t) (-1)) && (k1 > 0) && (wc1 == '\b'))
1579  {
1580  const size_t k2 =
1581  mbrtowc(&wc1, (char *) buf + ch + k + k1, cnt - ch - k - k1, &mbstate1);
1582  if ((k2 == (size_t) (-2)) || (k2 == (size_t) (-1)) || (k2 == 0) || (!IsWPrint(wc1)))
1583  break;
1584 
1585  if (wc == wc1)
1586  {
1587  special |= ((wc == '_') && (special & A_UNDERLINE)) ? A_UNDERLINE : A_BOLD;
1588  }
1589  else if ((wc == '_') || (wc1 == '_'))
1590  {
1591  special |= A_UNDERLINE;
1592  wc = (wc1 == '_') ? wc : wc1;
1593  }
1594  else
1595  {
1596  /* special = 0; / * overstrike: nothing to do! */
1597  wc = wc1;
1598  }
1599 
1600  ch += k + k1;
1601  k = k2;
1602  mbstate = mbstate1;
1603  k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
1604  }
1605  }
1606 
1607  if (pa && ((flags & (MUTT_SHOWCOLOR | MUTT_SEARCH | MUTT_PAGER_MARKER)) ||
1608  special || last_special || pa->attr))
1609  {
1610  resolve_color(win, *line_info, n, vch, flags, special, pa);
1611  last_special = special;
1612  }
1613 
1614  /* no-break space, narrow no-break space */
1615  if (IsWPrint(wc) || (CharsetIsUtf8 && ((wc == 0x00A0) || (wc == 0x202F))))
1616  {
1617  if (wc == ' ')
1618  {
1619  space = ch;
1620  }
1621  t = wcwidth(wc);
1622  if (col + t > wrap_cols)
1623  break;
1624  col += t;
1625  if (pa)
1626  mutt_addwch(win, wc);
1627  }
1628  else if (wc == '\n')
1629  break;
1630  else if (wc == '\t')
1631  {
1632  space = ch;
1633  t = (col & ~7) + 8;
1634  if (t > wrap_cols)
1635  break;
1636  if (pa)
1637  for (; col < t; col++)
1638  mutt_window_addch(win, ' ');
1639  else
1640  col = t;
1641  }
1642  else if ((wc < 0x20) || (wc == 0x7f))
1643  {
1644  if (col + 2 > wrap_cols)
1645  break;
1646  col += 2;
1647  if (pa)
1648  mutt_window_printf(win, "^%c", ('@' + wc) & 0x7f);
1649  }
1650  else if (wc < 0x100)
1651  {
1652  if (col + 4 > wrap_cols)
1653  break;
1654  col += 4;
1655  if (pa)
1656  mutt_window_printf(win, "\\%03o", wc);
1657  }
1658  else
1659  {
1660  if (col + 1 > wrap_cols)
1661  break;
1662  col += k;
1663  if (pa)
1665  }
1666  }
1667  *pspace = space;
1668  *pcol = col;
1669  *pvch = vch;
1670  *pspecial = special;
1671  return ch;
1672 }
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
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
static bool is_ansi(const char *str)
Is this an ANSI escape sequence?
Definition: dlg_pager.c:1282
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
Container for Accounts, Notifications.
Definition: neomutt.h:36
#define MUTT_SEARCH
Resolve search patterns.
Definition: lib.h:58
AnsiFlags attr
Attributes, e.g. underline, bold, etc.
Definition: dlg_pager.c:149
#define MUTT_PAGER_MARKER
Use markers if option is set.
Definition: lib.h:64
int mutt_window_printf(struct MuttWindow *win, const char *fmt,...)
Write a formatted string to a Window.
Definition: mutt_window.c:453
wchar_t ReplacementChar
When a Unicode character can&#39;t be displayed, use this instead.
Definition: charset.c:57
#define MUTT_SHOWCOLOR
Show characters in color otherwise don&#39;t show characters.
Definition: lib.h:56
int mutt_window_wrap_cols(int width, short wrap)
Calculate the wrap column for a given screen width.
Definition: mutt_window.c:386
int mutt_addwch(struct MuttWindow *win, wchar_t wc)
addwch would be provided by an up-to-date curses library
Definition: curs_lib.c:682
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
static void resolve_color(struct MuttWindow *win, struct Line *line_info, int n, int cnt, PagerFlags flags, int special, struct AnsiAttr *a)
Set the colour for a line of text.
Definition: dlg_pager.c:384
bool CharsetIsUtf8
Is the user&#39;s current character set utf-8?
Definition: charset.c:62
static int check_attachment_marker(const char *p)
Check that the unique marker is present.
Definition: dlg_pager.c:944
Log at debug level 1.
Definition: logging.h:40
static int grok_ansi(const unsigned char *buf, int pos, struct AnsiAttr *a)
Parse an ANSI escape sequence.
Definition: dlg_pager.c:1296
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition: lib.h:67
static int check_protected_header_marker(const char *p)
Check that the unique marker is present.
Definition: dlg_pager.c:954
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
Log at debug level 3.
Definition: logging.h:42
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
Definition: mutt_window.c:402
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ display_line()

static int display_line ( FILE *  fp,
LOFF_T *  last_pos,
struct Line **  line_info,
int  n,
int *  last,
int *  max,
PagerFlags  flags,
struct QClass **  quote_list,
int *  q_level,
bool *  force_redraw,
regex_t *  search_re,
struct MuttWindow win_pager 
)
static

Print a line on screen.

Parameters
[in]fpFile to read from
[out]last_posOffset into file
[out]line_infoLine attributes
[in]nLine number
[out]lastLast line
[out]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 1692 of file dlg_pager.c.

1696 {
1697  unsigned char *buf = NULL, *fmt = NULL;
1698  size_t buflen = 0;
1699  unsigned char *buf_ptr = NULL;
1700  int ch, vch, col, cnt, b_read;
1701  int buf_ready = 0;
1702  bool change_last = false;
1703  const bool c_smart_wrap = cs_subset_bool(NeoMutt->sub, "smart_wrap");
1704  int special;
1705  int offset;
1706  int def_color;
1707  int m;
1708  int rc = -1;
1709  struct AnsiAttr a = { 0, 0, 0, -1 };
1710  regmatch_t pmatch[1];
1711 
1712  if (n == *last)
1713  {
1714  (*last)++;
1715  change_last = true;
1716  }
1717 
1718  if (*last == *max)
1719  {
1720  mutt_mem_realloc(line_info, sizeof(struct Line) * (*max += LINES));
1721  for (ch = *last; ch < *max; ch++)
1722  {
1723  memset(&((*line_info)[ch]), 0, sizeof(struct Line));
1724  (*line_info)[ch].type = -1;
1725  (*line_info)[ch].search_cnt = -1;
1726  (*line_info)[ch].syntax = mutt_mem_malloc(sizeof(struct TextSyntax));
1727  ((*line_info)[ch].syntax)[0].first = -1;
1728  ((*line_info)[ch].syntax)[0].last = -1;
1729  }
1730  }
1731 
1732  struct Line *const curr_line = &(*line_info)[n];
1733 
1734  if (flags & MUTT_PAGER_LOGS)
1735  {
1736  /* determine the line class */
1737  if (fill_buffer(fp, last_pos, curr_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1738  {
1739  if (change_last)
1740  (*last)--;
1741  goto out;
1742  }
1743 
1744  curr_line->type = MT_COLOR_MESSAGE_LOG;
1745  if (buf[11] == 'M')
1746  curr_line->syntax[0].color = MT_COLOR_MESSAGE;
1747  else if (buf[11] == 'W')
1748  curr_line->syntax[0].color = MT_COLOR_WARNING;
1749  else if (buf[11] == 'E')
1750  curr_line->syntax[0].color = MT_COLOR_ERROR;
1751  else
1752  curr_line->syntax[0].color = MT_COLOR_NORMAL;
1753  }
1754 
1755  /* only do color highlighting if we are viewing a message */
1756  if (flags & (MUTT_SHOWCOLOR | MUTT_TYPES))
1757  {
1758  if (curr_line->type == -1)
1759  {
1760  /* determine the line class */
1761  if (fill_buffer(fp, last_pos, curr_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1762  {
1763  if (change_last)
1764  (*last)--;
1765  goto out;
1766  }
1767 
1768  resolve_types(win_pager, (char *) fmt, (char *) buf, *line_info, n, *last,
1769  quote_list, q_level, force_redraw, flags & MUTT_SHOWCOLOR);
1770 
1771  /* avoid race condition for continuation lines when scrolling up */
1772  for (m = n + 1; m < *last && (*line_info)[m].offset && (*line_info)[m].continuation; m++)
1773  (*line_info)[m].type = curr_line->type;
1774  }
1775 
1776  /* this also prevents searching through the hidden lines */
1777  const short c_toggle_quoted_show_levels =
1778  cs_subset_number(NeoMutt->sub, "toggle_quoted_show_levels");
1779  if ((flags & MUTT_HIDE) && (curr_line->type == MT_COLOR_QUOTED) &&
1780  ((curr_line->quote == NULL) || (curr_line->quote->index >= c_toggle_quoted_show_levels)))
1781  {
1782  flags = 0; /* MUTT_NOSHOW */
1783  }
1784  }
1785 
1786  /* At this point, (*line_info[n]).quote may still be undefined. We
1787  * don't want to compute it every time MUTT_TYPES is set, since this
1788  * would slow down the "bottom" function unacceptably. A compromise
1789  * solution is hence to call regexec() again, just to find out the
1790  * length of the quote prefix. */
1791  if ((flags & MUTT_SHOWCOLOR) && !curr_line->continuation &&
1792  (curr_line->type == MT_COLOR_QUOTED) && !curr_line->quote)
1793  {
1794  if (fill_buffer(fp, last_pos, curr_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1795  {
1796  if (change_last)
1797  (*last)--;
1798  goto out;
1799  }
1800 
1801  const struct Regex *c_quote_regex =
1802  cs_subset_regex(NeoMutt->sub, "quote_regex");
1803  if (mutt_regex_capture(c_quote_regex, (char *) fmt, 1, pmatch))
1804  {
1805  curr_line->quote =
1806  classify_quote(quote_list, (char *) fmt + pmatch[0].rm_so,
1807  pmatch[0].rm_eo - pmatch[0].rm_so, force_redraw, q_level);
1808  }
1809  else
1810  {
1811  goto out;
1812  }
1813  }
1814 
1815  if ((flags & MUTT_SEARCH) && !curr_line->continuation && (curr_line->search_cnt == -1))
1816  {
1817  if (fill_buffer(fp, last_pos, curr_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1818  {
1819  if (change_last)
1820  (*last)--;
1821  goto out;
1822  }
1823 
1824  offset = 0;
1825  curr_line->search_cnt = 0;
1826  while (regexec(search_re, (char *) fmt + offset, 1, pmatch,
1827  (offset ? REG_NOTBOL : 0)) == 0)
1828  {
1829  if (++(curr_line->search_cnt) > 1)
1830  {
1831  mutt_mem_realloc(&(curr_line->search),
1832  (curr_line->search_cnt) * sizeof(struct TextSyntax));
1833  }
1834  else
1835  curr_line->search = mutt_mem_malloc(sizeof(struct TextSyntax));
1836  pmatch[0].rm_so += offset;
1837  pmatch[0].rm_eo += offset;
1838  (curr_line->search)[curr_line->search_cnt - 1].first = pmatch[0].rm_so;
1839  (curr_line->search)[curr_line->search_cnt - 1].last = pmatch[0].rm_eo;
1840 
1841  if (pmatch[0].rm_eo == pmatch[0].rm_so)
1842  offset++; /* avoid degenerate cases */
1843  else
1844  offset = pmatch[0].rm_eo;
1845  if (!fmt[offset])
1846  break;
1847  }
1848  }
1849 
1850  if (!(flags & MUTT_SHOW) && ((*line_info)[n + 1].offset > 0))
1851  {
1852  /* we've already scanned this line, so just exit */
1853  rc = 0;
1854  goto out;
1855  }
1856  if ((flags & MUTT_SHOWCOLOR) && *force_redraw && ((*line_info)[n + 1].offset > 0))
1857  {
1858  /* no need to try to display this line... */
1859  rc = 1;
1860  goto out; /* fake display */
1861  }
1862 
1863  b_read = fill_buffer(fp, last_pos, curr_line->offset, &buf, &fmt, &buflen, &buf_ready);
1864  if (b_read < 0)
1865  {
1866  if (change_last)
1867  (*last)--;
1868  goto out;
1869  }
1870 
1871  /* now chose a good place to break the line */
1872  cnt = format_line(win_pager, line_info, n, buf, flags, NULL, b_read, &ch,
1873  &vch, &col, &special, win_pager->state.cols);
1874  buf_ptr = buf + cnt;
1875 
1876  /* move the break point only if smart_wrap is set */
1877  if (c_smart_wrap)
1878  {
1879  if ((cnt < b_read) && (ch != -1) &&
1880  !mutt_color_is_header(curr_line->type) && !IS_SPACE(buf[cnt]))
1881  {
1882  buf_ptr = buf + ch;
1883  /* skip trailing blanks */
1884  while (ch && ((buf[ch] == ' ') || (buf[ch] == '\t') || (buf[ch] == '\r')))
1885  ch--;
1886  /* A very long word with leading spaces causes infinite
1887  * wrapping when MUTT_PAGER_NSKIP is set. A folded header
1888  * with a single long word shouldn't be smartwrapped
1889  * either. So just disable smart_wrap if it would wrap at the
1890  * beginning of the line. */
1891  if (ch == 0)
1892  buf_ptr = buf + cnt;
1893  else
1894  cnt = ch + 1;
1895  }
1896  if (!(flags & MUTT_PAGER_NSKIP))
1897  {
1898  /* skip leading blanks on the next line too */
1899  while ((*buf_ptr == ' ') || (*buf_ptr == '\t'))
1900  buf_ptr++;
1901  }
1902  }
1903 
1904  if (*buf_ptr == '\r')
1905  buf_ptr++;
1906  if (*buf_ptr == '\n')
1907  buf_ptr++;
1908 
1909  if (((int) (buf_ptr - buf) < b_read) && !(*line_info)[n + 1].continuation)
1910  append_line(*line_info, n, (int) (buf_ptr - buf));
1911  (*line_info)[n + 1].offset = curr_line->offset + (long) (buf_ptr - buf);
1912 
1913  /* if we don't need to display the line we are done */
1914  if (!(flags & MUTT_SHOW))
1915  {
1916  rc = 0;
1917  goto out;
1918  }
1919 
1920  /* display the line */
1921  format_line(win_pager, line_info, n, buf, flags, &a, cnt, &ch, &vch, &col,
1922  &special, win_pager->state.cols);
1923 
1924 /* avoid a bug in ncurses... */
1925 #ifndef USE_SLANG_CURSES
1926  if (col == 0)
1927  {
1929  mutt_window_addch(win_pager, ' ');
1930  }
1931 #endif
1932 
1933  /* end the last color pattern (needed by S-Lang) */
1934  if (special || ((col != win_pager->state.cols) && (flags & (MUTT_SHOWCOLOR | MUTT_SEARCH))))
1935  resolve_color(win_pager, *line_info, n, vch, flags, 0, &a);
1936 
1937  /* Fill the blank space at the end of the line with the prevailing color.
1938  * ncurses does an implicit clrtoeol() when you do mutt_window_addch('\n') so we have
1939  * to make sure to reset the color *after* that */
1940  if (flags & MUTT_SHOWCOLOR)
1941  {
1942  m = (curr_line->continuation) ? (curr_line->syntax)[0].first : n;
1943  if ((*line_info)[m].type == MT_COLOR_HEADER)
1944  def_color = ((*line_info)[m].syntax)[0].color;
1945  else
1946  def_color = mutt_color((*line_info)[m].type);
1947 
1948  mutt_curses_set_attr(def_color);
1949  }
1950 
1951  if (col < win_pager->state.cols)
1952  mutt_window_clrtoeol(win_pager);
1953 
1954  /* reset the color back to normal. This *must* come after the
1955  * clrtoeol, otherwise the color for this line will not be
1956  * filled to the right margin. */
1957  if (flags & MUTT_SHOWCOLOR)
1959 
1960  /* build a return code */
1961  if (!(flags & MUTT_SHOW))
1962  flags = 0;
1963 
1964  rc = flags;
1965 
1966 out:
1967  FREE(&buf);
1968  FREE(&fmt);
1969  return rc;
1970 }
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
#define MUTT_PAGER_NSKIP
Preserve whitespace with smartwrap.
Definition: lib.h:63
Informational message.
Definition: color.h:56
bool mutt_color_is_header(enum ColorId color_id)
Colour is for an Email header.
Definition: color.c:1560
void mutt_curses_set_color(enum ColorId color)
Set the current colour for text.
Definition: mutt_curses.c:56
static struct QClass * classify_quote(struct QClass **quote_list, const char *qptr, size_t length, bool *force_redraw, int *q_level)
Find a style for a string.
Definition: dlg_pager.c:616
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:259
static int fill_buffer(FILE *fp, LOFF_T *last_pos, LOFF_T offset, unsigned char **buf, unsigned char **fmt, size_t *blen, int *buf_ready)
Fill a buffer from a file.
Definition: dlg_pager.c:1448
#define MUTT_HIDE
Don&#39;t show quoted text.
Definition: lib.h:57
An ANSI escape sequence.
Definition: dlg_pager.c:147
short type
Definition: dlg_pager.c:134
static int format_line(struct MuttWindow *win, struct Line **line_info, int n, unsigned char *buf, PagerFlags flags, struct AnsiAttr *pa, int cnt, int *pspace, int *pvch, int *pcol, int *pspecial, int width)
Display a line of text in the pager.
Definition: dlg_pager.c:1497
Pager: quoted text.
Definition: color.h:62
#define MUTT_SHOW
Definition: lib.h:60
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
Container for Accounts, Notifications.
Definition: neomutt.h:36
#define MUTT_SEARCH
Resolve search patterns.
Definition: lib.h:58
Menu showing log messages.
Definition: color.h:57
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:243
Message headers (takes a pattern)
Definition: color.h:53
short continuation
Definition: dlg_pager.c:135
Plain text.
Definition: color.h:58
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:60
struct QClass * quote
Definition: dlg_pager.c:140
short search_cnt
Definition: dlg_pager.c:137
struct TextSyntax * search
Definition: dlg_pager.c:139
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
A line of text in the pager.
Definition: dlg_pager.c:131
#define MUTT_SHOWCOLOR
Show characters in color otherwise don&#39;t show characters.
Definition: lib.h:56
#define MUTT_PAGER_LOGS
Logview mode.
Definition: lib.h:68
int mutt_color(enum ColorId id)
Return the color of an object.
Definition: color.c:1427
#define MUTT_TYPES
Compute line&#39;s type.
Definition: lib.h:59
static void resolve_color(struct MuttWindow *win, struct Line *line_info, int n, int cnt, PagerFlags flags, int special, struct AnsiAttr *a)
Set the colour for a line of text.
Definition: dlg_pager.c:384
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:596
#define IS_SPACE(ch)
Definition: string2.h:38
Highlighting for a line of text.
Definition: dlg_pager.c:121
Error message.
Definition: color.h:51
Cached regular expression.
Definition: regex3.h:89
#define FREE(x)
Definition: memory.h:40
int index
Definition: dlg_pager.c:111
struct TextSyntax * syntax
Definition: dlg_pager.c:138
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
static void append_line(struct Line *line_info, int n, int cnt)
Add a new Line to the array.
Definition: dlg_pager.c:517
void mutt_curses_set_attr(int attr)
Set the attributes for text.
Definition: mutt_curses.c:39
LOFF_T offset
Definition: dlg_pager.c:133
static void resolve_types(struct MuttWindow *win, char *buf, char *raw, struct Line *line_info, int n, int last, struct QClass **quote_list, int *q_level, bool *force_redraw, bool q_classify)
Determine the style for a line of text.
Definition: dlg_pager.c:1014
Warning messages.
Definition: color.h:79
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
Definition: mutt_window.c:402
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ up_n_lines()

static int up_n_lines ( int  nlines,
struct Line info,
int  cur,
bool  hiding 
)
static

Reposition the pager's view up by n lines.

Parameters
nlinesNumber of lines to move
infoLine info array
curCurrent line number
hidingtrue if lines have been hidden
Return values
numNew current line number

Definition at line 1980 of file dlg_pager.c.

1981 {
1982  while ((cur > 0) && (nlines > 0))
1983  {
1984  cur--;
1985  if (!hiding || (info[cur].type != MT_COLOR_QUOTED))
1986  nlines--;
1987  }
1988 
1989  return cur;
1990 }
Pager: quoted text.
Definition: color.h:62
+ Here is the caller graph for this function:

◆ mutt_clear_pager_position()

void mutt_clear_pager_position ( void  )

Reset the pager's viewing position.

Definition at line 1995 of file dlg_pager.c.

1996 {
1997  TopLine = 0;
1998  OldEmail = NULL;
1999 }
static struct Email * OldEmail
Definition: dlg_pager.c:167
static int TopLine
Definition: dlg_pager.c:166
+ Here is the caller graph for this function:

◆ pager_queue_redraw()

void pager_queue_redraw ( struct PagerPrivateData priv,
MenuRedrawFlags  redraw 
)

Queue a request for a redraw.

Parameters
privPrivate Pager data
redrawItem to redraw, e.g. MENU_REDRAW_FULL

Definition at line 2006 of file dlg_pager.c.

2007 {
2008  priv->redraw |= redraw;
2009  // win_pager->actions |= WA_RECALC;
2010 }
MenuRedrawFlags redraw
When to redraw the screen.
Definition: private_data.h:69
+ Here is the caller graph for this function:

◆ pager_custom_redraw()

static void pager_custom_redraw ( struct PagerPrivateData priv)
static

Redraw the pager window.

Parameters
privPrivate Pager data

Definition at line 2016 of file dlg_pager.c.

2017 {
2018  //---------------------------------------------------------------------------
2019  // ASSUMPTIONS & SANITY CHECKS
2020  //---------------------------------------------------------------------------
2021  // Since pager_custom_redraw() is a static function and it is always called
2022  // after mutt_pager() we can rely on a series of sanity checks in
2023  // mutt_pager(), namely:
2024  // - PAGER_MODE_EMAIL guarantees ( data->email) and (!data->body)
2025  // - PAGER_MODE_ATTACH guarantees ( data->email) and ( data->body)
2026  // - PAGER_MODE_OTHER guarantees (!data->email) and (!data->body)
2027  //
2028  // Additionally, while refactoring is still in progress the following checks
2029  // are still here to ensure data model consistency.
2030  assert(priv); // Redraw function can't be called without its data.
2031  assert(priv->pview); // Redraw data can't exist separately without the view.
2032  assert(priv->pview->pdata); // View can't exist without its data
2033  //---------------------------------------------------------------------------
2034 
2035  char buf[1024] = { 0 };
2036  struct IndexSharedData *shared = dialog_find(priv->pview->win_pager)->wdata;
2037 
2038  const bool c_tilde = cs_subset_bool(NeoMutt->sub, "tilde");
2039  const short c_pager_index_lines =
2040  cs_subset_number(NeoMutt->sub, "pager_index_lines");
2041 
2042  if (priv->redraw & MENU_REDRAW_FULL)
2043  {
2046 
2047  if ((priv->pview->mode == PAGER_MODE_EMAIL) &&
2048  ((shared->mailbox->vcount + 1) < c_pager_index_lines))
2049  {
2050  priv->indexlen = shared->mailbox->vcount + 1;
2051  }
2052  else
2053  {
2054  priv->indexlen = c_pager_index_lines;
2055  }
2056 
2057  priv->indicator = priv->indexlen / 3;
2058 
2059  if (Resize)
2060  {
2062  if (priv->search_compiled)
2063  {
2064  uint16_t flags = mutt_mb_is_lower(priv->searchbuf) ? REG_ICASE : 0;
2065  const int err = REG_COMP(&priv->search_re, priv->searchbuf, REG_NEWLINE | flags);
2066  if (err == 0)
2067  {
2068  priv->search_flag = MUTT_SEARCH;
2069  priv->search_back = Resize->search_back;
2070  }
2071  else
2072  {
2073  regerror(err, &priv->search_re, buf, sizeof(buf));
2074  mutt_error("%s", buf);
2075  priv->search_compiled = false;
2076  }
2077  }
2078  priv->lines = Resize->line;
2080 
2081  FREE(&Resize);
2082  }
2083 
2084  if ((priv->pview->mode == PAGER_MODE_EMAIL) && (c_pager_index_lines != 0) && priv->menu)
2085  {
2087 
2088  /* some fudge to work out whereabouts the indicator should go */
2089  const int index = menu_get_index(priv->menu);
2090  if ((index - priv->indicator) < 0)
2091  priv->menu->top = 0;
2092  else if ((priv->menu->max - index) < (priv->menu->pagelen - priv->indicator))
2093  priv->menu->top = priv->menu->max - priv->menu->pagelen;
2094  else
2095  priv->menu->top = index - priv->indicator;
2096 
2097  menu_redraw_index(priv->menu);
2098  }
2099 
2101  }
2102 
2103  if (priv->redraw & MENU_REDRAW_FLOW)
2104  {
2105  if (!(priv->pview->flags & MUTT_PAGER_RETWINCH))
2106  {
2107  priv->lines = -1;
2108  for (int i = 0; i <= priv->topline; i++)
2109  if (!priv->line_info[i].continuation)
2110  priv->lines++;
2111  for (int i = 0; i < priv->max_line; i++)
2112  {
2113  priv->line_info[i].offset = 0;
2114  priv->line_info[i].type = -1;
2115  priv->line_info[i].continuation = 0;
2116  priv->line_info[i].chunks = 0;
2117  priv->line_info[i].search_cnt = -1;
2118  priv->line_info[i].quote = NULL;
2119 
2120  mutt_mem_realloc(&(priv->line_info[i].syntax), sizeof(struct TextSyntax));
2121  if (priv->search_compiled && priv->line_info[i].search)
2122  FREE(&(priv->line_info[i].search));
2123  }
2124 
2125  priv->last_line = 0;
2126  priv->topline = 0;
2127  }
2128  int i = -1;
2129  int j = -1;
2130  while (display_line(priv->fp, &priv->last_pos, &priv->line_info, ++i,
2131  &priv->last_line, &priv->max_line,
2132  priv->has_types | priv->search_flag | (priv->pview->flags & MUTT_PAGER_NOWRAP),
2133  &priv->quote_list, &priv->q_level, &priv->force_redraw,
2134  &priv->search_re, priv->pview->win_pager) == 0)
2135  {
2136  if (!priv->line_info[i].continuation && (++j == priv->lines))
2137  {
2138  priv->topline = i;
2139  if (!priv->search_flag)
2140  break;
2141  }
2142  }
2143  }
2144 
2145  if ((priv->redraw & MENU_REDRAW_BODY) || (priv->topline != priv->oldtopline))
2146  {
2147  do
2148  {
2149  mutt_window_move(priv->pview->win_pager, 0, 0);
2150  priv->curline = priv->topline;
2151  priv->oldtopline = priv->topline;
2152  priv->lines = 0;
2153  priv->force_redraw = false;
2154 
2155  while ((priv->lines < priv->pview->win_pager->state.rows) &&
2156  (priv->line_info[priv->curline].offset <= priv->sb.st_size - 1))
2157  {
2158  if (display_line(priv->fp, &priv->last_pos, &priv->line_info,
2159  priv->curline, &priv->last_line, &priv->max_line,
2160  (priv->pview->flags & MUTT_DISPLAYFLAGS) | priv->hide_quoted |
2161  priv->search_flag | (priv->pview->flags & MUTT_PAGER_NOWRAP),
2162  &priv->quote_list, &priv->q_level, &priv->force_redraw,
2163  &priv->search_re, priv->pview->win_pager) > 0)
2164  {
2165  priv->lines++;
2166  }
2167  priv->curline++;
2168  mutt_window_move(priv->pview->win_pager, 0, priv->lines);
2169  }
2170  priv->last_offset = priv->line_info[priv->curline].offset;
2171  } while (priv->force_redraw);
2172 
2174  while (priv->lines < priv->pview->win_pager->state.rows)
2175  {
2177  if (c_tilde)
2178  mutt_window_addch(priv->pview->win_pager, '~');
2179  priv->lines++;
2180  mutt_window_move(priv->pview->win_pager, 0, priv->lines);
2181  }
2183 
2184  /* We are going to update the pager status bar, so it isn't
2185  * necessary to reset to normal color now. */
2186 
2187  pager_queue_redraw(priv, MENU_REDRAW_STATUS); /* need to update the % seen */
2188  }
2189 
2190  if (priv->redraw & MENU_REDRAW_STATUS)
2191  {
2192  char pager_progress_str[65]; /* Lots of space for translations */
2193 
2194  if (priv->last_pos < priv->sb.st_size - 1)
2195  {
2196  snprintf(pager_progress_str, sizeof(pager_progress_str), OFF_T_FMT "%%",
2197  (100 * priv->last_offset / priv->sb.st_size));
2198  }
2199  else
2200  {
2201  const char *msg = (priv->topline == 0) ?
2202  /* L10N: Status bar message: the entire email is visible in the pager */
2203  _("all") :
2204  /* L10N: Status bar message: the end of the email is visible in the pager */
2205  _("end");
2206  mutt_str_copy(pager_progress_str, msg, sizeof(pager_progress_str));
2207  }
2208 
2209  /* print out the pager status bar */
2210  mutt_window_move(priv->pview->win_pbar, 0, 0);
2212 
2213  if (priv->pview->mode == PAGER_MODE_EMAIL || priv->pview->mode == PAGER_MODE_ATTACH_E)
2214  {
2215  const size_t l1 = priv->pview->win_pbar->state.cols * MB_LEN_MAX;
2216  const size_t l2 = sizeof(buf);
2217  const size_t buflen = (l1 < l2) ? l1 : l2;
2218  struct Email *e = (priv->pview->mode == PAGER_MODE_EMAIL) ? shared->email : // PAGER_MODE_EMAIL
2219  priv->pview->pdata->body->email; // PAGER_MODE_ATTACH_E
2220 
2221  const char *const c_pager_format =
2222  cs_subset_string(NeoMutt->sub, "pager_format");
2223  mutt_make_string(buf, buflen, priv->pview->win_pbar->state.cols,
2224  NONULL(c_pager_format), shared->mailbox, shared->ctx->msg_in_pager,
2225  e, MUTT_FORMAT_NO_FLAGS, pager_progress_str);
2227  priv->pview->win_pbar->state.cols, buf, l2);
2228  }
2229  else
2230  {
2231  char bn[256];
2232  snprintf(bn, sizeof(bn), "%s (%s)", priv->pview->banner, pager_progress_str);
2234  priv->pview->win_pbar->state.cols, bn, sizeof(bn));
2235  }
2237  const bool c_ts_enabled = cs_subset_bool(NeoMutt->sub, "ts_enabled");
2238  if (c_ts_enabled && TsSupported && priv->menu)
2239  {
2240  const char *const c_ts_status_format =
2241  cs_subset_string(NeoMutt->sub, "ts_status_format");
2242  menu_status_line(buf, sizeof(buf), shared, priv->menu, sizeof(buf),
2243  NONULL(c_ts_status_format));
2244  mutt_ts_status(buf);
2245  const char *const c_ts_icon_format =
2246  cs_subset_string(NeoMutt->sub, "ts_icon_format");
2247  menu_status_line(buf, sizeof(buf), shared, priv->menu, sizeof(buf),
2248  NONULL(c_ts_icon_format));
2249  mutt_ts_icon(buf);
2250  }
2251  }
2252 
2253  priv->redraw = MENU_REDRAW_NO_FLAGS;
2254  mutt_debug(LL_DEBUG5, "repaint done\n");
2255 }
struct PagerData * pdata
Data that pager displays. NOTNULL.
Definition: lib.h:156
#define MUTT_PAGER_RETWINCH
Need reformatting on SIGWINCH.
Definition: lib.h:65
#define NONULL(x)
Definition: string2.h:37
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
int msg_in_pager
Message currently shown in the pager.
Definition: context.h:43
struct stat sb
Definition: private_data.h:67
The envelope/body of an email.
Definition: email.h:37
struct Email * email
Currently selected Email.
Definition: shared_data.h:42
#define mutt_error(...)
Definition: logging.h:88
short chunks
Definition: dlg_pager.c:136
void mutt_curses_set_color(enum ColorId color)
Set the current colour for text.
Definition: mutt_curses.c:56
PagerFlags hide_quoted
Definition: private_data.h:55
struct Body * body
Current attachment.
Definition: lib.h:145
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:259
#define _(a)
Definition: message.h:28
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:54
Data shared between Index, Pager and Sidebar.
Definition: shared_data.h:36
bool search_back
Definition: dlg_pager.c:162
int indicator
the indicator line of the PI
Definition: private_data.h:46
bool search_compiled
Definition: dlg_pager.c:161
short type
Definition: dlg_pager.c:134
struct QClass * quote_list
Definition: private_data.h:57
static int display_line(FILE *fp, LOFF_T *last_pos, struct Line **line_info, int n, int *last, int *max, PagerFlags flags, struct QClass **quote_list, int *q_level, bool *force_redraw, regex_t *search_re, struct MuttWindow *win_pager)
Print a line on screen.
Definition: dlg_pager.c:1692
A special case of PAGER_MODE_ATTACH - attachment is a full-blown email message.
Definition: lib.h:133
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
Container for Accounts, Notifications.
Definition: neomutt.h:36
#define MUTT_SEARCH
Resolve search patterns.
Definition: lib.h:58
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
int vcount
The number of virtual messages.
Definition: mailbox.h:102
int mutt_window_move(struct MuttWindow *win, int col, int row)
Move the cursor in a Window.
Definition: mutt_window.c:310
int line
Definition: dlg_pager.c:160
void mutt_window_clear(struct MuttWindow *win)
Clear a Window.
Definition: mutt_window.c:716
Pager: empty lines after message.
Definition: color.h:76
void mutt_make_string(char *buf, size_t buflen, int cols, const char *s, struct Mailbox *m, int inpgr, struct Email *e, MuttFormatFlags flags, const char *progress)
Create formatted strings using mailbox expandos.
Definition: hdrline.c:1409
struct Menu * menu
Definition: private_data.h:41
short continuation
Definition: dlg_pager.c:135
void pager_queue_redraw(struct PagerPrivateData *priv, MenuRedrawFlags redraw)
Queue a request for a redraw.
Definition: dlg_pager.c:2006
Plain text.
Definition: color.h:58
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
struct Context * ctx
Current Mailbox view.
Definition: shared_data.h:39
enum PagerMode mode
Pager mode.
Definition: lib.h:157
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:60
struct QClass * quote
Definition: dlg_pager.c:140
Pager is invoked via 1st path. The mime part is selected automatically.
Definition: lib.h:131
short search_cnt
Definition: dlg_pager.c:137
struct TextSyntax * search
Definition: dlg_pager.c:139
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
Status bar (takes a pattern)
Definition: color.h:75
short rows
Number of rows, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:61
int top
Entry that is the top of the current page.
Definition: lib.h:89
struct Line * line_info
Definition: private_data.h:65
void mutt_draw_statusline(struct MuttWindow *win, int cols, const char *buf, size_t buflen)
Draw a highlighted status bar.
Definition: dlg_index.c:875
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
PagerFlags flags
Additional settings to tweak pager&#39;s function.
Definition: lib.h:158
int pagelen
Number of entries per screen.
Definition: lib.h:74
void menu_status_line(char *buf, size_t buflen, struct IndexSharedData *shared, struct Menu *menu, int cols, const char *fmt)
Create the status line.
Definition: status.c:445
struct PagerView * pview
Definition: private_data.h:44
bool TsSupported
Terminal Setting is supported.
Definition: terminal.c:43
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
int max
Number of entries in the menu.
Definition: lib.h:71
Highlighting for a line of text.
Definition: dlg_pager.c:121
#define MUTT_DISPLAYFLAGS
Definition: lib.h:72
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:749
void menu_redraw_index(struct Menu *menu)
Force the redraw of the index.
Definition: draw.c:371
#define FREE(x)
Definition: memory.h:40
struct MuttWindow * win_pager
Definition: lib.h:164
void mutt_ts_icon(char *str)
Set the icon in the terminal title bar.
Definition: terminal.c:118
struct MuttWindow * win_pbar
Definition: lib.h:163
struct TextSyntax * syntax
Definition: dlg_pager.c:138
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition: lib.h:67
Keep track of screen resizing.
Definition: dlg_pager.c:158
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
void mutt_ts_status(char *str)
Set the text of the terminal title bar.
Definition: terminal.c:104
PagerFlags search_flag
Definition: private_data.h:62
struct Mailbox * mailbox
Current Mailbox.
Definition: shared_data.h:41
Log at debug level 5.
Definition: logging.h:44
const char * banner
Title to display in status bar.
Definition: lib.h:159
LOFF_T offset
Definition: dlg_pager.c:133
void * wdata
Private data.
Definition: mutt_window.h:145
bool mutt_mb_is_lower(const char *s)
Does a multi-byte string contain only lowercase characters?
Definition: mbyte.c:357
struct Email * email
header information for message/rfc822
Definition: body.h:55
struct MuttWindow * dialog_find(struct MuttWindow *win)
Find the parent Dialog of a Window.
Definition: dialog.c:85
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
Definition: mutt_window.c:402
MenuRedrawFlags redraw
When to redraw the screen.
Definition: private_data.h:69
char searchbuf[256]
Definition: private_data.h:64
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ pager_resolve_help_mapping()

static const struct Mapping* pager_resolve_help_mapping ( enum PagerMode  mode,
enum MailboxType  type 
)
static

determine help mapping based on pager mode and mailbox type

Parameters
modepager mode
typemailbox type

Definition at line 2263 of file dlg_pager.c.

2264 {
2265  const struct Mapping *result;
2266  switch (mode)
2267  {
2268  case PAGER_MODE_EMAIL:
2269  case PAGER_MODE_ATTACH:
2270  case PAGER_MODE_ATTACH_E:
2271  if (type == MUTT_NNTP)
2272  result = PagerNewsHelp;
2273  else
2274  result = PagerNormalHelp;
2275  break;
2276 
2277  case PAGER_MODE_HELP:
2278  result = PagerHelpHelp;
2279  break;
2280 
2281  case PAGER_MODE_OTHER:
2282  result = PagerHelp;
2283  break;
2284 
2285  case PAGER_MODE_UNKNOWN:
2286  case PAGER_MODE_MAX:
2287  default:
2288  assert(false); // something went really wrong
2289  }
2290  assert(result);
2291  return result;
2292 }
A default and invalid mode, should never be used.
Definition: lib.h:129
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:52
static const struct Mapping PagerNormalHelp[]
Help Bar for the Pager of a normal Mailbox.
Definition: dlg_pager.c:202
A special case of PAGER_MODE_ATTACH - attachment is a full-blown email message.
Definition: lib.h:133
Pager is invoked via 3rd path to show help.
Definition: lib.h:134
Pager is invoked via 1st path. The mime part is selected automatically.
Definition: lib.h:131
static const struct Mapping PagerNewsHelp[]
Help Bar for the Pager of an NNTP Mailbox.
Definition: dlg_pager.c:218
Another invalid mode, should never be used.
Definition: lib.h:137
Pager is invoked via 2nd path. A user-selected attachment (mime part or a nested email) will be shown...
Definition: lib.h:132
Mapping between user-readable string and a constant.
Definition: mapping.h:31
static const struct Mapping PagerHelp[]
Help Bar for the Pager&#39;s Help Page.
Definition: dlg_pager.c:181
Pager is invoked via 3rd path. Non-email content is likely to be shown.
Definition: lib.h:135
static const struct Mapping PagerHelpHelp[]
Help Bar for the Help Page itself.
Definition: dlg_pager.c:192
+ Here is the caller graph for this function:

◆ jump_to_bottom()

static bool jump_to_bottom ( struct PagerPrivateData priv,
struct PagerView pview 
)
static

make sure the bottom line is displayed

Parameters
privPrivate Pager data
pviewPagerView
Return values
trueSomething changed
falseBottom was already displayed

Definition at line 2301 of file dlg_pager.c.

2302 {
2303  if (!(priv->line_info[priv->curline].offset < (priv->sb.st_size - 1)))
2304  {
2305  return false;
2306  }
2307 
2308  int line_num = priv->curline;
2309  /* make sure the types are defined to the end of file */
2310  while (display_line(priv->fp, &priv->last_pos, &priv->line_info, line_num,
2311  &priv->last_line, &priv->max_line,
2312  priv->has_types | (pview->flags & MUTT_PAGER_NOWRAP),
2313  &priv->quote_list, &priv->q_level, &priv->force_redraw,
2314  &priv->search_re, priv->pview->win_pager) == 0)
2315  {
2316  line_num++;
2317  }
2318  priv->topline = up_n_lines(priv->pview->win_pager->state.rows, priv->line_info,
2319  priv->last_line, priv->hide_quoted);
2320  return true;
2321 }
static int up_n_lines(int nlines, struct Line *info, int cur, bool hiding)
Reposition the pager&#39;s view up by n lines.
Definition: dlg_pager.c:1980
struct stat sb
Definition: private_data.h:67
PagerFlags hide_quoted
Definition: private_data.h:55
struct QClass * quote_list
Definition: private_data.h:57
static int display_line(FILE *fp, LOFF_T *last_pos, struct Line **line_info, int n, int *last, int *max, PagerFlags flags, struct QClass **quote_list, int *q_level, bool *force_redraw, regex_t *search_re, struct MuttWindow *win_pager)
Print a line on screen.
Definition: dlg_pager.c:1692
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
short rows
Number of rows, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:61
struct Line * line_info
Definition: private_data.h:65
PagerFlags flags
Additional settings to tweak pager&#39;s function.
Definition: lib.h:158
struct PagerView * pview
Definition: private_data.h:44
struct MuttWindow * win_pager
Definition: lib.h:164
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition: lib.h:67
LOFF_T offset
Definition: dlg_pager.c:133
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ check_read_delay()

static bool check_read_delay ( uint64_t *  timestamp)
static

Is it time to mark the message read?

Parameters
timestampTime when message should be marked read, or 0
Return values
trueMessage should be marked read
falseNo action necessary

Definition at line 2329 of file dlg_pager.c.

2330 {
2331  if ((*timestamp != 0) && (mutt_date_epoch_ms() > *timestamp))
2332  {
2333  *timestamp = 0;
2334  return true;
2335  }
2336  return false;
2337 }
uint64_t mutt_date_epoch_ms(void)
Return the number of milliseconds since the Unix epoch.
Definition: date.c:436
static const char * timestamp(time_t stamp)
Create a YYYY-MM-DD HH:MM:SS timestamp.
Definition: logging.c:77
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_pager()

int mutt_pager ( struct PagerView pview)

Display an email, attachment, or help, in a window.

Parameters
pviewPager view settings
Return values
0Success
-1Error

This pager is actually not so simple as it once was. But it will be again. Currently it operates in 3 modes:

  • viewing messages. (PAGER_MODE_EMAIL)
  • viewing attachments. (PAGER_MODE_ATTACH)
  • viewing other stuff (e.g. help). (PAGER_MODE_OTHER) These can be distinguished by PagerMode in PagerView. Data is not yet polymorphic and is fused into a single struct (PagerData). Different elements of PagerData are expected to be present depending on the mode:
  • PAGER_MODE_EMAIL expects data->email and not expects data->body
  • PAGER_MODE_ATTACH expects data->email and data->body special sub-case of this mode is viewing attached email message it is recognized by presence of data->fp and data->body->email
  • PAGER_MODE_OTHER does not expect data->email or data->body

Definition at line 2360 of file dlg_pager.c.

2361 {
2362  //===========================================================================
2363  // ACT 1 - Ensure sanity of the caller and determine the mode
2364  //===========================================================================
2365  assert(pview);
2366  assert((pview->mode > PAGER_MODE_UNKNOWN) && (pview->mode < PAGER_MODE_MAX));
2367  assert(pview->pdata); // view can't exist in a vacuum
2368  assert(pview->win_pager);
2369  assert(pview->win_pbar);
2370 
2371  struct MuttWindow *dlg = dialog_find(pview->win_pager);
2372  struct IndexSharedData *shared = dlg->wdata;
2373 
2374  switch (pview->mode)
2375  {
2376  case PAGER_MODE_EMAIL:
2377  // This case was previously identified by IsEmail macro
2378  // we expect data to contain email and not contain body
2379  // We also expect email to always belong to some mailbox
2380  assert(shared->ctx);
2381  assert(shared->mailbox);
2382  assert(shared->email);
2383  assert(!pview->pdata->body);
2384  break;
2385 
2386  case PAGER_MODE_ATTACH:
2387  // this case was previously identified by IsAttach and IsMsgAttach
2388  // macros, we expect data to contain:
2389  // - body (viewing regular attachment)
2390  // - email
2391  // - fp and body->email in special case of viewing an attached email.
2392  assert(shared->email); // This should point to the top level email
2393  assert(pview->pdata->body);
2394  if (pview->pdata->fp && pview->pdata->body->email)
2395  {
2396  // Special case: attachment is a full-blown email message.
2397  // Yes, emails can contain other emails.
2398  pview->mode = PAGER_MODE_ATTACH_E;
2399  }
2400  break;
2401 
2402  case PAGER_MODE_HELP:
2403  case PAGER_MODE_OTHER:
2404  assert(!shared->ctx);
2405  assert(!shared->email);
2406  assert(!pview->pdata->body);
2407  break;
2408 
2409  case PAGER_MODE_UNKNOWN:
2410  case PAGER_MODE_MAX:
2411  default:
2412  // Unexpected mode. Catch fire and explode.
2413  // This *should* happen if mode is PAGER_MODE_ATTACH_E, since
2414  // we do not expect any caller to pass it to us.
2415  assert(false);
2416  break;
2417  }
2418 
2419  //===========================================================================
2420  // ACT 2 - Declare, initialize local variables, read config, etc.
2421  //===========================================================================
2422 
2423  //---------- reading config values needed now--------------------------------
2424  const short c_pager_index_lines =
2425  cs_subset_number(NeoMutt->sub, "pager_index_lines");
2426  const short c_pager_read_delay =
2427  cs_subset_number(NeoMutt->sub, "pager_read_delay");
2428 
2429  //---------- local variables ------------------------------------------------
2430  char buf[1024] = { 0 };
2431  int op = 0;
2432  int rc = -1;
2433  int searchctx = 0;
2434  int index_space = shared->mailbox ? MIN(c_pager_index_lines, shared->mailbox->vcount) :
2435  c_pager_index_lines;
2436  bool first = true;
2437  bool wrapped = false;
2438  enum MailboxType mailbox_type = shared->mailbox ? shared->mailbox->type : MUTT_UNKNOWN;
2439  uint64_t delay_read_timestamp = 0;
2440 
2441  struct PagerPrivateData *priv = pview->win_pager->parent->wdata;
2442  {
2443  // Wipe any previous state info
2444  struct Menu *menu = priv->menu;
2445  memset(priv, 0, sizeof(*priv));
2446  priv->menu = menu;
2447  priv->win_pbar = pview->win_pbar;
2448  }
2449 
2450  //---------- setup flags ----------------------------------------------------
2451  if (!(pview->flags & MUTT_SHOWCOLOR))
2452  pview->flags |= MUTT_SHOWFLAT;
2453 
2454  if (pview->mode == PAGER_MODE_EMAIL && !shared->email->read)
2455  {
2456  shared->ctx->msg_in_pager = shared->email->msgno;
2457  priv->win_pbar->actions |= WA_RECALC;
2458  if (c_pager_read_delay == 0)
2459  {
2460  mutt_set_flag(shared->mailbox, shared->email, MUTT_READ, true);
2461  }
2462  else
2463  {
2464  delay_read_timestamp = mutt_date_epoch_ms() + (1000 * c_pager_read_delay);
2465  }
2466  }
2467  //---------- setup help menu ------------------------------------------------
2468  pview->win_pager->help_data = pager_resolve_help_mapping(pview->mode, mailbox_type);
2469  pview->win_pager->help_menu = MENU_PAGER;
2470 
2471  //---------- initialize redraw pdata -----------------------------------------
2473  priv->pview = pview;
2474  priv->indexlen = c_pager_index_lines;
2475  priv->indicator = priv->indexlen / 3;
2476  priv->max_line = LINES; // number of lines on screen, from curses
2477  priv->line_info = mutt_mem_calloc(priv->max_line, sizeof(struct Line));
2478  priv->fp = fopen(pview->pdata->fname, "r");
2479  priv->has_types =
2480  ((pview->mode == PAGER_MODE_EMAIL) || (pview->flags & MUTT_SHOWCOLOR)) ?
2481  MUTT_TYPES :
2482  0; // main message or rfc822 attachment
2483 
2484  for (size_t i = 0; i < priv->max_line; i++)
2485  {
2486  priv->line_info[i].type = -1;
2487  priv->line_info[i].search_cnt = -1;
2488  priv->line_info[i].syntax = mutt_mem_malloc(sizeof(struct TextSyntax));
2489  (priv->line_info[i].syntax)[0].first = -1;
2490  (priv->line_info[i].syntax)[0].last = -1;
2491  }
2492 
2493  // ---------- try to open the pdata file -------------------------------------
2494  if (!priv->fp)
2495  {
2496  mutt_perror(pview->pdata->fname);
2497  return -1;
2498  }
2499 
2500  if (stat(pview->pdata->fname, &priv->sb) != 0)
2501  {
2502  mutt_perror(pview->pdata->fname);
2503  mutt_file_fclose(&priv->fp);
2504  return -1;
2505  }
2506  unlink(pview->pdata->fname);
2507 
2508  //---------- restore global state if needed ---------------------------------
2509  while ((pview->mode == PAGER_MODE_EMAIL) && (OldEmail == shared->email) && // are we "resuming" to the same Email?
2510  (TopLine != priv->topline) && // is saved offset different?
2511  (priv->line_info[priv->curline].offset < (priv->sb.st_size - 1)))
2512  {
2514  pager_custom_redraw(priv);
2515  // trick user, as if nothing happened
2516  // scroll down to previosly saved offset
2517  priv->topline =
2518  ((TopLine - priv->topline) > priv->lines) ? priv->topline + priv->lines : TopLine;
2519  }
2520 
2521  TopLine = 0;
2522  OldEmail = NULL;
2523 
2524  //---------- show windows, set focus and visibility --------------------------
2525  if (priv->pview->win_index)
2526  {
2527  priv->menu = priv->pview->win_index->wdata;
2529  priv->pview->win_index->req_rows = index_space;
2531  window_set_visible(priv->pview->win_index->parent, (index_space > 0));
2532  }
2533  window_set_visible(priv->pview->win_pager->parent, true);
2534  mutt_window_reflow(dlg);
2535  window_set_focus(pview->win_pager);
2536 
2537  //---------- jump to the bottom if requested ------------------------------
2538  if (pview->flags & MUTT_PAGER_BOTTOM)
2539  {
2540  jump_to_bottom(priv, pview);
2541  }
2542 
2543  //-------------------------------------------------------------------------
2544  // ACT 3: Read user input and decide what to do with it
2545  // ...but also do a whole lot of other things.
2546  //-------------------------------------------------------------------------
2547  while (op != -1)
2548  {
2549  if (check_read_delay(&delay_read_timestamp))
2550  {
2551  mutt_set_flag(shared->mailbox, shared->email, MUTT_READ, true);
2552  }
2554 
2556  window_redraw(NULL);
2557  pager_custom_redraw(priv);
2558 
2559  const bool c_braille_friendly =
2560  cs_subset_bool(NeoMutt->sub, "braille_friendly");
2561  if (c_braille_friendly)
2562  {
2563  if (braille_row != -1)
2564  {
2566  braille_row = -1;
2567  }
2568  }
2569  else
2570  mutt_window_move(priv->pview->win_pbar, priv->pview->win_pager->state.cols - 1, 0);
2571 
2572  // force redraw of the screen at every iteration of the event loop
2573  mutt_refresh();
2574 
2575  //-------------------------------------------------------------------------
2576  // Check if information in the status bar needs an update
2577  // This is done because pager is a single-threaded application, which
2578  // tries to emulate concurrency.
2579  //-------------------------------------------------------------------------
2580  bool do_new_mail = false;
2581  if (shared->mailbox && !OptAttachMsg)
2582  {
2583  int oldcount = shared->mailbox->msg_count;
2584  /* check for new mail */
2585  enum MxStatus check = mx_mbox_check(shared->mailbox);
2586  if (check == MX_STATUS_ERROR)
2587  {
2588  if (!shared->mailbox || mutt_buffer_is_empty(&shared->mailbox->pathbuf))
2589  {
2590  /* fatal error occurred */
2592  break;
2593  }
2594  }
2595  else if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED) ||
2596  (check == MX_STATUS_FLAGS))
2597  {
2598  /* notify user of newly arrived mail */
2599  if (check == MX_STATUS_NEW_MAIL)
2600  {
2601  for (size_t i = oldcount; i < shared->mailbox->msg_count; i++)
2602  {
2603  struct Email *e = shared->mailbox->emails[i];
2604 
2605  if (e && !e->read)
2606  {
2607  mutt_message(_("New mail in this mailbox"));
2608  do_new_mail = true;
2609  break;
2610  }
2611  }
2612  }
2613 
2614  if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
2615  {
2616  if (priv->menu && shared->mailbox)
2617  {
2618  /* After the mailbox has been updated, selection might be invalid */
2619  int index = menu_get_index(priv->menu);
2620  menu_set_index(priv->menu, MIN(index, MAX(shared->mailbox->msg_count - 1, 0)));
2621  index = menu_get_index(priv->menu);
2622  struct Email *e = mutt_get_virt_email(shared->mailbox, index);
2623  if (!e)
2624  continue;
2625 
2626  bool verbose = shared->mailbox->verbose;
2627  shared->mailbox->verbose = false;
2628  mutt_update_index(priv->menu, shared->ctx, check, oldcount, shared);
2629  shared->mailbox->verbose = verbose;
2630 
2631  priv->menu->max = shared->mailbox->vcount;
2632 
2633  /* If these header pointers don't match, then our email may have
2634  * been deleted. Make the pointer safe, then leave the pager.
2635  * This have a unpleasant behaviour to close the pager even the
2636  * deleted message is not the opened one, but at least it's safe. */
2637  e = mutt_get_virt_email(shared->mailbox, index);
2638  if (shared->email != e)
2639  {
2640  shared->email = e;
2641  break;
2642  }
2643  }
2644 
2646  OptSearchInvalid = true;
2647  }
2648  }
2649 
2650  if (mutt_mailbox_notify(shared->mailbox) || do_new_mail)
2651  {
2652  const bool c_beep_new = cs_subset_bool(NeoMutt->sub, "beep_new");
2653  if (c_beep_new)
2654  mutt_beep(true);
2655  const char *const c_new_mail_command =
2656  cs_subset_string(NeoMutt->sub, "new_mail_command");
2657  if (c_new_mail_command)
2658  {
2659  char cmd[1024];
2660  menu_status_line(cmd, sizeof(cmd), shared, priv->menu, sizeof(cmd),
2661  NONULL(c_new_mail_command));
2662  if (mutt_system(cmd) != 0)
2663  mutt_error(_("Error running \"%s\""), cmd);
2664  }
2665  }
2666  }
2667  //-------------------------------------------------------------------------
2668 
2669  if (SigWinch)
2670  {
2671  SigWinch = false;
2673  clearok(stdscr, true); /* force complete redraw */
2675 
2676  if (pview->flags & MUTT_PAGER_RETWINCH)
2677  {
2678  /* Store current position. */
2679  priv->lines = -1;
2680  for (size_t i = 0; i <= priv->topline; i++)
2681  if (!priv->line_info[i].continuation)
2682  priv->lines++;
2683 
2684  Resize = mutt_mem_malloc(sizeof(struct Resize));
2685 
2686  Resize->line = priv->lines;
2688  Resize->search_back = priv->search_back;
2689 
2690  op = -1;
2691  rc = OP_REFORMAT_WINCH;
2692  }
2693  else
2694  {
2695  /* note: mutt_resize_screen() -> mutt_window_reflow() sets
2696  * MENU_REDRAW_FULL and MENU_REDRAW_FLOW */
2697  op = 0;
2698  }
2699  continue;
2700  }
2701  //-------------------------------------------------------------------------
2702  // Finally, read user's key press
2703  //-------------------------------------------------------------------------
2704  // km_dokey() reads not only user's key strokes, but also a MacroBuffer
2705  // MacroBuffer may contain OP codes of the operations.
2706  // MacroBuffer is global
2707  // OP codes inserted into the MacroBuffer by various functions.
2708  // One of such functions is `mutt_enter_command()`
2709  // Some OP codes are not handled by pager, they cause pager to quit returning
2710  // OP code to index. Index handles the operation and then restarts pager
2711  op = km_dokey(MENU_PAGER);
2712 
2713  if (op >= 0)
2714  {
2715  mutt_clear_error();
2716  mutt_debug(LL_DEBUG1, "Got op %s (%d)\n", OpStrings[op][0], op);
2717  }
2719 
2720  if (op < 0)
2721  {
2722  op = 0;
2724  continue;
2725  }
2726 
2727  rc = op;
2728 
2729  switch (op)
2730  {
2731  //=======================================================================
2732 
2733  case OP_EXIT:
2734  rc = -1;
2735  op = -1;
2736  break;
2737 
2738  //=======================================================================
2739 
2740  case OP_QUIT:
2741  {
2742  const enum QuadOption c_quit = cs_subset_quad(NeoMutt->sub, "quit");
2743  if (query_quadoption(c_quit, _("Quit NeoMutt?")) == MUTT_YES)
2744  {
2745  /* avoid prompting again in the index menu */
2746  cs_subset_str_native_set(NeoMutt->sub, "quit", MUTT_YES, NULL);
2747  op = -1;
2748  }
2749  break;
2750  }
2751 
2752  //=======================================================================
2753 
2754  case OP_NEXT_PAGE:
2755  {
2756  const bool c_pager_stop = cs_subset_bool(NeoMutt->sub, "pager_stop");
2757  if (priv->line_info[priv->curline].offset < (priv->sb.st_size - 1))
2758  {
2759  const short c_pager_context =
2760  cs_subset_number(NeoMutt->sub, "pager_context");
2761  priv->topline = up_n_lines(c_pager_context, priv->line_info,
2762  priv->curline, priv->hide_quoted);
2763  }
2764  else if (c_pager_stop)
2765  {
2766  /* emulate "less -q" and don't go on to the next message. */
2767  mutt_message(_("Bottom of message is shown"));
2768  }
2769  else
2770  {
2771  /* end of the current message, so display the next message. */
2772  rc = OP_MAIN_NEXT_UNDELETED;
2773  op = -1;
2774  }
2775  break;
2776  }
2777 
2778  //=======================================================================
2779 
2780  case OP_PREV_PAGE:
2781  if (priv->topline == 0)
2782  {
2783  mutt_message(_("Top of message is shown"));
2784  }
2785  else
2786  {
2787  const short c_pager_context =
2788  cs_subset_number(NeoMutt->sub, "pager_context");
2789  priv->topline =
2790  up_n_lines(priv->pview->win_pager->state.rows - c_pager_context,
2791  priv->line_info, priv->topline, priv->hide_quoted);
2792  }
2793  break;
2794 
2795  //=======================================================================
2796 
2797  case OP_NEXT_LINE:
2798  if (priv->line_info[priv->curline].offset < (priv->sb.st_size - 1))
2799  {
2800  priv->topline++;
2801  if (priv->hide_quoted)
2802  {
2803  while ((priv->line_info[priv->topline].type == MT_COLOR_QUOTED) &&
2804  (priv->topline < priv->last_line))
2805  {
2806  priv->topline++;
2807  }
2808  }
2809  }
2810  else
2811  mutt_message(_("Bottom of message is shown"));
2812  break;
2813 
2814  //=======================================================================
2815 
2816  case OP_PREV_LINE:
2817  if (priv->topline)
2818  priv->topline = up_n_lines(1, priv->line_info, priv->topline, priv->hide_quoted);
2819  else
2820  mutt_message(_("Top of message is shown"));
2821  break;
2822 
2823  //=======================================================================
2824 
2825  case OP_PAGER_TOP:
2826  if (priv->topline)
2827  priv->topline = 0;
2828  else
2829  mutt_message(_("Top of message is shown"));
2830  break;
2831 
2832  //=======================================================================
2833 
2834  case OP_HALF_UP:
2835  if (priv->topline)
2836  {
2837  priv->topline = up_n_lines(priv->pview->win_pager->state.rows / 2 +
2838  (priv->pview->win_pager->state.rows % 2),
2839  priv->line_info, priv->topline, priv->hide_quoted);
2840  }
2841  else
2842  mutt_message(_("Top of message is shown"));
2843  break;
2844 
2845  //=======================================================================
2846 
2847  case OP_HALF_DOWN:
2848  {
2849  const bool c_pager_stop = cs_subset_bool(NeoMutt->sub, "pager_stop");
2850  if (priv->line_info[priv->curline].offset < (priv->sb.st_size - 1))
2851  {
2852  priv->topline = up_n_lines(priv->pview->win_pager->state.rows / 2,
2853  priv->line_info, priv->curline, priv->hide_quoted);
2854  }
2855  else if (c_pager_stop)
2856  {
2857  /* emulate "less -q" and don't go on to the next message. */
2858  mutt_message(_("Bottom of message is shown"));
2859  }
2860  else
2861  {
2862  /* end of the current message, so display the next message. */
2863  rc = OP_MAIN_NEXT_UNDELETED;
2864  op = -1;
2865  }
2866  break;
2867  }
2868 
2869  //=======================================================================
2870 
2871  case OP_SEARCH_NEXT:
2872  case OP_SEARCH_OPPOSITE:
2873  if (priv->search_compiled)
2874  {
2875  const short c_search_context =
2876  cs_subset_number(NeoMutt->sub, "search_context");
2877  wrapped = false;
2878 
2879  if (c_search_context < priv->pview->win_pager->state.rows)
2880  searchctx = c_search_context;
2881  else
2882  searchctx = 0;
2883 
2884  search_next:
2885  if ((!priv->search_back && (op == OP_SEARCH_NEXT)) ||
2886  (priv->search_back && (op == OP_SEARCH_OPPOSITE)))
2887  {
2888  /* searching forward */
2889  int i;
2890  for (i = wrapped ? 0 : priv->topline + searchctx + 1; i < priv->last_line; i++)
2891  {
2892  if ((!priv->hide_quoted || (priv->line_info[i].type != MT_COLOR_QUOTED)) &&
2893  !priv->line_info[i].continuation && (priv->line_info[i].search_cnt > 0))
2894  {
2895  break;
2896  }
2897  }
2898 
2899  const bool c_wrap_search =
2900  cs_subset_bool(NeoMutt->sub, "wrap_search");
2901  if (i < priv->last_line)
2902  priv->topline = i;
2903  else if (wrapped || !c_wrap_search)
2904  mutt_error(_("Not found"));
2905  else
2906  {
2907  mutt_message(_("Search wrapped to top"));
2908  wrapped = true;
2909  goto search_next;
2910  }
2911  }
2912  else
2913  {
2914  /* searching backward */
2915  int i;
2916  for (i = wrapped ? priv->last_line : priv->topline + searchctx - 1; i >= 0; i--)
2917  {
2918  if ((!priv->hide_quoted ||
2919  (priv->has_types && (priv->line_info[i].type != MT_COLOR_QUOTED))) &&
2920  !priv->line_info[i].continuation && (priv->line_info[i].search_cnt > 0))
2921  {
2922  break;
2923  }
2924  }
2925 
2926  const bool c_wrap_search =
2927  cs_subset_bool(NeoMutt->sub, "wrap_search");
2928  if (i >= 0)
2929  priv->topline = i;
2930  else if (wrapped || !c_wrap_search)
2931  mutt_error(_("Not found"));
2932  else
2933  {
2934  mutt_message(_("Search wrapped to bottom"));
2935  wrapped = true;
2936  goto search_next;
2937  }
2938  }
2939 
2940  if (priv->line_info[priv->topline].search_cnt > 0)
2941  {
2942  priv->search_flag = MUTT_SEARCH;
2943  /* give some context for search results */
2944  if (priv->topline - searchctx > 0)
2945  priv->topline -= searchctx;
2946  }
2947 
2948  break;
2949  }
2950  /* no previous search pattern */
2951  /* fallthrough */
2952  //=======================================================================
2953 
2954  case OP_SEARCH:
2955  case OP_SEARCH_REVERSE:
2956  mutt_str_copy(buf, priv->searchbuf, sizeof(buf));
2957  if (mutt_get_field(((op == OP_SEARCH) || (op == OP_SEARCH_NEXT)) ?
2958  _("Search for: ") :
2959  _("Reverse search for: "),
2960  buf, sizeof(buf), MUTT_CLEAR | MUTT_PATTERN, false,
2961  NULL, NULL) != 0)
2962  {
2963  break;
2964  }
2965 
2966  if (strcmp(buf, priv->searchbuf) == 0)
2967  {
2968  if (priv->search_compiled)
2969  {
2970  /* do an implicit search-next */
2971  if (op == OP_SEARCH)
2972  op = OP_SEARCH_NEXT;
2973  else
2974  op = OP_SEARCH_OPPOSITE;
2975 
2976  wrapped = false;
2977  goto search_next;
2978  }
2979  }
2980 
2981  if (buf[0] == '\0')
2982  break;
2983 
2984  mutt_str_copy(priv->searchbuf, buf, sizeof(priv->searchbuf));
2985 
2986  /* leave search_back alone if op == OP_SEARCH_NEXT */
2987  if (op == OP_SEARCH)
2988  priv->search_back = false;
2989  else if (op == OP_SEARCH_REVERSE)
2990  priv->search_back = true;
2991 
2992  if (priv->search_compiled)
2993  {
2994  regfree(&priv->search_re);
2995  for (size_t i = 0; i < priv->last_line; i++)
2996  {
2997  FREE(&(priv->line_info[i].search));
2998  priv->line_info[i].search_cnt = -1;
2999  }
3000  }
3001 
3002  uint16_t rflags = mutt_mb_is_lower(priv->searchbuf) ? REG_ICASE : 0;
3003  int err = REG_COMP(&priv->search_re, priv->searchbuf, REG_NEWLINE | rflags);
3004  if (err != 0)
3005  {
3006  regerror(err, &priv->search_re, buf, sizeof(buf));
3007  mutt_error("%s", buf);
3008  for (size_t i = 0; i < priv->max_line; i++)
3009  {
3010  /* cleanup */
3011  FREE(&(priv->line_info[i].search));
3012  priv->line_info[i].search_cnt = -1;
3013  }
3014  priv->search_flag = 0;
3015  priv->search_compiled = false;
3016  }
3017  else
3018  {
3019  priv->search_compiled = true;
3020  /* update the search pointers */
3021  int line_num = 0;
3022  while (display_line(priv->fp, &priv->last_pos, &priv->line_info,
3023  line_num, &priv->last_line, &priv->max_line,
3024  MUTT_SEARCH | (pview->flags & MUTT_PAGER_NSKIP) |
3025  (pview->flags & MUTT_PAGER_NOWRAP),
3026  &priv->quote_list, &priv->q_level, &priv->force_redraw,
3027  &priv->search_re, priv->pview->win_pager) == 0)
3028  {
3029  line_num++;
3030  }
3031 
3032  if (!priv->search_back)
3033  {
3034  /* searching forward */
3035  int i;
3036  for (i = priv->topline; i < priv->last_line; i++)
3037  {
3038  if ((!priv->hide_quoted || (priv->line_info[i].type != MT_COLOR_QUOTED)) &&
3039  !priv->line_info[i].continuation && (priv->line_info[i].search_cnt > 0))
3040  {
3041  break;
3042  }
3043  }
3044 
3045  if (i < priv->last_line)
3046  priv->topline = i;
3047  }
3048  else
3049  {
3050  /* searching backward */
3051  int i;
3052  for (i = priv->topline; i >= 0; i--)
3053  {
3054  if ((!priv->hide_quoted || (priv->line_info[i].type != MT_COLOR_QUOTED)) &&
3055  !priv->line_info[i].continuation && (priv->line_info[i].search_cnt > 0))
3056  {
3057  break;
3058  }
3059  }
3060 
3061  if (i >= 0)
3062  priv->topline = i;
3063  }
3064 
3065  if (priv->line_info[priv->topline].search_cnt == 0)
3066  {
3067  priv->search_flag = 0;
3068  mutt_error(_("Not found"));
3069  }
3070  else
3071  {
3072  const short c_search_context =
3073  cs_subset_number(NeoMutt->sub, "search_context");
3074  priv->search_flag = MUTT_SEARCH;
3075  /* give some context for search results */
3076  if (c_search_context < priv->pview->win_pager->state.rows)
3077  searchctx = c_search_context;
3078  else
3079  searchctx = 0;
3080  if (priv->topline - searchctx > 0)
3081  priv->topline -= searchctx;
3082  }
3083  }
3085  break;
3086 
3087  //=======================================================================
3088 
3089  case OP_SEARCH_TOGGLE:
3090  if (priv->search_compiled)
3091  {
3092  priv->search_flag ^= MUTT_SEARCH;
3094  }
3095  break;
3096 
3097  //=======================================================================
3098 
3099  case OP_SORT:
3100  case OP_SORT_REVERSE:
3101  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
3102  break;
3103  if (mutt_select_sort(op == OP_SORT_REVERSE))
3104  {
3105  OptNeedResort = true;
3106  op = -1;
3107  rc = OP_DISPLAY_MESSAGE;
3108  }
3109  break;
3110 
3111  //=======================================================================
3112 
3113  case OP_HELP:
3114  if (pview->mode == PAGER_MODE_HELP)
3115  {
3116  /* don't let the user enter the help-menu from the help screen! */
3117  mutt_error(_("Help is currently being shown"));
3118  break;
3119  }
3122  break;
3123 
3124  //=======================================================================
3125 
3126  case OP_PAGER_HIDE_QUOTED:
3127  if (!priv->has_types)
3128  break;
3129 
3130  priv->hide_quoted ^= MUTT_HIDE;
3131  if (priv->hide_quoted && (priv->line_info[priv->topline].type == MT_COLOR_QUOTED))
3132  priv->topline = up_n_lines(1, priv->line_info, priv->topline, priv->hide_quoted);
3133  else
3135  break;
3136 
3137  //=======================================================================
3138 
3139  case OP_PAGER_SKIP_QUOTED:
3140  {
3141  if (!priv->has_types)
3142  break;
3143 
3144  const short c_skip_quoted_context =
3145  cs_subset_number(NeoMutt->sub, "pager_skip_quoted_context");
3146  int dretval = 0;
3147  int new_topline = priv->topline;
3148  int num_quoted = 0;
3149 
3150  /* In a header? Skip all the email headers, and done */
3151  if (mutt_color_is_header(priv->line_info[new_topline].type))
3152  {
3153  while (((new_topline < priv->last_line) ||
3154  (0 == (dretval = display_line(
3155  priv->fp, &priv->last_pos, &priv->line_info,
3156  new_topline, &priv->last_line, &priv->max_line,
3157  MUTT_TYPES | (pview->flags & MUTT_PAGER_NOWRAP),
3158  &priv->quote_list, &priv->q_level, &priv->force_redraw,
3159  &priv->search_re, priv->pview->win_pager)))) &&
3160  mutt_color_is_header(priv->line_info[new_topline].type))
3161  {
3162  new_topline++;
3163  }
3164  priv->topline = new_topline;
3165  break;
3166  }
3167 
3168  /* Already in the body? Skip past previous "context" quoted lines */
3169  if (c_skip_quoted_context > 0)
3170  {
3171  while (((new_topline < priv->last_line) ||
3172  (0 == (dretval = display_line(
3173  priv->fp, &priv->last_pos, &priv->line_info,
3174  new_topline, &priv->last_line, &priv->max_line,
3175  MUTT_TYPES | (pview->flags & MUTT_PAGER_NOWRAP),
3176  &priv->quote_list, &priv->q_level, &priv->force_redraw,
3177  &priv->search_re, priv->pview->win_pager)))) &&
3178  (priv->line_info[new_topline].type == MT_COLOR_QUOTED))
3179  {
3180  new_topline++;
3181  num_quoted++;
3182  }
3183 
3184  if (dretval < 0)
3185  {
3186  mutt_error(_("No more unquoted text after quoted text"));
3187  break;
3188  }
3189  }
3190 
3191  if (num_quoted <= c_skip_quoted_context)
3192  {
3193  num_quoted = 0;
3194 
3195  while (((new_topline < priv->last_line) ||
3196  (0 == (dretval = display_line(
3197  priv->fp, &priv->last_pos, &priv->line_info,
3198  new_topline, &priv->last_line, &priv->max_line,
3199  MUTT_TYPES | (pview->flags & MUTT_PAGER_NOWRAP),
3200  &priv->quote_list, &priv->q_level, &priv->force_redraw,
3201  &priv->search_re, priv->pview->win_pager)))) &&
3202  (priv->line_info[new_topline].type != MT_COLOR_QUOTED))
3203  {
3204  new_topline++;
3205  }
3206 
3207  if (dretval < 0)
3208  {
3209  mutt_error(_("No more quoted text"));
3210  break;
3211  }
3212 
3213  while (((new_topline < priv->last_line) ||
3214  (0 == (dretval = display_line(
3215  priv->fp, &priv->last_pos, &priv->line_info,
3216  new_topline, &priv->last_line, &priv->max_line,
3217  MUTT_TYPES | (pview->flags & MUTT_PAGER_NOWRAP),
3218  &priv->quote_list, &priv->q_level, &priv->force_redraw,
3219  &priv->search_re, priv->pview->win_pager)))) &&
3220  (priv->line_info[new_topline].type == MT_COLOR_QUOTED))
3221  {
3222  new_topline++;
3223  num_quoted++;
3224  }
3225 
3226  if (dretval < 0)
3227  {
3228  mutt_error(_("No more unquoted text after quoted text"));
3229  break;
3230  }
3231  }
3232  priv->topline = new_topline - MIN(c_skip_quoted_context, num_quoted);
3233  break;
3234  }
3235 
3236  //=======================================================================
3237 
3238  case OP_PAGER_SKIP_HEADERS:
3239  {
3240  if (!priv->has_types)
3241  break;
3242 
3243  int dretval = 0;
3244  int new_topline = 0;
3245 
3246  while (((new_topline < priv->last_line) ||
3247  (0 == (dretval = display_line(
3248  priv->fp, &priv->last_pos, &priv->line_info, new_topline, &priv->last_line,
3249  &priv->max_line, MUTT_TYPES | (pview->flags & MUTT_PAGER_NOWRAP),
3250  &priv->quote_list, &priv->q_level, &priv->force_redraw,
3251  &priv->search_re, priv->pview->win_pager)))) &&
3252  mutt_color_is_header(priv->line_info[new_topline].type))
3253  {
3254  new_topline++;
3255  }
3256 
3257  if (dretval < 0)
3258  {
3259  /* L10N: Displayed if <skip-headers> is invoked in the pager, but
3260  there is no text past the headers.
3261  (I don't think this is actually possible in Mutt's code, but
3262  display some kind of message in case it somehow occurs.) */
3263  mutt_warning(_("No text past headers"));
3264  break;
3265  }
3266  priv->topline = new_topline;
3267  break;
3268  }
3269 
3270  //=======================================================================
3271 
3272  case OP_PAGER_BOTTOM: /* move to the end of the file */
3273  if (!jump_to_bottom(priv, pview))
3274  {
3275  mutt_message(_("Bottom of message is shown"));
3276  }
3277  break;
3278 
3279  //=======================================================================
3280 
3281  case OP_REDRAW:
3282  mutt_window_reflow(NULL);
3283  clearok(stdscr, true);
3285  break;
3286 
3287  //=======================================================================
3288 
3289  case OP_NULL:
3291  break;
3292 
3293  //=======================================================================
3294  // The following are operations on the current message rather than
3295  // adjusting the view of the message.
3296  //=======================================================================
3297  case OP_BOUNCE_MESSAGE:
3298  {
3299  if (!assert_pager_mode((pview->mode == PAGER_MODE_EMAIL) ||
3300  (pview->mode == PAGER_MODE_ATTACH_E)))
3301  {
3302  break;
3303  }
3305  break;
3306  if (pview->mode == PAGER_MODE_ATTACH_E)
3307  {
3308  mutt_attach_bounce(shared->mailbox, pview->pdata->fp,
3309  pview->pdata->actx, pview->pdata->body);
3310  }
3311  else
3312  {
3313  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3314  emaillist_add_email(&el, shared->email);
3315  ci_bounce_message(shared->mailbox, &el);
3316  emaillist_clear(&el);
3317  }
3318  break;
3319  }
3320 
3321  //=======================================================================
3322 
3323  case OP_RESEND:
3324  if (!assert_pager_mode((pview->mode == PAGER_MODE_EMAIL) ||
3325  (pview->mode == PAGER_MODE_ATTACH_E)))
3326  {
3327  break;
3328  }
3330  break;
3331  if (pview->mode == PAGER_MODE_ATTACH_E)
3332  {
3333  mutt_attach_resend(pview->pdata->fp, ctx_mailbox(shared->ctx),
3334  pview->pdata->actx, pview->pdata->body);
3335  }
3336  else
3337  {
3338  mutt_resend_message(NULL, ctx_mailbox(shared->ctx), shared->email,
3339  NeoMutt->sub);
3340  }
3342  break;
3343 
3344  //=======================================================================
3345 
3346  case OP_COMPOSE_TO_SENDER:
3347  if (!assert_pager_mode((pview->mode == PAGER_MODE_EMAIL) ||
3348  (pview->mode == PAGER_MODE_ATTACH_E)))
3349  {
3350  break;
3351  }
3353  break;
3354  if (pview->mode == PAGER_MODE_ATTACH_E)
3355  {
3356  mutt_attach_mail_sender(pview->pdata->fp, shared->email,
3357  pview->pdata->actx, pview->pdata->body);
3358  }
3359  else
3360  {
3361  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3362  emaillist_add_email(&el, shared->email);
3363 
3364  mutt_send_message(SEND_TO_SENDER, NULL, NULL,
3365  ctx_mailbox(shared->ctx), &el, NeoMutt->sub);
3366  emaillist_clear(&el);
3367  }
3369  break;
3370 
3371  //=======================================================================
3372 
3373  case OP_CHECK_TRADITIONAL:
3374  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
3375  break;
3376  if (!(WithCrypto & APPLICATION_PGP))
3377  break;
3378  if (!(shared->email->security & PGP_TRADITIONAL_CHECKED))
3379  {
3380  op = -1;
3381  rc = OP_CHECK_TRADITIONAL;
3382  }
3383  break;
3384 
3385  //=======================================================================
3386 
3387  case OP_CREATE_ALIAS:
3388  if (!assert_pager_mode((pview->mode == PAGER_MODE_EMAIL) ||
3389  (pview->mode == PAGER_MODE_ATTACH_E)))
3390  {
3391  break;
3392  }
3393  struct AddressList *al = NULL;
3394  if (pview->mode == PAGER_MODE_ATTACH_E)
3395  al = mutt_get_address(pview->pdata->body->email->env, NULL);
3396  else
3397  al = mutt_get_address(shared->email->env, NULL);
3398  alias_create(al, NeoMutt->sub);
3399  break;
3400 
3401  //=======================================================================
3402 
3403  case OP_PURGE_MESSAGE:
3404  case OP_DELETE:
3405  {
3406  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
3407  break;
3408  if (!assert_mailbox_writable(shared->mailbox))
3409  break;
3410  /* L10N: CHECK_ACL */
3412  _("Can't delete message")))
3413  {
3414  break;
3415  }
3416 
3417  mutt_set_flag(shared->mailbox, shared->email, MUTT_DELETE, true);
3418  mutt_set_flag(shared->mailbox, shared->email, MUTT_PURGE, (op == OP_PURGE_MESSAGE));
3419  const bool c_delete_untag =
3420  cs_subset_bool(NeoMutt->sub, "delete_untag");
3421  if (c_delete_untag)
3422  mutt_set_flag(shared->mailbox, shared->email, MUTT_TAG, false);
3424  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3425  if (c_resolve)
3426  {
3427  op = -1;
3428  rc = OP_MAIN_NEXT_UNDELETED;
3429  }
3430  break;
3431  }
3432 
3433  //=======================================================================
3434 
3435  case OP_MAIN_SET_FLAG:
3436  case OP_MAIN_CLEAR_FLAG:
3437  {
3438  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
3439  break;
3440  if (!assert_mailbox_writable(shared->mailbox))
3441  break;
3442 
3443  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3444  emaillist_add_email(&el, shared->email);
3445 
3446  if (mutt_change_flag(shared->mailbox, &el, (op == OP_MAIN_SET_FLAG)) == 0)
3448  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3449  if (shared->email->deleted && c_resolve)
3450  {
3451  op = -1;
3452  rc = OP_MAIN_NEXT_UNDELETED;
3453  }
3454  emaillist_clear(&el);
3455  break;
3456  }
3457 
3458  //=======================================================================
3459 
3460  case OP_DELETE_THREAD:
3461  case OP_DELETE_SUBTHREAD:
3462  case OP_PURGE_THREAD:
3463  {
3464  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
3465  break;
3466  if (!assert_mailbox_writable(shared->mailbox))
3467  break;
3468  /* L10N: CHECK_ACL */
3469  /* L10N: Due to the implementation details we do not know whether we
3470  delete zero, 1, 12, ... messages. So in English we use
3471  "messages". Your language might have other means to express this. */
3473  _("Can't delete messages")))
3474  {
3475  break;
3476  }
3477 
3478  int subthread = (op == OP_DELETE_SUBTHREAD);
3479  int r = mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_DELETE, 1, subthread);
3480  if (r == -1)
3481  break;
3482  if (op == OP_PURGE_THREAD)
3483  {
3484  r = mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_PURGE, true, subthread);
3485  if (r == -1)
3486  break;
3487  }
3488 
3489  const bool c_delete_untag =
3490  cs_subset_bool(NeoMutt->sub, "delete_untag");
3491  if (c_delete_untag)
3492  mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_TAG, 0, subthread);
3493  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3494  if (c_resolve)
3495  {
3496  rc = OP_MAIN_NEXT_UNDELETED;
3497  op = -1;
3498  }
3499 
3500  if (!c_resolve &&
3501  (cs_subset_number(NeoMutt->sub, "pager_index_lines") != 0))
3503  else
3505 
3506  break;
3507  }
3508 
3509  //=======================================================================
3510 
3511  case OP_DISPLAY_ADDRESS:
3512  if (!assert_pager_mode((pview->mode == PAGER_MODE_EMAIL) ||
3513  (pview->mode == PAGER_MODE_ATTACH_E)))
3514  {
3515  break;
3516  }
3517  if (pview->mode == PAGER_MODE_ATTACH_E)
3519  else
3520  mutt_display_address(shared->email->env);
3521  break;
3522 
3523  //=======================================================================
3524 
3525  case OP_ENTER_COMMAND:
3528 
3529  if (OptNeedResort)
3530  {
3531  OptNeedResort = false;
3532  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
3533  break;
3534  OptNeedResort = true;
3535  }
3536 
3537  if ((priv->redraw & MENU_REDRAW_FLOW) && (pview->flags & MUTT_PAGER_RETWINCH))
3538  {
3539  op = -1;
3540  rc = OP_REFORMAT_WINCH;
3541  continue;
3542  }
3543 
3544  op = 0;
3545  break;
3546 
3547  //=======================================================================
3548 
3549  case OP_FLAG_MESSAGE:
3550  {
3551  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
3552  break;
3553  if (!assert_mailbox_writable(shared->mailbox))
3554  break;
3555  /* L10N: CHECK_ACL */
3556  if (!assert_mailbox_permissions(shared->mailbox, MUTT_ACL_WRITE, "Can't flag message"))
3557  break;
3558 
3559  mutt_set_flag(shared->mailbox, shared->email, MUTT_FLAG, !shared->email->flagged);
3561  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3562  if (c_resolve)
3563  {
3564  op = -1;
3565  rc = OP_MAIN_NEXT_UNDELETED;
3566  }
3567  break;
3568  }
3569 
3570  //=======================================================================
3571 
3572  case OP_PIPE:
3573  if (!assert_pager_mode((pview->mode == PAGER_MODE_EMAIL) ||
3574  (pview->mode == PAGER_MODE_ATTACH)))
3575  {
3576  break;
3577  }
3578  if (pview->mode == PAGER_MODE_ATTACH)
3579  {
3580  mutt_pipe_attachment_list(pview->pdata->actx, pview->pdata->fp, false,
3581  pview->pdata->body, false);
3582  }
3583  else
3584  {
3585  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3586  el_add_tagged(&el, shared->ctx, shared->email, false);
3587  mutt_pipe_message(shared->mailbox, &el);
3588  emaillist_clear(&el);
3589  }
3590  break;
3591 
3592  //=======================================================================
3593 
3594  case OP_PRINT:
3595  if (!assert_pager_mode((pview->mode == PAGER_MODE_EMAIL) ||
3596  (pview->mode == PAGER_MODE_ATTACH)))
3597  {
3598  break;
3599  }
3600  if (pview->mode == PAGER_MODE_ATTACH)
3601  {
3602  mutt_print_attachment_list(pview->pdata->actx, pview->pdata->fp,
3603  false, pview->pdata->body);
3604  }
3605  else
3606  {
3607  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3608  el_add_tagged(&el, shared->ctx, shared->email, false);
3609  mutt_print_message(shared->mailbox, &el);
3610  emaillist_clear(&el);
3611  }
3612  break;
3613 
3614  //=======================================================================
3615 
3616  case OP_MAIL:
3617  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
3618  break;
3620  break;
3621 
3622  mutt_send_message(SEND_NO_FLAGS, NULL, NULL, ctx_mailbox(shared->ctx),
3623  NULL, NeoMutt->sub);
3625  break;
3626 
3627  //=======================================================================
3628 
3629 #ifdef USE_NNTP
3630  case OP_POST:
3631  {
3632  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
3633  break;
3635  break;
3636  const enum QuadOption c_post_moderated =
3637  cs_subset_quad(NeoMutt->sub, "post_moderated");
3638  if ((shared->mailbox->type == MUTT_NNTP) &&
3639  !((struct NntpMboxData *) shared->mailbox->mdata)->allowed && (query_quadoption(c_post_moderated, _("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES))
3640  {
3641  break;
3642  }
3643 
3644  mutt_send_message(SEND_NEWS, NULL, NULL, ctx_mailbox(shared->ctx), NULL,
3645  NeoMutt->sub);
3647  break;
3648  }
3649 
3650  //=======================================================================
3651 
3652  case OP_FORWARD_TO_GROUP:
3653  {
3654  if (!assert_pager_mode((pview->mode == PAGER_MODE_EMAIL) ||
3655  (pview->mode == PAGER_MODE_ATTACH_E)))
3656  {
3657  break;
3658  }
3660  break;
3661  const enum QuadOption c_post_moderated =
3662  cs_subset_quad(NeoMutt->sub, "post_moderated");
3663  if ((shared->mailbox->type == MUTT_NNTP) &&
3664  !((struct NntpMboxData *) shared->mailbox->mdata)->allowed && (query_quadoption(c_post_moderated, _("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES))
3665  {
3666  break;
3667  }
3668  if (pview->mode == PAGER_MODE_ATTACH_E)
3669  {
3670  mutt_attach_forward(pview->pdata->fp, shared->email,
3671  pview->pdata->actx, pview->pdata->body, SEND_NEWS);
3672  }
3673  else
3674  {
3675  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3676  emaillist_add_email(&el, shared->email);
3677 
3679  ctx_mailbox(shared->ctx), &el, NeoMutt->sub);
3680  emaillist_clear(&el);
3681  }
3683  break;
3684  }
3685 
3686  //=======================================================================
3687 
3688  case OP_FOLLOWUP:
3689  if (!assert_pager_mode((pview->mode == PAGER_MODE_EMAIL) ||
3690  (pview->mode == PAGER_MODE_ATTACH_E)))
3691  {
3692  break;
3693  }
3695  break;
3696 
3697  char *followup_to = NULL;
3698  if (pview->mode == PAGER_MODE_ATTACH_E)
3699  followup_to = pview->pdata->body->email->env->followup_to;
3700  else
3701  followup_to = shared->email->env->followup_to;
3702 
3703  const enum QuadOption c_followup_to_poster =
3704  cs_subset_quad(NeoMutt->sub, "followup_to_poster");
3705  if (!followup_to || !mutt_istr_equal(followup_to, "poster") ||
3706  (query_quadoption(c_followup_to_poster,
3707  _("Reply by mail as poster prefers?")) != MUTT_YES))
3708  {
3709  const enum QuadOption c_post_moderated =
3710  cs_subset_quad(NeoMutt->sub, "post_moderated");
3711  if ((shared->mailbox->type == MUTT_NNTP) &&
3712  !((struct NntpMboxData *) shared->mailbox->mdata)->allowed && (query_quadoption(c_post_moderated, _("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES))
3713  {
3714  break;
3715  }
3716  if (pview->mode == PAGER_MODE_ATTACH_E)
3717  {
3718  mutt_attach_reply(pview->pdata->fp, shared->mailbox, shared->email,
3719  pview->pdata->actx, pview->pdata->body, SEND_NEWS | SEND_REPLY);
3720  }
3721  else
3722  {
3723  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3724  emaillist_add_email(&el, shared->email);
3725  mutt_send_message(SEND_NEWS | SEND_REPLY, NULL, NULL,
3726  ctx_mailbox(shared->ctx), &el, NeoMutt->sub);
3727  emaillist_clear(&el);
3728  }
3730  break;
3731  }
3732 
3733  //=======================================================================
3734 
3735 #endif
3736  /* fallthrough */
3737  case OP_REPLY:
3738  case OP_GROUP_REPLY:
3739  case OP_GROUP_CHAT_REPLY:
3740  case OP_LIST_REPLY:
3741  {
3742  if (!assert_pager_mode((pview->mode == PAGER_MODE_EMAIL) ||
3743  (pview->mode == PAGER_MODE_ATTACH_E)))
3744  {
3745  break;
3746  }
3748  break;
3749 
3750  SendFlags replyflags = SEND_REPLY;
3751  if (op == OP_GROUP_REPLY)
3752  replyflags |= SEND_GROUP_REPLY;
3753  else if (op == OP_GROUP_CHAT_REPLY)
3754  replyflags |= SEND_GROUP_CHAT_REPLY;
3755  else if (op == OP_LIST_REPLY)
3756  replyflags |= SEND_LIST_REPLY;
3757 
3758  if (pview->mode == PAGER_MODE_ATTACH_E)
3759  {
3760  mutt_attach_reply(pview->pdata->fp, shared->mailbox, shared->email,
3761  pview->pdata->actx, pview->pdata->body, replyflags);
3762  }
3763  else
3764  {
3765  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3766  emaillist_add_email(&el, shared->email);
3767  mutt_send_message(replyflags, NULL, NULL, ctx_mailbox(shared->ctx),
3768  &el, NeoMutt->sub);
3769  emaillist_clear(&el);
3770  }
3772  break;
3773  }
3774 
3775  //=======================================================================
3776 
3777  case OP_RECALL_MESSAGE:
3778  {
3779  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
3780  break;
3782  break;
3783  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3784  emaillist_add_email(&el, shared->email);
3785 
3786  mutt_send_message(SEND_POSTPONED, NULL, NULL, ctx_mailbox(shared->ctx),
3787  &el, NeoMutt->sub);
3788  emaillist_clear(&el);
3790  break;
3791  }
3792 
3793  //=======================================================================
3794 
3795  case OP_FORWARD_MESSAGE:
3796  if (!assert_pager_mode((pview->mode == PAGER_MODE_EMAIL) ||
3797  (pview->mode == PAGER_MODE_ATTACH_E)))
3798  {
3799  break;
3800  }
3802  break;
3803  if (pview->mode == PAGER_MODE_ATTACH_E)
3804  {
3805  mutt_attach_forward(pview->pdata->fp, shared->email, pview->pdata->actx,
3806  pview->pdata->body, SEND_NO_FLAGS);
3807  }
3808  else
3809  {
3810  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3811  emaillist_add_email(&el, shared->email);
3812 
3813  mutt_send_message(SEND_FORWARD, NULL, NULL, ctx_mailbox(shared->ctx),
3814  &el, NeoMutt->sub);
3815  emaillist_clear(&el);
3816  }
3818  break;
3819 
3820  //=======================================================================
3821 
3822  case OP_DECRYPT_SAVE:
3823  if (!WithCrypto)
3824  {
3825  op = -1;
3826  break;
3827  }
3828  /* fallthrough */
3829  //=======================================================================
3830 
3831  case OP_SAVE:
3832  if (pview->mode == PAGER_MODE_ATTACH)
3833  {
3834  mutt_save_attachment_list(pview->pdata->actx, pview->pdata->fp, false,
3835  pview->pdata->body, shared->email, NULL);
3836  break;
3837  }
3838  /* fallthrough */
3839  //=======================================================================
3840 
3841  case OP_COPY_MESSAGE:
3842  case OP_DECODE_SAVE:
3843  case OP_DECODE_COPY:
3844  case OP_DECRYPT_COPY:
3845  {
3846  if (!(WithCrypto != 0) && (op == OP_DECRYPT_COPY))
3847  {
3848  op = -1;
3849  break;
3850  }
3851  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
3852  break;
3853  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3854  emaillist_add_email(&el, shared->email);
3855 
3856  const enum MessageSaveOpt save_opt =
3857  ((op == OP_SAVE) || (op == OP_DECODE_SAVE) || (op == OP_DECRYPT_SAVE)) ?
3858  SAVE_MOVE :
3859  SAVE_COPY;
3860 
3861  enum MessageTransformOpt transform_opt =
3862  ((op == OP_DECODE_SAVE) || (op == OP_DECODE_COPY)) ? TRANSFORM_DECODE :
3863  ((op == OP_DECRYPT_SAVE) || (op == OP_DECRYPT_COPY)) ? TRANSFORM_DECRYPT :
3865 
3866  const int rc2 = mutt_save_message(shared->mailbox, &el, save_opt, transform_opt);
3867  if ((rc2 == 0) && (save_opt == SAVE_MOVE))
3868  {
3869  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3870  if (c_resolve)
3871  {
3872  op = -1;
3873  rc = OP_MAIN_NEXT_UNDELETED;
3874  }
3875  else
3877  }
3878  emaillist_clear(&el);
3879  break;
3880  }
3881 
3882  //=======================================================================
3883 
3884  case OP_SHELL_ESCAPE:
3885  if (mutt_shell_escape())
3886  {
3888  }
3889  break;
3890 
3891  //=======================================================================
3892 
3893  case OP_TAG:
3894  {
3895  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
3896  break;
3897  mutt_set_flag(shared->mailbox, shared->email, MUTT_TAG, !shared->email->tagged);
3898 
3900  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3901  if (c_resolve)
3902  {
3903  op = -1;
3904  rc = OP_NEXT_ENTRY;
3905  }
3906  break;
3907  }
3908 
3909  //=======================================================================
3910 
3911  case OP_TOGGLE_NEW:
3912  {
3913  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
3914  break;
3915  if (!assert_mailbox_writable(shared->mailbox))
3916  break;
3917  /* L10N: CHECK_ACL */
3918  if (!assert_mailbox_permissions(shared->mailbox, MUTT_ACL_SEEN, _("Can't toggle new")))
3919  break;
3920 
3921  if (shared->email->read || shared->email->old)
3922  mutt_set_flag(shared->mailbox, shared->email, MUTT_NEW, true);
3923  else if (!first || (delay_read_timestamp != 0))
3924  mutt_set_flag(shared->mailbox, shared->email, MUTT_READ, true);
3925  delay_read_timestamp = 0;
3926  first = false;
3927  shared->ctx->msg_in_pager = -1;
3928  priv->win_pbar->actions |= WA_RECALC;
3930  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3931  if (c_resolve)
3932  {
3933  op = -1;
3934  rc = OP_MAIN_NEXT_UNDELETED;
3935  }
3936  break;
3937  }
3938 
3939  //=======================================================================
3940 
3941  case OP_UNDELETE:
3942  {
3943  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
3944  break;
3945  if (!assert_mailbox_writable(shared->mailbox))
3946  break;
3947  /* L10N: CHECK_ACL */
3949  _("Can't undelete message")))
3950  {
3951  break;
3952  }
3953 
3954  mutt_set_flag(shared->mailbox, shared->email, MUTT_DELETE, false);
3955  mutt_set_flag(shared->mailbox, shared->email, MUTT_PURGE, false);
3957  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3958  if (c_resolve)
3959  {
3960  op = -1;
3961  rc = OP_NEXT_ENTRY;
3962  }
3963  break;
3964  }
3965 
3966  //=======================================================================
3967 
3968  case OP_UNDELETE_THREAD:
3969  case OP_UNDELETE_SUBTHREAD:
3970  {
3971  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
3972  break;
3973  if (!assert_mailbox_writable(shared->mailbox))
3974  break;
3975  /* L10N: CHECK_ACL */
3976  /* L10N: Due to the implementation details we do not know whether we
3977  undelete zero, 1, 12, ... messages. So in English we use
3978  "messages". Your language might have other means to express this. */
3980  _("Can't undelete messages")))
3981  {
3982  break;
3983  }
3984 
3985  int r = mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_DELETE,
3986  false, (op != OP_UNDELETE_THREAD));
3987  if (r != -1)
3988  {
3989  r = mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_PURGE,
3990  false, (op != OP_UNDELETE_THREAD));
3991  }
3992  if (r != -1)
3993  {
3994  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
3995  if (c_resolve)
3996  {
3997  rc = (op == OP_DELETE_THREAD) ? OP_MAIN_NEXT_THREAD : OP_MAIN_NEXT_SUBTHREAD;
3998  op = -1;
3999  }
4000 
4001  if (!c_resolve &&
4002  (cs_subset_number(NeoMutt->sub, "pager_index_lines") != 0))
4004  else
4006  }
4007  break;
4008  }
4009 
4010  //=======================================================================
4011 
4012  case OP_VERSION:
4014  break;
4015 
4016  //=======================================================================
4017 
4018  case OP_MAILBOX_LIST:
4020  break;
4021 
4022  //=======================================================================
4023 
4024  case OP_VIEW_ATTACHMENTS:
4025  if (pview->flags & MUTT_PAGER_ATTACHMENT)
4026  {
4027  op = -1;
4028  rc = OP_ATTACH_COLLAPSE;
4029  break;
4030  }
4031  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
4032  break;
4034  pview->pdata->fp);
4035  if (shared->email->attach_del)
4036  shared->mailbox->changed = true;
4038  break;
4039 
4040  //=======================================================================
4041 
4042  case OP_MAIL_KEY:
4043  {
4044  if (!(WithCrypto & APPLICATION_PGP))
4045  {
4046  op = -1;
4047  break;
4048  }
4049  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
4050  break;
4052  break;
4053  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
4054  emaillist_add_email(&el, shared->email);
4055 
4056  mutt_send_message(SEND_KEY, NULL, NULL, ctx_mailbox(shared->ctx), &el,
4057  NeoMutt->sub);
4058  emaillist_clear(&el);
4060  break;
4061  }
4062 
4063  //=======================================================================
4064 
4065  case OP_EDIT_LABEL:
4066  {
4067  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
4068  break;
4069 
4070  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
4071  emaillist_add_email(&el, shared->email);
4072  rc = mutt_label_message(shared->mailbox, &el);
4073  emaillist_clear(&el);
4074 
4075  if (rc > 0)
4076  {
4077  shared->mailbox->changed = true;
4079  mutt_message(ngettext("%d label changed", "%d labels changed", rc), rc);
4080  }
4081  else
4082  {
4083  mutt_message(_("No labels changed"));
4084  }
4085  break;
4086  }
4087 
4088  //=======================================================================
4089 
4090  case OP_FORGET_PASSPHRASE:
4092  break;
4093 
4094  //=======================================================================
4095 
4096  case OP_EXTRACT_KEYS:
4097  {
4098  if (!WithCrypto)
4099  {
4100  op = -1;
4101  break;
4102  }
4103  if (!assert_pager_mode(pview->mode == PAGER_MODE_EMAIL))
4104  break;
4105  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
4106  emaillist_add_email(&el, shared->email);
4108  emaillist_clear(&el);
4110  break;
4111  }
4112 
4113  //=======================================================================
4114 
4115  case OP_WHAT_KEY:
4116  mutt_what_key();
4117  break;
4118 
4119  //=======================================================================
4120 
4121  case OP_CHECK_STATS:
4122  mutt_check_stats(shared->mailbox);
4123  break;
4124 
4125  //=======================================================================
4126 
4127 #ifdef USE_SIDEBAR
4128  case OP_SIDEBAR_FIRST:
4129  case OP_SIDEBAR_LAST:
4130  case OP_SIDEBAR_NEXT:
4131  case OP_SIDEBAR_NEXT_NEW:
4132  case OP_SIDEBAR_PAGE_DOWN:
4133  case OP_SIDEBAR_PAGE_UP:
4134  case OP_SIDEBAR_PREV:
4135  case OP_SIDEBAR_PREV_NEW:
4136  {
4137  struct MuttWindow *win_sidebar = window_find_child(dlg, WT_SIDEBAR);
4138  if (!win_sidebar)
4139  break;
4140  sb_change_mailbox(win_sidebar, op);
4141  break;
4142  }
4143 
4144  //=======================================================================
4145 
4146  case OP_SIDEBAR_TOGGLE_VISIBLE:
4147  bool_str_toggle(NeoMutt->sub, "sidebar_visible", NULL);
4148  mutt_window_reflow(dlg);
4149  break;
4150 
4151  //=======================================================================
4152 #endif
4153 
4154  default:
4155  op = -1;
4156  break;
4157  }
4158  }
4159  //-------------------------------------------------------------------------
4160  // END OF ACT 3: Read user input loop - while (op != -1)
4161  //-------------------------------------------------------------------------
4162 
4163  if (check_read_delay(&delay_read_timestamp))
4164  {
4165  mutt_set_flag(shared->mailbox, shared->email, MUTT_READ, true);
4166  }
4167  mutt_file_fclose(&priv->fp);
4168  if (pview->mode == PAGER_MODE_EMAIL)
4169  {
4170  shared->ctx->msg_in_pager = -1;
4171  priv->win_pbar->actions |= WA_RECALC;
4172  switch (rc)
4173  {
4174  case -1:
4175  case OP_DISPLAY_HEADERS:
4177  break;
4178  default:
4179  TopLine = priv->topline;
4180  OldEmail = shared->email;
4181  break;
4182  }
4183  }
4184 
4185  cleanup_quote(&priv->quote_list);
4186 
4187  for (size_t i = 0; i < priv->max_line; i++)
4188  {
4189  FREE(&(priv->line_info[i].syntax));
4190  if (priv->search_compiled && priv->line_info[i].search)
4191  FREE(&(priv->line_info[i].search));
4192  }
4193  if (priv->search_compiled)
4194  {
4195  regfree(&priv->search_re);
4196  priv->search_compiled = false;
4197  }
4198  FREE(&priv->line_info);
4199 
4200  if (priv->pview->win_index)
4201  {
4206  window_set_visible(priv->pview->win_index->parent, true);
4207  }
4208  window_set_visible(priv->pview->win_pager->parent, false);
4209  mutt_window_reflow(dlg);
4210 
4211  return (rc != -1) ? rc : 0;
4212 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
static bool assert_pager_mode(bool test)
Check that pager is in correct mode.
Definition: dlg_pager.c:241
The "current" mailbox.
Definition: context.h:37
struct MuttWindow * window_find_child(struct MuttWindow *win, enum WindowType type)
Recursively find a child Window of a given type.
Definition: mutt_window.c:550
struct PagerData * pdata
Data that pager displays. NOTNULL.
Definition: lib.h:156
#define MUTT_PAGER_RETWINCH
Need reformatting on SIGWINCH.
Definition: lib.h:65
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
Decrypt message.
Definition: commands.h:42
bool mutt_mailbox_list(void)
List the mailboxes with new mail.
Definition: mutt_mailbox.c:222
#define NONULL(x)
Definition: string2.h:37
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
int msg_count
Total number of messages.
Definition: mailbox.h:91
static int up_n_lines(int nlines, struct Line *info, int cur, bool hiding)
Reposition the pager&#39;s view up by n lines.
Definition: dlg_pager.c:1980
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:66
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
uint64_t mutt_date_epoch_ms(void)
Return the number of milliseconds since the Unix epoch.
Definition: date.c:436
#define WithCrypto
Definition: lib.h:113
#define SEND_TO_SENDER
Compose new email to sender.
Definition: send.h:51
int msg_in_pager
Message currently shown in the pager.
Definition: context.h:43
void mutt_pipe_message(struct Mailbox *m, struct EmailList *el)
Pipe a message.
Definition: commands.c:760
struct stat sb
Definition: private_data.h:67
A default and invalid mode, should never be used.
Definition: lib.h:129
The envelope/body of an email.
Definition: email.h:37
#define MUTT_CLEAR
Clear input if printable character is pressed.
Definition: mutt.h:58
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: context.c:412
#define MIN(a, b)
Definition: memory.h:31
Definition: lib.h:67
#define MUTT_PAGER_NSKIP
Preserve whitespace with smartwrap.
Definition: lib.h:63
static void cleanup_quote(struct QClass **quote_list)
Free a quote list.
Definition: dlg_pager.c:592
struct Mailbox * ctx_mailbox(struct Context *ctx)
wrapper to get the mailbox in a Context, or NULL
Definition: context.c:444
void mutt_clear_pager_position(void)
Reset the pager&#39;s viewing position.
Definition: dlg_pager.c:1995
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:52
struct Email * email
Currently selected Email.
Definition: shared_data.h:42
#define mutt_error(...)
Definition: logging.h:88
static int braille_col
Definition: dlg_pager.c:170
bool mutt_color_is_header(enum ColorId color_id)
Colour is for an Email header.
Definition: color.c:1560
int help_menu
Menu for key bindings, e.g. MENU_PAGER.
Definition: mutt_window.h:141
int mutt_get_field(const char *field, char *buf, size_t buflen, CompletionFlags complete, bool multiple, char ***files, int *numfiles)
Ask the user for a string.
Definition: curs_lib.c:335
void mutt_display_address(struct Envelope *env)
Display the address of a message.
Definition: commands.c:1026
bool mutt_shell_escape(void)
invoke a command in a subshell
Definition: commands.c:943
void mutt_update_index(struct Menu *menu, struct Context *ctx, enum MxStatus check, int oldcount, struct IndexSharedData *shared)
Update the index.
Definition: dlg_index.c:541
PagerFlags hide_quoted
Definition: private_data.h:55
struct Body * body
Current attachment.
Definition: lib.h:145
void mutt_save_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top, struct Email *e, struct Menu *menu)
Save a list of attachments.
Definition: recvattach.c:788
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:49
#define SEND_FORWARD
Forward email.
Definition: send.h:43
void mutt_resize_screen(void)
Update NeoMutt&#39;s opinion about the window size (CURSES)
Definition: resize.c:101
WHERE bool OptNeedResort
(pseudo) used to force a re-sort
Definition: options.h:42
void crypt_forget_passphrase(void)
Forget a passphrase and display a message.
Definition: crypt.c:93
No transformation.
Definition: commands.h:41
void mutt_enter_command(void)
enter a neomutt command
Definition: commands.c:978
Nondestructive flags change (IMAP)
Definition: mxapi.h:82
#define MUTT_HIDE
Don&#39;t show quoted text.
Definition: lib.h:57
New mail received in Mailbox.
Definition: mxapi.h:79
struct MuttWindow * window_set_focus(struct MuttWindow *win)
Set the Window focus.
Definition: mutt_window.c:680
Flagged messages.
Definition: mutt.h:98
#define MUTT_SHOWFLAT
Show characters (used for displaying help)
Definition: lib.h:55
#define _(a)
Definition: message.h:28
Mailbox wasn&#39;t recognised.
Definition: mailbox.h:47
static struct Email * OldEmail
Definition: dlg_pager.c:167
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:54
void window_redraw(struct MuttWindow *win)
Reflow, recalc and repaint a tree of Windows.
Definition: mutt_window.c:632
Data shared between Index, Pager and Sidebar.
Definition: shared_data.h:36
Messages to be purged (bypass trash)
Definition: mutt.h:96
static int braille_row
Definition: dlg_pager.c:169
void emaillist_clear(struct EmailList *el)
Drop a private list of Emails.
Definition: email.c:138
static bool assert_mailbox_permissions(struct Mailbox *m, AclFlags acl, char *action)
checks that mailbox is has requested acl flags set
Definition: dlg_pager.c:300
bool search_back
Definition: dlg_pager.c:162
Copy message, making a duplicate in another mailbox.
Definition: commands.h:51
int indicator
the indicator line of the PI
Definition: private_data.h:46
bool search_compiled
Definition: dlg_pager.c:161
short type
Definition: dlg_pager.c:134
struct QClass * quote_list
Definition: private_data.h:57
uint16_t SendFlags
Flags for mutt_send_message(), e.g. SEND_REPLY.
Definition: send.h:36
static int display_line(FILE *fp, LOFF_T *last_pos, struct Line **line_info, int n, int *last, int *max, PagerFlags flags, struct QClass **quote_list, int *q_level, bool *force_redraw, regex_t *search_re, struct MuttWindow *win_pager)
Print a line on screen.
Definition: dlg_pager.c:1692
Pager: quoted text.
Definition: color.h:62
A special case of PAGER_MODE_ATTACH - attachment is a full-blown email message.
Definition: lib.h:133
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
#define SEND_POSTPONED
Recall a postponed email.
Definition: send.h:44
void mutt_help(enum MenuType menu)
Display the help menu.
Definition: help.c:389
void msgwin_clear_text(void)
Clear the text in the Message Window.
Definition: msgwin.c:242
#define mutt_perror(...)
Definition: logging.h:89
struct MuttWindow * win_index
Definition: lib.h:162
Container for Accounts, Notifications.
Definition: neomutt.h:36
#define MUTT_PATTERN
Pattern mode - only used for history classes.
Definition: mutt.h:60
#define MUTT_SEARCH
Resolve search patterns.
Definition: lib.h:58
#define MUTT_ACL_DELETE
Delete a message.
Definition: mailbox.h:66
int vcount
The number of virtual messages.
Definition: mailbox.h:102
Mailbox was reopened.
Definition: mxapi.h:81
QuadOption
Possible values for a quad-option.
Definition: quad.h:35
int mutt_window_move(struct MuttWindow *win, int col, int row)
Move the cursor in a Window.
Definition: mutt_window.c:310
void mutt_print_message(struct Mailbox *m, struct EmailList *el)
Print a message.
Definition: commands.c:791
static bool check_read_delay(uint64_t *timestamp)
Is it time to mark the message read?
Definition: dlg_pager.c:2329
#define SEND_NO_FLAGS
No flags are set.
Definition: send.h:39
Private state data for the Pager.
Definition: private_data.h:39
void mutt_beep(bool force)
Irritate the user.
Definition: curs_lib.c:105
#define MAX(a, b)
Definition: memory.h:30
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition: subset.c:305
int line
Definition: dlg_pager.c:160
enum MxStatus mx_mbox_check(struct Mailbox *m)
Check for new mail - Wrapper for MxOps::mbox_check()
Definition: mx.c:1119
WindowActionFlags actions
Actions to be performed, e.g. WA_RECALC.
Definition: mutt_window.h:132
Pager pager (email viewer)
Definition: type.h:54
bool tagged
Email is tagged.
Definition: email.h:44
bool read
Email is read.
Definition: email.h:51
void mutt_print_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top)
Print a list of Attachments.
Definition: recvattach.c:1240
void mutt_curses_set_cursor(enum MuttCursorState state)
Set the cursor state.
Definition: mutt_curses.c:71
#define MUTT_PAGER_BOTTOM
Start at the bottom.
Definition: lib.h:69
bool old
Email is seen, but unread.
Definition: email.h:50
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
void mutt_what_key(void)
Ask the user to press a key.
Definition: keymap.c:1718
struct Envelope * env
Envelope information.
Definition: email.h:90
Display a normal cursor.
Definition: mutt_curses.h:81
void km_error_key(enum MenuType mtype)
Handle an unbound key sequence.
Definition: keymap.c:1144
struct Menu * menu
Definition: private_data.h:41
void alias_create(struct AddressList *al, const struct ConfigSubset *sub)
Create a new Alias from an Address.
Definition: alias.c:372
bool mutt_mailbox_notify(struct Mailbox *m_cur)
Notify the user if there&#39;s new mail.
Definition: mutt_mailbox.c:209
short continuation
Definition: dlg_pager.c:135
int mutt_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile, struct Mailbox *m, struct EmailList *el, struct ConfigSubset *sub)
Send an email.
Definition: send.c:2125