NeoMutt  2025-12-11-911-gd8d604
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
message.c
Go to the documentation of this file.
1
23
29
30#include "config.h"
31#include <stdbool.h>
32#include <stdlib.h>
33#include <string.h>
34#include "private.h"
35#include "mutt/lib.h"
36#include "core/lib.h"
37#include "gui/lib.h"
38#include "lib.h"
39#include "menu/lib.h"
40#include "module_data.h"
41
42#define KILO 1024
43#define MEGA 1048576
44
54
62static int report_regerror(int regerr, regex_t *preg, struct Buffer *err)
63{
64 size_t ds = err->dsize;
65
66 if (regerror(regerr, preg, err->data, ds) > ds)
67 mutt_debug(LL_DEBUG2, "warning: buffer too small for regerror\n");
68 /* The return value is fixed, exists only to shorten code at callsite */
69 return RANGE_E_SYNTAX;
70}
71
82static bool is_menu_available(struct Buffer *s, regmatch_t pmatch[], int kind,
83 struct Buffer *err, const struct Menu *menu)
84{
85 const char *context_req_chars[] = {
86 [RANGE_K_REL] = ".0123456789",
87 [RANGE_K_ABS] = ".",
88 [RANGE_K_LT] = "",
89 [RANGE_K_GT] = "",
90 [RANGE_K_BARE] = ".",
91 };
92
93 /* First decide if we're going to need the menu at all.
94 * Relative patterns need it if they contain a dot or a number.
95 * Absolute patterns only need it if they contain a dot. */
96 char *context_loc = strpbrk(s->dptr + pmatch[0].rm_so, context_req_chars[kind]);
97 if (!context_loc || (context_loc >= &s->dptr[pmatch[0].rm_eo]))
98 return true;
99
100 /* We need a current message. Do we actually have one? */
101 if (menu)
102 return true;
103
104 /* Nope. */
105 buf_strcpy(err, _("No current message"));
106 return false;
107}
108
118static int scan_range_num(struct Buffer *s, regmatch_t pmatch[], int group,
119 int kind, struct MailboxView *mv)
120{
121 int num = (int) strtol(&s->dptr[pmatch[group].rm_so], NULL, 0);
122 unsigned char c = (unsigned char) (s->dptr[pmatch[group].rm_eo - 1]);
123 if (mutt_toupper(c) == 'K')
124 num *= KILO;
125 else if (mutt_toupper(c) == 'M')
126 num *= MEGA;
127 switch (kind)
128 {
129 case RANGE_K_REL:
130 {
131 struct Mailbox *m = mv->mailbox;
132 struct Menu *menu = mv->menu;
133 struct Email *e = mutt_get_virt_email(m, menu_get_index(menu));
134 if (!e)
135 return num;
136 return num + email_msgno(e);
137 }
138 case RANGE_K_LT:
139 return num - 1;
140 case RANGE_K_GT:
141 return num + 1;
142 default:
143 return num;
144 }
145}
146
157static int scan_range_slot(struct Buffer *s, regmatch_t pmatch[], int grp,
158 int side, int kind, struct MailboxView *mv)
159{
160 struct Mailbox *m = mv->mailbox;
161 struct Menu *menu = mv->menu;
162
163 /* This means the left or right subpattern was empty, e.g. ",." */
164 if ((pmatch[grp].rm_so == -1) || (pmatch[grp].rm_so == pmatch[grp].rm_eo))
165 {
166 if (side == RANGE_S_LEFT)
167 return 1;
168 if (side == RANGE_S_RIGHT)
169 return m->msg_count;
170 }
171 /* We have something, so determine what */
172 unsigned char c = (unsigned char) (s->dptr[pmatch[grp].rm_so]);
173 switch (c)
174 {
175 case RANGE_CIRCUM:
176 return 1;
177 case RANGE_DOLLAR:
178 return m->msg_count;
179 case RANGE_DOT:
180 {
181 struct Email *e = mutt_get_virt_email(m, menu_get_index(menu));
182 if (!e)
183 return 1;
184 return email_msgno(e);
185 }
186 case RANGE_LT:
187 case RANGE_GT:
188 return scan_range_num(s, pmatch, grp + 1, kind, mv);
189 default:
190 /* Only other possibility: a number */
191 return scan_range_num(s, pmatch, grp, kind, mv);
192 }
193}
194
199static void order_range(struct Pattern *pat)
200{
201 if (pat->min <= pat->max)
202 return;
203 long num = pat->min;
204 pat->min = pat->max;
205 pat->max = num;
206}
207
217static int eat_range_by_regex(struct Pattern *pat, struct Buffer *s, int kind,
218 struct Buffer *err, struct MailboxView *mv)
219{
221 int regerr;
222 regmatch_t pmatch[RANGE_RX_GROUPS] = { 0 };
223 struct RangeRegex *pspec = &mod_data->range_regexes[kind];
224
225 /* First time through, compile the big regex */
226 if (!pspec->ready)
227 {
228 regerr = regcomp(&pspec->cooked, pspec->raw, REG_EXTENDED);
229 if (regerr != 0)
230 return report_regerror(regerr, &pspec->cooked, err);
231 pspec->ready = true;
232 }
233
234 /* Match the pattern buffer against the compiled regex.
235 * No match means syntax error. */
236 regerr = regexec(&pspec->cooked, s->dptr, RANGE_RX_GROUPS, pmatch, 0);
237 if (regerr != 0)
238 return report_regerror(regerr, &pspec->cooked, err);
239
240 struct Mailbox *m = mv->mailbox;
241 struct Menu *menu = mv->menu;
242 if (!is_menu_available(s, pmatch, kind, err, menu))
243 return RANGE_E_MVIEW;
244
245 /* Snarf the contents of the two sides of the range. */
246 pat->min = scan_range_slot(s, pmatch, pspec->lgrp, RANGE_S_LEFT, kind, mv);
247 pat->max = scan_range_slot(s, pmatch, pspec->rgrp, RANGE_S_RIGHT, kind, mv);
248 mutt_debug(LL_DEBUG1, "pat->min=%ld pat->max=%ld\n", pat->min, pat->max);
249
250 /* Special case for a bare 0. */
251 if ((kind == RANGE_K_BARE) && (pat->min == 0) && (pat->max == 0))
252 {
253 if (!m || !menu)
254 {
255 buf_strcpy(err, _("No current message"));
256 return RANGE_E_MVIEW;
257 }
258 struct Email *e = mutt_get_virt_email(m, menu_get_index(menu));
259 if (!e)
260 return RANGE_E_MVIEW;
261
262 pat->max = email_msgno(e);
263 pat->min = pat->max;
264 }
265
266 /* Since we don't enforce order, we must swap bounds if they're backward */
267 order_range(pat);
268
269 /* Slide pointer past the entire match. */
270 s->dptr += pmatch[0].rm_eo;
271 return RANGE_E_OK;
272}
273
284 struct Buffer *s, struct Buffer *err, struct MailboxView *mv)
285{
286 if (!mv || !mv->mailbox || !mv->menu)
287 {
288 // We need these for pretty much anything
289 buf_strcpy(err, _("No mailbox is open"));
290 return false;
291 }
292
293 bool skip_quote = false;
294
295 /* If simple_search is set to "~m %s", the range will have double quotes
296 * around it... */
297 if (*s->dptr == '"')
298 {
299 s->dptr++;
300 skip_quote = true;
301 }
302
303 for (int i_kind = 0; i_kind != RANGE_K_INVALID; i_kind++)
304 {
305 switch (eat_range_by_regex(pat, s, i_kind, err, mv))
306 {
307 case RANGE_E_MVIEW:
308 /* This means it matched syntactically but lacked context.
309 * No point in continuing. */
310 break;
311 case RANGE_E_SYNTAX:
312 /* Try another syntax, then */
313 continue;
314 case RANGE_E_OK:
315 if (skip_quote && (*s->dptr == '"'))
316 s->dptr++;
317 SKIPWS(s->dptr);
318 return true;
319 }
320 }
321 return false;
322}
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
Convenience wrapper for the core headers.
int mutt_toupper(int arg)
Wrapper for toupper(3)
Definition ctype.c:140
bool eat_message_range(struct Pattern *pat, PatternCompFlags flags, struct Buffer *s, struct Buffer *err, struct MailboxView *mv)
Parse a range of message numbers - Implements eat_arg_t -.
Definition message.c:283
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
Convenience wrapper for the gui headers.
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
GUI present the user with a selectable list.
int menu_get_index(struct Menu *menu)
Get the current selection in the Menu.
Definition menu.c:155
@ MODULE_ID_PATTERN
ModulePattern, Pattern
Definition module_api.h:85
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition mview.c:376
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:663
Match patterns to emails.
uint8_t PatternCompFlags
Definition lib.h:78
static int eat_range_by_regex(struct Pattern *pat, struct Buffer *s, int kind, struct Buffer *err, struct MailboxView *mv)
Parse a range given as a regex.
Definition message.c:217
static int scan_range_slot(struct Buffer *s, regmatch_t pmatch[], int grp, int side, int kind, struct MailboxView *mv)
Parse a range of message numbers.
Definition message.c:157
#define MEGA
1048576 bytes (1 mebibyte)
Definition message.c:43
EatRangeError
Error codes for eat_range_by_regex()
Definition message.c:49
@ RANGE_E_MVIEW
Range requires MailboxView, but none available.
Definition message.c:52
@ RANGE_E_OK
Range is valid.
Definition message.c:50
@ RANGE_E_SYNTAX
Range contains syntax error.
Definition message.c:51
static int scan_range_num(struct Buffer *s, regmatch_t pmatch[], int group, int kind, struct MailboxView *mv)
Parse a number range.
Definition message.c:118
#define KILO
1024 bytes (1 kibibyte)
Definition message.c:42
static void order_range(struct Pattern *pat)
Put a range in order.
Definition message.c:199
static int report_regerror(int regerr, regex_t *preg, struct Buffer *err)
Create a regex error message.
Definition message.c:62
static bool is_menu_available(struct Buffer *s, regmatch_t pmatch[], int kind, struct Buffer *err, const struct Menu *menu)
Do we need a MailboxView for this Pattern?
Definition message.c:82
Pattern private Module data.
Shared constants/structs that are private to libpattern.
@ RANGE_S_LEFT
Left side of range.
Definition private.h:135
@ RANGE_S_RIGHT
Right side of range.
Definition private.h:136
#define RANGE_DOLLAR
Last message indicator '$'.
Definition private.h:126
#define RANGE_GT
Greater-than operator '>'.
Definition private.h:128
#define RANGE_RX_GROUPS
Number of capture groups in range regexes.
Definition private.h:122
#define RANGE_CIRCUM
First message indicator '^'.
Definition private.h:125
#define RANGE_DOT
Current position indicator '.'.
Definition private.h:124
static int email_msgno(struct Email *e)
Helper to get the Email's message number.
Definition private.h:144
#define RANGE_LT
Less-than operator '<'.
Definition private.h:127
@ RANGE_K_REL
Relative range.
Definition private.h:93
@ RANGE_K_ABS
Absolute range.
Definition private.h:94
@ RANGE_K_LT
Less-than range.
Definition private.h:95
@ RANGE_K_INVALID
Range is invalid.
Definition private.h:99
@ RANGE_K_BARE
Single symbol.
Definition private.h:97
@ RANGE_K_GT
Greater-than range.
Definition private.h:96
#define SKIPWS(ch)
Definition string2.h:52
String manipulation buffer.
Definition buffer.h:36
char * dptr
Current read/write position.
Definition buffer.h:38
size_t dsize
Length of data.
Definition buffer.h:39
char * data
Pointer to data.
Definition buffer.h:37
The envelope/body of an email.
Definition email.h:39
View of a Mailbox.
Definition mview.h:40
struct Menu * menu
Needed for pattern compilation.
Definition mview.h:47
struct Mailbox * mailbox
Current Mailbox.
Definition mview.h:51
A mailbox.
Definition mailbox.h:81
int msg_count
Total number of messages.
Definition mailbox.h:90
Definition lib.h:86
Container for Accounts, Notifications.
Definition neomutt.h:41
Pattern private Module data.
Definition module_data.h:32
struct RangeRegex range_regexes[RANGE_K_INVALID]
Set of Regexes for various range types.
Definition module_data.h:34
A simple (non-regex) pattern.
Definition lib.h:84
long min
Minimum for range checks.
Definition lib.h:95
long max
Maximum for range checks.
Definition lib.h:96
Regular expression representing a range.
Definition private.h:80
int lgrp
Paren group matching the left side.
Definition private.h:82
int rgrp
Paren group matching the right side.
Definition private.h:83
regex_t cooked
Compiled form.
Definition private.h:85
bool ready
Compiled yet?
Definition private.h:84
const char * raw
Regex as string.
Definition private.h:81