Commit 5de55f85 authored by Kevin J. McCarthy's avatar Kevin J. McCarthy

Add a flag for dynamic date range evaluation in patterns.

By default, Mutt evaluates relative date patterns, such as "~d<1d"
against the date when the pattern is compiled.

For index-format-hook, we need to pattern match against a message
based on the current date/time, so that for example, conditional date
format patterns correctly match after Mutt has been open for many
days/weeks.

Add a flag, MUTT_PATTERN_DYNAMIC, that changes ~d and ~r evaluation to
reevaluate the min/max range with each match.

This will of course be slower, but for the index, where a screenful of
messages at a time are evaluated, is an acceptable tradeoff against
accurate pattern matching.
parent 7ebb6205
Pipeline #35009108 passed with stages
in 14 minutes and 48 seconds
......@@ -350,7 +350,14 @@ int mutt_parse_idxfmt_hook (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *
break;
}
if ((pat = mutt_pattern_comp (pattern->data, MUTT_FULL_MSG, err)) == NULL)
/* MUTT_PATTERN_DYNAMIC sets so that date ranges are regenerated during
* matching. This of course is slower, but index-format-hook is commonly
* used for date ranges, and they need to be evaluated relative to "now", not
* the hook compilation time.
*/
if ((pat = mutt_pattern_comp (pattern->data,
MUTT_FULL_MSG | MUTT_PATTERN_DYNAMIC,
err)) == NULL)
goto out;
if (ptr)
......
......@@ -876,7 +876,8 @@ struct mutt_thread
/* flag to mutt_pattern_comp() */
#define MUTT_FULL_MSG (1<<0) /* enable body and header matching */
#define MUTT_FULL_MSG (1<<0) /* enable body and header matching */
#define MUTT_PATTERN_DYNAMIC (1<<1) /* enable runtime date range evaluation */
typedef enum {
MUTT_MATCH_FULL_ADDRESS = 1
......@@ -904,6 +905,7 @@ typedef struct pattern_t
unsigned int groupmatch : 1;
unsigned int ign_case : 1; /* ignore case for local stringmatch searches */
unsigned int isalias : 1;
unsigned int dynamic : 1; /* evaluate date ranges at run time */
int min;
int max;
struct pattern_t *next;
......
......@@ -43,9 +43,9 @@
#include "imap/imap.h"
#endif
static int eat_regexp (pattern_t *pat, BUFFER *, BUFFER *);
static int eat_date (pattern_t *pat, BUFFER *, BUFFER *);
static int eat_range (pattern_t *pat, BUFFER *, BUFFER *);
static int eat_regexp (pattern_t *pat, int, BUFFER *, BUFFER *);
static int eat_date (pattern_t *pat, int, BUFFER *, BUFFER *);
static int eat_range (pattern_t *pat, int, BUFFER *, BUFFER *);
static int patmatch (const pattern_t *pat, const char *buf);
static const struct pattern_flags
......@@ -53,7 +53,7 @@ static const struct pattern_flags
int tag; /* character used to represent this op */
int op; /* operation to perform */
int class;
int (*eat_arg) (pattern_t *, BUFFER *, BUFFER *);
int (*eat_arg) (pattern_t *, int, BUFFER *, BUFFER *);
}
Flags[] =
{
......@@ -253,7 +253,7 @@ msg_search (CONTEXT *ctx, pattern_t* pat, int msgno)
return match;
}
static int eat_regexp (pattern_t *pat, BUFFER *s, BUFFER *err)
static int eat_regexp (pattern_t *pat, int flags, BUFFER *s, BUFFER *err)
{
BUFFER buf;
char errmsg[STRING];
......@@ -311,7 +311,7 @@ static int eat_regexp (pattern_t *pat, BUFFER *s, BUFFER *err)
return 0;
}
int eat_range (pattern_t *pat, BUFFER *s, BUFFER *err)
static int eat_range (pattern_t *pat, int flags, BUFFER *s, BUFFER *err)
{
char *tmp;
int do_exclusive = 0;
......@@ -576,27 +576,9 @@ static const char * parse_date_range (const char* pc, struct tm *min,
return ((flag & MUTT_PDR_ERROR) ? NULL : pc);
}
static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
static int eval_date_minmax (pattern_t *pat, const char *s, BUFFER *err)
{
BUFFER buffer;
struct tm min, max;
char *pexpr;
mutt_buffer_init (&buffer);
pexpr = s->dptr;
if (mutt_extract_token (&buffer, s, MUTT_TOKEN_COMMENT | MUTT_TOKEN_PATTERN) != 0
|| !buffer.data)
{
FREE (&buffer.data);
snprintf (err->data, err->dsize, _("Error in expression: %s"), pexpr);
return (-1);
}
if (!*buffer.data)
{
FREE (&buffer.data);
snprintf (err->data, err->dsize, "%s", _("Empty expression"));
return (-1);
}
memset (&min, 0, sizeof (min));
/* the `0' time is Jan 1, 1970 UTC, so in order to prevent a negative time
......@@ -617,7 +599,7 @@ static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
max.tm_min = 59;
max.tm_sec = 59;
if (strchr ("<>=", buffer.data[0]))
if (strchr ("<>=", s[0]))
{
/* offset from current time
<3d less than three days ago
......@@ -627,7 +609,7 @@ static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
struct tm *tm = localtime (&now);
int exact = 0;
if (buffer.data[0] == '<')
if (s[0] == '<')
{
memcpy (&min, tm, sizeof (min));
tm = &min;
......@@ -637,14 +619,14 @@ static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
memcpy (&max, tm, sizeof (max));
tm = &max;
if (buffer.data[0] == '=')
if (s[0] == '=')
exact++;
}
tm->tm_hour = 23;
tm->tm_min = tm->tm_sec = 59;
/* force negative offset */
get_offset (tm, buffer.data + 1, -1);
get_offset (tm, s + 1, -1);
if (exact)
{
......@@ -655,7 +637,7 @@ static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
}
else
{
const char *pc = buffer.data;
const char *pc = s;
int haveMin = FALSE;
int untilNow = FALSE;
......@@ -664,7 +646,6 @@ static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
/* minimum date specified */
if ((pc = getDate (pc, &min, err)) == NULL)
{
FREE (&buffer.data);
return (-1);
}
haveMin = TRUE;
......@@ -690,7 +671,7 @@ static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
memcpy (&min, tm, sizeof (min));
min.tm_hour = min.tm_sec = min.tm_min = 0;
}
/* preset max date for relative offsets,
if nothing follows we search for messages on a specific day */
max.tm_year = min.tm_year;
......@@ -699,7 +680,6 @@ static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
if (!parse_date_range (pc, &min, &max, haveMin, &baseMin, err))
{ /* bail out on any parsing error */
FREE (&buffer.data);
return (-1);
}
}
......@@ -711,9 +691,41 @@ static int eat_date (pattern_t *pat, BUFFER *s, BUFFER *err)
pat->min = mutt_mktime (&min, 1);
pat->max = mutt_mktime (&max, 1);
return 0;
}
static int eat_date (pattern_t *pat, int flags, BUFFER *s, BUFFER *err)
{
BUFFER buffer;
char *pexpr;
int rc = -1;
mutt_buffer_init (&buffer);
pexpr = s->dptr;
if (mutt_extract_token (&buffer, s, MUTT_TOKEN_COMMENT | MUTT_TOKEN_PATTERN) != 0
|| !buffer.data)
{
snprintf (err->data, err->dsize, _("Error in expression: %s"), pexpr);
goto out;
}
if (!*buffer.data)
{
snprintf (err->data, err->dsize, "%s", _("Empty expression"));
goto out;
}
if (flags & MUTT_PATTERN_DYNAMIC)
{
pat->dynamic = 1;
pat->p.str = safe_strdup (buffer.data);
}
rc = eval_date_minmax (pat, buffer.data, err);
out:
FREE (&buffer.data);
return 0;
return rc;
}
static int patmatch (const pattern_t* pat, const char* buf)
......@@ -764,7 +776,7 @@ void mutt_pattern_free (pattern_t **pat)
tmp = *pat;
*pat = (*pat)->next;
if (tmp->stringmatch)
if (tmp->stringmatch || tmp->dynamic)
FREE (&tmp->p.str);
else if (tmp->groupmatch)
tmp->p.g = NULL;
......@@ -951,7 +963,7 @@ pattern_t *mutt_pattern_comp (/* const */ char *s, int flags, BUFFER *err)
mutt_pattern_free (&curlist);
return NULL;
}
if (entry->eat_arg (tmp, &ps, err) == -1)
if (entry->eat_arg (tmp, flags, &ps, err) == -1)
{
mutt_pattern_free (&curlist);
return NULL;
......@@ -1161,6 +1173,18 @@ static int match_content_type(const pattern_t* pat, BODY *b)
return 0;
}
static int match_update_dynamic_date (pattern_t *pat)
{
BUFFER err;
int rc;
mutt_buffer_init (&err);
rc = eval_date_minmax (pat, pat->p.str, &err);
FREE (&err.data);
return rc;
}
static int match_mime_content_type(const pattern_t *pat, CONTEXT *ctx, HEADER *hdr)
{
mutt_parse_mime_message(ctx, hdr);
......@@ -1236,8 +1260,12 @@ mutt_pattern_exec (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx,
return (pat->not ^ (h->msgno >= pat->min - 1 && (pat->max == MUTT_MAXRANGE ||
h->msgno <= pat->max - 1)));
case MUTT_DATE:
if (pat->dynamic)
match_update_dynamic_date (pat);
return (pat->not ^ (h->date_sent >= pat->min && h->date_sent <= pat->max));
case MUTT_DATE_RECEIVED:
if (pat->dynamic)
match_update_dynamic_date (pat);
return (pat->not ^ (h->received >= pat->min && h->received <= pat->max));
case MUTT_BODY:
case MUTT_HEADER:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment