+void mutt_free_spam_list (SPAM_LIST ** list)
+{
+ SPAM_LIST *p;
+
+ if (!list)
+ return;
+ while (*list) {
+ p = *list;
+ *list = (*list)->next;
+ rx_free (&p->rx);
+ mem_free(&p->template);
+ mem_free(&p);
+ }
+}
+
+int mutt_match_spam_list (const char *s, SPAM_LIST * l, char *text, int x)
+{
+ static regmatch_t *pmatch = NULL;
+ static int nmatch = 0;
+ int i, n, tlen;
+ char *p;
+
+ if (!s)
+ return 0;
+
+ tlen = 0;
+
+ for (; l; l = l->next) {
+ /* If this pattern needs more matches, expand pmatch. */
+ if (l->nmatch > nmatch) {
+ mem_realloc (&pmatch, l->nmatch * sizeof (regmatch_t));
+ nmatch = l->nmatch;
+ }
+
+ /* Does this pattern match? */
+ if (regexec
+ (l->rx->rx, s, (size_t) l->nmatch, (regmatch_t *) pmatch,
+ (int) 0) == 0) {
+ debug_print (5, ("%s matches %s\n%d subst", s, l->rx->pattern, l->rx->rx->re_nsub));
+
+ /* Copy template into text, with substitutions. */
+ for (p = l->template; *p;) {
+ if (*p == '%') {
+ n = atoi (++p); /* find pmatch index */
+ while (isdigit (*p))
+ ++p; /* skip subst token */
+ for (i = pmatch[n].rm_so; (i < pmatch[n].rm_eo) && (tlen < x); i++)
+ text[tlen++] = s[i];
+ }
+ else {
+ text[tlen++] = *p++;
+ }
+ }
+ text[tlen] = '\0';
+ debug_print (5, ("\"%s\"\n", text));
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int mutt_cmp_header (const HEADER * h1, const HEADER * h2) {
+ if (h1 && h2) {
+ if (h1->received != h2->received ||
+ h1->date_sent != h2->date_sent ||
+ h1->content->length != h2->content->length ||
+ h1->lines != h2->lines ||
+ h1->zhours != h2->zhours ||
+ h1->zminutes != h2->zminutes ||
+ h1->zoccident != h2->zoccident ||
+ h1->mime != h2->mime ||
+ !mutt_cmp_env (h1->env, h2->env) ||
+ !mutt_cmp_body (h1->content, h2->content))
+ return (0);
+ else
+ return (1);
+ }
+ else {
+ if (h1 == NULL && h2 == NULL)
+ return (1);
+ else
+ return (0);
+ }
+}
+
+/* return 1 if address lists are strictly identical */
+int mutt_cmp_addr (const ADDRESS * a, const ADDRESS * b)