NeoMutt  2025-12-11-1009-ga75d9e
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
display.h File Reference

Pager Display. More...

#include "config.h"
#include <stdbool.h>
#include <stdio.h>
#include "mutt/lib.h"
#include "lib.h"
#include "color/lib.h"
Include dependency graph for display.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  TextSyntax
 Highlighting for a piece of text. More...
struct  Line
 A line of text in the pager. More...

Functions

 ARRAY_HEAD (TextSyntaxArray, struct TextSyntax)
int display_line (FILE *fp, LOFF_T *bytes_read, struct Line **lines, int line_num, int *lines_used, int *lines_max, PagerFlags flags, struct QuoteStyle **quote_list, int *q_level, bool *force_redraw, regex_t *search_re, struct MuttWindow *win_pager, struct AttrColorList *ansi_list)
 Print a line on screen.
bool color_is_header (enum ColorId cid)
 Colour is for an Email header.

Detailed Description

Pager Display.

Authors
  • Richard Russon

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file display.h.

Function Documentation

◆ ARRAY_HEAD()

ARRAY_HEAD ( TextSyntaxArray ,
struct TextSyntax  )

◆ display_line()

int display_line ( FILE * fp,
LOFF_T * bytes_read,
struct Line ** lines,
int line_num,
int * lines_used,
int * lines_max,
PagerFlags flags,
struct QuoteStyle ** quote_list,
int * q_level,
bool * force_redraw,
regex_t * search_re,
struct MuttWindow * win_pager,
struct AttrColorList * ansi_list )

Print a line on screen.

Parameters
[in]fpFile to read from
[out]bytes_readOffset into file
[out]linesLine attributes
[in]line_numLine number
[out]lines_usedLast line
[out]lines_maxMaximum number of lines
[in]flagsFlags, see PagerFlags
[out]quote_listEmail quoting style
[out]q_levelLevel of quoting
[out]force_redrawForce a repaint
[out]search_reRegex to highlight
[in]win_pagerWindow to draw into
[in]ansi_listList of ANSI colours/attributes
Return values
-1EOF was reached
0normal exit, line was not displayed
>0normal exit, line was displayed

Definition at line 1063 of file display.c.

1068{
1069 unsigned char *buf = NULL;
1070 unsigned char *fmt = NULL;
1071 size_t buflen = 0;
1072 unsigned char *buf_ptr = NULL;
1073 int ch = 0;
1074 int vch = 0;
1075 int col = 0;
1076 int cnt;
1077 int b_read;
1078 int buf_ready = 0;
1079 bool change_last = false;
1080 int special = 0;
1081 int offset;
1082 const struct AttrColor *def_color = NULL;
1083 int m;
1084 int rc = -1;
1085 struct AnsiColor ansi = { { COLOR_DEFAULT, 0, 0 }, { COLOR_DEFAULT, 0, 0 }, 0, NULL };
1086 regmatch_t pmatch[1] = { 0 };
1087
1088 struct PagerPrivateData *priv = win_pager->parent->wdata;
1089 enum PagerMode mode = priv->pview->mode;
1090
1091 if (line_num == *lines_used)
1092 {
1093 (*lines_used)++;
1094 change_last = true;
1095 }
1096
1097 if (*lines_used == *lines_max)
1098 {
1099 *lines_max += LINES;
1101 for (ch = *lines_used; ch < *lines_max; ch++)
1102 {
1103 memset(&((*lines)[ch]), 0, sizeof(struct Line));
1104 (*lines)[ch].cid = -1;
1105 (*lines)[ch].search_arr_size = -1;
1106 (*lines)[ch].syntax = MUTT_MEM_CALLOC(1, struct TextSyntax);
1107 ((*lines)[ch].syntax)[0].first = -1;
1108 ((*lines)[ch].syntax)[0].last = -1;
1109 }
1110 }
1111
1112 struct Line *const cur_line = &(*lines)[line_num];
1113
1114 if (flags & MUTT_PAGER_LOGS)
1115 {
1116 /* determine the line class */
1117 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1118 {
1119 if (change_last)
1120 (*lines_used)--;
1121 goto out;
1122 }
1123
1124 if ((cur_line->cont_line) && (line_num > 0))
1125 {
1126 struct Line *const old_line = &(*lines)[line_num - 1];
1127 cur_line->cid = old_line->cid;
1128 cur_line->syntax[0].attr_color = old_line->syntax[0].attr_color;
1129 }
1130 else
1131 {
1132 cur_line->cid = MT_COLOR_NORMAL;
1133 size_t blen = mutt_str_len((const char *) buf);
1134 if ((blen > 11) && (buf[11] == 'M'))
1136 else if ((blen > 11) && (buf[11] == 'W'))
1138 else if ((blen > 11) && (buf[11] == 'E'))
1140 else
1142 }
1143 }
1144
1145 /* only do color highlighting if we are viewing a message */
1146 if (flags & (MUTT_SHOWCOLOR | MUTT_TYPES))
1147 {
1148 if (cur_line->cid == -1)
1149 {
1150 /* determine the line class */
1151 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1152 {
1153 if (change_last)
1154 (*lines_used)--;
1155 goto out;
1156 }
1157
1158 if (mode == PAGER_MODE_EMAIL)
1159 {
1160 resolve_types(win_pager, (char *) fmt, (char *) buf, *lines, line_num, *lines_used,
1161 quote_list, q_level, force_redraw, flags & MUTT_SHOWCOLOR);
1162 }
1163 else
1164 {
1165 (*lines)[line_num].cid = MT_COLOR_NORMAL;
1166 }
1167
1168 /* avoid race condition for continuation lines when scrolling up */
1169 for (m = line_num + 1;
1170 m < *lines_used && (*lines)[m].offset && (*lines)[m].cont_line; m++)
1171 {
1172 (*lines)[m].cid = cur_line->cid;
1173 }
1174 }
1175
1176 /* this also prevents searching through the hidden lines */
1177 const short c_toggle_quoted_show_levels = cs_subset_number(NeoMutt->sub, "toggle_quoted_show_levels");
1178 if ((flags & MUTT_HIDE) && COLOR_QUOTED(cur_line->cid) &&
1179 (!cur_line->quote || (cur_line->quote->quote_n >= c_toggle_quoted_show_levels)))
1180 {
1181 flags = 0; /* MUTT_NOSHOW */
1182 }
1183 }
1184
1185 /* At this point, (*lines[line_num]).quote may still be undefined. We
1186 * don't want to compute it every time MUTT_TYPES is set, since this
1187 * would slow down the "bottom" function unacceptably. A compromise
1188 * solution is hence to call regexec() again, just to find out the
1189 * length of the quote prefix. */
1190 if ((flags & MUTT_SHOWCOLOR) && !cur_line->cont_line &&
1191 COLOR_QUOTED(cur_line->cid) && !cur_line->quote)
1192 {
1193 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1194 {
1195 if (change_last)
1196 (*lines_used)--;
1197 goto out;
1198 }
1199
1200 const struct Regex *c_quote_regex = cs_subset_regex(NeoMutt->sub, "quote_regex");
1201 if (mutt_regex_capture(c_quote_regex, (char *) fmt, 1, pmatch))
1202 {
1203 cur_line->quote = qstyle_classify(quote_list, (char *) fmt + pmatch[0].rm_so,
1204 pmatch[0].rm_eo - pmatch[0].rm_so,
1205 force_redraw, q_level);
1206 }
1207 else
1208 {
1209 goto out;
1210 }
1211 }
1212
1213 if ((flags & MUTT_SEARCH) && !cur_line->cont_line && (cur_line->search_arr_size == -1))
1214 {
1215 if (fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready) < 0)
1216 {
1217 if (change_last)
1218 (*lines_used)--;
1219 goto out;
1220 }
1221
1222 offset = 0;
1223 cur_line->search_arr_size = 0;
1224 while (regexec(search_re, (char *) fmt + offset, 1, pmatch,
1225 (offset ? REG_NOTBOL : 0)) == 0)
1226 {
1227 if (++(cur_line->search_arr_size) > 1)
1228 {
1229 MUTT_MEM_REALLOC(&(cur_line->search), cur_line->search_arr_size, struct TextSyntax);
1230 // Zero the new entry
1231 const int index = cur_line->search_arr_size - 1;
1232 struct TextSyntax *ts = &cur_line->search[index];
1233 memset(ts, 0, sizeof(*ts));
1234 }
1235 else
1236 {
1237 cur_line->search = MUTT_MEM_CALLOC(1, struct TextSyntax);
1238 }
1239 pmatch[0].rm_so += offset;
1240 pmatch[0].rm_eo += offset;
1241 (cur_line->search)[cur_line->search_arr_size - 1].first = pmatch[0].rm_so;
1242 (cur_line->search)[cur_line->search_arr_size - 1].last = pmatch[0].rm_eo;
1243
1244 if (pmatch[0].rm_eo == pmatch[0].rm_so)
1245 offset++; /* avoid degenerate cases */
1246 else
1247 offset = pmatch[0].rm_eo;
1248 if (!fmt[offset])
1249 break;
1250 }
1251 }
1252
1253 if (!(flags & MUTT_SHOW) && ((*lines)[line_num + 1].offset > 0))
1254 {
1255 /* we've already scanned this line, so just exit */
1256 rc = 0;
1257 goto out;
1258 }
1259 if ((flags & MUTT_SHOWCOLOR) && *force_redraw && ((*lines)[line_num + 1].offset > 0))
1260 {
1261 /* no need to try to display this line... */
1262 rc = 1;
1263 goto out; /* fake display */
1264 }
1265
1266 b_read = fill_buffer(fp, bytes_read, cur_line->offset, &buf, &fmt, &buflen, &buf_ready);
1267 if (b_read < 0)
1268 {
1269 if (change_last)
1270 (*lines_used)--;
1271 goto out;
1272 }
1273
1274 /* now chose a good place to break the line */
1275 cnt = format_line(win_pager, lines, line_num, buf, flags, NULL, b_read, &ch,
1276 &vch, &col, &special, win_pager->state.cols, ansi_list);
1277 buf_ptr = buf + cnt;
1278
1279 /* move the break point only if smart_wrap is set */
1280 const bool c_smart_wrap = cs_subset_bool(NeoMutt->sub, "smart_wrap");
1281 if (c_smart_wrap)
1282 {
1283 if ((cnt < b_read) && (ch != -1) && !color_is_header(cur_line->cid) &&
1284 !mutt_isspace(buf[cnt]))
1285 {
1286 buf_ptr = buf + ch;
1287 /* skip trailing blanks */
1288 while (ch && ((buf[ch] == ' ') || (buf[ch] == '\t') || (buf[ch] == '\r')))
1289 ch--;
1290
1291 /* A folded header with a single long word shouldn't be smartwrapped.
1292 * So just disable smart_wrap if it would wrap at the beginning of the line. */
1293 if (ch == 0)
1294 buf_ptr = buf + cnt;
1295 else
1296 cnt = ch + 1;
1297 }
1298
1299 /* skip leading blanks on the next line too */
1300 while ((*buf_ptr == ' ') || (*buf_ptr == '\t'))
1301 buf_ptr++;
1302 }
1303
1304 if (*buf_ptr == '\r')
1305 buf_ptr++;
1306 if (*buf_ptr == '\n')
1307 buf_ptr++;
1308
1309 if (((int) (buf_ptr - buf) < b_read) && !(*lines)[line_num + 1].cont_line)
1310 append_line(*lines, line_num, (int) (buf_ptr - buf));
1311 (*lines)[line_num + 1].offset = cur_line->offset + (long) (buf_ptr - buf);
1312
1313 /* if we don't need to display the line we are done */
1314 if (!(flags & MUTT_SHOW))
1315 {
1316 rc = 0;
1317 goto out;
1318 }
1319
1320 if (flags & MUTT_PAGER_STRIPES)
1321 {
1322 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1324 }
1325
1326 /* display the line */
1327 format_line(win_pager, lines, line_num, buf, flags, &ansi, cnt, &ch, &vch,
1328 &col, &special, win_pager->state.cols, ansi_list);
1329
1330 /* avoid a bug in ncurses... */
1331 if (col == 0)
1332 {
1333 if (flags & MUTT_PAGER_STRIPES)
1334 {
1335 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1337 }
1338 else
1339 {
1341 }
1342
1343 mutt_window_addch(win_pager, ' ');
1344 }
1345
1346 /* Fill the blank space at the end of the line with the prevailing color.
1347 * ncurses does an implicit clrtoeol() when you do mutt_window_addch('\n') so we have
1348 * to make sure to reset the color *after* that */
1349 if (flags & MUTT_SHOWCOLOR)
1350 {
1351 m = (cur_line->cont_line) ? (cur_line->syntax)[0].first : line_num;
1352 if ((*lines)[m].cid == MT_COLOR_HEADER)
1353 {
1354 def_color = ((*lines)[m].syntax)[0].attr_color;
1355 }
1356 else
1357 {
1358 def_color = simple_color_get((*lines)[m].cid);
1359 }
1360 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
1361 const struct AttrColor *ac_eol = NULL;
1362 if (def_color)
1363 ac_eol = merged_color_overlay(ac_normal, def_color);
1364 else
1365 ac_eol = ac_normal;
1366 mutt_curses_set_color(ac_eol);
1367 }
1368
1369 if (col < win_pager->state.cols)
1370 {
1371 if (flags & MUTT_PAGER_STRIPES)
1372 {
1373 const enum ColorId cid = ((line_num % 2) == 0) ? MT_COLOR_STRIPE_ODD : MT_COLOR_STRIPE_EVEN;
1374 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
1375 const struct AttrColor *stripe_color = simple_color_get(cid);
1376 const struct AttrColor *ac_eol = merged_color_overlay(ac_normal, stripe_color);
1377 mutt_curses_set_color(ac_eol);
1378 }
1379 else
1380 {
1381 // Colours may be disabled, but we may still need to end the "search" colour
1382 if (!(flags & MUTT_SHOWCOLOR))
1384 }
1385 mutt_window_clrtoeol(win_pager);
1386 }
1387
1388 /* reset the color back to normal. This *must* come after the
1389 * clrtoeol, otherwise the color for this line will not be
1390 * filled to the right margin. */
1391 if (flags & MUTT_SHOWCOLOR)
1393
1394 /* build a return code */
1395 if (!(flags & MUTT_SHOW))
1396 flags = 0;
1397
1398 rc = flags;
1399
1400out:
1401 FREE(&buf);
1402 FREE(&fmt);
1403 return rc;
1404}
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition simple.c:98
#define COLOR_DEFAULT
Definition color.h:102
ColorId
List of all coloured objects.
Definition color.h:35
@ MT_COLOR_MESSAGE
Informational message.
Definition color.h:52
@ MT_COLOR_HEADER
Message headers (takes a pattern).
Definition color.h:48
@ MT_COLOR_STRIPE_EVEN
Stripes: even lines of the Help Page.
Definition color.h:79
@ MT_COLOR_ERROR
Error message.
Definition color.h:46
@ MT_COLOR_NORMAL
Plain text.
Definition color.h:53
@ MT_COLOR_STRIPE_ODD
Stripes: odd lines of the Help Page.
Definition color.h:80
@ MT_COLOR_WARNING
Warning messages.
Definition color.h:84
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition helpers.c:217
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition helpers.c:143
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
bool mutt_isspace(int arg)
Wrapper for isspace(3).
Definition ctype.c:96
static int format_line(struct MuttWindow *win, struct Line **lines, int line_num, unsigned char *buf, PagerFlags flags, struct AnsiColor *ansi, int cnt, int *pspace, int *pvch, int *pcol, int *pspecial, int width, struct AttrColorList *ansi_list)
Display a line of text in the pager.
Definition display.c:854
static int fill_buffer(FILE *fp, LOFF_T *bytes_read, LOFF_T offset, unsigned char **buf, unsigned char **fmt, size_t *blen, int *buf_ready)
Fill a buffer from a file.
Definition display.c:798
bool color_is_header(enum ColorId cid)
Colour is for an Email header.
Definition display.c:488
static void resolve_types(struct MuttWindow *win, char *buf, char *raw, struct Line *lines, int line_num, int lines_used, struct QuoteStyle **quote_list, int *q_level, bool *force_redraw, bool q_classify)
Determine the style for a line of text.
Definition display.c:506
static void append_line(struct Line *lines, int line_num, int cnt)
Add a new Line to the array.
Definition display.c:259
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition memory.h:55
const struct AttrColor * merged_color_overlay(const struct AttrColor *base, const struct AttrColor *over)
Combine two colours.
Definition merged.c:110
bool mutt_regex_capture(const struct Regex *regex, const char *str, size_t nmatch, regmatch_t matches[])
Match a regex against a string, with provided options.
Definition regex.c:601
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
const struct AttrColor * mutt_curses_set_normal_backed_color_by_id(enum ColorId cid)
Set the colour and attributes by the Colour ID.
Definition mutt_curses.c:63
const struct AttrColor * mutt_curses_set_color_by_id(enum ColorId cid)
Set the colour and attributes by the Colour ID.
Definition mutt_curses.c:79
void mutt_curses_set_color(const struct AttrColor *ac)
Set the colour and attributes for text.
Definition mutt_curses.c:38
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
#define MUTT_HIDE
Don't show quoted text.
Definition lib.h:66
#define MUTT_TYPES
Compute line's type.
Definition lib.h:68
#define MUTT_PAGER_STRIPES
Striped highlighting.
Definition lib.h:77
#define MUTT_SHOWCOLOR
Show characters in color otherwise don't show characters.
Definition lib.h:65
#define MUTT_PAGER_LOGS
Logview mode.
Definition lib.h:75
#define MUTT_SEARCH
Resolve search patterns.
Definition lib.h:67
PagerMode
Determine the behaviour of the Pager.
Definition lib.h:136
@ PAGER_MODE_EMAIL
Pager is invoked via 1st path. The mime part is selected automatically.
Definition lib.h:139
#define MUTT_SHOW
Definition lib.h:69
struct QuoteStyle * qstyle_classify(struct QuoteStyle **quote_list, const char *qptr, size_t length, bool *force_redraw, int *q_level)
Find a style for a string.
Definition qstyle.c:136
#define COLOR_QUOTED(cid)
Definition quoted.h:28
An ANSI escape sequence.
Definition ansi.h:35
A curses colour and its attributes.
Definition attr.h:65
A line of text in the pager.
Definition display.h:50
short search_arr_size
Number of items in search array.
Definition display.h:59
struct TextSyntax * search
Array of search text in the line.
Definition display.h:60
bool cont_line
Continuation of a previous line (wrapped by NeoMutt).
Definition display.h:53
short cid
Default line colour, e.g. MT_COLOR_SIGNATURE.
Definition display.h:52
struct QuoteStyle * quote
Quoting style for this line (pointer into PagerPrivateData->quote_list).
Definition display.h:62
LOFF_T offset
Offset into Email file (PagerPrivateData->fp).
Definition display.h:51
struct TextSyntax * syntax
Array of coloured text in the line.
Definition display.h:57
struct WindowState state
Current state of the Window.
void * wdata
Private data.
struct MuttWindow * parent
Parent Window.
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
Private state data for the Pager.
int lines_used
Size of lines array (used entries).
int lines_max
Capacity of lines array (total entries).
struct Line * lines
Array of text lines in pager.
bool first
First time flag for toggle-new.
struct PagerView * pview
Object to view in the pager.
enum PagerMode mode
Pager mode.
Definition lib.h:175
int quote_n
The quoteN colour index for this level.
Definition qstyle.h:56
Cached regular expression.
Definition regex3.h:85
Highlighting for a piece of text.
Definition display.h:39
const struct AttrColor * attr_color
Curses colour of text.
Definition display.h:40
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition mutt_window.h:60
Here is the call graph for this function:
Here is the caller graph for this function:

◆ color_is_header()

bool color_is_header ( enum ColorId cid)

Colour is for an Email header.

Parameters
cidColour ID, e.g. MT_COLOR_HEADER
Return values
trueColour is for an Email header

Definition at line 488 of file display.c.

489{
490 return (cid == MT_COLOR_HEADER) || (cid == MT_COLOR_HDRDEFAULT);
491}
@ MT_COLOR_HDRDEFAULT
Header default colour.
Definition color.h:47
Here is the caller graph for this function: