NeoMutt  2018-07-16 +952-a2da0a
Teaching an old dog new tricks
DOXYGEN
pager.c File Reference

GUI display a file/email/help in a viewport with paging. More...

#include "config.h"
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <regex.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <wchar.h>
#include "mutt/mutt.h"
#include "config/lib.h"
#include "email/lib.h"
#include "mutt.h"
#include "pager.h"
#include "alias.h"
#include "color.h"
#include "commands.h"
#include "context.h"
#include "curs_lib.h"
#include "format_flags.h"
#include "globals.h"
#include "hdrline.h"
#include "hook.h"
#include "index.h"
#include "keymap.h"
#include "mailbox.h"
#include "menu.h"
#include "mutt_attach.h"
#include "mutt_curses.h"
#include "mutt_header.h"
#include "mutt_logging.h"
#include "mutt_window.h"
#include "muttlib.h"
#include "mx.h"
#include "ncrypt/ncrypt.h"
#include "opcodes.h"
#include "options.h"
#include "protos.h"
#include "recvattach.h"
#include "recvcmd.h"
#include "send.h"
#include "status.h"
#include "terminal.h"
#include "sidebar.h"
#include "nntp/nntp.h"
#include <libintl.h>
+ Include dependency graph for pager.c:

Go to the source code of this file.

Data Structures

struct  QClass
 Style of quoted text. More...
 
struct  Syntax
 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...
 
struct  PagerRedrawData
 Keep track when the pager needs redrawing. More...
 

Macros

#define ISHEADER(x)   ((x) == MT_COLOR_HEADER || (x) == MT_COLOR_HDEFAULT)
 
#define IsAttach(x)   (x && (x)->bdy)
 
#define IsMsgAttach(x)   (x && (x)->fp && (x)->bdy && (x)->bdy->email)
 
#define IsHeader(x)   (x && (x)->email && !(x)->bdy)
 
#define CHECK_MODE(x)
 
#define CHECK_READONLY
 
#define CHECK_ATTACH
 
#define CHECK_ACL(aclbit, action)
 
#define ANSI_OFF   (1 << 0)
 
#define ANSI_BLINK   (1 << 1)
 
#define ANSI_BOLD   (1 << 2)
 
#define ANSI_UNDERLINE   (1 << 3)
 
#define ANSI_REVERSE   (1 << 4)
 
#define ANSI_COLOR   (1 << 5)
 
#define NUM_SIG_LINES   4
 

Functions

static int check_sig (const char *s, struct Line *info, int n)
 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 Line *line_info, int n, int cnt, int 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 new_class_color (struct QClass *class, 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_attachment_marker (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 (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 (unsigned char *buf)
 Is this an ANSI escape sequence? More...
 
static int grok_ansi (unsigned char *buf, int pos, struct AnsiAttr *a)
 Parse an ANSI escape sequence. More...
 
static int fill_buffer (FILE *f, 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 Line **line_info, int n, unsigned char *buf, int flags, struct AnsiAttr *pa, int cnt, int *pspace, int *pvch, int *pcol, int *pspecial, struct MuttWindow *pager_window)
 Display a line of text in the pager. More...
 
static int display_line (FILE *f, LOFF_T *last_pos, struct Line **line_info, int n, int *last, int *max, int flags, struct QClass **quote_list, int *q_level, bool *force_redraw, regex_t *search_re, struct MuttWindow *pager_window)
 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...
 
static void pager_custom_redraw (struct Menu *pager_menu)
 Redraw the pager window - Implements Menu::menu_custom_redraw() More...
 
int mutt_pager (const char *banner, const char *fname, int flags, struct Pager *extra)
 Display a file, or help, in a window. More...
 

Variables

bool AllowAnsi
 Config: Allow ANSI colour codes in rich text messages. More...
 
bool HeaderColorPartial
 Config: Only colour the part of the header matching the regex. More...
 
short PagerContext
 Config: Number of lines of overlap when changing pages in the pager. More...
 
short PagerIndexLines
 Config: Number of index lines to display above the pager. More...
 
bool PagerStop
 Config: Don't automatically open the next message when at the end of a message. More...
 
short SearchContext
 Config: Context to display around search matches. More...
 
short SkipQuotedOffset
 Config: Lines of context to show when skipping quoted text. More...
 
bool SmartWrap
 Config: Wrap text at word boundaries. More...
 
struct RegexSmileys
 Config: Regex to match smileys to prevent mistakes when quoting text. More...
 
bool Tilde
 Config: Character to pad blank lines in the pager. More...
 
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 int TopLine = 0
 
static struct EmailOldHdr = NULL
 
static short InHelp = 0
 
static struct ResizeResize = NULL
 
static int braille_line = -1
 
static int braille_col = -1
 
static const struct Mapping PagerHelp []
 
static const struct Mapping PagerHelpExtra []
 
static struct Mapping PagerNewsHelpExtra []
 

Detailed Description

GUI display a file/email/help in a viewport with paging.

Authors
  • Michael R. Elkins

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

Macro Definition Documentation

#define ISHEADER (   x)    ((x) == MT_COLOR_HEADER || (x) == MT_COLOR_HDEFAULT)

Definition at line 97 of file pager.c.

#define IsAttach (   x)    (x && (x)->bdy)

Definition at line 99 of file pager.c.

#define IsMsgAttach (   x)    (x && (x)->fp && (x)->bdy && (x)->bdy->email)

Definition at line 100 of file pager.c.

#define IsHeader (   x)    (x && (x)->email && !(x)->bdy)

Definition at line 101 of file pager.c.

#define CHECK_MODE (   x)
Value:
if (!(x)) \
{ \
break; \
}
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:753
#define _(a)
Definition: message.h:28
static const char * Not_available_in_this_menu
Definition: pager.c:103
#define mutt_error(...)
Definition: logging.h:88

Definition at line 113 of file pager.c.

#define CHECK_READONLY
Value:
{ \
break; \
}
The "current" mailbox.
Definition: context.h:36
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:753
#define _(a)
Definition: message.h:28
struct Mailbox * mailbox
Definition: context.h:50
bool readonly
don&#39;t allow changes to the mailbox
Definition: mailbox.h:113
static const char * Mailbox_is_read_only
Definition: pager.c:105
#define mutt_error(...)
Definition: logging.h:88

Definition at line 121 of file pager.c.

#define CHECK_ATTACH
Value:
{ \
break; \
}
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:753
#define _(a)
Definition: message.h:28
WHERE bool OptAttachMsg
(pseudo) used by attach-message
Definition: options.h:31
static const char * Function_not_permitted_in_attach_message_mode
Definition: pager.c:106
#define mutt_error(...)
Definition: logging.h:88

Definition at line 129 of file pager.c.

#define CHECK_ACL (   aclbit,
  action 
)
Value:
{ \
/* L10N: %s is one of the CHECK_ACL entries below. */ \
mutt_error(_("%s: Operation not permitted by ACL"), action); \
break; \
}
The "current" mailbox.
Definition: context.h:36
#define mutt_bit_isset(v, n)
Definition: memory.h:39
unsigned char rights[(MUTT_ACL_MAX+7)/8]
ACL bits.
Definition: mailbox.h:115
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:753
#define _(a)
Definition: message.h:28
struct Mailbox * mailbox
Definition: context.h:50
#define mutt_error(...)
Definition: logging.h:88

Definition at line 137 of file pager.c.

#define ANSI_OFF   (1 << 0)

Definition at line 185 of file pager.c.

#define ANSI_BLINK   (1 << 1)

Definition at line 186 of file pager.c.

#define ANSI_BOLD   (1 << 2)

Definition at line 187 of file pager.c.

#define ANSI_UNDERLINE   (1 << 3)

Definition at line 188 of file pager.c.

#define ANSI_REVERSE   (1 << 4)

Definition at line 189 of file pager.c.

#define ANSI_COLOR   (1 << 5)

Definition at line 190 of file pager.c.

#define NUM_SIG_LINES   4

Definition at line 215 of file pager.c.

Function Documentation

static int check_sig ( const char *  s,
struct Line info,
int  n 
)
static

Check for an email signature.

Parameters
sText to examine
infoLine info array to update
nFirst line to check
Return values
0Success
-1Error

Definition at line 225 of file pager.c.

226 {
227  int count = 0;
228 
229  while (n > 0 && count <= NUM_SIG_LINES)
230  {
231  if (info[n].type != MT_COLOR_SIGNATURE)
232  break;
233  count++;
234  n--;
235  }
236 
237  if (count == 0)
238  return -1;
239 
240  if (count > NUM_SIG_LINES)
241  {
242  /* check for a blank line */
243  while (*s)
244  {
245  if (!ISSPACE(*s))
246  return 0;
247  s++;
248  }
249 
250  return -1;
251  }
252 
253  return 0;
254 }
Pager: signature lines.
Definition: mutt_curses.h:126
#define ISSPACE(c)
Definition: string2.h:40
#define NUM_SIG_LINES
Definition: pager.c:215

+ Here is the caller graph for this function:

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 264 of file pager.c.

265 {
266  const int *cnt = (const int *) m1;
267  const struct Syntax *stx = (const struct Syntax *) m2;
268 
269  if (*cnt < stx->first)
270  return -1;
271  if (*cnt >= stx->last)
272  return 1;
273  return 0;
274 }
int first
Definition: pager.c:165
int last
Definition: pager.c:166
Highlighting for a line of text.
Definition: pager.c:162

+ Here is the caller graph for this function:

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

Set the colour for a line of text.

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

Definition at line 285 of file pager.c.

287 {
288  int def_color; /* color without syntax highlight */
289  int color; /* final color */
290  static int last_color; /* last color set */
291  bool search = false;
292  int m;
293  struct Syntax *matching_chunk = NULL;
294 
295  if (!cnt)
296  last_color = -1; /* force attrset() */
297 
298  if (line_info[n].continuation)
299  {
300  if (!cnt && Markers)
301  {
303  addch('+');
304  last_color = ColorDefs[MT_COLOR_MARKERS];
305  }
306  m = (line_info[n].syntax)[0].first;
307  cnt += (line_info[n].syntax)[0].last;
308  }
309  else
310  m = n;
311  if (flags & MUTT_PAGER_LOGS)
312  {
313  def_color = ColorDefs[(line_info[n].syntax)[0].color];
314  }
315  else if (!(flags & MUTT_SHOWCOLOR))
316  def_color = ColorDefs[MT_COLOR_NORMAL];
317  else if (line_info[m].type == MT_COLOR_HEADER)
318  def_color = (line_info[m].syntax)[0].color;
319  else
320  def_color = ColorDefs[line_info[m].type];
321 
322  if ((flags & MUTT_SHOWCOLOR) && line_info[m].type == MT_COLOR_QUOTED)
323  {
324  struct QClass *class = line_info[m].quote;
325 
326  if (class)
327  {
328  def_color = class->color;
329 
330  while (class && class->length > cnt)
331  {
332  def_color = class->color;
333  class = class->up;
334  }
335  }
336  }
337 
338  color = def_color;
339  if ((flags & MUTT_SHOWCOLOR) && line_info[m].chunks)
340  {
341  matching_chunk = bsearch(&cnt, line_info[m].syntax, line_info[m].chunks,
342  sizeof(struct Syntax), comp_syntax_t);
343  if (matching_chunk && (cnt >= matching_chunk->first) &&
344  (cnt < matching_chunk->last))
345  {
346  color = matching_chunk->color;
347  }
348  }
349 
350  if ((flags & MUTT_SEARCH) && line_info[m].search_cnt)
351  {
352  matching_chunk = bsearch(&cnt, line_info[m].search, line_info[m].search_cnt,
353  sizeof(struct Syntax), comp_syntax_t);
354  if (matching_chunk && (cnt >= matching_chunk->first) &&
355  (cnt < matching_chunk->last))
356  {
357  color = ColorDefs[MT_COLOR_SEARCH];
358  search = 1;
359  }
360  }
361 
362  /* handle "special" bold & underlined characters */
363  if (special || a->attr)
364  {
365 #ifdef HAVE_COLOR
366  if ((a->attr & ANSI_COLOR))
367  {
368  if (a->pair == -1)
369  a->pair = mutt_alloc_color(a->fg, a->bg);
370  color = a->pair;
371  if (a->attr & ANSI_BOLD)
372  color |= A_BOLD;
373  }
374  else
375 #endif
376  if ((special & A_BOLD) || (a->attr & ANSI_BOLD))
377  {
378  if (ColorDefs[MT_COLOR_BOLD] && !search)
379  color = ColorDefs[MT_COLOR_BOLD];
380  else
381  color ^= A_BOLD;
382  }
383  if ((special & A_UNDERLINE) || (a->attr & ANSI_UNDERLINE))
384  {
385  if (ColorDefs[MT_COLOR_UNDERLINE] && !search)
386  color = ColorDefs[MT_COLOR_UNDERLINE];
387  else
388  color ^= A_UNDERLINE;
389  }
390  else if (a->attr & ANSI_REVERSE)
391  {
392  color ^= A_REVERSE;
393  }
394  else if (a->attr & ANSI_BLINK)
395  {
396  color ^= A_BLINK;
397  }
398  else if (a->attr == ANSI_OFF)
399  {
400  a->attr = 0;
401  }
402  }
403 
404  if (color != last_color)
405  {
406  ATTRSET(color);
407  last_color = color;
408  }
409 }
int first
Definition: pager.c:165
int mutt_alloc_color(int fg, int bg)
#define ANSI_COLOR
Definition: pager.c:190
#define MUTT_SHOWCOLOR
Definition: pager.h:43
int pair
Definition: pager.c:200
Pager: quoted text.
Definition: mutt_curses.h:125
Bold text.
Definition: mutt_curses.h:140
int last
Definition: pager.c:166
Message headers (takes a pattern)
Definition: mutt_curses.h:135
Pager: search matches.
Definition: mutt_curses.h:139
Highlighting for a line of text.
Definition: pager.c:162
Pager: markers, line continuation.
Definition: mutt_curses.h:133
short type
Definition: pager.c:175
int color
Definition: pager.c:164
#define ANSI_BOLD
Definition: pager.c:187
Plain text.
Definition: mutt_curses.h:130
#define ATTRSET
Definition: mutt_curses.h:228
static int comp_syntax_t(const void *m1, const void *m2)
Search for a Syntax using bsearch.
Definition: pager.c:264
#define ANSI_BLINK
Definition: pager.c:186
int attr
Definition: pager.c:197
Underlined text.
Definition: mutt_curses.h:141
#define ANSI_REVERSE
Definition: pager.c:189
int bg
Definition: pager.c:199
struct Syntax * syntax
Definition: pager.c:179
#define ANSI_UNDERLINE
Definition: pager.c:188
int ColorDefs[MT_COLOR_MAX]
Array of all fixed colours, see enum ColorId.
Definition: color.c:51
#define MUTT_PAGER_LOGS
Logview mode.
Definition: pager.h:56
#define SETCOLOR(X)
Definition: mutt_curses.h:220
WHERE bool Markers
Config: Display a &#39;+&#39; at the beginning of wrapped lines in the pager.
Definition: globals.h:243
int fg
Definition: pager.c:198
#define MUTT_SEARCH
Definition: pager.h:45
int color
Definition: pager.c:153
Style of quoted text.
Definition: pager.c:149
#define ANSI_OFF
Definition: pager.c:185

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

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 417 of file pager.c.

418 {
419  int m;
420 
421  line_info[n + 1].type = line_info[n].type;
422  (line_info[n + 1].syntax)[0].color = (line_info[n].syntax)[0].color;
423  line_info[n + 1].continuation = 1;
424 
425  /* find the real start of the line */
426  for (m = n; m >= 0; m--)
427  if (line_info[m].continuation == 0)
428  break;
429 
430  (line_info[n + 1].syntax)[0].first = m;
431  (line_info[n + 1].syntax)[0].last =
432  (line_info[n].continuation) ? cnt + (line_info[n].syntax)[0].last : cnt;
433 }
short type
Definition: pager.c:175
short continuation
Definition: pager.c:176
struct Syntax * syntax
Definition: pager.c:179

+ Here is the caller graph for this function:

static void new_class_color ( struct QClass class,
int *  q_level 
)
static

Create a new quoting colour.

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

Definition at line 440 of file pager.c.

441 {
442  class->index = (*q_level)++;
443  class->color = ColorQuote[class->index % ColorQuoteUsed];
444 }
int * ColorQuote
Array of colours for quoted email text.
Definition: color.c:49
int ColorQuoteUsed
Number of colours for quoted email text.
Definition: color.c:50

+ Here is the caller graph for this function:

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 453 of file pager.c.

455 {
456  struct QClass *q_list = quote_list;
457  new_class->index = -1;
458 
459  while (q_list)
460  {
461  if (q_list->index >= index)
462  {
463  q_list->index++;
464  q_list->color = ColorQuote[q_list->index % ColorQuoteUsed];
465  }
466  if (q_list->down)
467  q_list = q_list->down;
468  else if (q_list->next)
469  q_list = q_list->next;
470  else
471  {
472  while (!q_list->next)
473  {
474  q_list = q_list->up;
475  if (!q_list)
476  break;
477  }
478  if (q_list)
479  q_list = q_list->next;
480  }
481  }
482 
483  new_class->index = index;
484  new_class->color = ColorQuote[index % ColorQuoteUsed];
485  (*q_level)++;
486 }
int * ColorQuote
Array of colours for quoted email text.
Definition: color.c:49
int ColorQuoteUsed
Number of colours for quoted email text.
Definition: color.c:50
struct QClass * down
Definition: pager.c:156
struct QClass * next
Definition: pager.c:155
int color
Definition: pager.c:153
int index
Definition: pager.c:152
struct QClass * up
Definition: pager.c:156
Style of quoted text.
Definition: pager.c:149

+ Here is the caller graph for this function:

static void cleanup_quote ( struct QClass **  quote_list)
static

Free a quote list.

Parameters
quote_listQuote list to free

Definition at line 492 of file pager.c.

493 {
494  struct QClass *ptr = NULL;
495 
496  while (*quote_list)
497  {
498  if ((*quote_list)->down)
499  cleanup_quote(&((*quote_list)->down));
500  ptr = (*quote_list)->next;
501  if ((*quote_list)->prefix)
502  FREE(&(*quote_list)->prefix);
503  FREE(quote_list);
504  *quote_list = ptr;
505  }
506 }
static void cleanup_quote(struct QClass **quote_list)
Free a quote list.
Definition: pager.c:492
struct QClass * next
Definition: pager.c:155
#define FREE(x)
Definition: memory.h:46
Style of quoted text.
Definition: pager.c:149

+ Here is the caller graph for this function:

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
[in]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 517 of file pager.c.

519 {
520  struct QClass *q_list = *quote_list;
521  struct QClass *class = NULL, *tmp = NULL, *ptr = NULL, *save = NULL;
522  char *tail_qptr = NULL;
523  int offset, tail_lng;
524  int index = -1;
525 
526  if (ColorQuoteUsed <= 1)
527  {
528  /* not much point in classifying quotes... */
529 
530  if (!*quote_list)
531  {
532  class = mutt_mem_calloc(1, sizeof(struct QClass));
533  class->color = ColorQuote[0];
534  *quote_list = class;
535  }
536  return *quote_list;
537  }
538 
539  /* classify quoting prefix */
540  while (q_list)
541  {
542  if (length <= q_list->length)
543  {
544  /* case 1: check the top level nodes */
545 
546  if (mutt_str_strncmp(qptr, q_list->prefix, length) == 0)
547  {
548  if (length == q_list->length)
549  return q_list; /* same prefix: return the current class */
550 
551  /* found shorter prefix */
552  if (!tmp)
553  {
554  /* add a node above q_list */
555  tmp = mutt_mem_calloc(1, sizeof(struct QClass));
556  tmp->prefix = mutt_mem_calloc(1, length + 1);
557  strncpy(tmp->prefix, qptr, length);
558  tmp->length = length;
559 
560  /* replace q_list by tmp in the top level list */
561  if (q_list->next)
562  {
563  tmp->next = q_list->next;
564  q_list->next->prev = tmp;
565  }
566  if (q_list->prev)
567  {
568  tmp->prev = q_list->prev;
569  q_list->prev->next = tmp;
570  }
571 
572  /* make q_list a child of tmp */
573  tmp->down = q_list;
574  q_list->up = tmp;
575 
576  /* q_list has no siblings for now */
577  q_list->next = NULL;
578  q_list->prev = NULL;
579 
580  /* update the root if necessary */
581  if (q_list == *quote_list)
582  *quote_list = tmp;
583 
584  index = q_list->index;
585 
586  /* tmp should be the return class too */
587  class = tmp;
588 
589  /* next class to test; if tmp is a shorter prefix for another
590  * node, that node can only be in the top level list, so don't
591  * go down after this point
592  */
593  q_list = tmp->next;
594  }
595  else
596  {
597  /* found another branch for which tmp is a shorter prefix */
598 
599  /* save the next sibling for later */
600  save = q_list->next;
601 
602  /* unlink q_list from the top level list */
603  if (q_list->next)
604  q_list->next->prev = q_list->prev;
605  if (q_list->prev)
606  q_list->prev->next = q_list->next;
607 
608  /* at this point, we have a tmp->down; link q_list to it */
609  ptr = tmp->down;
610  /* sibling order is important here, q_list should be linked last */
611  while (ptr->next)
612  ptr = ptr->next;
613  ptr->next = q_list;
614  q_list->next = NULL;
615  q_list->prev = ptr;
616  q_list->up = tmp;
617 
618  index = q_list->index;
619 
620  /* next class to test; as above, we shouldn't go down */
621  q_list = save;
622  }
623 
624  /* we found a shorter prefix, so certain quotes have changed classes */
625  *force_redraw = true;
626  continue;
627  }
628  else
629  {
630  /* shorter, but not a substring of the current class: try next */
631  q_list = q_list->next;
632  continue;
633  }
634  }
635  else
636  {
637  /* case 2: try subclassing the current top level node */
638 
639  /* tmp != NULL means we already found a shorter prefix at case 1 */
640  if (!tmp && (mutt_str_strncmp(qptr, q_list->prefix, q_list->length) == 0))
641  {
642  /* ok, it's a subclass somewhere on this branch */
643 
644  ptr = q_list;
645  offset = q_list->length;
646 
647  q_list = q_list->down;
648  tail_lng = length - offset;
649  tail_qptr = (char *) qptr + offset;
650 
651  while (q_list)
652  {
653  if (length <= q_list->length)
654  {
655  if (mutt_str_strncmp(tail_qptr, (q_list->prefix) + offset, tail_lng) == 0)
656  {
657  /* same prefix: return the current class */
658  if (length == q_list->length)
659  return q_list;
660 
661  /* found shorter common prefix */
662  if (!tmp)
663  {
664  /* add a node above q_list */
665  tmp = mutt_mem_calloc(1, sizeof(struct QClass));
666  tmp->prefix = mutt_mem_calloc(1, length + 1);
667  strncpy(tmp->prefix, qptr, length);
668  tmp->length = length;
669 
670  /* replace q_list by tmp */
671  if (q_list->next)
672  {
673  tmp->next = q_list->next;
674  q_list->next->prev = tmp;
675  }
676  if (q_list->prev)
677  {
678  tmp->prev = q_list->prev;
679  q_list->prev->next = tmp;
680  }
681 
682  /* make q_list a child of tmp */
683  tmp->down = q_list;
684  tmp->up = q_list->up;
685  q_list->up = tmp;
686  if (tmp->up->down == q_list)
687  tmp->up->down = tmp;
688 
689  /* q_list has no siblings */
690  q_list->next = NULL;
691  q_list->prev = NULL;
692 
693  index = q_list->index;
694 
695  /* tmp should be the return class too */
696  class = tmp;
697 
698  /* next class to test */
699  q_list = tmp->next;
700  }
701  else
702  {
703  /* found another branch for which tmp is a shorter prefix */
704 
705  /* save the next sibling for later */
706  save = q_list->next;
707 
708  /* unlink q_list from the top level list */
709  if (q_list->next)
710  q_list->next->prev = q_list->prev;
711  if (q_list->prev)
712  q_list->prev->next = q_list->next;
713 
714  /* at this point, we have a tmp->down; link q_list to it */
715  ptr = tmp->down;
716  while (ptr->next)
717  ptr = ptr->next;
718  ptr->next = q_list;
719  q_list->next = NULL;
720  q_list->prev = ptr;
721  q_list->up = tmp;
722 
723  index = q_list->index;
724 
725  /* next class to test */
726  q_list = save;
727  }
728 
729  /* we found a shorter prefix, so we need a redraw */
730  *force_redraw = true;
731  continue;
732  }
733  else
734  {
735  q_list = q_list->next;
736  continue;
737  }
738  }
739  else
740  {
741  /* longer than the current prefix: try subclassing it */
742  if (!tmp && (mutt_str_strncmp(tail_qptr, (q_list->prefix) + offset,
743  q_list->length - offset) == 0))
744  {
745  /* still a subclass: go down one level */
746  ptr = q_list;
747  offset = q_list->length;
748 
749  q_list = q_list->down;
750  tail_lng = length - offset;
751  tail_qptr = (char *) qptr + offset;
752 
753  continue;
754  }
755  else
756  {
757  /* nope, try the next prefix */
758  q_list = q_list->next;
759  continue;
760  }
761  }
762  }
763 
764  /* still not found so far: add it as a sibling to the current node */
765  if (!class)
766  {
767  tmp = mutt_mem_calloc(1, sizeof(struct QClass));
768  tmp->prefix = mutt_mem_calloc(1, length + 1);
769  strncpy(tmp->prefix, qptr, length);
770  tmp->length = length;
771 
772  if (ptr->down)
773  {
774  tmp->next = ptr->down;
775  ptr->down->prev = tmp;
776  }
777  ptr->down = tmp;
778  tmp->up = ptr;
779 
780  new_class_color(tmp, q_level);
781 
782  return tmp;
783  }
784  else
785  {
786  if (index != -1)
787  shift_class_colors(*quote_list, tmp, index, q_level);
788 
789  return class;
790  }
791  }
792  else
793  {
794  /* nope, try the next prefix */
795  q_list = q_list->next;
796  continue;
797  }
798  }
799  }
800 
801  if (!class)
802  {
803  /* not found so far: add it as a top level class */
804  class = mutt_mem_calloc(1, sizeof(struct QClass));
805  class->prefix = mutt_mem_calloc(1, length + 1);
806  strncpy(class->prefix, qptr, length);
807  class->length = length;
808  new_class_color(class, q_level);
809 
810  if (*quote_list)
811  {
812  class->next = *quote_list;
813  (*quote_list)->prev = class;
814  }
815  *quote_list = class;
816  }
817 
818  if (index != -1)
819  shift_class_colors(*quote_list, tmp, index, q_level);
820 
821  return class;
822 }
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:51
int * ColorQuote
Array of colours for quoted email text.
Definition: color.c:49
static void new_class_color(struct QClass *class, int *q_level)
Create a new quoting colour.
Definition: pager.c:440
size_t length
Definition: pager.c:151
int ColorQuoteUsed
Number of colours for quoted email text.
Definition: color.c:50
struct QClass * down
Definition: pager.c:156
char * prefix
Definition: pager.c:154
struct QClass * next
Definition: pager.c:155
int mutt_str_strncmp(const char *a, const char *b, size_t l)
Compare two strings (to a maximum), safely.
Definition: string.c:639
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: pager.c:453
int index
Definition: pager.c:152
struct QClass * prev
Definition: pager.c:155
struct QClass * up
Definition: pager.c:156
Style of quoted text.
Definition: pager.c:149

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static int check_attachment_marker ( char *  p)
static

Check that the unique marker is present.

Parameters
pString to check
Return values
numOffset of marker

Definition at line 832 of file pager.c.

833 {
834  char *q = AttachmentMarker;
835 
836  for (; *p == *q && *q && *p && *q != '\a' && *p != '\a'; p++, q++)
837  ;
838  return (int) (*p - *q);
839 }
WHERE char AttachmentMarker[STRING]
Unique ANSI string to mark PGP messages in an email.
Definition: globals.h:45

+ Here is the caller graph for this function:

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 QuoteRegex and doesn't match Smileys. This is used by the pager for calling classify_quote.

Definition at line 850 of file pager.c.

851 {
852  bool is_quote = false;
853  regmatch_t pmatch_internal[1], smatch[1];
854 
855  if (!pmatch)
856  pmatch = pmatch_internal;
857 
858  if (QuoteRegex && QuoteRegex->regex && regexec(QuoteRegex->regex, line, 1, pmatch, 0) == 0)
859  {
860  if (Smileys && Smileys->regex && regexec(Smileys->regex, line, 1, smatch, 0) == 0)
861  {
862  if (smatch[0].rm_so > 0)
863  {
864  char c = line[smatch[0].rm_so];
865  line[smatch[0].rm_so] = 0;
866 
867  if (regexec(QuoteRegex->regex, line, 1, pmatch, 0) == 0)
868  is_quote = true;
869 
870  line[smatch[0].rm_so] = c;
871  }
872  }
873  else
874  is_quote = true;
875  }
876 
877  return is_quote;
878 }
regex_t * regex
compiled expression
Definition: regex3.h:60
struct Regex * Smileys
Config: Regex to match smileys to prevent mistakes when quoting text.
Definition: pager.c:94
const char * line
Definition: common.c:35
WHERE struct Regex * QuoteRegex
Config: Regex to match quoted text in a reply.
Definition: globals.h:182

+ Here is the caller graph for this function:

static void resolve_types ( 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
bufFormatted text
rawRaw text
line_infoLine info array
nLine number (index into line_info)
lastLast line
quote_listList of quote colours
q_levelQuote level
force_redrawSet to true if a screen redraw is needed
q_classifyIf true, style the text

Definition at line 892 of file pager.c.

895 {
896  struct ColorLine *color_line = NULL;
897  regmatch_t pmatch[1];
898  bool found;
899  bool null_rx;
900  int offset, i = 0;
901 
902  if (n == 0 || ISHEADER(line_info[n - 1].type))
903  {
904  if (buf[0] == '\n') /* end of header */
905  {
906  line_info[n].type = MT_COLOR_NORMAL;
907  getyx(stdscr, braille_line, braille_col);
908  }
909  else
910  {
911  /* if this is a continuation of the previous line, use the previous
912  * line's color as default. */
913  if (n > 0 && (buf[0] == ' ' || buf[0] == '\t'))
914  {
915  line_info[n].type = line_info[n - 1].type; /* wrapped line */
916  if (!HeaderColorPartial)
917  {
918  (line_info[n].syntax)[0].color = (line_info[n - 1].syntax)[0].color;
919  line_info[n].is_cont_hdr = 1;
920  }
921  }
922  else
923  {
924  line_info[n].type = MT_COLOR_HDEFAULT;
925  }
926 
927  /* When this option is unset, we color the entire header the
928  * same color. Otherwise, we handle the header patterns just
929  * like body patterns (further below).
930  */
931  if (!HeaderColorPartial)
932  {
933  STAILQ_FOREACH(color_line, &ColorHdrList, entries)
934  {
935  if (regexec(&color_line->regex, buf, 0, NULL, 0) == 0)
936  {
937  line_info[n].type = MT_COLOR_HEADER;
938  line_info[n].syntax[0].color = color_line->pair;
939  if (line_info[n].is_cont_hdr)
940  {
941  /* adjust the previous continuation lines to reflect the color of this continuation line */
942  int j;
943  for (j = n - 1; j >= 0 && line_info[j].is_cont_hdr; --j)
944  {
945  line_info[j].type = line_info[n].type;
946  line_info[j].syntax[0].color = line_info[n].syntax[0].color;
947  }
948  /* now adjust the first line of this header field */
949  if (j >= 0)
950  {
951  line_info[j].type = line_info[n].type;
952  line_info[j].syntax[0].color = line_info[n].syntax[0].color;
953  }
954  *force_redraw = true; /* the previous lines have already been drawn on the screen */
955  }
956  break;
957  }
958  }
959  }
960  }
961  }
962  else if (mutt_str_startswith(raw, "\033[0m", CASE_MATCH)) /* a little hack... */
963  line_info[n].type = MT_COLOR_NORMAL;
964  else if (check_attachment_marker((char *) raw) == 0)
965  line_info[n].type = MT_COLOR_ATTACHMENT;
966  else if ((mutt_str_strcmp("-- \n", buf) == 0) || (mutt_str_strcmp("-- \r\n", buf) == 0))
967  {
968  i = n + 1;
969 
970  line_info[n].type = MT_COLOR_SIGNATURE;
971  while (i < last && check_sig(buf, line_info, i - 1) == 0 &&
972  (line_info[i].type == MT_COLOR_NORMAL || line_info[i].type == MT_COLOR_QUOTED ||
973  line_info[i].type == MT_COLOR_HEADER))
974  {
975  /* oops... */
976  if (line_info[i].chunks)
977  {
978  line_info[i].chunks = 0;
979  mutt_mem_realloc(&(line_info[n].syntax), sizeof(struct Syntax));
980  }
981  line_info[i++].type = MT_COLOR_SIGNATURE;
982  }
983  }
984  else if (check_sig(buf, line_info, n - 1) == 0)
985  line_info[n].type = MT_COLOR_SIGNATURE;
986  else if (mutt_is_quote_line(buf, pmatch))
987 
988  {
989  if (q_classify && line_info[n].quote == NULL)
990  {
991  line_info[n].quote = classify_quote(quote_list, buf + pmatch[0].rm_so,
992  pmatch[0].rm_eo - pmatch[0].rm_so,
993  force_redraw, q_level);
994  }
995  line_info[n].type = MT_COLOR_QUOTED;
996  }
997  else
998  line_info[n].type = MT_COLOR_NORMAL;
999 
1000  /* body patterns */
1001  if (line_info[n].type == MT_COLOR_NORMAL || line_info[n].type == MT_COLOR_QUOTED ||
1002  (line_info[n].type == MT_COLOR_HDEFAULT && HeaderColorPartial))
1003  {
1004  size_t nl;
1005 
1006  /* don't consider line endings part of the buffer
1007  * for regex matching */
1008  nl = mutt_str_strlen(buf);
1009  if ((nl > 0) && (buf[nl - 1] == '\n'))
1010  buf[nl - 1] = 0;
1011 
1012  i = 0;
1013  offset = 0;
1014  line_info[n].chunks = 0;
1015  do
1016  {
1017  if (!buf[offset])
1018  break;
1019 
1020  found = false;
1021  null_rx = false;
1022  struct ColorLineHead *head = NULL;
1023  if (line_info[n].type == MT_COLOR_HDEFAULT)
1024  head = &ColorHdrList;
1025  else
1026  head = &ColorBodyList;
1027  STAILQ_FOREACH(color_line, head, entries)
1028  {
1029  if (regexec(&color_line->regex, buf + offset, 1, pmatch,
1030  (offset ? REG_NOTBOL : 0)) == 0)
1031  {
1032  if (pmatch[0].rm_eo != pmatch[0].rm_so)
1033  {
1034  if (!found)
1035  {
1036  /* Abort if we fill up chunks.
1037  * Yes, this really happened. See #3888 */
1038  if (line_info[n].chunks == SHRT_MAX)
1039  {
1040  null_rx = false;
1041  break;
1042  }
1043  if (++(line_info[n].chunks) > 1)
1044  {
1045  mutt_mem_realloc(&(line_info[n].syntax),
1046  (line_info[n].chunks) * sizeof(struct Syntax));
1047  }
1048  }
1049  i = line_info[n].chunks - 1;
1050  pmatch[0].rm_so += offset;
1051  pmatch[0].rm_eo += offset;
1052  if (!found || pmatch[0].rm_so < (line_info[n].syntax)[i].first ||
1053  (pmatch[0].rm_so == (line_info[n].syntax)[i].first &&
1054  pmatch[0].rm_eo > (line_info[n].syntax)[i].last))
1055  {
1056  (line_info[n].syntax)[i].color = color_line->pair;
1057  (line_info[n].syntax)[i].first = pmatch[0].rm_so;
1058  (line_info[n].syntax)[i].last = pmatch[0].rm_eo;
1059  }
1060  found = true;
1061  null_rx = false;
1062  }
1063  else
1064  null_rx = true; /* empty regex; don't add it, but keep looking */
1065  }
1066  }
1067 
1068  if (null_rx)
1069  offset++; /* avoid degenerate cases */
1070  else
1071  offset = (line_info[n].syntax)[i].last;
1072  } while (found || null_rx);
1073  if (nl > 0)
1074  buf[nl] = '\n';
1075  }
1076 
1077  /* attachment patterns */
1078  if (line_info[n].type == MT_COLOR_ATTACHMENT)
1079  {
1080  size_t nl;
1081 
1082  /* don't consider line endings part of the buffer for regex matching */
1083  nl = mutt_str_strlen(buf);
1084  if ((nl > 0) && (buf[nl - 1] == '\n'))
1085  buf[nl - 1] = 0;
1086 
1087  i = 0;
1088  offset = 0;
1089  line_info[n].chunks = 0;
1090  do
1091  {
1092  if (!buf[offset])
1093  break;
1094 
1095  found = false;
1096  null_rx = false;
1097  STAILQ_FOREACH(color_line, &ColorAttachList, entries)
1098  {
1099  if (regexec(&color_line->regex, buf + offset, 1, pmatch,
1100  (offset ? REG_NOTBOL : 0)) == 0)
1101  {
1102  if (pmatch[0].rm_eo != pmatch[0].rm_so)
1103  {
1104  if (!found)
1105  {
1106  if (++(line_info[n].chunks) > 1)
1107  {
1108  mutt_mem_realloc(&(line_info[n].syntax),
1109  (line_info[n].chunks) * sizeof(struct Syntax));
1110  }
1111  }
1112  i = line_info[n].chunks - 1;
1113  pmatch[0].rm_so += offset;
1114  pmatch[0].rm_eo += offset;
1115  if (!found || pmatch[0].rm_so < (line_info[n].syntax)[i].first ||
1116  (pmatch[0].rm_so == (line_info[n].syntax)[i].first &&
1117  pmatch[0].rm_eo > (line_info[n].syntax)[i].last))
1118  {
1119  (line_info[n].syntax)[i].color = color_line->pair;
1120  (line_info[n].syntax)[i].first = pmatch[0].rm_so;
1121  (line_info[n].syntax)[i].last = pmatch[0].rm_eo;
1122  }
1123  found = 1;
1124  null_rx = 0;
1125  }
1126  else
1127  null_rx = 1; /* empty regex; don't add it, but keep looking */
1128  }
1129  }
1130 
1131  if (null_rx)
1132  offset++; /* avoid degenerate cases */
1133  else
1134  offset = (line_info[n].syntax)[i].last;
1135  } while (found || null_rx);
1136  if (nl > 0)
1137  buf[nl] = '\n';
1138  }
1139 }
Pager: signature lines.
Definition: mutt_curses.h:126
MIME attachments text (entire line)
Definition: mutt_curses.h:137
bool HeaderColorPartial
Config: Only colour the part of the header matching the regex.
Definition: pager.c:87
short chunks
Definition: pager.c:177
static int check_attachment_marker(char *p)
Check that the unique marker is present.
Definition: pager.c:832
Pager: quoted text.
Definition: mutt_curses.h:125
#define ISHEADER(x)
Definition: pager.c:97
struct ColorLineHead ColorBodyList
List of colours applied to the email body.
Definition: color.c:53
Message headers (takes a pattern)
Definition: mutt_curses.h:135
Match case when comparing strings.
Definition: string2.h:69
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:663
Highlighting for a line of text.
Definition: pager.c:162
short type
Definition: pager.c:175
int color
Definition: pager.c:164
Plain text.
Definition: mutt_curses.h:130
static int braille_line
Definition: pager.c:824
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:124
struct QClass * quote
Definition: pager.c:181
static int braille_col
Definition: pager.c:825
struct Syntax * syntax
Definition: pager.c:179
struct ColorLineHead ColorHdrList
List of colours applied to the email headers.
Definition: color.c:54
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:350
size_t mutt_str_startswith(const char *str, const char *prefix, enum CaseSensitivity cs)
Check whether a string starts with a prefix.
Definition: string.c:166
regex_t regex
Definition: mutt_curses.h:180
Header default colour.
Definition: mutt_curses.h:124
int mutt_is_quote_line(char *line, regmatch_t *pmatch)
Is a line of message text a quote?
Definition: pager.c:850
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: pager.c:517
static int check_sig(const char *s, struct Line *info, int n)
Check for an email signature.
Definition: pager.c:225
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:612
unsigned int is_cont_hdr
this line is a continuation of the previous header line
Definition: pager.c:182
A regular expression and a color to highlight a line.
Definition: mutt_curses.h:178
struct ColorLineHead ColorAttachList
List of colours applied to the attachment headers.
Definition: color.c:52

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static bool is_ansi ( unsigned char *  buf)
static

Is this an ANSI escape sequence?

Parameters
bufString to check
Return values
trueIf it is

Definition at line 1146 of file pager.c.

1147 {
1148  while (*buf && (isdigit(*buf) || *buf == ';'))
1149  buf++;
1150  return *buf == 'm';
1151 }

+ Here is the caller graph for this function:

static int grok_ansi ( 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 1160 of file pager.c.

1161 {
1162  int x = pos;
1163 
1164  while (isdigit(buf[x]) || buf[x] == ';')
1165  x++;
1166 
1167  /* Character Attributes */
1168  if (AllowAnsi && a && (buf[x] == 'm'))
1169  {
1170  if (pos == x)
1171  {
1172 #ifdef HAVE_COLOR
1173  if (a->pair != -1)
1174  mutt_free_color(a->fg, a->bg);
1175 #endif
1176  a->attr = ANSI_OFF;
1177  a->pair = -1;
1178  }
1179  while (pos < x)
1180  {
1181  if (buf[pos] == '1' && (pos + 1 == x || buf[pos + 1] == ';'))
1182  {
1183  a->attr |= ANSI_BOLD;
1184  pos += 2;
1185  }
1186  else if (buf[pos] == '4' && (pos + 1 == x || buf[pos + 1] == ';'))
1187  {
1188  a->attr |= ANSI_UNDERLINE;
1189  pos += 2;
1190  }
1191  else if (buf[pos] == '5' && (pos + 1 == x || buf[pos + 1] == ';'))
1192  {
1193  a->attr |= ANSI_BLINK;
1194  pos += 2;
1195  }
1196  else if (buf[pos] == '7' && (pos + 1 == x || buf[pos + 1] == ';'))
1197  {
1198  a->attr |= ANSI_REVERSE;
1199  pos += 2;
1200  }
1201  else if (buf[pos] == '0' && (pos + 1 == x || buf[pos + 1] == ';'))
1202  {
1203 #ifdef HAVE_COLOR
1204  if (a->pair != -1)
1205  mutt_free_color(a->fg, a->bg);
1206 #endif
1207  a->attr = ANSI_OFF;
1208  a->pair = -1;
1209  pos += 2;
1210  }
1211  else if (buf[pos] == '3' && isdigit(buf[pos + 1]))
1212  {
1213 #ifdef HAVE_COLOR
1214  if (a->pair != -1)
1215  mutt_free_color(a->fg, a->bg);
1216 #endif
1217  a->pair = -1;
1218  a->attr |= ANSI_COLOR;
1219  a->fg = buf[pos + 1] - '0';
1220  pos += 3;
1221  }
1222  else if (buf[pos] == '4' && isdigit(buf[pos + 1]))
1223  {
1224 #ifdef HAVE_COLOR
1225  if (a->pair != -1)
1226  mutt_free_color(a->fg, a->bg);
1227 #endif
1228  a->pair = -1;
1229  a->attr |= ANSI_COLOR;
1230  a->bg = buf[pos + 1] - '0';
1231  pos += 3;
1232  }
1233  else
1234  {
1235  while (pos < x && buf[pos] != ';')
1236  pos++;
1237  pos++;
1238  }
1239  }
1240  }
1241  pos = x;
1242  return pos;
1243 }
#define ANSI_COLOR
Definition: pager.c:190
int pair
Definition: pager.c:200
#define ANSI_BOLD
Definition: pager.c:187
#define ANSI_BLINK
Definition: pager.c:186
int attr
Definition: pager.c:197
#define ANSI_REVERSE
Definition: pager.c:189
int bg
Definition: pager.c:199
bool AllowAnsi
Config: Allow ANSI colour codes in rich text messages.
Definition: pager.c:86
#define ANSI_UNDERLINE
Definition: pager.c:188
void mutt_free_color(int fg, int bg)
int fg
Definition: pager.c:198
#define ANSI_OFF
Definition: pager.c:185

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static int fill_buffer ( FILE *  f,
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]fFile 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 1257 of file pager.c.

1259 {
1260  unsigned char *p = NULL, *q = NULL;
1261  static int b_read;
1262  int l = 0;
1263 
1264  if (*buf_ready == 0)
1265  {
1266  if (offset != *last_pos)
1267  fseeko(f, offset, SEEK_SET);
1268  *buf = (unsigned char *) mutt_file_read_line((char *) *buf, blen, f, &l, MUTT_EOL);
1269  if (!*buf)
1270  {
1271  fmt[0] = 0;
1272  return -1;
1273  }
1274  *last_pos = ftello(f);
1275  b_read = (int) (*last_pos - offset);
1276  *buf_ready = 1;
1277 
1278  mutt_mem_realloc(fmt, *blen);
1279 
1280  /* copy "buf" to "fmt", but without bold and underline controls */
1281  p = *buf;
1282  q = *fmt;
1283  while (*p)
1284  {
1285  if (*p == '\010' && (p > *buf))
1286  {
1287  if (*(p + 1) == '_') /* underline */
1288  p += 2;
1289  else if (*(p + 1) && q > *fmt) /* bold or overstrike */
1290  {
1291  *(q - 1) = *(p + 1);
1292  p += 2;
1293  }
1294  else /* ^H */
1295  *q++ = *p++;
1296  }
1297  else if (*p == '\033' && *(p + 1) == '[' && is_ansi(p + 2))
1298  {
1299  while (*p++ != 'm') /* skip ANSI sequence */
1300  ;
1301  }
1302  else if (*p == '\033' && *(p + 1) == ']' && check_attachment_marker((char *) p) == 0)
1303  {
1304  mutt_debug(2, "Seen attachment marker.\n");
1305  while (*p++ != '\a') /* skip pseudo-ANSI sequence */
1306  ;
1307  }
1308  else
1309  *q++ = *p++;
1310  }
1311  *q = 0;
1312  }
1313  return b_read;
1314 }
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, int flags)
Read a line from a file.
Definition: file.c:632
static int check_attachment_marker(char *p)
Check that the unique marker is present.
Definition: pager.c:832
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:124
#define MUTT_EOL
don&#39;t strip \n / \r\n
Definition: file.h:37
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
static bool is_ansi(unsigned char *buf)
Is this an ANSI escape sequence?
Definition: pager.c:1146

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static int format_line ( struct Line **  line_info,
int  n,
unsigned char *  buf,
int  flags,
struct AnsiAttr pa,
int  cnt,
int *  pspace,
int *  pvch,
int *  pcol,
int *  pspecial,
struct MuttWindow pager_window 
)
static

Display a line of text in the pager.

Parameters
[in]line_infoLine info
[in]nLine number (index into line_info)
[in]bufText to display
[in]flagsFlags, e.g. MUTT_PAGER_NOWRAP
[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]pager_windowWindow to write to
Return values
numNumber of characters displayed

Definition at line 1331 of file pager.c.

1334 {
1335  int space = -1; /* index of the last space or TAB */
1336  int col = Markers ? (*line_info)[n].continuation : 0;
1337  size_t k;
1338  int ch, vch, last_special = -1, special = 0, t;
1339  wchar_t wc;
1340  mbstate_t mbstate;
1341  int wrap_cols =
1342  mutt_window_wrap_cols(pager_window, (flags & MUTT_PAGER_NOWRAP) ? 0 : Wrap);
1343 
1344  if (check_attachment_marker((char *) buf) == 0)
1345  wrap_cols = pager_window->cols;
1346 
1347  /* FIXME: this should come from line_info */
1348  memset(&mbstate, 0, sizeof(mbstate));
1349 
1350  for (ch = 0, vch = 0; ch < cnt; ch += k, vch += k)
1351  {
1352  /* Handle ANSI sequences */
1353  while (cnt - ch >= 2 && buf[ch] == '\033' && buf[ch + 1] == '[' && is_ansi(buf + ch + 2))
1354  ch = grok_ansi(buf, ch + 2, pa) + 1;
1355 
1356  while (cnt - ch >= 2 && buf[ch] == '\033' && buf[ch + 1] == ']' &&
1357  check_attachment_marker((char *) buf + ch) == 0)
1358  {
1359  while (buf[ch++] != '\a')
1360  if (ch >= cnt)
1361  break;
1362  }
1363 
1364  /* is anything left to do? */
1365  if (ch >= cnt)
1366  break;
1367 
1368  k = mbrtowc(&wc, (char *) buf + ch, cnt - ch, &mbstate);
1369  if (k == (size_t)(-2) || k == (size_t)(-1))
1370  {
1371  if (k == (size_t)(-1))
1372  memset(&mbstate, 0, sizeof(mbstate));
1373  mutt_debug(1, "mbrtowc returned %lu; errno = %d.\n", k, errno);
1374  if (col + 4 > wrap_cols)
1375  break;
1376  col += 4;
1377  if (pa)
1378  printw("\\%03o", buf[ch]);
1379  k = 1;
1380  continue;
1381  }
1382  if (k == 0)
1383  k = 1;
1384 
1385  if (CharsetIsUtf8)
1386  {
1387  /* zero width space, zero width no-break space */
1388  if (wc == 0x200B || wc == 0xFEFF)
1389  {
1390  mutt_debug(3, "skip zero-width character U+%04X\n", (unsigned short) wc);
1391  continue;
1392  }
1394  {
1395  mutt_debug(3, "filtered U+%04X\n", (unsigned short) wc);
1396  continue;
1397  }
1398  }
1399 
1400  /* Handle backspace */
1401  special = 0;
1402  if (IsWPrint(wc))
1403  {
1404  wchar_t wc1;
1405  mbstate_t mbstate1 = mbstate;
1406  size_t k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
1407  while ((k1 != (size_t)(-2)) && (k1 != (size_t)(-1)) && (k1 > 0) && (wc1 == '\b'))
1408  {
1409  const size_t k2 =
1410  mbrtowc(&wc1, (char *) buf + ch + k + k1, cnt - ch - k - k1, &mbstate1);
1411  if ((k2 == (size_t)(-2)) || (k2 == (size_t)(-1)) || (k2 == 0) || (!IsWPrint(wc1)))
1412  break;
1413 
1414  if (wc == wc1)
1415  {
1416  special |= (wc == '_' && special & A_UNDERLINE) ? A_UNDERLINE : A_BOLD;
1417  }
1418  else if (wc == '_' || wc1 == '_')
1419  {
1420  special |= A_UNDERLINE;
1421  wc = (wc1 == '_') ? wc : wc1;
1422  }
1423  else
1424  {
1425  /* special = 0; / * overstrike: nothing to do! */
1426  wc = wc1;
1427  }
1428 
1429  ch += k + k1;
1430  k = k2;
1431  mbstate = mbstate1;
1432  k1 = mbrtowc(&wc1, (char *) buf + ch + k, cnt - ch - k, &mbstate1);
1433  }
1434  }
1435 
1436  if (pa && ((flags & (MUTT_SHOWCOLOR | MUTT_SEARCH | MUTT_PAGER_MARKER)) ||
1437  special || last_special || pa->attr))
1438  {
1439  resolve_color(*line_info, n, vch, flags, special, pa);
1440  last_special = special;
1441  }
1442 
1443  /* no-break space, narrow no-break space */
1444  if (IsWPrint(wc) || (CharsetIsUtf8 && (wc == 0x00A0 || wc == 0x202F)))
1445  {
1446  if (wc == ' ')
1447  {
1448  space = ch;
1449  }
1450  t = wcwidth(wc);
1451  if (col + t > wrap_cols)
1452  break;
1453  col += t;
1454  if (pa)
1455  mutt_addwch(wc);
1456  }
1457  else if (wc == '\n')
1458  break;
1459  else if (wc == '\t')
1460  {
1461  space = ch;
1462  t = (col & ~7) + 8;
1463  if (t > wrap_cols)
1464  break;
1465  if (pa)
1466  for (; col < t; col++)
1467  addch(' ');
1468  else
1469  col = t;
1470  }
1471  else if (wc < 0x20 || wc == 0x7f)
1472  {
1473  if (col + 2 > wrap_cols)
1474  break;
1475  col += 2;
1476  if (pa)
1477  printw("^%c", ('@' + wc) & 0x7f);
1478  }
1479  else if (wc < 0x100)
1480  {
1481  if (col + 4 > wrap_cols)
1482  break;
1483  col += 4;
1484  if (pa)
1485  printw("\\%03o", wc);
1486  }
1487  else
1488  {
1489  if (col + 1 > wrap_cols)
1490  break;
1491  col += k;
1492  if (pa)
1493  addch(ReplacementChar);
1494  }
1495  }
1496  *pspace = space;
1497  *pcol = col;
1498  *pvch = vch;
1499  *pspecial = special;
1500  return ch;
1501 }
bool mutt_mb_is_display_corrupting_utf8(wchar_t wc)
Will this character corrupt the display?
Definition: mbyte.c:375
#define IsWPrint(wc)
Definition: mbyte.h:41
#define MUTT_SHOWCOLOR
Definition: pager.h:43
static int check_attachment_marker(char *p)
Check that the unique marker is present.
Definition: pager.c:832
WHERE short Wrap
Config: Width to wrap text in the pager.
Definition: globals.h:156
#define MUTT_PAGER_NOWRAP
format for term width, ignore $wrap
Definition: pager.h:55
wchar_t ReplacementChar
When a Unicode character can&#39;t be displayed, use this instead.
Definition: charset.c:57
int attr
Definition: pager.c:197
static void resolve_color(struct Line *line_info, int n, int cnt, int flags, int special, struct AnsiAttr *a)
Set the colour for a line of text.
Definition: pager.c:285
WHERE bool Markers
Config: Display a &#39;+&#39; at the beginning of wrapped lines in the pager.
Definition: globals.h:243
bool CharsetIsUtf8
Is the user&#39;s current character set utf-8?
Definition: charset.c:62
#define MUTT_SEARCH
Definition: pager.h:45
static int grok_ansi(unsigned char *buf, int pos, struct AnsiAttr *a)
Parse an ANSI escape sequence.
Definition: pager.c:1160
#define MUTT_PAGER_MARKER
use markers if option is set
Definition: pager.h:51
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
static bool is_ansi(unsigned char *buf)
Is this an ANSI escape sequence?
Definition: pager.c:1146
int mutt_addwch(wchar_t wc)
addwch would be provided by an up-to-date curses library
Definition: curs_lib.c:877
int mutt_window_wrap_cols(struct MuttWindow *win, short wrap)
Calculate the wrap column for a Window.
Definition: mutt_window.c:309

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

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

Print a line on screen.

Parameters
fFile to read from
last_posOffset into file
line_infoLine attributes
nLine number
lastLast line
maxMaximum number of lines
flagsSee below
quote_listEmail quoting style
q_levelLevel of quoting
force_redrawForce a repaint
search_reRegex to highlight
pager_windowWindow to draw into
Return values
-1EOF was reached
0normal exit, line was not displayed
>0normal exit, line was displayed

flags:

Definition at line 1530 of file pager.c.

1534 {
1535  unsigned char *buf = NULL, *fmt = NULL;
1536  size_t buflen = 0;
1537  unsigned char *buf_ptr = NULL;
1538  int ch, vch, col, cnt, b_read;
1539  int buf_ready = 0;
1540  bool change_last = false;
1541  int special;
1542  int offset;
1543  int def_color;
1544  int m;
1545  int rc = -1;
1546  struct AnsiAttr a = { 0, 0, 0, -1 };
1547  regmatch_t pmatch[1];
1548 
1549  if (n == *last)
1550  {
1551  (*last)++;
1552  change_last = true;
1553  }
1554 
1555  if (*last == *max)
1556  {
1557  mutt_mem_realloc(line_info, sizeof(struct Line) * (*max += LINES));
1558  for (ch = *last; ch < *max; ch++)
1559  {
1560  memset(&((*line_info)[ch]), 0, sizeof(struct Line));
1561  (*line_info)[ch].type = -1;
1562  (*line_info)[ch].search_cnt = -1;
1563  (*line_info)[ch].syntax = mutt_mem_malloc(sizeof(struct Syntax));
1564  ((*line_info)[ch].syntax)[0].first = -1;
1565  ((*line_info)[ch].syntax)[0].last = -1;
1566  }
1567  }
1568 
1569  if (flags & MUTT_PAGER_LOGS)
1570  {
1571  /* determine the line class */
1572  if (fill_buffer(f, last_pos, (*line_info)[n].offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1573  {
1574  if (change_last)
1575  (*last)--;
1576  goto out;
1577  }
1578 
1579  (*line_info)[n].type = MT_COLOR_MESSAGE_LOG;
1580  if (buf[11] == 'M')
1581  (*line_info)[n].syntax[0].color = MT_COLOR_MESSAGE;
1582  else if (buf[11] == 'E')
1583  (*line_info)[n].syntax[0].color = MT_COLOR_ERROR;
1584  else
1585  (*line_info)[n].syntax[0].color = MT_COLOR_NORMAL;
1586  }
1587 
1588  /* only do color highlighting if we are viewing a message */
1589  if (flags & (MUTT_SHOWCOLOR | MUTT_TYPES))
1590  {
1591  if ((*line_info)[n].type == -1)
1592  {
1593  /* determine the line class */
1594  if (fill_buffer(f, last_pos, (*line_info)[n].offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1595  {
1596  if (change_last)
1597  (*last)--;
1598  goto out;
1599  }
1600 
1601  resolve_types((char *) fmt, (char *) buf, *line_info, n, *last,
1602  quote_list, q_level, force_redraw, flags & MUTT_SHOWCOLOR);
1603 
1604  /* avoid race condition for continuation lines when scrolling up */
1605  for (m = n + 1; m < *last && (*line_info)[m].offset && (*line_info)[m].continuation; m++)
1606  (*line_info)[m].type = (*line_info)[n].type;
1607  }
1608 
1609  /* this also prevents searching through the hidden lines */
1610  if ((flags & MUTT_HIDE) && (*line_info)[n].type == MT_COLOR_QUOTED)
1611  flags = 0; /* MUTT_NOSHOW */
1612  }
1613 
1614  /* At this point, (*line_info[n]).quote may still be undefined. We
1615  * don't want to compute it every time MUTT_TYPES is set, since this
1616  * would slow down the "bottom" function unacceptably. A compromise
1617  * solution is hence to call regexec() again, just to find out the
1618  * length of the quote prefix.
1619  */
1620  if ((flags & MUTT_SHOWCOLOR) && !(*line_info)[n].continuation &&
1621  ((*line_info)[n].type == MT_COLOR_QUOTED) && !(*line_info)[n].quote)
1622  {
1623  if (fill_buffer(f, last_pos, (*line_info)[n].offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1624  {
1625  if (change_last)
1626  (*last)--;
1627  goto out;
1628  }
1629  if (QuoteRegex && QuoteRegex->regex &&
1630  regexec(QuoteRegex->regex, (char *) fmt, 1, pmatch, 0) == 0)
1631  {
1632  (*line_info)[n].quote =
1633  classify_quote(quote_list, (char *) fmt + pmatch[0].rm_so,
1634  pmatch[0].rm_eo - pmatch[0].rm_so, force_redraw, q_level);
1635  }
1636  else
1637  {
1638  goto out;
1639  }
1640  }
1641 
1642  if ((flags & MUTT_SEARCH) && !(*line_info)[n].continuation &&
1643  (*line_info)[n].search_cnt == -1)
1644  {
1645  if (fill_buffer(f, last_pos, (*line_info)[n].offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1646  {
1647  if (change_last)
1648  (*last)--;
1649  goto out;
1650  }
1651 
1652  offset = 0;
1653  (*line_info)[n].search_cnt = 0;
1654  while (regexec(search_re, (char *) fmt + offset, 1, pmatch,
1655  (offset ? REG_NOTBOL : 0)) == 0)
1656  {
1657  if (++((*line_info)[n].search_cnt) > 1)
1658  {
1659  mutt_mem_realloc(&((*line_info)[n].search),
1660  ((*line_info)[n].search_cnt) * sizeof(struct Syntax));
1661  }
1662  else
1663  (*line_info)[n].search = mutt_mem_malloc(sizeof(struct Syntax));
1664  pmatch[0].rm_so += offset;
1665  pmatch[0].rm_eo += offset;
1666  ((*line_info)[n].search)[(*line_info)[n].search_cnt - 1].first = pmatch[0].rm_so;
1667  ((*line_info)[n].search)[(*line_info)[n].search_cnt - 1].last = pmatch[0].rm_eo;
1668 
1669  if (pmatch[0].rm_eo == pmatch[0].rm_so)
1670  offset++; /* avoid degenerate cases */
1671  else
1672  offset = pmatch[0].rm_eo;
1673  if (!fmt[offset])
1674  break;
1675  }
1676  }
1677 
1678  if (!(flags & MUTT_SHOW) && (*line_info)[n + 1].offset > 0)
1679  {
1680  /* we've already scanned this line, so just exit */
1681  rc = 0;
1682  goto out;
1683  }
1684  if ((flags & MUTT_SHOWCOLOR) && *force_redraw && (*line_info)[n + 1].offset > 0)
1685  {
1686  /* no need to try to display this line... */
1687  rc = 1;
1688  goto out; /* fake display */
1689  }
1690 
1691  b_read = fill_buffer(f, last_pos, (*line_info)[n].offset, &buf, &fmt, &buflen, &buf_ready);
1692  if (b_read < 0)
1693  {
1694  if (change_last)
1695  (*last)--;
1696  goto out;
1697  }
1698 
1699  /* now chose a good place to break the line */
1700  cnt = format_line(line_info, n, buf, flags, 0, b_read, &ch, &vch, &col, &special, pager_window);
1701  buf_ptr = buf + cnt;
1702 
1703  /* move the break point only if smart_wrap is set */
1704  if (SmartWrap)
1705  {
1706  if ((cnt < b_read) && (ch != -1) && !ISHEADER((*line_info)[n].type) &&
1707  !ISSPACE(buf[cnt]))
1708  {
1709  buf_ptr = buf + ch;
1710  /* skip trailing blanks */
1711  while (ch && (buf[ch] == ' ' || buf[ch] == '\t' || buf[ch] == '\r'))
1712  ch--;
1713  /* A very long word with leading spaces causes infinite
1714  * wrapping when MUTT_PAGER_NSKIP is set. A folded header
1715  * with a single long word shouldn't be smartwrapped
1716  * either. So just disable smart_wrap if it would wrap at the
1717  * beginning of the line. */
1718  if (!ch)
1719  buf_ptr = buf + cnt;
1720  else
1721  cnt = ch + 1;
1722  }
1723  if (!(flags & MUTT_PAGER_NSKIP))
1724  {
1725  /* skip leading blanks on the next line too */
1726  while (*buf_ptr == ' ' || *buf_ptr == '\t')
1727  buf_ptr++;
1728  }
1729  }
1730 
1731  if (*buf_ptr == '\r')
1732  buf_ptr++;
1733  if (*buf_ptr == '\n')
1734  buf_ptr++;
1735 
1736  if ((int) (buf_ptr - buf) < b_read && !(*line_info)[n + 1].continuation)
1737  append_line(*line_info, n, (int) (buf_ptr - buf));
1738  (*line_info)[n + 1].offset = (*line_info)[n].offset + (long) (buf_ptr - buf);
1739 
1740  /* if we don't need to display the line we are done */
1741  if (!(flags & MUTT_SHOW))
1742  {
1743  rc = 0;
1744  goto out;
1745  }
1746 
1747  /* display the line */
1748  format_line(line_info, n, buf, flags, &a, cnt, &ch, &vch, &col, &special, pager_window);
1749 
1750 /* avoid a bug in ncurses... */
1751 #ifndef USE_SLANG_CURSES
1752  if (col == 0)
1753  {
1754  NORMAL_COLOR;
1755  addch(' ');
1756  }
1757 #endif
1758 
1759  /* end the last color pattern (needed by S-Lang) */
1760  if (special || (col != pager_window->cols && (flags & (MUTT_SHOWCOLOR | MUTT_SEARCH))))
1761  resolve_color(*line_info, n, vch, flags, 0, &a);
1762 
1763  /* Fill the blank space at the end of the line with the prevailing color.
1764  * ncurses does an implicit clrtoeol() when you do addch('\n') so we have
1765  * to make sure to reset the color *after* that
1766  */
1767  if (flags & MUTT_SHOWCOLOR)
1768  {
1769  m = ((*line_info)[n].continuation) ? ((*line_info)[n].syntax)[0].first : n;
1770  if ((*line_info)[m].type == MT_COLOR_HEADER)
1771  def_color = ((*line_info)[m].syntax)[0].color;
1772  else
1773  def_color = ColorDefs[(*line_info)[m].type];
1774 
1775  ATTRSET(def_color);
1776  }
1777 
1778  if (col < pager_window->cols)
1779  mutt_window_clrtoeol(pager_window);
1780 
1781  /* reset the color back to normal. This *must* come after the
1782  * clrtoeol, otherwise the color for this line will not be
1783  * filled to the right margin.
1784  */
1785  if (flags & MUTT_SHOWCOLOR)
1786  NORMAL_COLOR;
1787 
1788  /* build a return code */
1789  if (!(flags & MUTT_SHOW))
1790  flags = 0;
1791 
1792  rc = flags;
1793 
1794 out:
1795  FREE(&buf);
1796  FREE(&fmt);
1797  return rc;
1798 }
#define MUTT_TYPES
Definition: pager.h:46
static int format_line(struct Line **line_info, int n, unsigned char *buf, int flags, struct AnsiAttr *pa, int cnt, int *pspace, int *pvch, int *pcol, int *pspecial, struct MuttWindow *pager_window)
Display a line of text in the pager.
Definition: pager.c:1331
#define NORMAL_COLOR
Definition: mutt_curses.h:235
#define MUTT_SHOWCOLOR
Definition: pager.h:43
Pager: quoted text.
Definition: mutt_curses.h:125
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:81
regex_t * regex
compiled expression
Definition: regex3.h:60
#define ISHEADER(x)
Definition: pager.c:97
Message headers (takes a pattern)
Definition: mutt_curses.h:135
An ANSI escape sequence.
Definition: pager.c:195
Highlighting for a line of text.
Definition: pager.c:162
Plain text.
Definition: mutt_curses.h:130
#define ATTRSET
Definition: mutt_curses.h:228
static void resolve_types(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: pager.c:892
#define ISSPACE(c)
Definition: string2.h:40
#define MUTT_PAGER_NSKIP
preserve whitespace with smartwrap
Definition: pager.h:50
static void resolve_color(struct Line *line_info, int n, int cnt, int flags, int special, struct AnsiAttr *a)
Set the colour for a line of text.
Definition: pager.c:285
#define MUTT_SHOW
Definition: pager.h:47
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:124
bool SmartWrap
Config: Wrap text at word boundaries.
Definition: pager.c:93
Informational message.
Definition: mutt_curses.h:136
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:99
A line of text in the pager.
Definition: pager.c:172
#define MUTT_HIDE
Definition: pager.h:44
int ColorDefs[MT_COLOR_MAX]
Array of all fixed colours, see enum ColorId.
Definition: color.c:51
static int fill_buffer(FILE *f, 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: pager.c:1257
#define MUTT_PAGER_LOGS
Logview mode.
Definition: pager.h:56
Menu showing log messages.
Definition: mutt_curses.h:153
#define MUTT_SEARCH
Definition: pager.h:45
#define FREE(x)
Definition: memory.h:46
Error message.
Definition: mutt_curses.h:131
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: pager.c:517
static void append_line(struct Line *line_info, int n, int cnt)
Add a new Line to the array.
Definition: pager.c:417
WHERE struct Regex * QuoteRegex
Config: Regex to match quoted text in a reply.
Definition: globals.h:182

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

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 1808 of file pager.c.

1809 {
1810  while (cur > 0 && nlines > 0)
1811  {
1812  cur--;
1813  if (!hiding || info[cur].type != MT_COLOR_QUOTED)
1814  nlines--;
1815  }
1816 
1817  return cur;
1818 }
Pager: quoted text.
Definition: mutt_curses.h:125

+ Here is the caller graph for this function:

void mutt_clear_pager_position ( void  )

Reset the pager's viewing position.

Definition at line 1848 of file pager.c.

1849 {
1850  TopLine = 0;
1851  OldHdr = NULL;
1852 }
static int TopLine
Definition: pager.c:110
static struct Email * OldHdr
Definition: pager.c:111

+ Here is the caller graph for this function:

static void pager_custom_redraw ( struct Menu pager_menu)
static

Redraw the pager window - Implements Menu::menu_custom_redraw()

Definition at line 1896 of file pager.c.

1897 {
1898  struct PagerRedrawData *rd = pager_menu->redraw_data;
1899  char buffer[LONG_STRING];
1900 
1901  if (!rd)
1902  return;
1903 
1904  if (pager_menu->redraw & REDRAW_FULL)
1905  {
1907  NORMAL_COLOR;
1908  /* clear() doesn't optimize screen redraws */
1909  move(0, 0);
1910  clrtobot();
1911 
1912  if (IsHeader(rd->extra) && Context && ((Context->mailbox->vcount + 1) < PagerIndexLines))
1913  rd->indexlen = Context->mailbox->vcount + 1;
1914  else
1915  rd->indexlen = PagerIndexLines;
1916 
1917  rd->indicator = rd->indexlen / 3;
1918 
1919  memcpy(rd->pager_window, MuttIndexWindow, sizeof(struct MuttWindow));
1920  memcpy(rd->pager_status_window, MuttStatusWindow, sizeof(struct MuttWindow));
1921  rd->index_status_window->rows = 0;
1922  rd->index_window->rows = 0;
1923 
1924  if (IsHeader(rd->extra) && PagerIndexLines)
1925  {
1926  memcpy(rd->index_window, MuttIndexWindow, sizeof(struct MuttWindow));
1927  rd->index_window->rows = rd->indexlen > 0 ? rd->indexlen - 1 : 0;
1928 
1929  if (StatusOnTop)
1930  {
1931  memcpy(rd->index_status_window, MuttStatusWindow, sizeof(struct MuttWindow));
1932 
1933  memcpy(rd->pager_status_window, MuttIndexWindow, sizeof(struct MuttWindow));
1934  rd->pager_status_window->rows = 1;
1936 
1937  rd->pager_window->rows -=
1939  rd->pager_window->row_offset +=
1941  }
1942  else
1943  {
1944  memcpy(rd->index_status_window, MuttIndexWindow, sizeof(struct MuttWindow));
1945  rd->index_status_window->rows = 1;
1947 
1948  rd->pager_window->rows -=
1950  rd->pager_window->row_offset +=
1952  }
1953  }
1954 
1955  if (Help)
1956  {
1960  NORMAL_COLOR;
1961  }
1962 
1963  if (Resize)
1964  {
1966  if (rd->search_compiled)
1967  {
1968  int flags = mutt_mb_is_lower(rd->searchbuf) ? REG_ICASE : 0;
1969  const int err = REGCOMP(&rd->search_re, rd->searchbuf, REG_NEWLINE | flags);
1970  if (err != 0)
1971  {
1972  regerror(err, &rd->search_re, buffer, sizeof(buffer));
1973  mutt_error("%s", buffer);
1974  rd->search_compiled = false;
1975  }
1976  else
1977  {
1978  rd->search_flag = MUTT_SEARCH;
1980  }
1981  }
1982  rd->lines = Resize->line;
1983  pager_menu->redraw |= REDRAW_FLOW;
1984 
1985  FREE(&Resize);
1986  }
1987 
1988  if (IsHeader(rd->extra) && PagerIndexLines)
1989  {
1990  if (!rd->index)
1991  {
1992  /* only allocate the space if/when we need the index.
1993  Initialise the menu as per the main index */
1994  rd->index = mutt_menu_new(MENU_MAIN);
1996  rd->index->menu_color = index_color;
1997  rd->index->max = Context ? Context->mailbox->vcount : 0;
1998  rd->index->current = rd->extra->email->virtual;
1999  rd->index->indexwin = rd->index_window;
2000  rd->index->statuswin = rd->index_status_window;
2001  }
2002 
2003  NORMAL_COLOR;
2004  rd->index->pagelen = rd->index_window->rows;
2005 
2006  /* some fudge to work out whereabouts the indicator should go */
2007  if (rd->index->current - rd->indicator < 0)
2008  rd->index->top = 0;
2009  else if (rd->index->max - rd->index->current < rd->index->pagelen - rd->indicator)
2010  rd->index->top = rd->index->max - rd->index->pagelen;
2011  else
2012  rd->index->top = rd->index->current - rd->indicator;
2013 
2014  menu_redraw_index(rd->index);
2015  }
2016 
2017  pager_menu->redraw |= REDRAW_BODY | REDRAW_INDEX | REDRAW_STATUS;
2018 #ifdef USE_SIDEBAR
2019  pager_menu->redraw |= REDRAW_SIDEBAR;
2020 #endif
2021  mutt_show_error();
2022  }
2023 
2024  if (pager_menu->redraw & REDRAW_FLOW)
2025  {
2026  if (!(rd->flags & MUTT_PAGER_RETWINCH))
2027  {
2028  rd->lines = -1;
2029  for (int i = 0; i <= rd->topline; i++)
2030  if (!rd->line_info[i].continuation)
2031  rd->lines++;
2032  for (int i = 0; i < rd->max_line; i++)
2033  {
2034  rd->line_info[i].offset = 0;
2035  rd->line_info[i].type = -1;
2036  rd->line_info[i].continuation = 0;
2037  rd->line_info[i].chunks = 0;
2038  rd->line_info[i].search_cnt = -1;
2039  rd->line_info[i].quote = NULL;
2040 
2041  mutt_mem_realloc(&(rd->line_info[i].syntax), sizeof(struct Syntax));
2042  if (rd->search_compiled && rd->line_info[i].search)
2043  FREE(&(rd->line_info[i].search));
2044  }
2045 
2046  rd->last_line = 0;
2047  rd->topline = 0;
2048  }
2049  int i = -1;
2050  int j = -1;
2051  while (display_line(rd->fp, &rd->last_pos, &rd->line_info, ++i, &rd->last_line,
2052  &rd->max_line, rd->has_types | rd->search_flag | (rd->flags & MUTT_PAGER_NOWRAP),
2053  &rd->quote_list, &rd->q_level, &rd->force_redraw,
2054  &rd->search_re, rd->pager_window) == 0)
2055  {
2056  if (!rd->line_info[i].continuation && ++j == rd->lines)
2057  {
2058  rd->topline = i;
2059  if (!rd->search_flag)
2060  break;
2061  }
2062  }
2063  }
2064 
2065 #ifdef USE_SIDEBAR
2066  if (pager_menu->redraw & REDRAW_SIDEBAR)
2067  {
2068  menu_redraw_sidebar(pager_menu);
2069  }
2070 #endif
2071 
2072  if ((pager_menu->redraw & REDRAW_BODY) || rd->topline != rd->oldtopline)
2073  {
2074  do
2075  {
2076  mutt_window_move(rd->pager_window, 0, 0);
2077  rd->curline = rd->topline;
2078  rd->oldtopline = rd->topline;
2079  rd->lines = 0;
2080  rd->force_redraw = false;
2081 
2082  while (rd->lines < rd->pager_window->rows &&
2083  rd->line_info[rd->curline].offset <= rd->sb.st_size - 1)
2084  {
2085  if (display_line(rd->fp, &rd->last_pos, &rd->line_info, rd->curline,
2086  &rd->last_line, &rd->max_line,
2087  (rd->flags & MUTT_DISPLAYFLAGS) | rd->hide_quoted |
2088  rd->search_flag | (rd->flags & MUTT_PAGER_NOWRAP),
2089  &rd->quote_list, &rd->q_level, &rd->force_redraw,
2090  &rd->search_re, rd->pager_window) > 0)
2091  {
2092  rd->lines++;
2093  }
2094  rd->curline++;
2095  mutt_window_move(rd->pager_window, rd->lines, 0);
2096  }
2097  rd->last_offset = rd->line_info[rd->curline].offset;
2098  } while (rd->force_redraw);
2099 
2101  while (rd->lines < rd->pager_window->rows)
2102  {
2104  if (Tilde)
2105  addch('~');
2106  rd->lines++;
2107  mutt_window_move(rd->pager_window, rd->lines, 0);
2108  }
2109  NORMAL_COLOR;
2110 
2111  /* We are going to update the pager status bar, so it isn't
2112  * necessary to reset to normal color now. */
2113 
2114  pager_menu->redraw |= REDRAW_STATUS; /* need to update the % seen */
2115  }
2116 
2117  if (pager_menu->redraw & REDRAW_STATUS)
2118  {
2119  struct HdrFormatInfo hfi;
2120  char pager_progress_str[65]; /* Lots of space for translations */
2121 
2122  hfi.ctx = Context;
2123  hfi.pager_progress = pager_progress_str;
2124 
2125  if (rd->last_pos < rd->sb.st_size - 1)
2126  {
2127  snprintf(pager_progress_str, sizeof(pager_progress_str), OFF_T_FMT "%%",
2128  (100 * rd->last_offset / rd->sb.st_size));
2129  }
2130  else
2131  {
2132  const char *msg = (rd->topline == 0) ?
2133  /* L10N: Status bar message: the entire email is visible in the pager */
2134  _("all") :
2135  /* L10N: Status bar message: the end of the email is visible in the pager */
2136  _("end");
2137  mutt_str_strfcpy(pager_progress_str, msg, sizeof(pager_progress_str));
2138  }
2139 
2140  /* print out the pager status bar */
2143 
2144  if (IsHeader(rd->extra) || IsMsgAttach(rd->extra))
2145  {
2146  size_t l1 = rd->pager_status_window->cols * MB_LEN_MAX;
2147  size_t l2 = sizeof(buffer);
2148  hfi.email = (IsHeader(rd->extra)) ? rd->extra->email : rd->extra->bdy->email;
2149  mutt_make_string_info(buffer, l1 < l2 ? l1 : l2, rd->pager_status_window->cols,
2151  mutt_draw_statusline(rd->pager_status_window->cols, buffer, l2);
2152  }
2153  else
2154  {
2155  char bn[STRING];
2156  snprintf(bn, sizeof(bn), "%s (%s)", rd->banner, pager_progress_str);
2157  mutt_draw_statusline(rd->pager_status_window->cols, bn, sizeof(bn));
2158  }
2159  NORMAL_COLOR;
2160  if (TsEnabled && TsSupported && rd->index)
2161  {
2162  menu_status_line(buffer, sizeof(buffer), rd->index, NONULL(TsStatusFormat));
2163  mutt_ts_status(buffer);
2164  menu_status_line(buffer, sizeof(buffer), rd->index, NONULL(TsIconFormat));
2165  mutt_ts_icon(buffer);
2166  }
2167  }
2168 
2169  if ((pager_menu->redraw & REDRAW_INDEX) && rd->index)
2170  {
2171  /* redraw the pager_index indicator, because the
2172  * flags for this message might have changed. */
2173  if (rd->index_window->rows > 0)
2175 
2176  /* print out the index status bar */
2177  menu_status_line(buffer, sizeof(buffer), rd->index, NONULL(StatusFormat));
2178 
2181  mutt_draw_statusline(rd->index_status_window->cols, buffer, sizeof(buffer));
2182  NORMAL_COLOR;
2183  }
2184 
2185  pager_menu->redraw = 0;
2186 }
struct Context * ctx
Definition: hdrline.h:45
The "current" mailbox.
Definition: context.h:36
#define NONULL(x)
Definition: string2.h:39
int redraw
when to redraw the screen
Definition: menu.h:63
bool Tilde
Config: Character to pad blank lines in the pager.
Definition: pager.c:95
const char * banner
Definition: pager.c:1885
struct MuttWindow * MuttStatusWindow
Status Window.
Definition: mutt_window.c:40
#define MUTT_DISPLAYFLAGS
Definition: pager.h:58
short chunks
Definition: pager.c:177
#define IsHeader(x)
Definition: pager.c:101
Keep track when the pager needs redrawing.
Definition: pager.c:1857
#define NORMAL_COLOR
Definition: mutt_curses.h:235
Pager: empty lines after message.
Definition: mutt_curses.h:132
WHERE bool Help
Config: Display a help line with common key bindings.
Definition: globals.h:222
int virtual
virtual message number
Definition: email.h:89
struct MuttWindow * pager_status_window
Definition: pager.c:1878
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:81
void index_make_entry(char *buf, size_t buflen, struct Menu *menu, int line)
Format a menu item for the index list - Implements Menu::menu_make_entry()
Definition: index.c:657
#define _(a)
Definition: message.h:28
struct Line * line_info
Definition: pager.c:1888
int index_color(int line)
Calculate the colour for a line of the index - Implements Menu::menu_color()
Definition: index.c:729
void menu_status_line(char *buf, size_t buflen, struct Menu *menu, const char *p)
Create the status line.
Definition: status.c:387
struct MuttWindow * MuttHelpWindow
Help Window.
Definition: mutt_window.c:38
A division of the screen.
Definition: mutt_window.h:33
bool search_back
Definition: pager.c:212
Highlighting for a line of text.
Definition: pager.c:162
bool search_compiled
Definition: pager.c:211
make sure that all chars are printable
Definition: format_flags.h:35
short type
Definition: pager.c:175
#define MUTT_PAGER_NOWRAP
format for term width, ignore $wrap
Definition: pager.h:55
#define LONG_STRING
Definition: string2.h:36
void(* menu_make_entry)(char *buf, size_t buflen, struct Menu *menu, int line)
Format a item for a menu.
Definition: menu.h:96
int vcount
the number of virtual messages
Definition: mailbox.h:96
void mutt_paddstr(int n, const char *s)
Display a string on screen, padded if necessary.
Definition: curs_lib.c:1083
LOFF_T last_pos
Definition: pager.c:1874
struct MuttWindow * index_status_window
Definition: pager.c:1876
int line
Definition: pager.c:210
void * redraw_data
Definition: menu.h:126
struct Mailbox * mailbox
Definition: context.h:50
Data passed to index_format_str()
Definition: hdrline.h:43
LOFF_T last_offset
Definition: pager.c:1875
FILE * fp
Definition: pager.c:1889
short continuation
Definition: pager.c:176
WHERE char * TsIconFormat
Config: printf-like format string for the terminal&#39;s icon title.
Definition: globals.h:150
int hide_quoted
Definition: pager.c:1871
bool force_redraw
Definition: pager.c:1869
void mutt_window_reflow(void)
Resize the Windows to fit the screen.
Definition: mutt_window.c:222
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:124
static int display_line(FILE *f, LOFF_T *last_pos, struct Line **line_info, int n, int *last, int *max, int flags, struct QClass **quote_list, int *q_level, bool *force_redraw, regex_t *search_re, struct MuttWindow *pager_window)
Print a line on screen.
Definition: pager.c:1530
int oldtopline
Definition: pager.c:1863
struct Pager * extra
Definition: pager.c:1860
struct QClass * quote
Definition: pager.c:181
short search_cnt
Definition: pager.c:178
struct MuttWindow * statuswin
Definition: menu.h:70
WHERE struct Context * Context
Definition: globals.h:41
bool search_compiled
Definition: pager.c:1882
struct Syntax * syntax
Definition: pager.c:179
struct MuttWindow * MuttIndexWindow
Index Window.
Definition: mutt_window.c:39
struct MuttWindow * pager_window
Definition: pager.c:1879
struct MuttWindow * indexwin
Definition: menu.h:69
struct MuttWindow * index_window
Definition: pager.c:1877
int top
entry that is the top of the current page
Definition: menu.h:84
WHERE bool StatusOnTop
Config: Display the status bar at the top.
Definition: globals.h:257
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:742
WHERE char * PagerFormat
Config: printf-like format string for the pager&#39;s status bar.
Definition: globals.h:136
WHERE char * TsStatusFormat
Config: printf-like format string for the terminal&#39;s status (window title)
Definition: globals.h:149
int pagelen
number of entries per screen
Definition: menu.h:66
bool TsSupported
Terminal Setting is supported.
Definition: terminal.c:43
void mutt_make_string_info(char *buf, size_t buflen, int cols, const char *s, struct HdrFormatInfo *hfi, enum FormatFlag flags)
Create pager status bar string.
Definition: hdrline.c:1476
int mutt_window_move(struct MuttWindow *win, int row, int col)
Move the cursor in a Window.
Definition: mutt_window.c:162
int(* menu_color)(int line)
Calculate the colour for a line of the menu.
Definition: menu.h:120
#define SETCOLOR(X)
Definition: mutt_curses.h:220
int max
the number of entries in the menu
Definition: menu.h:62
WHERE char * StatusFormat
Config: printf-like format string for the index&#39;s status line.
Definition: globals.h:148
struct Menu * index
the Pager Index (PI)
Definition: pager.c:1880
struct QClass * quote_list
Definition: pager.c:1873
int indicator
the indicator line of the PI
Definition: pager.c:1862
struct Email * email
current message
Definition: pager.h:66
#define MUTT_PAGER_RETWINCH
need reformatting on SIGWINCH
Definition: pager.h:52
#define MUTT_SEARCH
Definition: pager.h:45
#define STRING
Definition: string2.h:35
char * searchbuf
Definition: pager.c:1887
#define mutt_error(...)
Definition: logging.h:88
int row_offset
Definition: mutt_window.h:37
#define FREE(x)
Definition: memory.h:46
Status bar.
Definition: mutt_curses.h:128
struct Syntax * search
Definition: pager.c:180
WHERE bool TsEnabled
Config: Allow NeoMutt to set the terminal status line and icon.
Definition: globals.h:260
#define IsMsgAttach(x)
Definition: pager.c:100
void mutt_ts_icon(char *str)
Set the icon in the terminal title bar.
Definition: terminal.c:118
void mutt_show_error(void)
Show the user an error message.
Definition: curs_lib.c:484
int search_flag
Definition: pager.c:1883
Keep track of screen resizing.
Definition: pager.c:208
void mutt_ts_status(char *str)
Set the text of the terminal title bar.
Definition: terminal.c:104
struct stat sb
Definition: pager.c:1890
int current
current entry
Definition: menu.h:61
char * helpstr
Definition: pager.c:1886
LOFF_T offset
Definition: pager.c:174
short PagerIndexLines
Config: Number of index lines to display above the pager.
Definition: pager.c:89
bool mutt_mb_is_lower(const char *s)
Does a multi-byte string contain only lowercase characters?
Definition: mbyte.c:346
struct Email * email
header information for message/rfc822
Definition: body.h:59
Index panel (list of emails)
Definition: keymap.h:73
void mutt_draw_statusline(int cols, const char *buf, size_t buflen)
Draw a highlighted status bar.
Definition: index.c:758
#define REGCOMP(X, Y, Z)
Compile a regular expression.
Definition: regex3.h:52
struct Body * bdy
current attachment
Definition: pager.h:67
regex_t search_re
Definition: pager.c:1881
bool search_back
Definition: pager.c:1884

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

int mutt_pager ( const char *  banner,
const char *  fname,
int  flags,
struct Pager extra 
)

Display a file, or help, in a window.

Parameters
bannerTitle to display in status bar
fnameName of file to read
flagsFlags, e.g. MUTT_SHOWCOLOR
extraInfo about email to display
Return values
0Success
-1Error

This pager is actually not so simple as it once was. It now operates in two modes: one for viewing messages and the other for viewing help. These can be distinguished by whether or not hdr'' is NULL. Thehdr'' arg is there so that we can do operations on the current message without the need to pop back out to the main-menu.

Definition at line 2203 of file pager.c.

2204 {
2205  static char searchbuf[STRING] = "";
2206  char buffer[LONG_STRING];
2207  char helpstr[SHORT_STRING * 2];
2208  char tmphelp[SHORT_STRING * 2];
2209  int i, ch = 0, rc = -1;
2210  int err, first = 1;
2211  int r = -1, searchctx = 0;
2212  bool wrapped = false;
2213 
2214  struct Menu *pager_menu = NULL;
2215  int old_PagerIndexLines; /* some people want to resize it
2216  * while inside the pager... */
2217  int index_hint = 0; /* used to restore cursor position */
2218  int oldcount = -1;
2219  int check;
2220 
2221 #ifdef USE_NNTP
2222  char *followup_to = NULL;
2223 #endif
2224 
2225  struct PagerRedrawData rd;
2226 
2227  if (!(flags & MUTT_SHOWCOLOR))
2228  flags |= MUTT_SHOWFLAT;
2229 
2230  memset(&rd, 0, sizeof(rd));
2231  rd.banner = banner;
2232  rd.flags = flags;
2233  rd.extra = extra;
2234  rd.indexlen = PagerIndexLines;
2235  rd.indicator = rd.indexlen / 3;
2236  rd.helpstr = helpstr;
2237  rd.searchbuf = searchbuf;
2238  rd.has_types = (IsHeader(extra) || (flags & MUTT_SHOWCOLOR)) ? MUTT_TYPES : 0; /* main message or rfc822 attachment */
2239 
2240  rd.fp = fopen(fname, "r");
2241  if (!rd.fp)
2242  {
2243  mutt_perror(fname);
2244  return -1;
2245  }
2246 
2247  if (stat(fname, &rd.sb) != 0)
2248  {
2249  mutt_perror(fname);
2250  mutt_file_fclose(&rd.fp);
2251  return -1;
2252  }
2253  unlink(fname);
2254 
2255  /* Initialize variables */
2256 
2257  if (Context && IsHeader(extra) && !extra->email->read)
2258  {
2259  Context->msgnotreadyet = extra->email->msgno;
2261  }
2262 
2263  rd.max_line = LINES; /* number of lines on screen, from curses */
2264  rd.line_info = mutt_mem_calloc(rd.max_line, sizeof(struct Line));
2265  for (i = 0; i < rd.max_line; i++)
2266  {
2267  rd.line_info[i].type = -1;
2268  rd.line_info[i].search_cnt = -1;
2269  rd.line_info[i].syntax = mutt_mem_malloc(sizeof(struct Syntax));
2270  (rd.line_info[i].syntax)[0].first = -1;
2271  (rd.line_info[i].syntax)[0].last = -1;
2272  }
2273 
2274  mutt_compile_help(helpstr, sizeof(helpstr), MENU_PAGER, PagerHelp);
2275  if (IsHeader(extra))
2276  {
2277  mutt_str_strfcpy(tmphelp, helpstr, sizeof(tmphelp));
2278  mutt_compile_help(buffer, sizeof(buffer), MENU_PAGER,
2279 #ifdef USE_NNTP
2280  (Context && (Context->mailbox->magic == MUTT_NNTP)) ?
2282 #endif
2283  PagerHelpExtra);
2284  snprintf(helpstr, sizeof(helpstr), "%s %s", tmphelp, buffer);
2285  }
2286  if (!InHelp)
2287  {
2288  mutt_str_strfcpy(tmphelp, helpstr, sizeof(tmphelp));
2289  mutt_make_help(buffer, sizeof(buffer), _("Help"), MENU_PAGER, OP_HELP);
2290  snprintf(helpstr, sizeof(helpstr), "%s %s", tmphelp, buffer);
2291  }
2292 
2293  rd.index_status_window = mutt_mem_calloc(1, sizeof(struct MuttWindow));
2294  rd.index_window = mutt_mem_calloc(1, sizeof(struct MuttWindow));
2295  rd.pager_status_window = mutt_mem_calloc(1, sizeof(struct MuttWindow));
2296  rd.pager_window = mutt_mem_calloc(1, sizeof(struct MuttWindow));
2297 
2298  pager_menu = mutt_menu_new(MENU_PAGER);
2300  pager_menu->redraw_data = &rd;
2301  mutt_menu_push_current(pager_menu);
2302 
2303  while (ch != -1)
2304  {
2305  mutt_curs_set(0);
2306 
2307  pager_custom_redraw(pager_menu);
2308 
2309  if (BrailleFriendly)
2310  {
2311  if (braille_line != -1)
2312  {
2313  move(braille_line + 1, 0);
2314  braille_line = -1;
2315  }
2316  }
2317  else
2318  mutt_window_move(rd.pager_status_window, 0, rd.pager_status_window->cols - 1);
2319 
2320  mutt_refresh();
2321 
2322  if (IsHeader(extra) && OldHdr == extra->email && TopLine != rd.topline &&
2323  rd.line_info[rd.curline].offset < rd.sb.st_size - 1)
2324  {
2325  if (TopLine - rd.topline > rd.lines)
2326  rd.topline += rd.lines;
2327  else
2328  rd.topline = TopLine;
2329  continue;
2330  }
2331  else
2332  OldHdr = NULL;
2333 
2334  ch = km_dokey(MENU_PAGER);
2335  if (ch >= 0)
2336  {
2337  mutt_clear_error();
2338  }
2339  mutt_curs_set(1);
2340 
2341  bool do_new_mail = false;
2342 
2343  if (Context && Context->mailbox && !OptAttachMsg)
2344  {
2345  oldcount = Context->mailbox->msg_count;
2346  /* check for new mail */
2347  check = mx_mbox_check(Context, &index_hint);
2348  if (check < 0)
2349  {
2350  if (!Context->mailbox || Context->mailbox->path[0] == '\0')
2351  {
2352  /* fatal error occurred */
2354  pager_menu->redraw = REDRAW_FULL;
2355  break;
2356  }
2357  }
2358  else if ((check == MUTT_NEW_MAIL) || (check == MUTT_REOPENED) || (check == MUTT_FLAGS))
2359  {
2360  /* notify user of newly arrived mail */
2361  if (check == MUTT_NEW_MAIL)
2362  {
2363  for (i = oldcount; i < Context->mailbox->msg_count; i++)
2364  {
2365  struct Email *e = Context->mailbox->hdrs[i];
2366 
2367  if (e && !e->read)
2368  {
2369  mutt_message(_("New mail in this mailbox"));
2370  do_new_mail = true;
2371  break;
2372  }
2373  }
2374  }
2375 
2376  if ((check == MUTT_NEW_MAIL) || (check == MUTT_REOPENED))
2377  {
2378  if (rd.index && Context)
2379  {
2380  /* After the mailbox has been updated,
2381  * rd.index->current might be invalid */
2382  rd.index->current =
2383  MIN(rd.index->current, (Context->mailbox->msg_count - 1));
2384  index_hint =
2385  Context->mailbox->hdrs[Context->mailbox->v2r[rd.index->current]]->index;
2386 
2387  bool q = Context->mailbox->quiet;
2388  Context->mailbox->quiet = true;
2389  update_index(rd.index, Context, check, oldcount, index_hint);
2390  Context->mailbox->quiet = q;
2391 
2392  rd.index->max = Context->mailbox->vcount;
2393 
2394  /* If these header pointers don't match, then our email may have
2395  * been deleted. Make the pointer safe, then leave the pager.
2396  * This have a unpleasant behaviour to close the pager even the
2397  * deleted message is not the opened one, but at least it's safe. */
2398  if (extra->email !=
2399  Context->mailbox->hdrs[Context->mailbox->v2r[rd.index->current]])
2400  {
2401  extra->email =
2402  Context->mailbox->hdrs[Context->mailbox->v2r[rd.index->current]];
2403  break;
2404  }
2405  }
2406 
2407  pager_menu->redraw = REDRAW_FULL;
2408  OptSearchInvalid = true;
2409  }
2410  }
2411 
2412  if (mutt_mailbox_notify() || do_new_mail)
2413  {
2414  if (BeepNew)
2415  beep();
2416  if (NewMailCommand)
2417  {
2418  char cmd[LONG_STRING];
2419  menu_status_line(cmd, sizeof(cmd), rd.index, NONULL(NewMailCommand));
2420  if (mutt_system(cmd) != 0)
2421  mutt_error(_("Error running \"%s\""), cmd);
2422  }
2423  }
2424  }
2425 
2426  if (SigWinch)
2427  {
2428  SigWinch = 0;
2430  clearok(stdscr, TRUE); /* force complete redraw */
2431 
2432  if (flags & MUTT_PAGER_RETWINCH)
2433  {
2434  /* Store current position. */
2435  rd.lines = -1;
2436  for (i = 0; i <= rd.topline; i++)
2437  if (!rd.line_info[i].continuation)
2438  rd.lines++;
2439 
2440  Resize = mutt_mem_malloc(sizeof(struct Resize));
2441 
2442  Resize->line = rd.lines;
2443  Resize->search_compiled = rd.search_compiled;
2444  Resize->search_back = rd.search_back;
2445 
2446  ch = -1;
2447  rc = OP_REFORMAT_WINCH;
2448  }
2449  else
2450  {
2451  /* note: mutt_resize_screen() -> mutt_window_reflow() sets
2452  * REDRAW_FULL and REDRAW_FLOW */
2453  ch = 0;
2454  }
2455  continue;
2456  }
2457 
2458  if (ch < 0)
2459  {
2460  ch = 0;
2462  continue;
2463  }
2464 
2465  rc = ch;
2466 
2467  switch (ch)
2468  {
2469  case OP_EXIT:
2470  rc = -1;
2471  ch = -1;
2472  break;
2473 
2474  case OP_QUIT:
2475  if (query_quadoption(Quit, _("Quit NeoMutt?")) == MUTT_YES)
2476  {
2477  /* avoid prompting again in the index menu */
2478  cs_str_native_set(Config, "quit", MUTT_YES, NULL);
2479  ch = -1;
2480  }
2481  break;
2482 
2483  case OP_NEXT_PAGE:
2484  if (rd.line_info[rd.curline].offset < rd.sb.st_size - 1)
2485  {
2486  rd.topline = up_n_lines(PagerContext, rd.line_info, rd.curline, rd.hide_quoted);
2487  }
2488  else if (PagerStop)
2489  {
2490  /* emulate "less -q" and don't go on to the next message. */
2491  mutt_error(_("Bottom of message is shown"));
2492  }
2493  else
2494  {
2495  /* end of the current message, so display the next message. */
2496  rc = OP_MAIN_NEXT_UNDELETED;
2497  ch = -1;
2498  }
2499  break;
2500 
2501  case OP_PREV_PAGE:
2502  if (rd.topline != 0)
2503  {
2504  rd.topline = up_n_lines(rd.pager_window->rows - PagerContext,
2505  rd.line_info, rd.topline, rd.hide_quoted);
2506  }
2507  else
2508  mutt_error(_("Top of message is shown"));
2509  break;
2510 
2511  case OP_NEXT_LINE:
2512  if (rd.line_info[rd.curline].offset < rd.sb.st_size - 1)
2513  {
2514  rd.topline++;
2515  if (rd.hide_quoted)
2516  {
2517  while (rd.line_info[rd.topline].type == MT_COLOR_QUOTED && rd.topline < rd.last_line)
2518  rd.topline++;
2519  }
2520  }
2521  else
2522  mutt_error(_("Bottom of message is shown"));
2523  break;
2524 
2525  case OP_PREV_LINE:
2526  if (rd.topline)
2527  rd.topline = up_n_lines(1, rd.line_info, rd.topline, rd.hide_quoted);
2528  else
2529  mutt_error(_("Top of message is shown"));
2530  break;
2531 
2532  case OP_PAGER_TOP:
2533  if (rd.topline)
2534  rd.topline = 0;
2535  else
2536  mutt_error(_("Top of message is shown"));
2537  break;
2538 
2539  case OP_HALF_UP:
2540  if (rd.topline)
2541  {
2542  rd.topline = up_n_lines(rd.pager_window->rows / 2, rd.line_info,
2543  rd.topline, rd.hide_quoted);
2544  }
2545  else
2546  mutt_error(_("Top of message is shown"));
2547  break;
2548 
2549  case OP_HALF_DOWN:
2550  if (rd.line_info[rd.curline].offset < rd.sb.st_size - 1)
2551  {
2552  rd.topline = up_n_lines(rd.pager_window->rows / 2, rd.line_info,
2553  rd.curline, rd.hide_quoted);
2554  }
2555  else if (PagerStop)
2556  {
2557  /* emulate "less -q" and don't go on to the next message. */
2558  mutt_error(_("Bottom of message is shown"));
2559  }
2560  else
2561  {
2562  /* end of the current message, so display the next message. */
2563  rc = OP_MAIN_NEXT_UNDELETED;
2564  ch = -1;
2565  }
2566  break;
2567 
2568  case OP_SEARCH_NEXT:
2569  case OP_SEARCH_OPPOSITE:
2570  if (rd.search_compiled)
2571  {
2572  wrapped = false;
2573 
2574  if (SearchContext > 0 && SearchContext < rd.pager_window->rows)
2575  searchctx = SearchContext;
2576  else
2577  searchctx = 0;
2578 
2579  search_next:
2580  if ((!rd.search_back && (ch == OP_SEARCH_NEXT)) ||
2581  (rd.search_back && (ch == OP_SEARCH_OPPOSITE)))
2582  {
2583  /* searching forward */
2584  for (i = wrapped ? 0 : rd.topline + searchctx + 1; i < rd.last_line; i++)
2585  {
2586  if ((!rd.hide_quoted || rd.line_info[i].type != MT_COLOR_QUOTED) &&
2587  !rd.line_info[i].continuation && rd.line_info[i].search_cnt > 0)
2588  {
2589  break;
2590  }
2591  }
2592 
2593  if (i < rd.last_line)
2594  rd.topline = i;
2595  else if (wrapped || !WrapSearch)
2596  mutt_error(_("Not found"));
2597  else
2598  {
2599  mutt_message(_("Search wrapped to top"));
2600  wrapped = true;
2601  goto search_next;
2602  }
2603  }
2604  else
2605  {
2606  /* searching backward */
2607  for (i = wrapped ? rd.last_line : rd.topline + searchctx - 1; i >= 0; i--)
2608  {
2609  if ((!rd.hide_quoted || (rd.has_types && rd.line_info[i].type != MT_COLOR_QUOTED)) &&
2610  !rd.line_info[i].continuation && rd.line_info[i].search_cnt > 0)
2611  {
2612  break;
2613  }
2614  }
2615 
2616  if (i >= 0)
2617  rd.topline = i;
2618  else if (wrapped || !WrapSearch)
2619  mutt_error(_("Not found"));
2620  else
2621  {
2622  mutt_message(_("Search wrapped to bottom"));
2623  wrapped = true;
2624  goto search_next;
2625  }
2626  }
2627 
2628  if (rd.line_info[rd.topline].search_cnt > 0)
2629  {
2630  rd.search_flag = MUTT_SEARCH;
2631  /* give some context for search results */
2632  if (rd.topline - searchctx > 0)
2633  rd.topline -= searchctx;
2634  }
2635 
2636  break;
2637  }
2638  /* no previous search pattern */
2639  /* fallthrough */
2640 
2641  case OP_SEARCH:
2642  case OP_SEARCH_REVERSE:
2643  mutt_str_strfcpy(buffer, searchbuf, sizeof(buffer));
2644  if (mutt_get_field((ch == OP_SEARCH || ch == OP_SEARCH_NEXT) ?
2645  _("Search for: ") :
2646  _("Reverse search for: "),
2647  buffer, sizeof(buffer), MUTT_CLEAR) != 0)
2648  {
2649  break;
2650  }
2651 
2652  if (strcmp(buffer, searchbuf) == 0)
2653  {
2654  if (rd.search_compiled)
2655  {
2656  /* do an implicit search-next */
2657  if (ch == OP_SEARCH)
2658  ch = OP_SEARCH_NEXT;
2659  else
2660  ch = OP_SEARCH_OPPOSITE;
2661 
2662  wrapped = false;
2663  goto search_next;
2664  }
2665  }
2666 
2667  if (!buffer[0])
2668  break;
2669 
2670  mutt_str_strfcpy(searchbuf, buffer, sizeof(searchbuf));
2671 
2672  /* leave search_back alone if ch == OP_SEARCH_NEXT */
2673  if (ch == OP_SEARCH)
2674  rd.search_back = false;
2675  else if (ch == OP_SEARCH_REVERSE)
2676  rd.search_back = true;
2677 
2678  if (rd.search_compiled)
2679  {
2680  regfree(&rd.search_re);
2681  for (i = 0; i < rd.last_line; i++)
2682  {
2683  if (rd.line_info[i].search)
2684  FREE(&(rd.line_info[i].search));
2685  rd.line_info[i].search_cnt = -1;
2686  }
2687  }
2688 
2689  int rflags = mutt_mb_is_lower(searchbuf) ? REG_ICASE : 0;
2690  err = REGCOMP(&rd.search_re, searchbuf, REG_NEWLINE | rflags);
2691  if (err != 0)
2692  {
2693  regerror(err, &rd.search_re, buffer, sizeof(buffer));
2694  mutt_error("%s", buffer);
2695  for (i = 0; i < rd.max_line; i++)
2696  {
2697  /* cleanup */
2698  if (rd.line_info[i].search)
2699  FREE(&(rd.line_info[i].search));
2700  rd.line_info[i].search_cnt = -1;
2701  }
2702  rd.search_flag = 0;
2703  rd.search_compiled = false;
2704  }
2705  else
2706  {
2707  rd.search_compiled = true;
2708  /* update the search pointers */
2709  i = 0;
2710  while (display_line(rd.fp, &rd.last_pos, &rd.line_info, i, &rd.last_line, &rd.max_line,
2711  MUTT_SEARCH | (flags & MUTT_PAGER_NSKIP) | (flags & MUTT_PAGER_NOWRAP),
2712  &rd.quote_list, &rd.q_level, &rd.force_redraw,
2713  &rd.search_re, rd.pager_window) == 0)
2714  i++;
2715 
2716  if (!rd.search_back)
2717  {
2718  /* searching forward */
2719  for (i = rd.topline; i < rd.last_line; i++)
2720  {
2721  if ((!rd.hide_quoted || rd.line_info[i].type != MT_COLOR_QUOTED) &&
2722  !rd.line_info[i].continuation && rd.line_info[i].search_cnt > 0)
2723  {
2724  break;
2725  }
2726  }
2727 
2728  if (i < rd.last_line)
2729  rd.topline = i;
2730  }
2731  else
2732  {
2733  /* searching backward */
2734  for (i = rd.topline; i >= 0; i--)
2735  {
2736  if ((!rd.hide_quoted || rd.line_info[i].type != MT_COLOR_QUOTED) &&
2737  !rd.line_info[i].continuation && rd.line_info[i].search_cnt > 0)
2738  {
2739  break;
2740  }
2741  }
2742 
2743  if (i >= 0)
2744  rd.topline = i;
2745  }
2746 
2747  if (rd.line_info[rd.topline].search_cnt == 0)
2748  {
2749  rd.search_flag = 0;
2750  mutt_error(_("Not found"));
2751  }
2752  else
2753  {
2754  rd.search_flag = MUTT_SEARCH;
2755  /* give some context for search results */
2756  if (SearchContext > 0 && SearchContext < rd.pager_window->rows)
2757  searchctx = SearchContext;
2758  else
2759  searchctx = 0;
2760  if (rd.topline - searchctx > 0)
2761  rd.topline -= searchctx;
2762  }
2763  }
2764  pager_menu->redraw = REDRAW_BODY;
2765  break;
2766 
2767  case OP_SEARCH_TOGGLE:
2768  if (rd.search_compiled)
2769  {
2770  rd.search_flag ^= MUTT_SEARCH;
2771  pager_menu->redraw = REDRAW_BODY;
2772  }
2773  break;
2774 
2775  case OP_SORT:
2776  case OP_SORT_REVERSE:
2777  CHECK_MODE(IsHeader(extra))
2778  if (mutt_select_sort((ch == OP_SORT_REVERSE)) == 0)
2779  {
2780  OptNeedResort = true;
2781  ch = -1;
2782  rc = OP_DISPLAY_MESSAGE;
2783  }
2784  break;
2785 
2786  case OP_HELP:
2787  /* don't let the user enter the help-menu from the help screen! */
2788  if (!InHelp)
2789  {
2790  InHelp = 1;
2792  pager_menu->redraw = REDRAW_FULL;
2793  InHelp = 0;
2794  }
2795  else
2796  mutt_error(_("Help is currently being shown"));
2797  break;
2798 
2799  case OP_PAGER_HIDE_QUOTED:
2800  if (rd.has_types)
2801  {
2802  rd.hide_quoted ^= MUTT_HIDE;
2803  if (rd.hide_quoted && rd.line_info[rd.topline].type == MT_COLOR_QUOTED)
2804  rd.topline = up_n_lines(1, rd.line_info, rd.topline, rd.hide_quoted);
2805  else
2806  pager_menu->redraw = REDRAW_BODY;
2807  }
2808  break;
2809 
2810  case OP_PAGER_SKIP_QUOTED:
2811  if (rd.has_types)
2812  {
2813  int dretval = 0;
2814  int new_topline = rd.topline;
2815 
2816  /* Skip all the email headers */
2817  if (ISHEADER(rd.line_info[new_topline].type))
2818  {
2819  while ((new_topline < rd.last_line ||
2820  (0 == (dretval = display_line(
2821  rd.fp, &rd.last_pos, &rd.line_info, new_topline, &rd.last_line,
2822  &rd.max_line, MUTT_TYPES | (flags & MUTT_PAGER_NOWRAP),
2823  &rd.quote_list, &rd.q_level, &rd.force_redraw,
2824  &rd.search_re, rd.pager_window)))) &&
2825  ISHEADER(rd.line_info[new_topline].type))
2826  {
2827  new_topline++;
2828  }
2829  rd.topline = new_topline;
2830  break;
2831  }
2832 
2833  while (((new_topline + SkipQuotedOffset) < rd.last_line ||
2834  (0 == (dretval = display_line(
2835  rd.fp, &rd.last_pos, &rd.line_info, new_topline, &rd.last_line,
2836  &rd.max_line, MUTT_TYPES | (flags & MUTT_PAGER_NOWRAP),
2837  &rd.quote_list, &rd.q_level, &rd.force_redraw,
2838  &rd.search_re, rd.pager_window)))) &&
2839  rd.line_info[new_topline + SkipQuotedOffset].type != MT_COLOR_QUOTED)
2840  new_topline++;
2841 
2842  if (dretval < 0)
2843  {
2844  mutt_error(_("No more quoted text"));
2845  break;
2846  }
2847 
2848  while (((new_topline + SkipQuotedOffset) < rd.last_line ||
2849  (0 == (dretval = display_line(
2850  rd.fp, &rd.last_pos, &rd.line_info, new_topline, &rd.last_line,
2851  &rd.max_line, MUTT_TYPES | (flags & MUTT_PAGER_NOWRAP),
2852  &rd.quote_list, &rd.q_level, &rd.force_redraw,
2853  &rd.search_re, rd.pager_window)))) &&
2854  rd.line_info[new_topline + SkipQuotedOffset].type == MT_COLOR_QUOTED)
2855  new_topline++;
2856 
2857  if (dretval < 0)
2858  {
2859  mutt_error(_("No more unquoted text after quoted text"));
2860  break;
2861  }
2862  rd.topline = new_topline;
2863  }
2864  break;
2865 
2866  case OP_PAGER_BOTTOM: /* move to the end of the file */
2867  if (rd.line_info[rd.curline].offset < rd.sb.st_size - 1)
2868  {
2869  i = rd.curline;
2870  /* make sure the types are defined to the end of file */
2871  while (display_line(rd.fp, &rd.last_pos, &rd.line_info, i, &rd.last_line,
2872  &rd.max_line, rd.has_types | (flags & MUTT_PAGER_NOWRAP),
2873  &rd.quote_list, &rd.q_level, &rd.force_redraw,
2874  &rd.search_re, rd.pager_window) == 0)
2875  i++;
2876  rd.topline = up_n_lines(rd.pager_window->rows, rd.line_info,
2877  rd.last_line, rd.hide_quoted);
2878  }
2879  else
2880  mutt_error(_("Bottom of message is shown"));
2881  break;
2882 
2883  case OP_REDRAW:
2884  clearok(stdscr, true);
2885  pager_menu->redraw = REDRAW_FULL;
2886  break;
2887 
2888  case OP_NULL:
2890  break;
2891 
2892  /* --------------------------------------------------------------------
2893  * The following are operations on the current message rather than
2894  * adjusting the view of the message.
2895  */
2896 
2897  case OP_BOUNCE_MESSAGE:
2898  CHECK_MODE(IsHeader(extra) || IsMsgAttach(extra))
2899  CHECK_ATTACH;
2900  if (IsMsgAttach(extra))
2901  mutt_attach_bounce(extra->fp, extra->actx, extra->bdy);
2902  else
2903  ci_bounce_message(extra->email);
2904  break;
2905 
2906  case OP_RESEND:
2907  CHECK_MODE(IsHeader(extra) || IsMsgAttach(extra))
2908  CHECK_ATTACH;
2909  if (IsMsgAttach(extra))
2910  mutt_attach_resend(extra->fp, extra->actx, extra->bdy);
2911  else
2912  mutt_resend_message(NULL, extra->ctx, extra->email);
2913  pager_menu->redraw = REDRAW_FULL;
2914  break;
2915 
2916  case OP_COMPOSE_TO_SENDER:
2917  CHECK_MODE(IsHeader(extra) || IsMsgAttach(extra));
2918  CHECK_ATTACH;
2919  if (IsMsgAttach(extra))
2920  mutt_attach_mail_sender(extra->fp, extra->email, extra->actx, extra->bdy);
2921  else
2922  ci_send_message(SEND_TO_SENDER, NULL, NULL, extra->ctx, extra->email);
2923  pager_menu->redraw = REDRAW_FULL;
2924  break;
2925 
2926  case OP_CHECK_TRADITIONAL:
2927  CHECK_MODE(IsHeader(extra));
2928  if (!(WithCrypto & APPLICATION_PGP))
2929  break;
2930  if (!(extra->email->security & PGP_TRADITIONAL_CHECKED))
2931  {
2932  ch = -1;
2933  rc = OP_CHECK_TRADITIONAL;
2934  }
2935  break;
2936 
2937  case OP_CREATE_ALIAS:
2938  CHECK_MODE(IsHeader(extra) || IsMsgAttach(extra));
2939  if (IsMsgAttach(extra))
2940  mutt_alias_create(extra->bdy->email->env, NULL);
2941  else
2942  mutt_alias_create(extra->email->env, NULL);
2943  break;
2944 
2945  case OP_PURGE_MESSAGE:
2946  case OP_DELETE:
2947  CHECK_MODE(IsHeader(extra));
2949  /* L10N: CHECK_ACL */
2950  CHECK_ACL(MUTT_ACL_DELETE, _("Cannot delete message"));
2951 
2953  mutt_set_flag(Context->mailbox, extra->email, MUTT_PURGE, (ch == OP_PURGE_MESSAGE));
2954  if (DeleteUntag)
2955  mutt_set_flag(Context->mailbox, extra->email, MUTT_TAG, 0);
2956  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
2957  if (Resolve)
2958  {
2959  ch = -1;
2960  rc = OP_MAIN_NEXT_UNDELETED;
2961  }
2962  break;
2963 
2964  case OP_MAIN_SET_FLAG:
2965  case OP_MAIN_CLEAR_FLAG:
2966  CHECK_MODE(IsHeader(extra));
2968 
2969  if (mutt_change_flag(extra->email, (ch == OP_MAIN_SET_FLAG)) == 0)
2970  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
2971  if (extra->email->deleted && Resolve)
2972  {
2973  ch = -1;
2974  rc = OP_MAIN_NEXT_UNDELETED;
2975  }
2976  break;
2977 
2978  case OP_DELETE_THREAD:
2979  case OP_DELETE_SUBTHREAD:
2980  case OP_PURGE_THREAD:
2981  CHECK_MODE(IsHeader(extra));
2983  /* L10N: CHECK_ACL */
2984  /* L10N: Due to the implementation details we do not know whether we
2985  delete zero, 1, 12, ... messages. So in English we use
2986  "messages". Your language might have other means to express this.
2987  */
2988  CHECK_ACL(MUTT_ACL_DELETE, _("Cannot delete messages"));
2989 
2990  {
2991  int subthread = (ch == OP_DELETE_SUBTHREAD);
2992  r = mutt_thread_set_flag(extra->email, MUTT_DELETE, 1, subthread);
2993  if (r == -1)
2994  break;
2995  if (ch == OP_PURGE_THREAD)
2996  {
2997  r = mutt_thread_set_flag(extra->email, MUTT_PURGE, 1, subthread);
2998  if (r == -1)
2999  break;
3000  }
3001 
3002  if (DeleteUntag)
3003  mutt_thread_set_flag(extra->email, MUTT_TAG, 0, subthread);
3004  if (Resolve)
3005  {
3006  rc = OP_MAIN_NEXT_UNDELETED;
3007  ch = -1;
3008  }
3009 
3010  if (!Resolve && PagerIndexLines)
3011  pager_menu->redraw = REDRAW_FULL;
3012  else
3013  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3014  }
3015  break;
3016 
3017  case OP_DISPLAY_ADDRESS:
3018  CHECK_MODE(IsHeader(extra) || IsMsgAttach(extra));
3019  if (IsMsgAttach(extra))
3020  mutt_display_address(extra->bdy->email->env);
3021  else
3022  mutt_display_address(extra->email->env);
3023  break;
3024 
3025  case OP_ENTER_COMMAND:
3026  old_PagerIndexLines = PagerIndexLines;
3027 
3029 
3030  if (OptNeedResort)
3031  {
3032  OptNeedResort = false;
3033  CHECK_MODE(IsHeader(extra));
3034  OptNeedResort = true;
3035  }
3036 
3037  if (old_PagerIndexLines != PagerIndexLines)
3038  {
3039  if (rd.index)
3040  mutt_menu_destroy(&rd.index);
3041  rd.index = NULL;
3042  }
3043 
3044  if ((pager_menu->redraw & REDRAW_FLOW) && (flags & MUTT_PAGER_RETWINCH))
3045  {
3046  ch = -1;
3047  rc = OP_REFORMAT_WINCH;
3048  continue;
3049  }
3050 
3051  ch = 0;
3052  break;
3053 
3054  case OP_FLAG_MESSAGE:
3055  CHECK_MODE(IsHeader(extra));
3057  /* L10N: CHECK_ACL */
3058  CHECK_ACL(MUTT_ACL_WRITE, "Cannot flag message");
3059 
3060  mutt_set_flag(Context->mailbox, extra->email, MUTT_FLAG, !extra->email->flagged);
3061  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3062  if (Resolve)
3063  {
3064  ch = -1;
3065  rc = OP_MAIN_NEXT_UNDELETED;
3066  }
3067  break;
3068 
3069  case OP_PIPE:
3070  CHECK_MODE(IsHeader(extra) || IsAttach(extra));
3071  if (IsAttach(extra))
3072  mutt_pipe_attachment_list(extra->actx, extra->fp, false, extra->bdy, false);
3073  else
3074  mutt_pipe_message(extra->email);
3075  break;
3076 
3077  case OP_PRINT:
3078  CHECK_MODE(IsHeader(extra) || IsAttach(extra));
3079  if (IsAttach(extra))
3080  mutt_print_attachment_list(extra->actx, extra->fp, false, extra->bdy);
3081  else
3082  mutt_print_message(extra->email);
3083  break;
3084 
3085  case OP_MAIL:
3086  CHECK_MODE(IsHeader(extra) && !IsAttach(extra));
3087  CHECK_ATTACH;
3088  ci_send_message(0, NULL, NULL, extra->ctx, NULL);
3089  pager_menu->redraw = REDRAW_FULL;
3090  break;
3091 
3092 #ifdef USE_NNTP
3093  case OP_POST:
3094  CHECK_MODE(IsHeader(extra) && !IsAttach(extra));
3095  CHECK_ATTACH;
3096  if (extra->ctx && extra->ctx->mailbox->magic == MUTT_NNTP &&
3097  !((struct NntpMboxData *) extra->ctx->mailbox->mdata)->allowed && query_quadoption(PostModerated, _("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES)
3098  {
3099  break;
3100  }
3101  ci_send_message(SEND_NEWS, NULL, NULL, extra->ctx, NULL);
3102  pager_menu->redraw = REDRAW_FULL;
3103  break;
3104 
3105  case OP_FORWARD_TO_GROUP:
3106  CHECK_MODE(IsHeader(extra) || IsMsgAttach(extra));
3107  CHECK_ATTACH;
3108  if (extra->ctx && extra->ctx->mailbox->magic == MUTT_NNTP &&
3109  !((struct NntpMboxData *) extra->ctx->mailbox->mdata)->allowed && query_quadoption(PostModerated, _("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES)
3110  {
3111  break;
3112  }
3113  if (IsMsgAttach(extra))
3114  mutt_attach_forward(extra->fp, extra->email, extra->actx, extra->bdy, SEND_NEWS);
3115  else
3116  ci_send_message(SEND_NEWS | SEND_FORWARD, NULL, NULL, extra->ctx,
3117  extra->email);
3118  pager_menu->redraw = REDRAW_FULL;
3119  break;
3120 
3121  case OP_FOLLOWUP:
3122  CHECK_MODE(IsHeader(extra) || IsMsgAttach(extra));
3123  CHECK_ATTACH;
3124 
3125  if (IsMsgAttach(extra))
3126  followup_to = extra->bdy->email->env->followup_to;
3127  else
3128  followup_to = extra->email->env->followup_to;
3129 
3130  if (!followup_to || (mutt_str_strcasecmp(followup_to, "poster") != 0) ||
3132  _("Reply by mail as poster prefers?")) != MUTT_YES)
3133  {
3134  if (extra->ctx && extra->ctx->mailbox->magic == MUTT_NNTP &&
3135  !((struct NntpMboxData *) extra->ctx->mailbox->mdata)->allowed && query_quadoption(PostModerated, _("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES)
3136  {
3137  break;
3138  }
3139  if (IsMsgAttach(extra))
3140  {
3141  mutt_attach_reply(extra->fp, extra->email, extra->actx, extra->bdy,
3142  SEND_NEWS | SEND_REPLY);
3143  }
3144  else
3145  ci_send_message(SEND_NEWS | SEND_REPLY, NULL, NULL, extra->ctx,
3146  extra->email);
3147  pager_menu->redraw = REDRAW_FULL;
3148  break;
3149  }
3150 #endif
3151  /* fallthrough */
3152  case OP_REPLY:
3153  CHECK_MODE(IsHeader(extra) || IsMsgAttach(extra));
3154  CHECK_ATTACH;
3155  if (IsMsgAttach(extra))
3156  mutt_attach_reply(extra->fp, extra->email, extra->actx, extra->bdy, SEND_REPLY);
3157  else
3158  ci_send_message(SEND_REPLY, NULL, NULL, extra->ctx, extra->email);
3159  pager_menu->redraw = REDRAW_FULL;
3160  break;
3161 
3162  case OP_RECALL_MESSAGE:
3163  CHECK_MODE(IsHeader(extra) && !IsAttach(extra));
3164  CHECK_ATTACH;
3165  ci_send_message(SEND_POSTPONED, NULL, NULL, extra->ctx, extra->email);
3166  pager_menu->redraw = REDRAW_FULL;
3167  break;
3168 
3169  case OP_GROUP_REPLY:
3170  CHECK_MODE(IsHeader(extra) || IsMsgAttach(extra));
3171  CHECK_ATTACH;
3172  if (IsMsgAttach(extra))
3173  {
3174  mutt_attach_reply(extra->fp, extra->email, extra->actx, extra->bdy,
3176  }
3177  else
3178  {
3179  ci_send_message(SEND_REPLY | SEND_GROUP_REPLY, NULL, NULL, extra->ctx,
3180  extra->email);
3181  }
3182  pager_menu->redraw = REDRAW_FULL;
3183  break;
3184 
3185  case OP_LIST_REPLY:
3186  CHECK_MODE(IsHeader(extra) || IsMsgAttach(extra));
3187  CHECK_ATTACH;
3188  if (IsMsgAttach(extra))
3189  {
3190  mutt_attach_reply(extra->fp, extra->email, extra->actx, extra->bdy,
3192  }
3193  else
3194  {
3195  ci_send_message(SEND_REPLY | SEND_LIST_REPLY, NULL, NULL, extra->ctx,
3196  extra->email);
3197  }
3198  pager_menu->redraw = REDRAW_FULL;
3199  break;
3200 
3201  case OP_FORWARD_MESSAGE:
3202  CHECK_MODE(IsHeader(extra) || IsMsgAttach(extra));
3203  CHECK_ATTACH;
3204  if (IsMsgAttach(extra))
3205  mutt_attach_forward(extra->fp, extra->email, extra->actx, extra->bdy, 0);
3206  else
3207  ci_send_message(SEND_FORWARD, NULL, NULL, extra->ctx, extra->email);
3208  pager_menu->redraw = REDRAW_FULL;
3209  break;
3210 
3211  case OP_DECRYPT_SAVE:
3212  if (!WithCrypto)
3213  {
3214  ch = -1;
3215  break;
3216  }
3217  /* fallthrough */
3218  case OP_SAVE:
3219  if (IsAttach(extra))
3220  {
3221  mutt_save_attachment_list(extra->actx, extra->fp, false, extra->bdy,
3222  extra->email, NULL);
3223  break;
3224  }
3225  /* fallthrough */
3226  case OP_COPY_MESSAGE:
3227  case OP_DECODE_SAVE:
3228  case OP_DECODE_COPY:
3229  case OP_DECRYPT_COPY:
3230  if (!(WithCrypto != 0) && ch == OP_DECRYPT_COPY)
3231  {
3232  ch = -1;
3233  break;
3234  }
3235  CHECK_MODE(IsHeader(extra));
3236  if ((mutt_save_message(
3237  extra->email, (ch == OP_DECRYPT_SAVE) || (ch == OP_SAVE) || (ch == OP_DECODE_SAVE),
3238  (ch == OP_DECODE_SAVE) || (ch == OP_DECODE_COPY),
3239  (ch == OP_DECRYPT_SAVE) || (ch == OP_DECRYPT_COPY)) == 0) &&
3240  ((ch == OP_SAVE) || (ch == OP_DECODE_SAVE) || (ch == OP_DECRYPT_SAVE)))
3241  {
3242  if (Resolve)
3243  {
3244  ch = -1;
3245  rc = OP_MAIN_NEXT_UNDELETED;
3246  }
3247  else
3248  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3249  }
3250  break;
3251 
3252  case OP_SHELL_ESCAPE:
3254  break;
3255 
3256  case OP_TAG:
3257  CHECK_MODE(IsHeader(extra));
3258  if (Context)
3259  {
3260  mutt_set_flag(Context->mailbox, extra->email, MUTT_TAG, !extra->email->tagged);
3261 
3262  Context->last_tag =
3263  extra->email->tagged ?
3264  extra->email :
3265  ((Context->last_tag == extra->email && !extra->email->tagged) ?
3266  NULL :
3267  Context->last_tag);
3268  }
3269 
3270  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3271  if (Resolve)
3272  {
3273  ch = -1;
3274  rc = OP_NEXT_ENTRY;
3275  }
3276  break;
3277 
3278  case OP_TOGGLE_NEW:
3279  CHECK_MODE(IsHeader(extra));
3281  /* L10N: CHECK_ACL */
3282  CHECK_ACL(MUTT_ACL_SEEN, _("Cannot toggle new"));
3283 
3284  if (extra->email->read || extra->email->old)
3285  mutt_set_flag(Context->mailbox, extra->email, MUTT_NEW, 1);
3286  else if (!first)
3288  first = 0;
3289  Context->msgnotreadyet = -1;
3290  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3291  if (Resolve)
3292  {
3293  ch = -1;
3294  rc = OP_MAIN_NEXT_UNDELETED;
3295  }
3296  break;
3297 
3298  case OP_UNDELETE:
3299  CHECK_MODE(IsHeader(extra));
3301  /* L10N: CHECK_ACL */
3302  CHECK_ACL(MUTT_ACL_DELETE, _("Cannot undelete message"));
3303 
3306  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3307  if (Resolve)
3308  {
3309  ch = -1;
3310  rc = OP_NEXT_ENTRY;
3311  }
3312  break;
3313 
3314  case OP_UNDELETE_THREAD:
3315  case OP_UNDELETE_SUBTHREAD:
3316  CHECK_MODE(IsHeader(extra));
3318  /* L10N: CHECK_ACL */
3319  /* L10N: Due to the implementation details we do not know whether we
3320  undelete zero, 1, 12, ... messages. So in English we use
3321  "messages". Your language might have other means to express this.
3322  */
3323  CHECK_ACL(MUTT_ACL_DELETE, _("Cannot undelete messages"));
3324 
3325  r = mutt_thread_set_flag(extra->email, MUTT_DELETE, 0,
3326  ch == OP_UNDELETE_THREAD ? 0 : 1);
3327  if (r != -1)
3328  {
3329  r = mutt_thread_set_flag(extra->email, MUTT_PURGE, 0,
3330  ch == OP_UNDELETE_THREAD ? 0 : 1);
3331  }
3332  if (r != -1)
3333  {
3334  if (Resolve)
3335  {
3336  rc = (ch == OP_DELETE_THREAD) ? OP_MAIN_NEXT_THREAD : OP_MAIN_NEXT_SUBTHREAD;
3337  ch = -1;
3338  }
3339 
3340  if (!Resolve && PagerIndexLines)
3341  pager_menu->redraw = REDRAW_FULL;
3342  else
3343  pager_menu->redraw |= REDRAW_STATUS | REDRAW_INDEX;
3344  }
3345  break;
3346 
3347  case OP_VERSION:
3349  break;
3350 
3351  case OP_MAILBOX_LIST:
3353  break;
3354 
3355  case OP_VIEW_ATTACHMENTS:
3356  if (flags & MUTT_PAGER_ATTACHMENT)
3357  {
3358  ch = -1;
3359  rc = OP_ATTACH_COLLAPSE;
3360  break;
3361  }
3362  CHECK_MODE(IsHeader(extra));
3363  mutt_view_attachments(extra->email);
3364  if (Context && extra->email->attach_del)
3365  Context->mailbox->changed = true;
3366  pager_menu->redraw = REDRAW_FULL;
3367  break;
3368 
3369  case OP_MAIL_KEY:
3370  if (!(WithCrypto & APPLICATION_PGP))
3371  {
3372  ch = -1;
3373  break;
3374  }
3375  CHECK_MODE(IsHeader(extra));
3376  CHECK_ATTACH;
3377  ci_send_message(SEND_KEY, NULL, NULL, extra->ctx, extra->email);
3378  pager_menu->redraw = REDRAW_FULL;
3379  break;
3380 
3381  case OP_EDIT_LABEL:
3382  CHECK_MODE(IsHeader(extra));
3383  rc = mutt_label_message(extra->email);
3384  if (rc > 0)
3385  {
3386  Context->mailbox->changed = true;
3387  pager_menu->redraw = REDRAW_FULL;
3388  mutt_message(ngettext("%d label changed", "%d labels changed", rc), rc);
3389  }
3390  else
3391  {
3392  mutt_message(_("No labels changed"));
3393  }
3394  break;
3395 
3396  case OP_FORGET_PASSPHRASE:
3398  break;
3399 
3400  case OP_EXTRACT_KEYS:
3401  if (!WithCrypto)
3402  {
3403  ch = -1;
3404  break;
3405  }
3406  CHECK_MODE(IsHeader(extra));
3408  pager_menu->redraw = REDRAW_FULL;
3409  break;
3410 
3411  case OP_WHAT_KEY:
3412  mutt_what_key();
3413  break;
3414 
3415  case OP_CHECK_STATS:
3416  mutt_check_stats();
3417  break;
3418 
3419 #ifdef USE_SIDEBAR
3420  case OP_SIDEBAR_NEXT:
3421  case OP_SIDEBAR_NEXT_NEW:
3422  case OP_SIDEBAR_PAGE_DOWN:
3423  case OP_SIDEBAR_PAGE_UP:
3424  case OP_SIDEBAR_PREV:
3425  case OP_SIDEBAR_PREV_NEW:
3427  break;
3428 
3429  case OP_SIDEBAR_TOGGLE_VISIBLE:
3430  bool_str_toggle(Config, "sidebar_visible", NULL);
3432  break;
3433 #endif
3434 
3435  default:
3436  ch = -1;
3437  break;
3438  }
3439  }
3440 
3441  mutt_file_fclose(&rd.fp);
3442  if (IsHeader(extra))
3443  {
3444  if (Context)
3445  Context->msgnotreadyet = -1;
3446  switch (rc)
3447  {
3448  case -1:
3449  case OP_DISPLAY_HEADERS:
3451  break;
3452  default:
3453  TopLine = rd.topline;
3454  OldHdr = extra->email;
3455  break;
3456  }
3457  }
3458 
3459  cleanup_quote(&rd.quote_list);
3460 
3461  for (i = 0; i < rd.max_line; i++)
3462  {
3463  FREE(&(rd.line_info[i].syntax));
3464  if (rd.search_compiled && rd.line_info[i].search)
3465  FREE(&(rd.line_info[i].search));
3466  }
3467  if (rd.search_compiled)
3468  {
3469  regfree(&rd.search_re);
3470  rd.search_compiled = false;
3471  }
3472  FREE(&rd.line_info);
3473  mutt_menu_pop_current(pager_menu);
3474  mutt_menu_destroy(&pager_menu);
3475  if (rd.index)
3476  mutt_menu_destroy(&rd.index);
3477 
3478  FREE(&rd.index_status_window);
3479  FREE(&rd.index_window);
3480  FREE(&rd.pager_status_window);
3481  FREE(&rd.pager_window);
3482 
3483  return rc != -1 ? rc : 0;
3484 }
WHERE unsigned char FollowupToPoster
Config: (nntp) Reply to the poster if &#39;poster&#39; is in the &#39;Followup-To&#39; header.
Definition: globals.h:196
The "current" mailbox.
Definition: context.h:36
void mutt_print_message(struct Email *e)
Print a message.
Definition: commands.c:588
FILE * fp
source stream
Definition: pager.h:68
#define MUTT_TYPES
Definition: pager.h:46
#define NONULL(x)
Definition: string2.h:39
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:51
int msg_count
total number of messages
Definition: mailbox.h:86
#define SHORT_STRING
Definition: string2.h:34
short SkipQuotedOffset
Config: Lines of context to show when skipping quoted text.
Definition: pager.c:92
int redraw
when to redraw the screen
Definition: menu.h:63
#define SEND_TO_SENDER
Definition: send.h:94
const char * banner
Definition: pager.c:1885
The envelope/body of an email.
Definition: email.h:35
#define MUTT_CLEAR
clear input if printable character is pressed
Definition: mutt.h:63
#define MIN(a, b)
Definition: memory.h:31
#define mutt_perror(...)
Definition: logging.h:89
GUI selectable list of items.
Definition: menu.h:56
int km_dokey(int menu)
Determine what a keypress should do.
Definition: keymap.c:572
void mutt_pipe_message(struct Email *e)
Pipe a message.
Definition: commands.c:569
#define IsHeader(x)
Definition: pager.c:101
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: magic.h:41
#define mutt_message(...)
Definition: logging.h:87
unsigned int security
bit 0-8: flags, bit 9,10: application.
Definition: email.h:37
Keep track when the pager needs redrawing.
Definition: pager.c:1857
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: pager.c:1808
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:39
void mutt_display_address(struct Envelope *env)
Display the address of a message.
Definition: commands.c:765
#define MUTT_SHOWCOLOR
Definition: pager.h:43
Messages to be deleted.
Definition: mutt.h:118
bool PagerStop
Config: Don&#39;t automatically open the next message when at the end of a message.
Definition: pager.c:90
void mutt_view_attachments(struct Email *e)
Show the attachments in a Menu.
Definition: recvattach.c:1317
static int TopLine
Definition: pager.c:110
static void cleanup_quote(struct QClass **quote_list)
Free a quote list.
Definition: pager.c:492
Pager: quoted text.
Definition: mutt_curses.h:125
void mutt_attach_reply(FILE *fp, struct Email *e, struct AttachCtx *actx, struct Body *cur, int flags)
Attach a reply.
Definition: recvcmd.c:894
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:583
bool attach_del
has an attachment marked for deletion
Definition: email.h:47
#define CHECK_ACL(aclbit, action)
Definition: pager.c:137
#define SEND_FORWARD
Definition: send.h:85
void mutt_resize_screen(void)
Update NeoMutt&#39;s opinion about the window size (CURSES)
Definition: resize.c:98
WHERE bool OptNeedResort
(pseudo) used to force a re-sort
Definition: options.h:40
void mutt_check_stats(void)
Forcibly update mailbox stats.
Definition: commands.c:1260
int msgnotreadyet
which msg "new" in pager, -1 if none
Definition: context.h:44
void crypt_forget_passphrase(void)
Forget a passphrase and display a message.
Definition: crypt.c:102
#define ISHEADER(x)
Definition: pager.c:97
void mutt_enter_command(void)
enter a neomutt command
Definition: commands.c:731
static void pager_custom_redraw(struct Menu *pager_menu)
Redraw the pager window - Implements Menu::menu_custom_redraw()
Definition: pager.c:1896
#define CHECK_MODE(x)
Definition: pager.c:113
#define PGP_TRADITIONAL_CHECKED
Definition: ncrypt.h:132
#define _(a)
Definition: message.h:28
WHERE struct ConfigSet * Config
Wrapper around the user&#39;s config settings.
Definition: globals.h:39
void mutt_attach_bounce(FILE *fp, struct AttachCtx *actx, struct Body *cur)
Bounce function, from the attachment menu.
Definition: recvcmd.c:163
void crypt_extract_keys_from_messages(struct Email *e)
Extract keys from a message.
Definition: crypt.c:808
void menu_status_line(char *buf, size_t buflen, struct Menu *menu, const char *p)
Create the status line.
Definition: status.c:387
A division of the screen.
Definition: mutt_window.h:33
WHERE bool BrailleFriendly
Config: Move the cursor to the beginning of the line.
Definition: globals.h:207
bool search_back
Definition: pager.c:212
Highlighting for a line of text.
Definition: pager.c:162
bool search_compiled
Definition: pager.c:211
#define MUTT_PAGER_NOWRAP
format for term width, ignore $wrap
Definition: pager.h:55
#define LONG_STRING
Definition: string2.h:36
#define SEND_POSTPONED
Definition: send.h:86
void km_error_key(int menu)
Handle an unbound key sequence.
Definition: keymap.c:1037
struct Email ** hdrs
Definition: mailbox.h:93
int mutt_thread_set_flag(struct Email *e, int flag, int bf, int subthread)
Set a flag on an entire thread.
Definition: flags.c:371
int vcount
the number of virtual messages
Definition: mailbox.h:96
#define MUTT_PAGER_ATTACHMENT
Definition: pager.h:54
char * mutt_compile_help(char *buf, size_t buflen, int menu, const struct Mapping *items)
Create the text for the help menu.
Definition: help.c:115
delete a message
Definition: mailbox.h:61
int mutt_change_flag(struct Email *e, int bf)
Change the flag on a Message.
Definition: flags.c:427
void mutt_make_help(char *buf, size_t buflen, const char *txt, int menu, int op)
Create one entry for the help bar.
Definition: help.c:92
int line
Definition: pager.c:210
void ci_bounce_message(struct Email *e)
Bounce an email.
Definition: commands.c:288
bool tagged
Definition: email.h:42
void * redraw_data
Definition: menu.h:126
bool read
Definition: email.h:49
void mutt_print_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top)
Print a list of Attachments.
Definition: recvattach.c:936
int mutt_save_message(struct Email *e, bool delete, bool decode, bool decrypt)
Save an email.
Definition: commands.c:882
struct Mailbox * mailbox
Definition: context.h:50
static int braille_line
Definition: pager.c:824
bool old
Definition: email.h:48
enum MailboxType magic
mailbox type
Definition: mailbox.h:99
int mutt_resend_message(FILE *fp, struct Context *ctx, struct Email *cur)
Resend an email.
Definition: send.c:1430
void mutt_what_key(void)
Ask the user to press a key.
Definition: keymap.c:1410
struct Envelope * env
envelope information
Definition: email.h:91
WHERE bool DeleteUntag
Config: Untag messages when they are marked for deletion.
Definition: globals.h:211
void mutt_shell_escape(void)
invoke a command in a subshell
Definition: commands.c:703
Tagged messages.
Definition: mutt.h:123
WHERE SIG_ATOMIC_VOLATILE_T SigWinch
true after SIGWINCH is received
Definition: globals.h:91
WHERE bool Resolve
Config: Move to the next email whenever a command modifies an email.
Definition: globals.h:248
void(* menu_custom_redraw)(struct Menu *menu)
Redraw the menu.
Definition: menu.h:125
#define MUTT_PAGER_NSKIP
preserve whitespace with smartwrap
Definition: pager.h:50
#define SEND_LIST_REPLY
Definition: send.h:84
static struct Mapping PagerNewsHelpExtra[]
Definition: pager.c:1836
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:141
void mutt_attach_forward(FILE *fp, struct Email *e, struct AttachCtx *actx, struct Body *cur, int flags)
Forward an Attachment.
Definition: recvcmd.c:742
void * mdata
driver specific data
Definition: mailbox.h:130
void mutt_window_reflow(void)
Resize the Windows to fit the screen.
Definition: mutt_window.c:222
#define SEND_KEY
Definition: send.h:89
static int display_line(FILE *f, LOFF_T *last_pos, struct Line **line_info, int n, int *last, int *max, int flags, struct QClass **quote_list, int *q_level, bool *force_redraw, regex_t *search_re, struct MuttWindow *pager_window)
Print a line on screen.
Definition: pager.c:1530
struct Pager * extra
Definition: pager.c:1860
struct Context * ctx
current mailbox
Definition: pager.h:65
void mutt_refresh(void)
Force a refresh of the screen.
Definition: curs_lib.c:99
WHERE bool OptAttachMsg
(pseudo) used by attach-message
Definition: options.h:31
#define CHECK_ATTACH
Definition: pager.c:129
int query_quadoption(int opt, const char *prompt)
Ask the user a quad-question.
Definition: init.c:3227
New messages.
Definition: mutt.h:113
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:99
nondestructive flags change (IMAP)
Definition: mx.h:75
Messages to be purged (bypass trash)
Definition: mutt.h:120
struct AttachCtx * actx
attachment information
Definition: pager.h:69
WHERE bool WrapSearch
Config: Wrap around when the search hits the end.
Definition: globals.h:263
int mutt_select_sort(int reverse)
Ask the user for a sort method.
Definition: commands.c:629
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:742
#define CHECK_READONLY
Definition: pager.c:121
A line of text in the pager.
Definition: pager.c:172
static const struct Mapping PagerHelp[]
Definition: pager.c:1820
static const struct Mapping PagerHelpExtra[]
Definition: pager.c:1827
#define mutt_set_flag(a, b, c, d)
Definition: protos.h:54
bool quiet
inhibit status messages?
Definition: mailbox.h:112
#define SEND_NEWS
Definition: send.h:95
WHERE bool OptSearchInvalid
(pseudo) used to invalidate the search pat
Definition: options.h:49
#define MUTT_HIDE
Definition: pager.h:44
void update_index(struct Menu *menu, struct Context *ctx, int check, int oldcount, int index_hint)
Update the index.
Definition: index.c:505
#define mutt_get_field(A, B, C, D)
Definition: curs_lib.h:78
#define SEND_REPLY
Definition: send.h:82
NNTP-specific Mailbox data -.
Definition: nntp.h:139
int mutt_window_move(struct MuttWindow *win, int row, int col)
Move the cursor in a Window.
Definition: mutt_window.c:162
void mutt_help(int menu)
Display the help menu.
Definition: help.c:438
short SearchContext
Config: Context to display around search matches.
Definition: pager.c:91
void mutt_clear_pager_position(void)
Reset the pager&#39;s viewing position.
Definition: pager.c:1848
#define APPLICATION_PGP
Definition: ncrypt.h:129
Messages that have been read.
Definition: mutt.h:116
char path[PATH_MAX]
Definition: mailbox.h:78
int * v2r
mapping from virtual to real msgno
Definition: mailbox.h:95
bool mutt_mailbox_list(void)
List the mailboxes with new mail.
Definition: mailbox.c:598
void mutt_pipe_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top, bool filter)
Pipe a list of attachments to a command.
Definition: recvattach.c:786
struct Email * email
current message
Definition: pager.h:66
#define MUTT_PAGER_RETWINCH
need reformatting on SIGWINCH
Definition: pager.h:52
#define SEND_GROUP_REPLY
Definition: send.h:83
#define MUTT_SEARCH
Definition: pager.h:45
#define STRING
Definition: string2.h:35
char * searchbuf
Definition: pager.c:1887
WHERE unsigned char Quit
Config: Prompt before exiting NeoMutt.
Definition: globals.h:190
bool flagged
marked important?
Definition: email.h:41
#define mutt_curs_set(x)
Definition: mutt_curses.h:93
change the &#39;seen&#39; status of a message
Definition: mailbox.h:68
bool deleted
Definition: email.h:43
int mutt_file_fclose(FILE **f)
Close a FILE handle (and NULL the pointer)
Definition: file.c:149
#define mutt_error(...)
Definition: logging.h:88
int ci_send_message(int flags, struct Email *msg, char *tempfile, struct Context *ctx, struct Email *cur)
Send an email.
Definition: send.c:1530
char * followup_to
Definition: envelope.h:55
int mutt_str_strcasecmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:625
void mutt_timeout_hook(void)
Execute any timeout hooks.
Definition: hook.c:667
WHERE char * NewMailCommand
Config: External command to run when new mail arrives.
Definition: globals.h:140
int index
the absolute (unsorted) message number
Definition: email.h:87
#define FREE(x)
Definition: memory.h:46
static short InHelp
Definition: pager.c:203
WHERE bool BeepNew
Config: Make a noise when new mail arrives.
Definition: globals.h:206
Flagged messages.
Definition: mutt.h:122
void mutt_attach_mail_sender(FILE *fp, struct Email *e, struct AttachCtx *actx, struct Body *cur)
Compose an email to the sender in the email attachment.
Definition: recvcmd.c:1053
#define IsMsgAttach(x)
Definition: pager.c:100
int mutt_label_message(struct Email *e)
Let the user label a message.
Definition: mutt_header.c:127
void mutt_alias_create(struct Envelope *cur, struct Address *iaddr)
Create a new Alias from an Envelope or an Address.
Definition: alias.c:367
bool mutt_mailbox_notify(void)
Notify the user if there&#39;s new mail.
Definition: mailbox.c:677
write to a message (for flagging, or linking threads)
Definition: mailbox.h:69
bool changed
mailbox has been modified
Definition: mailbox.h:108
Keep track of screen resizing.
Definition: pager.c:208
int bool_str_toggle(struct ConfigSet *cs, const char *name, struct Buffer *err)
Toggle the value of a bool.
Definition: bool.c:247
new mail received in mailbox
Definition: mx.h:72
char * helpstr
Definition: pager.c:1886
short PagerIndexLines
Config: Number of index lines to display above the pager.
Definition: pager.c:89
mailbox was reopened
Definition: mx.h:74
bool mutt_mb_is_lower(const char *s)
Does a multi-byte string contain only lowercase characters?
Definition: mbyte.c:346
struct Email * email
header information for message/rfc822
Definition: body.h:59
#define WithCrypto
Definition: ncrypt.h:154
#define MUTT_SHOWFLAT
Definition: pager.h:42
void mutt_attach_resend(FILE *fp, struct AttachCtx *actx, struct Body *cur)
resend-message, from the attachment menu
Definition: recvcmd.c:283
Pager pager (email viewer)
Definition: keymap.h:74
int mutt_system(const char *cmd)
Run an external command.
Definition: system.c:50
short PagerContext
Config: Number of lines of overlap when changing pages in the pager.
Definition: pager.c:88
#define REGCOMP(X, Y, Z)
Compile a regular expression.
Definition: regex3.h:52
struct Email * last_tag
last tagged msg.
Definition: context.h:41
#define IsAttach(x)
Definition: pager.c:99
struct Body * bdy
current attachment
Definition: pager.h:67
int msgno
number displayed to the user
Definition: email.h:88
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1492
int mx_mbox_check(struct Context *ctx, int *index_hint)
Check for new mail - Wrapper for MxOps::mbox_check()
Definition: mx.c:1117
static struct Email * OldHdr
Definition: pager.c:111
int cs_str_native_set(const struct ConfigSet *cs, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition: set.c:800
WHERE unsigned char PostModerated
Config: (nntp) Allow posting to moderated newsgroups.
Definition: globals.h:195
void mutt_context_free(struct Context **ctx)
Free a Context.
Definition: mailbox.c:730

+ Here is the caller graph for this function:

Variable Documentation

bool AllowAnsi

Config: Allow ANSI colour codes in rich text messages.

Definition at line 86 of file pager.c.

bool HeaderColorPartial

Config: Only colour the part of the header matching the regex.

Definition at line 87 of file pager.c.

short PagerContext

Config: Number of lines of overlap when changing pages in the pager.

Definition at line 88 of file pager.c.

short PagerIndexLines

Config: Number of index lines to display above the pager.

Definition at line 89 of file pager.c.

bool PagerStop

Config: Don't automatically open the next message when at the end of a message.

Definition at line 90 of file pager.c.

short SearchContext

Config: Context to display around search matches.

Definition at line 91 of file pager.c.

short SkipQuotedOffset

Config: Lines of context to show when skipping quoted text.

Definition at line 92 of file pager.c.

bool SmartWrap

Config: Wrap text at word boundaries.

Definition at line 93 of file pager.c.

struct Regex* Smileys

Config: Regex to match smileys to prevent mistakes when quoting text.

Definition at line 94 of file pager.c.

bool Tilde

Config: Character to pad blank lines in the pager.

Definition at line 95 of file pager.c.

const char* Not_available_in_this_menu
static
Initial value:
=
N_("Not available in this menu")
#define N_(a)
Definition: message.h:32

Definition at line 103 of file pager.c.

const char* Mailbox_is_read_only = N_("Mailbox is read-only")
static

Definition at line 105 of file pager.c.

const char* Function_not_permitted_in_attach_message_mode
static
Initial value:
=
N_("Function not permitted in attach-message mode")
#define N_(a)
Definition: message.h:32

Definition at line 106 of file pager.c.

int TopLine = 0
static

Definition at line 110 of file pager.c.

struct Email* OldHdr = NULL
static

Definition at line 111 of file pager.c.

short InHelp = 0
static

Definition at line 203 of file pager.c.

struct Resize * Resize = NULL
static
int braille_line = -1
static

Definition at line 824 of file pager.c.

int braille_col = -1
static

Definition at line 825 of file pager.c.

const struct Mapping PagerHelp[]
static
Initial value:
= {
{ N_("Exit"), OP_EXIT },
{ N_("PrevPg"), OP_PREV_PAGE },
{ N_("NextPg"), OP_NEXT_PAGE },
{ NULL, 0 },
}
#define N_(a)
Definition: message.h:32

Definition at line 1820 of file pager.c.

const struct Mapping PagerHelpExtra[]
static
Initial value:
= {
{ N_("View Attachm."), OP_VIEW_ATTACHMENTS },
{ N_("Del"), OP_DELETE },
{ N_("Reply"), OP_REPLY },
{ N_("Next"), OP_MAIN_NEXT_UNDELETED },
{ NULL, 0 },
}
#define N_(a)
Definition: message.h:32

Definition at line 1827 of file pager.c.

struct Mapping PagerNewsHelpExtra[]
static
Initial value:
= {
{ N_("Post"), OP_POST },
{ N_("Followup"), OP_FOLLOWUP },
{ N_("Del"), OP_DELETE },
{ N_("Next"), OP_MAIN_NEXT_UNDELETED },
{ NULL, 0 },
}
#define N_(a)
Definition: message.h:32

Definition at line 1836 of file pager.c.