From: Kevin McCarthy Date: Tue, 30 Oct 2018 18:53:37 +0000 (-0700) Subject: Add a flag for dynamic date range evaluation in patterns. X-Git-Tag: mutt-1-11-rel~22 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5de55f85a42d7fd0516c6fc526939f1260f207a5;p=mutt 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. --- diff --git a/hook.c b/hook.c index 4d0c0b24..6cfe50a6 100644 --- a/hook.c +++ b/hook.c @@ -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) diff --git a/mutt.h b/mutt.h index f4fd74f1..d80facec 100644 --- a/mutt.h +++ b/mutt.h @@ -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; diff --git a/pattern.c b/pattern.c index 890cfb90..aedbde11 100644 --- a/pattern.c +++ b/pattern.c @@ -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: