{
BUFFER err, token;
HOOK *hook;
+ pattern_cache_t cache;
current_hook_type = type;
err.dsize = STRING;
err.data = safe_malloc (err.dsize);
mutt_buffer_init (&token);
+ memset (&cache, 0, sizeof (cache));
for (hook = Hooks; hook; hook = hook->next)
{
if(!hook->command)
continue;
if (hook->type & type)
- if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr) > 0) ^ hook->rx.not)
+ if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr, &cache) > 0) ^ hook->rx.not)
+ {
if (mutt_parse_rc_line (hook->command, &token, &err) != 0)
{
FREE (&token.data);
return;
}
+ /* Executing arbitrary commands could affect the pattern results,
+ * so the cache has to be wiped */
+ memset (&cache, 0, sizeof (cache));
+ }
}
FREE (&token.data);
FREE (&err.data);
mutt_addr_hook (char *path, size_t pathlen, int type, CONTEXT *ctx, HEADER *hdr)
{
HOOK *hook;
+ pattern_cache_t cache;
+ memset (&cache, 0, sizeof (cache));
/* determine if a matching hook exists */
for (hook = Hooks; hook; hook = hook->next)
{
continue;
if (hook->type & type)
- if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr) > 0) ^ hook->rx.not)
+ if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr, &cache) > 0) ^ hook->rx.not)
{
mutt_make_string (path, pathlen, hook->command, ctx, hdr);
return 0;
}
static int
-perform_and (pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *hdr)
+perform_and (pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *hdr, pattern_cache_t *cache)
{
for (; pat; pat = pat->next)
- if (mutt_pattern_exec (pat, flags, ctx, hdr) <= 0)
+ if (mutt_pattern_exec (pat, flags, ctx, hdr, cache) <= 0)
return 0;
return 1;
}
static int
-perform_or (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *hdr)
+perform_or (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *hdr, pattern_cache_t *cache)
{
for (; pat; pat = pat->next)
- if (mutt_pattern_exec (pat, flags, ctx, hdr) > 0)
+ if (mutt_pattern_exec (pat, flags, ctx, hdr, cache) > 0)
return 1;
return 0;
}
return 0;
h = t->message;
if(h)
- if(mutt_pattern_exec(pat, flags, ctx, h))
+ if(mutt_pattern_exec(pat, flags, ctx, h, NULL))
return 1;
if(up && (a=match_threadcomplete(pat, flags, ctx, t->parent,1,1,1,0)))
return 0;
}
-/* flags
- MUTT_MATCH_FULL_ADDRESS match both personal and machine address */
+
+/* Sets a value in the pattern_cache_t cache entry.
+ * Normalizes the "true" value to 2. */
+static void set_pattern_cache_value (int *cache_entry, int value)
+{
+ *cache_entry = value ? 2 : 1;
+}
+
+/* Returns 1 if the cache value is set and has a true value.
+ * 0 otherwise (even if unset!) */
+static int get_pattern_cache_value (int cache_entry)
+{
+ return cache_entry == 2;
+}
+
+static int is_pattern_cache_set (int cache_entry)
+{
+ return cache_entry != 0;
+}
+
+
+/*
+ * flags: MUTT_MATCH_FULL_ADDRESS - match both personal and machine address
+ * cache: For repeated matches against the same HEADER, passing in non-NULL will
+ * store some of the cacheable pattern matches in this structure. */
int
-mutt_pattern_exec (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *h)
+mutt_pattern_exec (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx, HEADER *h,
+ pattern_cache_t *cache)
{
+ int result;
+ int *cache_entry;
+
switch (pat->op)
{
case MUTT_AND:
- return (pat->not ^ (perform_and (pat->child, flags, ctx, h) > 0));
+ return (pat->not ^ (perform_and (pat->child, flags, ctx, h, cache) > 0));
case MUTT_OR:
- return (pat->not ^ (perform_or (pat->child, flags, ctx, h) > 0));
+ return (pat->not ^ (perform_or (pat->child, flags, ctx, h, cache) > 0));
case MUTT_THREAD:
return (pat->not ^ match_threadcomplete(pat->child, flags, ctx, h->thread, 1, 1, 1, 1));
case MUTT_ALL:
return (pat->not ^ match_adrlist (pat, flags & MUTT_MATCH_FULL_ADDRESS,
2, h->env->to, h->env->cc));
case MUTT_LIST: /* known list, subscribed or not */
- return (pat->not ^ mutt_is_list_cc (pat->alladdr, h->env->to, h->env->cc));
+ if (cache)
+ {
+ cache_entry = pat->alladdr ? &cache->list_all : &cache->list_one;
+ if (!is_pattern_cache_set (*cache_entry))
+ set_pattern_cache_value (cache_entry,
+ mutt_is_list_cc (pat->alladdr, h->env->to, h->env->cc));
+ result = get_pattern_cache_value (*cache_entry);
+ }
+ else
+ result = mutt_is_list_cc (pat->alladdr, h->env->to, h->env->cc);
+ return (pat->not ^ result);
case MUTT_SUBSCRIBED_LIST:
- return (pat->not ^ mutt_is_list_recipient (pat->alladdr, h->env->to, h->env->cc));
+ if (cache)
+ {
+ cache_entry = pat->alladdr ? &cache->sub_all : &cache->sub_one;
+ if (!is_pattern_cache_set (*cache_entry))
+ set_pattern_cache_value (cache_entry,
+ mutt_is_list_recipient (pat->alladdr, h->env->to, h->env->cc));
+ result = get_pattern_cache_value (*cache_entry);
+ }
+ else
+ result = mutt_is_list_recipient (pat->alladdr, h->env->to, h->env->cc);
+ return (pat->not ^ result);
case MUTT_PERSONAL_RECIP:
- return (pat->not ^ match_user (pat->alladdr, h->env->to, h->env->cc));
+ if (cache)
+ {
+ cache_entry = pat->alladdr ? &cache->pers_recip_all : &cache->pers_recip_one;
+ if (!is_pattern_cache_set (*cache_entry))
+ set_pattern_cache_value (cache_entry,
+ match_user (pat->alladdr, h->env->to, h->env->cc));
+ result = get_pattern_cache_value (*cache_entry);
+ }
+ else
+ result = match_user (pat->alladdr, h->env->to, h->env->cc);
+ return (pat->not ^ result);
case MUTT_PERSONAL_FROM:
- return (pat->not ^ match_user (pat->alladdr, h->env->from, NULL));
+ if (cache)
+ {
+ cache_entry = pat->alladdr ? &cache->pers_from_all : &cache->pers_from_one;
+ if (!is_pattern_cache_set (*cache_entry))
+ set_pattern_cache_value (cache_entry,
+ match_user (pat->alladdr, h->env->from, NULL));
+ result = get_pattern_cache_value (*cache_entry);
+ }
+ else
+ result = match_user (pat->alladdr, h->env->from, NULL);
+ return (pat->not ^ result);
case MUTT_COLLAPSED:
return (pat->not ^ (h->collapsed && h->num_hidden > 1));
case MUTT_CRYPT_SIGN:
Context->hdrs[i]->limited = 0;
Context->hdrs[i]->collapsed = 0;
Context->hdrs[i]->num_hidden = 0;
- if (mutt_pattern_exec (pat, MUTT_MATCH_FULL_ADDRESS, Context, Context->hdrs[i]))
+ if (mutt_pattern_exec (pat, MUTT_MATCH_FULL_ADDRESS, Context, Context->hdrs[i], NULL))
{
Context->hdrs[i]->virtual = Context->vcount;
Context->hdrs[i]->limited = 1;
for (i = 0; i < Context->vcount; i++)
{
mutt_progress_update (&progress, i, -1);
- if (mutt_pattern_exec (pat, MUTT_MATCH_FULL_ADDRESS, Context, Context->hdrs[Context->v2r[i]]))
+ if (mutt_pattern_exec (pat, MUTT_MATCH_FULL_ADDRESS, Context, Context->hdrs[Context->v2r[i]], NULL))
{
switch (op)
{
{
/* remember that we've already searched this message */
h->searched = 1;
- if ((h->matched = (mutt_pattern_exec (SearchPattern, MUTT_MATCH_FULL_ADDRESS, Context, h) > 0)))
+ if ((h->matched = (mutt_pattern_exec (SearchPattern, MUTT_MATCH_FULL_ADDRESS, Context, h, NULL) > 0)))
{
mutt_clear_error();
if (msg && *msg)