📄 pager.c
字号:
/* * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #if HAVE_CONFIG_H# include "config.h"#endif#include "mutt.h"#include "mutt_curses.h"#include "mutt_regex.h"#include "keymap.h"#include "mutt_menu.h"#include "mapping.h"#include "sort.h"#include "pager.h"#include "attach.h"#include "mbyte.h"#include "mx.h"#ifdef USE_IMAP#include "imap_private.h"#endif#include "mutt_crypt.h"#include <sys/stat.h>#include <ctype.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <errno.h>#define ISHEADER(x) ((x) == MT_COLOR_HEADER || (x) == MT_COLOR_HDEFAULT)#define IsAttach(x) (x && (x)->bdy)#define IsRecvAttach(x) (x && (x)->bdy && (x)->fp)#define IsSendAttach(x) (x && (x)->bdy && !(x)->fp)#define IsMsgAttach(x) (x && (x)->fp && (x)->bdy && (x)->bdy->hdr)#define IsHeader(x) (x && (x)->hdr && !(x)->bdy)static const char *Not_available_in_this_menu = N_("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 = N_("Function not permitted in attach-message mode.");/* hack to return to position when returning from index to same message */static int TopLine = 0;static HEADER *OldHdr = NULL;#define CHECK_MODE(x) if (!(x)) \ { \ mutt_flushinp (); \ mutt_error _(Not_available_in_this_menu); \ break; \ }#define CHECK_READONLY if (Context->readonly) \ { \ mutt_flushinp (); \ mutt_error _(Mailbox_is_read_only); \ break; \ }#define CHECK_ATTACH if(option(OPTATTACHMSG)) \ {\ mutt_flushinp (); \ mutt_error _(Function_not_permitted_in_attach_message_mode); \ break; \ }#ifdef USE_IMAP /* the error message returned here could be better. */#define CHECK_IMAP_ACL(aclbit) if (Context->magic == M_IMAP) \ if (mutt_bit_isset (((IMAP_DATA *)Context->data)->capabilities, ACL) \ && !mutt_bit_isset(((IMAP_DATA *)Context->data)->rights,aclbit)){ \ mutt_flushinp(); \ mutt_error ("Operation not permitted by the IMAP ACL for this mailbox"); \ break; \ }#endifstruct q_class_t{ int length; int index; int color; char *prefix; struct q_class_t *next, *prev; struct q_class_t *down, *up;};struct syntax_t{ int color; int first; int last;};struct line_t{ LOFF_T offset; short type; short continuation; short chunks; short search_cnt; struct syntax_t *syntax; struct syntax_t *search; struct q_class_t *quote;};#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)typedef struct _ansi_attr { int attr; int fg; int bg; int pair;} ansi_attr;static short InHelp = 0;#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)static struct resize { int line; int SearchCompiled; int SearchBack;} *Resize = NULL;#endif#define NumSigLines 4static int check_sig (const char *s, struct line_t *info, int n){ int count = 0; while (n > 0 && count <= NumSigLines) { if (info[n].type != MT_COLOR_SIGNATURE) break; count++; n--; } if (count == 0) return (-1); if (count > NumSigLines) { /* check for a blank line */ while (*s) { if (!ISSPACE (*s)) return 0; s++; } return (-1); } return (0);}static voidresolve_color (struct line_t *lineInfo, int n, int cnt, int flags, int special, ansi_attr *a){ int def_color; /* color without syntax hilight */ int color; /* final color */ static int last_color; /* last color set */ int search = 0, i, m; if (!cnt) last_color = -1; /* force attrset() */ if (lineInfo[n].continuation) { if (!cnt && option (OPTMARKERS)) { SETCOLOR (MT_COLOR_MARKERS); addch ('+'); last_color = ColorDefs[MT_COLOR_MARKERS]; } m = (lineInfo[n].syntax)[0].first; cnt += (lineInfo[n].syntax)[0].last; } else m = n; if (!(flags & M_SHOWCOLOR)) def_color = ColorDefs[MT_COLOR_NORMAL]; else if (lineInfo[m].type == MT_COLOR_HEADER) def_color = (lineInfo[m].syntax)[0].color; else def_color = ColorDefs[lineInfo[m].type]; if ((flags & M_SHOWCOLOR) && lineInfo[m].type == MT_COLOR_QUOTED) { struct q_class_t *class = lineInfo[m].quote; if (class) { def_color = class->color; while (class && class->length > cnt) { def_color = class->color; class = class->up; } } } color = def_color; if (flags & M_SHOWCOLOR) { for (i = 0; i < lineInfo[m].chunks; i++) { /* we assume the chunks are sorted */ if (cnt > (lineInfo[m].syntax)[i].last) continue; if (cnt < (lineInfo[m].syntax)[i].first) break; if (cnt != (lineInfo[m].syntax)[i].last) { color = (lineInfo[m].syntax)[i].color; break; } /* don't break here, as cnt might be * in the next chunk as well */ } } if (flags & M_SEARCH) { for (i = 0; i < lineInfo[m].search_cnt; i++) { if (cnt > (lineInfo[m].search)[i].last) continue; if (cnt < (lineInfo[m].search)[i].first) break; if (cnt != (lineInfo[m].search)[i].last) { color = ColorDefs[MT_COLOR_SEARCH]; search = 1; break; } } } /* handle "special" bold & underlined characters */ if (special || a->attr) {#ifdef HAVE_COLOR if ((a->attr & ANSI_COLOR)) { if (a->pair == -1) a->pair = mutt_alloc_color (a->fg, a->bg); color = a->pair; if (a->attr & ANSI_BOLD) color |= A_BOLD; } else#endif if ((special & A_BOLD) || (a->attr & ANSI_BOLD)) { if (ColorDefs[MT_COLOR_BOLD] && !search) color = ColorDefs[MT_COLOR_BOLD]; else color ^= A_BOLD; } if ((special & A_UNDERLINE) || (a->attr & ANSI_UNDERLINE)) { if (ColorDefs[MT_COLOR_UNDERLINE] && !search) color = ColorDefs[MT_COLOR_UNDERLINE]; else color ^= A_UNDERLINE; } else if (a->attr & ANSI_REVERSE) { color ^= A_REVERSE; } else if (a->attr & ANSI_BLINK) { color ^= A_BLINK; } else if (a->attr & ANSI_OFF) { a->attr = 0; } } if (color != last_color) { attrset (color); last_color = color; }}static voidappend_line (struct line_t *lineInfo, int n, int cnt){ int m; lineInfo[n+1].type = lineInfo[n].type; (lineInfo[n+1].syntax)[0].color = (lineInfo[n].syntax)[0].color; lineInfo[n+1].continuation = 1; /* find the real start of the line */ for (m = n; m >= 0; m--) if (lineInfo[m].continuation == 0) break; (lineInfo[n+1].syntax)[0].first = m; (lineInfo[n+1].syntax)[0].last = (lineInfo[n].continuation) ? cnt + (lineInfo[n].syntax)[0].last : cnt;}static voidnew_class_color (struct q_class_t *class, int *q_level){ class->index = (*q_level)++; class->color = ColorQuote[class->index % ColorQuoteUsed];}static voidshift_class_colors (struct q_class_t *QuoteList, struct q_class_t *new_class, int index, int *q_level){ struct q_class_t * q_list; q_list = QuoteList; new_class->index = -1; while (q_list) { if (q_list->index >= index) { q_list->index++; q_list->color = ColorQuote[q_list->index % ColorQuoteUsed]; } if (q_list->down) q_list = q_list->down; else if (q_list->next) q_list = q_list->next; else { while (!q_list->next) { q_list = q_list->up; if (q_list == NULL) break; } if (q_list) q_list = q_list->next; } } new_class->index = index; new_class->color = ColorQuote[index % ColorQuoteUsed]; (*q_level)++;}static voidcleanup_quote (struct q_class_t **QuoteList){ struct q_class_t *ptr; while (*QuoteList) { if ((*QuoteList)->down) cleanup_quote (&((*QuoteList)->down)); ptr = (*QuoteList)->next; if ((*QuoteList)->prefix) FREE (&(*QuoteList)->prefix); FREE (QuoteList); /* __FREE_CHECKED__ */ *QuoteList = ptr; } return;}static struct q_class_t *classify_quote (struct q_class_t **QuoteList, const char *qptr, int length, int *force_redraw, int *q_level){ struct q_class_t *q_list = *QuoteList; struct q_class_t *class = NULL, *tmp = NULL, *ptr, *save; char *tail_qptr; int offset, tail_lng; int index = -1; if (ColorQuoteUsed <= 1) { /* not much point in classifying quotes... */ if (*QuoteList == NULL) { class = (struct q_class_t *) safe_calloc (1, sizeof (struct q_class_t)); class->color = ColorQuote[0]; *QuoteList = class; } return (*QuoteList); } /* Did I mention how much I like emulating Lisp in C? */ /* classify quoting prefix */ while (q_list) { if (length <= q_list->length) { /* case 1: check the top level nodes */ if (mutt_strncmp (qptr, q_list->prefix, length) == 0) { if (length == q_list->length) return q_list; /* same prefix: return the current class */ /* found shorter prefix */ if (tmp == NULL) { /* add a node above q_list */ tmp = (struct q_class_t *) safe_calloc (1, sizeof (struct q_class_t)); tmp->prefix = (char *) safe_calloc (1, length + 1); strncpy (tmp->prefix, qptr, length); tmp->length = length; /* replace q_list by tmp in the top level list */ if (q_list->next) { tmp->next = q_list->next; q_list->next->prev = tmp; } if (q_list->prev) { tmp->prev = q_list->prev; q_list->prev->next = tmp; } /* make q_list a child of tmp */ tmp->down = q_list; q_list->up = tmp; /* q_list has no siblings for now */ q_list->next = NULL; q_list->prev = NULL; /* update the root if necessary */ if (q_list == *QuoteList) *QuoteList = tmp; index = q_list->index; /* tmp should be the return class too */ class = tmp; /* next class to test; if tmp is a shorter prefix for another * node, that node can only be in the top level list, so don't * go down after this point */ q_list = tmp->next; } else { /* found another branch for which tmp is a shorter prefix */ /* save the next sibling for later */ save = q_list->next; /* unlink q_list from the top level list */ if (q_list->next) q_list->next->prev = q_list->prev; if (q_list->prev) q_list->prev->next = q_list->next; /* at this point, we have a tmp->down; link q_list to it */ ptr = tmp->down; /* sibling order is important here, q_list should be linked last */ while (ptr->next) ptr = ptr->next; ptr->next = q_list; q_list->next = NULL; q_list->prev = ptr; q_list->up = tmp; index = q_list->index; /* next class to test; as above, we shouldn't go down */ q_list = save; } /* we found a shorter prefix, so certain quotes have changed classes */ *force_redraw = 1; continue; } else { /* shorter, but not a substring of the current class: try next */ q_list = q_list->next; continue; } } else { /* case 2: try subclassing the current top level node */ /* tmp != NULL means we already found a shorter prefix at case 1 */ if (tmp == NULL && mutt_strncmp (qptr, q_list->prefix, q_list->length) == 0) { /* ok, it's a subclass somewhere on this branch */ ptr = q_list; offset = q_list->length; q_list = q_list->down; tail_lng = length - offset; tail_qptr = (char *) qptr + offset; while (q_list) { if (length <= q_list->length) { if (mutt_strncmp (tail_qptr, (q_list->prefix) + offset, tail_lng) == 0) { /* same prefix: return the current class */ if (length == q_list->length) return q_list; /* found shorter common prefix */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -