]> granicus.if.org Git - neomutt/commitdiff
Datastructures #1 - PoC of standardizing a couple of fields
authorPietro Cerutti <gahr@gahr.ch>
Fri, 21 Jul 2017 15:05:25 +0000 (15:05 +0000)
committerRichard Russon <rich@flatcap.org>
Sat, 5 Aug 2017 17:53:06 +0000 (18:53 +0100)
This commits changes the type of two fields in envelope.h from struct List to
to struct STailQHead. This new structure represents a singly-linked tail queue
implemented using FreeBSD's queue.h (imported).

This is a proof of concept to see how we like using macros to implement basic
datastructures (singly and doubly linked lists and tail queues) and algorithms
(traversal, removal, insertion, ...).

I encourage everybody to have a look at the result and comment. Is this the
direction we would like to take?

Issue #374

17 files changed:
copy.c
curs_main.c
envelope.h
hcache/hcache.c
headers.c
list.h
mbox.c
mutt.h
muttlib.c
parse.c
pattern.c
protos.h
queue.h [new file with mode: 0644]
recvcmd.c
send.c
sendlib.c
thread.c

diff --git a/copy.c b/copy.c
index 7b14809604c482a15fa098233b4f07da2303b585..f807433848ba6b95b94f62ceff34ac3447db4aa3 100644 (file)
--- a/copy.c
+++ b/copy.c
@@ -397,22 +397,22 @@ int mutt_copy_header(FILE *in, struct Header *h, FILE *out, int flags, const cha
     fputc('\n', out);
   }
 
-  if ((flags & CH_UPDATE_IRT) && h->env->in_reply_to)
+  if ((flags & CH_UPDATE_IRT) && !STAILQ_EMPTY(&h->env->in_reply_to))
   {
-    struct List *listp = h->env->in_reply_to;
     fputs("In-Reply-To:", out);
-    for (; listp; listp = listp->next)
+    struct STailQNode *np;
+    STAILQ_FOREACH(np, &h->env->in_reply_to, entries)
     {
       fputc(' ', out);
-      fputs(listp->data, out);
+      fputs(np->data, out);
     }
     fputc('\n', out);
   }
 
-  if ((flags & CH_UPDATE_REFS) && h->env->references)
+  if ((flags & CH_UPDATE_REFS) && !STAILQ_EMPTY(&h->env->references))
   {
     fputs("References:", out);
-    mutt_write_references(h->env->references, out, 0);
+    mutt_write_references(&h->env->references, out, 0);
     fputc('\n', out);
   }
 
index c387182339bef764efc282b5c703ed5916c747a4..bc2c22d8e2f5b93aecb053488dfa996c952e90fd 100644 (file)
@@ -1246,13 +1246,12 @@ int mutt_index_menu(void)
           }
           else
           {
-            struct List *ref = CURHDR->env->references;
-            if (!ref)
+            if (STAILQ_EMPTY(&CURHDR->env->references))
             {
               mutt_error(_("Article has no parent reference."));
               break;
             }
-            strfcpy(buf, ref->data, sizeof(buf));
+            strfcpy(buf, STAILQ_FIRST(&CURHDR->env->references)->data, sizeof(buf));
           }
           if (!Context->id_hash)
             Context->id_hash = mutt_make_id_hash(Context);
@@ -1319,8 +1318,8 @@ int mutt_index_menu(void)
           /* trying to find msgid of the root message */
           if (op == OP_RECONSTRUCT_THREAD)
           {
-            struct List *ref = CURHDR->env->references;
-            while (ref)
+            struct STailQNode *ref;
+            STAILQ_FOREACH(ref, &CURHDR->env->references, entries)
             {
               if (hash_find(Context->id_hash, ref->data) == NULL)
               {
@@ -1330,9 +1329,8 @@ int mutt_index_menu(void)
               }
 
               /* the last msgid in References is the root message */
-              if (!ref->next)
+              if (!STAILQ_NEXT(ref, entries))
                 strfcpy(buf, ref->data, sizeof(buf));
-              ref = ref->next;
             }
           }
 
@@ -2191,7 +2189,8 @@ int mutt_index_menu(void)
 
         if ((Sort & SORT_MASK) != SORT_THREADS)
           mutt_error(_("Threading is not enabled."));
-        else if (CURHDR->env->in_reply_to || CURHDR->env->references)
+        else if (!STAILQ_EMPTY(&CURHDR->env->in_reply_to) ||
+                 !STAILQ_EMPTY(&CURHDR->env->references))
         {
           {
             struct Header *oldcur = CURHDR;
index 3925a79057054c622338b83d0a200881d5f06c34..d6290793d0bea5185b47ece31e35e9e857d83953 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <stdbool.h>
 #include "lib/lib.h"
+#include "list.h"
 
 /**
  * struct Envelope - The header of an email
@@ -56,8 +57,8 @@ struct Envelope
   char *x_comment_to;
 #endif
   struct Buffer *spam;
-  struct List *references;  /**< message references (in reverse order) */
-  struct List *in_reply_to; /**< in-reply-to header content */
+  struct STailQHead references;  /**< message references (in reverse order) */
+  struct STailQHead in_reply_to; /**< in-reply-to header content */
   struct List *userhdrs;    /**< user defined headers */
   int kwtypes;
 
@@ -67,7 +68,10 @@ struct Envelope
 
 static inline struct Envelope *mutt_new_envelope(void)
 {
-  return safe_calloc(1, sizeof(struct Envelope));
+  struct Envelope *e = safe_calloc(1, sizeof(struct Envelope));
+  STAILQ_INIT(&e->references);
+  STAILQ_INIT(&e->in_reply_to);
+  return e;
 }
 
 #endif /* _MUTT_ENVELOPE_H */
index e359154beffc56d8df0bc620ea423606a4b29da0..250f2c2c4ac767cccc3b87d8acc63efae8657fd5 100644 (file)
@@ -305,6 +305,25 @@ static unsigned char *dump_list(struct List *l, unsigned char *d, int *off, int
   return d;
 }
 
+static unsigned char *dump_stailq(struct STailQHead *l, unsigned char *d, int *off, int convert)
+{
+  unsigned int counter = 0;
+  unsigned int start_off = *off;
+
+  d = dump_int(0xdeadbeef, d, off);
+
+  struct STailQNode *np;
+  STAILQ_FOREACH(np, l, entries)
+  {
+    d = dump_char(np->data, d, off, convert);
+    counter++;
+  }
+
+  memcpy(d + start_off, &counter, sizeof(int));
+
+  return d;
+}
+
 static void restore_list(struct List **l, const unsigned char *d, int *off, int convert)
 {
   unsigned int counter;
@@ -322,6 +341,23 @@ static void restore_list(struct List **l, const unsigned char *d, int *off, int
   *l = NULL;
 }
 
+static void restore_stailq(struct STailQHead *l, const unsigned char *d, int *off, int convert)
+{
+  unsigned int counter;
+
+  restore_int(&counter, d, off);
+
+  struct STailQNode *np;
+  while (counter)
+  {
+    np = safe_malloc(sizeof(struct STailQNode));
+    restore_char(&np->data, d, off, convert);
+    STAILQ_INSERT_TAIL(l, np, entries);
+    counter--;
+  }
+}
+
+
 static unsigned char *dump_buffer(struct Buffer *b, unsigned char *d, int *off, int convert)
 {
   if (!b)
@@ -472,8 +508,8 @@ static unsigned char *dump_envelope(struct Envelope *e, unsigned char *d, int *o
 
   d = dump_buffer(e->spam, d, off, convert);
 
-  d = dump_list(e->references, d, off, 0);
-  d = dump_list(e->in_reply_to, d, off, 0);
+  d = dump_stailq(&e->references, d, off, 0);
+  d = dump_stailq(&e->in_reply_to, d, off, 0);
   d = dump_list(e->userhdrs, d, off, convert);
 
 #ifdef USE_NNTP
@@ -514,8 +550,8 @@ static void restore_envelope(struct Envelope *e, const unsigned char *d, int *of
 
   restore_buffer(&e->spam, d, off, convert);
 
-  restore_list(&e->references, d, off, 0);
-  restore_list(&e->in_reply_to, d, off, 0);
+  restore_stailq(&e->references, d, off, 0);
+  restore_stailq(&e->in_reply_to, d, off, 0);
   restore_list(&e->userhdrs, d, off, convert);
 
 #ifdef USE_NNTP
index c427f12362e46756841d8346bc4d92c43120d263..fde5cc2ba333fd5d5171a6c04b078c4d52e698ef 100644 (file)
--- a/headers.c
+++ b/headers.c
@@ -130,15 +130,15 @@ void mutt_edit_headers(const char *editor, const char *body, struct Header *msg,
 #ifdef USE_NNTP
   if (!option(OPT_NEWS_SEND))
 #endif
-    if (msg->env->in_reply_to &&
-        (!n->in_reply_to ||
-         (mutt_strcmp(n->in_reply_to->data, msg->env->in_reply_to->data) != 0)))
-      mutt_free_list(&msg->env->references);
+    if (!STAILQ_EMPTY(&msg->env->in_reply_to) &&
+        (STAILQ_EMPTY(&n->in_reply_to) ||
+         (mutt_strcmp(STAILQ_FIRST(&n->in_reply_to)->data,
+                      STAILQ_FIRST(&msg->env->in_reply_to)->data) != 0)))
+      mutt_free_stailq(&msg->env->references);
 
   /* restore old info. */
-  mutt_free_list(&n->references);
-  n->references = msg->env->references;
-  msg->env->references = NULL;
+  mutt_free_stailq(&n->references);
+  STAILQ_SWAP(&n->references, &msg->env->references, STailQNode);
 
   mutt_free_envelope(&msg->env);
   msg->env = n;
diff --git a/list.h b/list.h
index 138bdc47ed13bb1ce9e0f9b03de98ab96f962e0a..1275b30279eb2076cee834fafe95d5b8fe877066 100644 (file)
--- a/list.h
+++ b/list.h
@@ -37,4 +37,17 @@ static inline struct List *mutt_new_list(void)
   return safe_calloc(1, sizeof(struct List));
 }
 
+/**
+ * New implementation using macros from queue.h
+ */
+
+#include "queue.h"
+
+STAILQ_HEAD(STailQHead, STailQNode);
+struct STailQNode
+{
+    char *data;
+    STAILQ_ENTRY(STailQNode) entries;
+};
+
 #endif /* _MUTT_LIST_H */
diff --git a/mbox.c b/mbox.c
index f816e6ad5b8304ac90240d1dc4db4b3d21c2334c..943524aa0674cb0316cec7a81b821c16835ffb9e 100644 (file)
--- a/mbox.c
+++ b/mbox.c
@@ -575,15 +575,18 @@ static int strict_addrcmp(const struct Address *a, const struct Address *b)
   return 1;
 }
 
-static int strict_cmp_lists(const struct List *a, const struct List *b)
+static int strict_cmp_stailq(const struct STailQHead *ah, const struct STailQHead *bh)
 {
+  struct STailQNode *a = STAILQ_FIRST(ah);
+  struct STailQNode *b = STAILQ_FIRST(bh);
+
   while (a && b)
   {
     if (mutt_strcmp(a->data, b->data) != 0)
       return 0;
 
-    a = a->next;
-    b = b->next;
+    a = STAILQ_NEXT(a, entries);
+    b = STAILQ_NEXT(b, entries);
   }
   if (a || b)
     return 0;
@@ -597,7 +600,7 @@ static int strict_cmp_envelopes(const struct Envelope *e1, const struct Envelope
   {
     if ((mutt_strcmp(e1->message_id, e2->message_id) != 0) ||
         (mutt_strcmp(e1->subject, e2->subject) != 0) ||
-        !strict_cmp_lists(e1->references, e2->references) ||
+        !strict_cmp_stailq(&e1->references, &e2->references) ||
         !strict_addrcmp(e1->from, e2->from) || !strict_addrcmp(e1->sender, e2->sender) ||
         !strict_addrcmp(e1->reply_to, e2->reply_to) ||
         !strict_addrcmp(e1->to, e2->to) || !strict_addrcmp(e1->cc, e2->cc) ||
diff --git a/mutt.h b/mutt.h
index cfef4a578515033869de59153a00bb05fc6b125b..66bae4d34a28422e259f1a29c7a4d72029f77293 100644 (file)
--- a/mutt.h
+++ b/mutt.h
@@ -34,6 +34,7 @@ struct List;
 struct ReplaceList;
 struct RxList;
 struct State;
+struct STailQHead;
 
 /* On OS X 10.5.x, wide char functions are inlined by default breaking
  * --without-wc-funcs compilation
@@ -313,9 +314,9 @@ enum QuadOptionVars
 #define MUTT_KEYWORDS       (1 << 3) /**< rfc2822 */
 
 void mutt_free_list(struct List **list);
+void mutt_free_stailq(struct STailQHead *list);
 void mutt_free_rx_list(struct RxList **list);
 void mutt_free_replace_list(struct ReplaceList **list);
-struct List *mutt_copy_list(struct List *p);
 int mutt_matches_ignore(const char *s);
 bool mutt_matches_list(const char *s, struct List *t);
 
@@ -323,6 +324,7 @@ bool mutt_matches_list(const char *s, struct List *t);
 struct List *mutt_add_list(struct List *head, const char *data);
 struct List *mutt_add_list_n(struct List *head, const void *data, size_t len);
 struct List *mutt_find_list(struct List *l, const char *data);
+struct STailQNode *mutt_find_stailq(struct STailQHead *h, const char *data);
 int mutt_remove_from_rx_list(struct RxList **l, const char *str);
 
 /* handle stack */
index d51d2583bf33e858543d5c6c7846c78a643a315b..d387491ced87b9eddbc651804cb206f73b7e5d05 100644 (file)
--- a/muttlib.c
+++ b/muttlib.c
@@ -287,6 +287,19 @@ struct List *mutt_find_list(struct List *l, const char *data)
   return NULL;
 }
 
+struct STailQNode *mutt_find_stailq(struct STailQHead *h, const char *data)
+{
+  struct STailQNode *np = NULL;
+  STAILQ_FOREACH(np, h, entries)
+  {
+    if (np->data == data)
+      return np;
+    if (data && np->data && (mutt_strcmp(np->data, data) == 0))
+      return np;
+  }
+  return NULL;
+}
+
 void mutt_push_list(struct List **head, const char *data)
 {
   struct List *tmp = NULL;
@@ -365,24 +378,16 @@ void mutt_free_list(struct List **list)
   }
 }
 
-struct List *mutt_copy_list(struct List *p)
+void mutt_free_stailq(struct STailQHead *h)
 {
-  struct List *t = NULL, *r = NULL, *l = NULL;
-
-  for (; p; p = p->next)
-  {
-    t = safe_malloc(sizeof(struct List));
-    t->data = safe_strdup(p->data);
-    t->next = NULL;
-    if (l)
+    struct STailQNode *np = NULL;
+    while (!STAILQ_EMPTY(h))
     {
-      r->next = t;
-      r = r->next;
+        np = STAILQ_FIRST(h);
+        STAILQ_REMOVE_HEAD(h, entries);
+        FREE(&np->data);
+        FREE(&np);
     }
-    else
-      l = r = t;
-  }
-  return l;
 }
 
 void mutt_free_header(struct Header **h)
@@ -738,8 +743,8 @@ void mutt_free_envelope(struct Envelope **p)
 
   mutt_buffer_free(&(*p)->spam);
 
-  mutt_free_list(&(*p)->references);
-  mutt_free_list(&(*p)->in_reply_to);
+  mutt_free_stailq(&(*p)->references);
+  mutt_free_stailq(&(*p)->in_reply_to);
   mutt_free_list(&(*p)->userhdrs);
   FREE(p);
 }
@@ -760,6 +765,13 @@ void mutt_merge_envelopes(struct Envelope *base, struct Envelope **extra)
     base->h = (*extra)->h;                                                     \
     (*extra)->h = NULL;                                                        \
   }
+
+#define MOVE_STAILQ(h)                                                         \
+  if (STAILQ_EMPTY(&base->h))                                                  \
+  {                                                                            \
+    STAILQ_SWAP(&base->h, &((*extra))->h, STailQNode);                         \
+  }
+
   MOVE_ELEM(return_path);
   MOVE_ELEM(from);
   MOVE_ELEM(to);
@@ -776,11 +788,11 @@ void mutt_merge_envelopes(struct Envelope *base, struct Envelope **extra)
   MOVE_ELEM(x_original_to);
   if (!base->refs_changed)
   {
-    MOVE_ELEM(references);
+    MOVE_STAILQ(references);
   }
   if (!base->irt_changed)
   {
-    MOVE_ELEM(in_reply_to);
+    MOVE_STAILQ(in_reply_to);
   }
 
   /* real_subj is subordinate to subject */
diff --git a/parse.c b/parse.c
index 411ea0f300b184f411c4840069660eed0e6efd67..2fb95afbb79d7f96a94dba87de56ea6b2cacef6c 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -114,24 +114,20 @@ char *mutt_read_rfc822_line(FILE *f, char *line, size_t *linelen)
   /* not reached */
 }
 
-static struct List *parse_references(char *s, int in_reply_to)
+static void parse_references(struct STailQHead *head, char *s)
 {
-  struct List *t = NULL, *lst = NULL;
+  struct STailQNode *np = NULL;
   char *m = NULL;
   const char *sp = NULL;
 
   m = mutt_extract_message_id(s, &sp);
   while (m)
   {
-    t = safe_malloc(sizeof(struct List));
-    t->data = m;
-    t->next = lst;
-    lst = t;
-
+    np  = safe_malloc(sizeof(struct STailQNode));
+    np->data = m;
+    STAILQ_INSERT_HEAD(head, np, entries);
     m = mutt_extract_message_id(NULL, &sp);
   }
-
-  return lst;
 }
 
 int mutt_check_encoding(const char *c)
@@ -1108,8 +1104,8 @@ int mutt_parse_rfc822_line(struct Envelope *e, struct Header *hdr, char *line,
     case 'i':
       if (mutt_strcasecmp(line + 1, "n-reply-to") == 0)
       {
-        mutt_free_list(&e->in_reply_to);
-        e->in_reply_to = parse_references(p, 1);
+        mutt_free_stailq(&e->in_reply_to);
+        parse_references(&e->in_reply_to, p);
         matched = 1;
       }
       break;
@@ -1209,8 +1205,8 @@ int mutt_parse_rfc822_line(struct Envelope *e, struct Header *hdr, char *line,
     case 'r':
       if (mutt_strcasecmp(line + 1, "eferences") == 0)
       {
-        mutt_free_list(&e->references);
-        e->references = parse_references(p, 0);
+        mutt_free_stailq(&e->references);
+        parse_references(&e->references, p);
         matched = 1;
       }
       else if (mutt_strcasecmp(line + 1, "eply-to") == 0)
index 83c3133d6727f3ed90efa1248cacbbc532cfc270..08814bf77a2b0e22ca72bf2d26723ff8bf32b5e9 100644 (file)
--- a/pattern.c
+++ b/pattern.c
@@ -1398,11 +1398,14 @@ static int match_adrlist(struct Pattern *pat, int match_personal, int n, ...)
   return pat->alladdr; /* No matches, or all matches if alladdr */
 }
 
-static bool match_reference(struct Pattern *pat, struct List *refs)
+static bool match_reference(struct Pattern *pat, struct STailQHead *refs)
 {
-  for (; refs; refs = refs->next)
-    if (patmatch(pat, refs->data) == 0)
+  struct STailQNode *np = NULL;
+  STAILQ_FOREACH(np, refs, entries)
+  {
+    if (patmatch(pat, np->data) == 0)
       return true;
+  }
   return false;
 }
 
@@ -1631,8 +1634,8 @@ int mutt_pattern_exec(struct Pattern *pat, enum PatternExecFlag flags,
       return (pat->not ^ (h->content->length >= pat->min &&
                           (pat->max == MUTT_MAXRANGE || h->content->length <= pat->max)));
     case MUTT_REFERENCE:
-      return (pat->not ^ (match_reference(pat, h->env->references) ||
-                          match_reference(pat, h->env->in_reply_to)));
+      return (pat->not ^ (match_reference(pat, &h->env->references) ||
+                          match_reference(pat, &h->env->in_reply_to)));
     case MUTT_ADDRESS:
       return (pat->not ^ match_adrlist(pat, flags & MUTT_MATCH_FULL_ADDRESS, 4,
                                        h->env->from, h->env->sender, h->env->to,
index 88f40574a6e0784777b58d2417d5786f51448589..2ef06ec44507c85420ab0b67026d78653e67608d 100644 (file)
--- a/protos.h
+++ b/protos.h
@@ -49,6 +49,7 @@ struct Regex;
 struct ReplaceList;
 struct RxList;
 struct State;
+struct STailQHead;
 
 struct stat;
 struct passwd;
@@ -149,8 +150,7 @@ const char *mutt_fqdn(short may_hide_host);
 struct Regex *mutt_compile_regexp(const char *s, int flags);
 
 void mutt_account_hook(const char *url);
-void mutt_add_to_reference_headers(struct Envelope *env, struct Envelope *curenv, struct List ***pp,
-                                   struct List ***qq);
+void mutt_add_to_reference_headers(struct Envelope *env, struct Envelope *curenv);
 void mutt_adv_mktemp(char *s, size_t l);
 void mutt_alias_menu(char *buf, size_t buflen, struct Alias *aliases);
 void mutt_allow_interrupt(int disposition);
@@ -367,7 +367,7 @@ int mutt_write_mime_header(struct Body *a, FILE *f);
 int mutt_write_one_header(FILE *fp, const char *tag, const char *value,
                           const char *pfx, int wraplen, int flags);
 int mutt_write_rfc822_header(FILE *fp, struct Envelope *env, struct Body *attach, int mode, int privacy);
-void mutt_write_references(struct List *r, FILE *f, int trim);
+void mutt_write_references(const struct STailQHead *r, FILE *f, size_t trim);
 int mutt_yesorno(const char *msg, int def);
 void mutt_set_header_color(struct Context *ctx, struct Header *curhdr);
 void mutt_sleep(short s);
diff --git a/queue.h b/queue.h
new file mode 100644 (file)
index 0000000..93df082
--- /dev/null
+++ b/queue.h
@@ -0,0 +1,858 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)queue.h     8.5 (Berkeley) 8/20/94
+ * $FreeBSD: head/sys/sys/queue.h 314436 2017-02-28 23:42:47Z imp $
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define        _SYS_QUEUE_H_
+
+#include <sys/cdefs.h>
+
+/*
+ * This file defines four types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists and tail queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may be traversed in either direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ * Below is a summary of implemented functions where:
+ *  +  means the macro is available
+ *  -  means the macro is not available
+ *  s  means the macro is available but is slow (runs in O(n) time)
+ *
+ *                             SLIST   LIST    STAILQ  TAILQ
+ * _HEAD                       +       +       +       +
+ * _CLASS_HEAD                 +       +       +       +
+ * _HEAD_INITIALIZER           +       +       +       +
+ * _ENTRY                      +       +       +       +
+ * _CLASS_ENTRY                        +       +       +       +
+ * _INIT                       +       +       +       +
+ * _EMPTY                      +       +       +       +
+ * _FIRST                      +       +       +       +
+ * _NEXT                       +       +       +       +
+ * _PREV                       -       +       -       +
+ * _LAST                       -       -       +       +
+ * _FOREACH                    +       +       +       +
+ * _FOREACH_FROM               +       +       +       +
+ * _FOREACH_SAFE               +       +       +       +
+ * _FOREACH_FROM_SAFE          +       +       +       +
+ * _FOREACH_REVERSE            -       -       -       +
+ * _FOREACH_REVERSE_FROM       -       -       -       +
+ * _FOREACH_REVERSE_SAFE       -       -       -       +
+ * _FOREACH_REVERSE_FROM_SAFE  -       -       -       +
+ * _INSERT_HEAD                        +       +       +       +
+ * _INSERT_BEFORE              -       +       -       +
+ * _INSERT_AFTER               +       +       +       +
+ * _INSERT_TAIL                        -       -       +       +
+ * _CONCAT                     s       s       +       +
+ * _REMOVE_AFTER               +       -       +       -
+ * _REMOVE_HEAD                        +       -       +       -
+ * _REMOVE                     s       +       s       +
+ * _SWAP                       +       +       +       +
+ *
+ */
+#ifdef QUEUE_MACRO_DEBUG
+#warn Use QUEUE_MACRO_DEBUG_TRACE and/or QUEUE_MACRO_DEBUG_TRASH
+#define        QUEUE_MACRO_DEBUG_TRACE
+#define        QUEUE_MACRO_DEBUG_TRASH
+#endif
+
+#ifdef QUEUE_MACRO_DEBUG_TRACE
+/* Store the last 2 places the queue element or head was altered */
+struct qm_trace {
+       unsigned long    lastline;
+       unsigned long    prevline;
+       const char      *lastfile;
+       const char      *prevfile;
+};
+
+#define        TRACEBUF        struct qm_trace trace;
+#define        TRACEBUF_INITIALIZER    { __LINE__, 0, __FILE__, NULL } ,
+
+#define        QMD_TRACE_HEAD(head) do {                                       \
+       (head)->trace.prevline = (head)->trace.lastline;                \
+       (head)->trace.prevfile = (head)->trace.lastfile;                \
+       (head)->trace.lastline = __LINE__;                              \
+       (head)->trace.lastfile = __FILE__;                              \
+} while (0)
+
+#define        QMD_TRACE_ELEM(elem) do {                                       \
+       (elem)->trace.prevline = (elem)->trace.lastline;                \
+       (elem)->trace.prevfile = (elem)->trace.lastfile;                \
+       (elem)->trace.lastline = __LINE__;                              \
+       (elem)->trace.lastfile = __FILE__;                              \
+} while (0)
+
+#else  /* !QUEUE_MACRO_DEBUG_TRACE */
+#define        QMD_TRACE_ELEM(elem)
+#define        QMD_TRACE_HEAD(head)
+#define        TRACEBUF
+#define        TRACEBUF_INITIALIZER
+#endif /* QUEUE_MACRO_DEBUG_TRACE */
+
+#ifdef QUEUE_MACRO_DEBUG_TRASH
+#define        TRASHIT(x)              do {(x) = (void *)-1;} while (0)
+#define        QMD_IS_TRASHED(x)       ((x) == (void *)(intptr_t)-1)
+#else  /* !QUEUE_MACRO_DEBUG_TRASH */
+#define        TRASHIT(x)
+#define        QMD_IS_TRASHED(x)       0
+#endif /* QUEUE_MACRO_DEBUG_TRASH */
+
+#if defined(QUEUE_MACRO_DEBUG_TRACE) || defined(QUEUE_MACRO_DEBUG_TRASH)
+#define        QMD_SAVELINK(name, link)        void **name = (void *)&(link)
+#else  /* !QUEUE_MACRO_DEBUG_TRACE && !QUEUE_MACRO_DEBUG_TRASH */
+#define        QMD_SAVELINK(name, link)
+#endif /* QUEUE_MACRO_DEBUG_TRACE || QUEUE_MACRO_DEBUG_TRASH */
+
+#ifdef __cplusplus
+/*
+ * In C++ there can be structure lists and class lists:
+ */
+#define        QUEUE_TYPEOF(type) type
+#else
+#define        QUEUE_TYPEOF(type) struct type
+#endif
+
+/*
+ * Singly-linked List declarations.
+ */
+#define        SLIST_HEAD(name, type)                                          \
+struct name {                                                          \
+       struct type *slh_first; /* first element */                     \
+}
+
+#define        SLIST_CLASS_HEAD(name, type)                                    \
+struct name {                                                          \
+       class type *slh_first;  /* first element */                     \
+}
+
+#define        SLIST_HEAD_INITIALIZER(head)                                    \
+       { NULL }
+
+#define        SLIST_ENTRY(type)                                               \
+struct {                                                               \
+       struct type *sle_next;  /* next element */                      \
+}
+
+#define        SLIST_CLASS_ENTRY(type)                                         \
+struct {                                                               \
+       class type *sle_next;           /* next element */              \
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define        QMD_SLIST_CHECK_PREVPTR(prevp, elm) do {                        \
+       if (*(prevp) != (elm))                                          \
+               panic("Bad prevptr *(%p) == %p != %p",                  \
+                   (prevp), *(prevp), (elm));                          \
+} while (0)
+#else
+#define        QMD_SLIST_CHECK_PREVPTR(prevp, elm)
+#endif
+
+#define SLIST_CONCAT(head1, head2, type, field) do {                   \
+       QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head1);                \
+       if (curelm == NULL) {                                           \
+               if ((SLIST_FIRST(head1) = SLIST_FIRST(head2)) != NULL)  \
+                       SLIST_INIT(head2);                              \
+       } else if (SLIST_FIRST(head2) != NULL) {                        \
+               while (SLIST_NEXT(curelm, field) != NULL)               \
+                       curelm = SLIST_NEXT(curelm, field);             \
+               SLIST_NEXT(curelm, field) = SLIST_FIRST(head2);         \
+               SLIST_INIT(head2);                                      \
+       }                                                               \
+} while (0)
+
+#define        SLIST_EMPTY(head)       ((head)->slh_first == NULL)
+
+#define        SLIST_FIRST(head)       ((head)->slh_first)
+
+#define        SLIST_FOREACH(var, head, field)                                 \
+       for ((var) = SLIST_FIRST((head));                               \
+           (var);                                                      \
+           (var) = SLIST_NEXT((var), field))
+
+#define        SLIST_FOREACH_FROM(var, head, field)                            \
+       for ((var) = ((var) ? (var) : SLIST_FIRST((head)));             \
+           (var);                                                      \
+           (var) = SLIST_NEXT((var), field))
+
+#define        SLIST_FOREACH_SAFE(var, head, field, tvar)                      \
+       for ((var) = SLIST_FIRST((head));                               \
+           (var) && ((tvar) = SLIST_NEXT((var), field), 1);            \
+           (var) = (tvar))
+
+#define        SLIST_FOREACH_FROM_SAFE(var, head, field, tvar)                 \
+       for ((var) = ((var) ? (var) : SLIST_FIRST((head)));             \
+           (var) && ((tvar) = SLIST_NEXT((var), field), 1);            \
+           (var) = (tvar))
+
+#define        SLIST_FOREACH_PREVPTR(var, varp, head, field)                   \
+       for ((varp) = &SLIST_FIRST((head));                             \
+           ((var) = *(varp)) != NULL;                                  \
+           (varp) = &SLIST_NEXT((var), field))
+
+#define        SLIST_INIT(head) do {                                           \
+       SLIST_FIRST((head)) = NULL;                                     \
+} while (0)
+
+#define        SLIST_INSERT_AFTER(slistelm, elm, field) do {                   \
+       SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field);       \
+       SLIST_NEXT((slistelm), field) = (elm);                          \
+} while (0)
+
+#define        SLIST_INSERT_HEAD(head, elm, field) do {                        \
+       SLIST_NEXT((elm), field) = SLIST_FIRST((head));                 \
+       SLIST_FIRST((head)) = (elm);                                    \
+} while (0)
+
+#define        SLIST_NEXT(elm, field)  ((elm)->field.sle_next)
+
+#define        SLIST_REMOVE(head, elm, type, field) do {                       \
+       QMD_SAVELINK(oldnext, (elm)->field.sle_next);                   \
+       if (SLIST_FIRST((head)) == (elm)) {                             \
+               SLIST_REMOVE_HEAD((head), field);                       \
+       }                                                               \
+       else {                                                          \
+               QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head);         \
+               while (SLIST_NEXT(curelm, field) != (elm))              \
+                       curelm = SLIST_NEXT(curelm, field);             \
+               SLIST_REMOVE_AFTER(curelm, field);                      \
+       }                                                               \
+       TRASHIT(*oldnext);                                              \
+} while (0)
+
+#define SLIST_REMOVE_AFTER(elm, field) do {                            \
+       SLIST_NEXT(elm, field) =                                        \
+           SLIST_NEXT(SLIST_NEXT(elm, field), field);                  \
+} while (0)
+
+#define        SLIST_REMOVE_HEAD(head, field) do {                             \
+       SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field);   \
+} while (0)
+
+#define        SLIST_REMOVE_PREVPTR(prevp, elm, field) do {                    \
+       QMD_SLIST_CHECK_PREVPTR(prevp, elm);                            \
+       *(prevp) = SLIST_NEXT(elm, field);                              \
+       TRASHIT((elm)->field.sle_next);                                 \
+} while (0)
+
+#define SLIST_SWAP(head1, head2, type) do {                            \
+       QUEUE_TYPEOF(type) *swap_first = SLIST_FIRST(head1);            \
+       SLIST_FIRST(head1) = SLIST_FIRST(head2);                        \
+       SLIST_FIRST(head2) = swap_first;                                \
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define        STAILQ_HEAD(name, type)                                         \
+struct name {                                                          \
+       struct type *stqh_first;/* first element */                     \
+       struct type **stqh_last;/* addr of last next element */         \
+}
+
+#define        STAILQ_CLASS_HEAD(name, type)                                   \
+struct name {                                                          \
+       class type *stqh_first; /* first element */                     \
+       class type **stqh_last; /* addr of last next element */         \
+}
+
+#define        STAILQ_HEAD_INITIALIZER(head)                                   \
+       { NULL, &(head).stqh_first }
+
+#define        STAILQ_ENTRY(type)                                              \
+struct {                                                               \
+       struct type *stqe_next; /* next element */                      \
+}
+
+#define        STAILQ_CLASS_ENTRY(type)                                        \
+struct {                                                               \
+       class type *stqe_next;  /* next element */                      \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define        STAILQ_CONCAT(head1, head2) do {                                \
+       if (!STAILQ_EMPTY((head2))) {                                   \
+               *(head1)->stqh_last = (head2)->stqh_first;              \
+               (head1)->stqh_last = (head2)->stqh_last;                \
+               STAILQ_INIT((head2));                                   \
+       }                                                               \
+} while (0)
+
+#define        STAILQ_EMPTY(head)      ((head)->stqh_first == NULL)
+
+#define        STAILQ_FIRST(head)      ((head)->stqh_first)
+
+#define        STAILQ_FOREACH(var, head, field)                                \
+       for((var) = STAILQ_FIRST((head));                               \
+          (var);                                                       \
+          (var) = STAILQ_NEXT((var), field))
+
+#define        STAILQ_FOREACH_FROM(var, head, field)                           \
+       for ((var) = ((var) ? (var) : STAILQ_FIRST((head)));            \
+          (var);                                                       \
+          (var) = STAILQ_NEXT((var), field))
+
+#define        STAILQ_FOREACH_SAFE(var, head, field, tvar)                     \
+       for ((var) = STAILQ_FIRST((head));                              \
+           (var) && ((tvar) = STAILQ_NEXT((var), field), 1);           \
+           (var) = (tvar))
+
+#define        STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar)                \
+       for ((var) = ((var) ? (var) : STAILQ_FIRST((head)));            \
+           (var) && ((tvar) = STAILQ_NEXT((var), field), 1);           \
+           (var) = (tvar))
+
+#define        STAILQ_INIT(head) do {                                          \
+       STAILQ_FIRST((head)) = NULL;                                    \
+       (head)->stqh_last = &STAILQ_FIRST((head));                      \
+} while (0)
+
+#define        STAILQ_INSERT_AFTER(head, tqelm, elm, field) do {               \
+       if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+               (head)->stqh_last = &STAILQ_NEXT((elm), field);         \
+       STAILQ_NEXT((tqelm), field) = (elm);                            \
+} while (0)
+
+#define        STAILQ_INSERT_HEAD(head, elm, field) do {                       \
+       if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
+               (head)->stqh_last = &STAILQ_NEXT((elm), field);         \
+       STAILQ_FIRST((head)) = (elm);                                   \
+} while (0)
+
+#define        STAILQ_INSERT_TAIL(head, elm, field) do {                       \
+       STAILQ_NEXT((elm), field) = NULL;                               \
+       *(head)->stqh_last = (elm);                                     \
+       (head)->stqh_last = &STAILQ_NEXT((elm), field);                 \
+} while (0)
+
+#define        STAILQ_LAST(head, type, field)                          \
+       (STAILQ_EMPTY((head)) ? NULL :                          \
+           __containerof((head)->stqh_last,                    \
+           QUEUE_TYPEOF(type), field.stqe_next))
+
+#define        STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define        STAILQ_REMOVE(head, elm, type, field) do {                      \
+       QMD_SAVELINK(oldnext, (elm)->field.stqe_next);                  \
+       if (STAILQ_FIRST((head)) == (elm)) {                            \
+               STAILQ_REMOVE_HEAD((head), field);                      \
+       }                                                               \
+       else {                                                          \
+               QUEUE_TYPEOF(type) *curelm = STAILQ_FIRST(head);        \
+               while (STAILQ_NEXT(curelm, field) != (elm))             \
+                       curelm = STAILQ_NEXT(curelm, field);            \
+               STAILQ_REMOVE_AFTER(head, curelm, field);               \
+       }                                                               \
+       TRASHIT(*oldnext);                                              \
+} while (0)
+
+#define STAILQ_REMOVE_AFTER(head, elm, field) do {                     \
+       if ((STAILQ_NEXT(elm, field) =                                  \
+            STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL)      \
+               (head)->stqh_last = &STAILQ_NEXT((elm), field);         \
+} while (0)
+
+#define        STAILQ_REMOVE_HEAD(head, field) do {                            \
+       if ((STAILQ_FIRST((head)) =                                     \
+            STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL)         \
+               (head)->stqh_last = &STAILQ_FIRST((head));              \
+} while (0)
+
+#define STAILQ_SWAP(head1, head2, type) do {                           \
+       QUEUE_TYPEOF(type) *swap_first = STAILQ_FIRST(head1);           \
+       QUEUE_TYPEOF(type) **swap_last = (head1)->stqh_last;            \
+       STAILQ_FIRST(head1) = STAILQ_FIRST(head2);                      \
+       (head1)->stqh_last = (head2)->stqh_last;                        \
+       STAILQ_FIRST(head2) = swap_first;                               \
+       (head2)->stqh_last = swap_last;                                 \
+       if (STAILQ_EMPTY(head1))                                        \
+               (head1)->stqh_last = &STAILQ_FIRST(head1);              \
+       if (STAILQ_EMPTY(head2))                                        \
+               (head2)->stqh_last = &STAILQ_FIRST(head2);              \
+} while (0)
+
+
+/*
+ * List declarations.
+ */
+#define        LIST_HEAD(name, type)                                           \
+struct name {                                                          \
+       struct type *lh_first;  /* first element */                     \
+}
+
+#define        LIST_CLASS_HEAD(name, type)                                     \
+struct name {                                                          \
+       class type *lh_first;   /* first element */                     \
+}
+
+#define        LIST_HEAD_INITIALIZER(head)                                     \
+       { NULL }
+
+#define        LIST_ENTRY(type)                                                \
+struct {                                                               \
+       struct type *le_next;   /* next element */                      \
+       struct type **le_prev;  /* address of previous next element */  \
+}
+
+#define        LIST_CLASS_ENTRY(type)                                          \
+struct {                                                               \
+       class type *le_next;    /* next element */                      \
+       class type **le_prev;   /* address of previous next element */  \
+}
+
+/*
+ * List functions.
+ */
+
+#if (defined(_KERNEL) && defined(INVARIANTS))
+/*
+ * QMD_LIST_CHECK_HEAD(LIST_HEAD *head, LIST_ENTRY NAME)
+ *
+ * If the list is non-empty, validates that the first element of the list
+ * points back at 'head.'
+ */
+#define        QMD_LIST_CHECK_HEAD(head, field) do {                           \
+       if (LIST_FIRST((head)) != NULL &&                               \
+           LIST_FIRST((head))->field.le_prev !=                        \
+            &LIST_FIRST((head)))                                       \
+               panic("Bad list head %p first->prev != head", (head));  \
+} while (0)
+
+/*
+ * QMD_LIST_CHECK_NEXT(TYPE *elm, LIST_ENTRY NAME)
+ *
+ * If an element follows 'elm' in the list, validates that the next element
+ * points back at 'elm.'
+ */
+#define        QMD_LIST_CHECK_NEXT(elm, field) do {                            \
+       if (LIST_NEXT((elm), field) != NULL &&                          \
+           LIST_NEXT((elm), field)->field.le_prev !=                   \
+            &((elm)->field.le_next))                                   \
+               panic("Bad link elm %p next->prev != elm", (elm));      \
+} while (0)
+
+/*
+ * QMD_LIST_CHECK_PREV(TYPE *elm, LIST_ENTRY NAME)
+ *
+ * Validates that the previous element (or head of the list) points to 'elm.'
+ */
+#define        QMD_LIST_CHECK_PREV(elm, field) do {                            \
+       if (*(elm)->field.le_prev != (elm))                             \
+               panic("Bad link elm %p prev->next != elm", (elm));      \
+} while (0)
+#else
+#define        QMD_LIST_CHECK_HEAD(head, field)
+#define        QMD_LIST_CHECK_NEXT(elm, field)
+#define        QMD_LIST_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define LIST_CONCAT(head1, head2, type, field) do {                          \
+       QUEUE_TYPEOF(type) *curelm = LIST_FIRST(head1);                       \
+       if (curelm == NULL) {                                                 \
+               if ((LIST_FIRST(head1) = LIST_FIRST(head2)) != NULL) {        \
+                       LIST_FIRST(head2)->field.le_prev =                    \
+                           &LIST_FIRST((head1));                             \
+                       LIST_INIT(head2);                                     \
+               }                                                             \
+       } else if (LIST_FIRST(head2) != NULL) {                               \
+               while (LIST_NEXT(curelm, field) != NULL)                      \
+                       curelm = LIST_NEXT(curelm, field);                    \
+               LIST_NEXT(curelm, field) = LIST_FIRST(head2);                 \
+               LIST_FIRST(head2)->field.le_prev = &LIST_NEXT(curelm, field); \
+               LIST_INIT(head2);                                             \
+       }                                                                     \
+} while (0)
+
+#define        LIST_EMPTY(head)        ((head)->lh_first == NULL)
+
+#define        LIST_FIRST(head)        ((head)->lh_first)
+
+#define        LIST_FOREACH(var, head, field)                                  \
+       for ((var) = LIST_FIRST((head));                                \
+           (var);                                                      \
+           (var) = LIST_NEXT((var), field))
+
+#define        LIST_FOREACH_FROM(var, head, field)                             \
+       for ((var) = ((var) ? (var) : LIST_FIRST((head)));              \
+           (var);                                                      \
+           (var) = LIST_NEXT((var), field))
+
+#define        LIST_FOREACH_SAFE(var, head, field, tvar)                       \
+       for ((var) = LIST_FIRST((head));                                \
+           (var) && ((tvar) = LIST_NEXT((var), field), 1);             \
+           (var) = (tvar))
+
+#define        LIST_FOREACH_FROM_SAFE(var, head, field, tvar)                  \
+       for ((var) = ((var) ? (var) : LIST_FIRST((head)));              \
+           (var) && ((tvar) = LIST_NEXT((var), field), 1);             \
+           (var) = (tvar))
+
+#define        LIST_INIT(head) do {                                            \
+       LIST_FIRST((head)) = NULL;                                      \
+} while (0)
+
+#define        LIST_INSERT_AFTER(listelm, elm, field) do {                     \
+       QMD_LIST_CHECK_NEXT(listelm, field);                            \
+       if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+               LIST_NEXT((listelm), field)->field.le_prev =            \
+                   &LIST_NEXT((elm), field);                           \
+       LIST_NEXT((listelm), field) = (elm);                            \
+       (elm)->field.le_prev = &LIST_NEXT((listelm), field);            \
+} while (0)
+
+#define        LIST_INSERT_BEFORE(listelm, elm, field) do {                    \
+       QMD_LIST_CHECK_PREV(listelm, field);                            \
+       (elm)->field.le_prev = (listelm)->field.le_prev;                \
+       LIST_NEXT((elm), field) = (listelm);                            \
+       *(listelm)->field.le_prev = (elm);                              \
+       (listelm)->field.le_prev = &LIST_NEXT((elm), field);            \
+} while (0)
+
+#define        LIST_INSERT_HEAD(head, elm, field) do {                         \
+       QMD_LIST_CHECK_HEAD((head), field);                             \
+       if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL)     \
+               LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+       LIST_FIRST((head)) = (elm);                                     \
+       (elm)->field.le_prev = &LIST_FIRST((head));                     \
+} while (0)
+
+#define        LIST_NEXT(elm, field)   ((elm)->field.le_next)
+
+#define        LIST_PREV(elm, head, type, field)                       \
+       ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL :   \
+           __containerof((elm)->field.le_prev,                 \
+           QUEUE_TYPEOF(type), field.le_next))
+
+#define        LIST_REMOVE(elm, field) do {                                    \
+       QMD_SAVELINK(oldnext, (elm)->field.le_next);                    \
+       QMD_SAVELINK(oldprev, (elm)->field.le_prev);                    \
+       QMD_LIST_CHECK_NEXT(elm, field);                                \
+       QMD_LIST_CHECK_PREV(elm, field);                                \
+       if (LIST_NEXT((elm), field) != NULL)                            \
+               LIST_NEXT((elm), field)->field.le_prev =                \
+                   (elm)->field.le_prev;                               \
+       *(elm)->field.le_prev = LIST_NEXT((elm), field);                \
+       TRASHIT(*oldnext);                                              \
+       TRASHIT(*oldprev);                                              \
+} while (0)
+
+#define LIST_SWAP(head1, head2, type, field) do {                      \
+       QUEUE_TYPEOF(type) *swap_tmp = LIST_FIRST(head1);               \
+       LIST_FIRST((head1)) = LIST_FIRST((head2));                      \
+       LIST_FIRST((head2)) = swap_tmp;                                 \
+       if ((swap_tmp = LIST_FIRST((head1))) != NULL)                   \
+               swap_tmp->field.le_prev = &LIST_FIRST((head1));         \
+       if ((swap_tmp = LIST_FIRST((head2))) != NULL)                   \
+               swap_tmp->field.le_prev = &LIST_FIRST((head2));         \
+} while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define        TAILQ_HEAD(name, type)                                          \
+struct name {                                                          \
+       struct type *tqh_first; /* first element */                     \
+       struct type **tqh_last; /* addr of last next element */         \
+       TRACEBUF                                                        \
+}
+
+#define        TAILQ_CLASS_HEAD(name, type)                                    \
+struct name {                                                          \
+       class type *tqh_first;  /* first element */                     \
+       class type **tqh_last;  /* addr of last next element */         \
+       TRACEBUF                                                        \
+}
+
+#define        TAILQ_HEAD_INITIALIZER(head)                                    \
+       { NULL, &(head).tqh_first, TRACEBUF_INITIALIZER }
+
+#define        TAILQ_ENTRY(type)                                               \
+struct {                                                               \
+       struct type *tqe_next;  /* next element */                      \
+       struct type **tqe_prev; /* address of previous next element */  \
+       TRACEBUF                                                        \
+}
+
+#define        TAILQ_CLASS_ENTRY(type)                                         \
+struct {                                                               \
+       class type *tqe_next;   /* next element */                      \
+       class type **tqe_prev;  /* address of previous next element */  \
+       TRACEBUF                                                        \
+}
+
+/*
+ * Tail queue functions.
+ */
+#if (defined(_KERNEL) && defined(INVARIANTS))
+/*
+ * QMD_TAILQ_CHECK_HEAD(TAILQ_HEAD *head, TAILQ_ENTRY NAME)
+ *
+ * If the tailq is non-empty, validates that the first element of the tailq
+ * points back at 'head.'
+ */
+#define        QMD_TAILQ_CHECK_HEAD(head, field) do {                          \
+       if (!TAILQ_EMPTY(head) &&                                       \
+           TAILQ_FIRST((head))->field.tqe_prev !=                      \
+            &TAILQ_FIRST((head)))                                      \
+               panic("Bad tailq head %p first->prev != head", (head)); \
+} while (0)
+
+/*
+ * QMD_TAILQ_CHECK_TAIL(TAILQ_HEAD *head, TAILQ_ENTRY NAME)
+ *
+ * Validates that the tail of the tailq is a pointer to pointer to NULL.
+ */
+#define        QMD_TAILQ_CHECK_TAIL(head, field) do {                          \
+       if (*(head)->tqh_last != NULL)                                  \
+               panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head));  \
+} while (0)
+
+/*
+ * QMD_TAILQ_CHECK_NEXT(TYPE *elm, TAILQ_ENTRY NAME)
+ *
+ * If an element follows 'elm' in the tailq, validates that the next element
+ * points back at 'elm.'
+ */
+#define        QMD_TAILQ_CHECK_NEXT(elm, field) do {                           \
+       if (TAILQ_NEXT((elm), field) != NULL &&                         \
+           TAILQ_NEXT((elm), field)->field.tqe_prev !=                 \
+            &((elm)->field.tqe_next))                                  \
+               panic("Bad link elm %p next->prev != elm", (elm));      \
+} while (0)
+
+/*
+ * QMD_TAILQ_CHECK_PREV(TYPE *elm, TAILQ_ENTRY NAME)
+ *
+ * Validates that the previous element (or head of the tailq) points to 'elm.'
+ */
+#define        QMD_TAILQ_CHECK_PREV(elm, field) do {                           \
+       if (*(elm)->field.tqe_prev != (elm))                            \
+               panic("Bad link elm %p prev->next != elm", (elm));      \
+} while (0)
+#else
+#define        QMD_TAILQ_CHECK_HEAD(head, field)
+#define        QMD_TAILQ_CHECK_TAIL(head, headname)
+#define        QMD_TAILQ_CHECK_NEXT(elm, field)
+#define        QMD_TAILQ_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define        TAILQ_CONCAT(head1, head2, field) do {                          \
+       if (!TAILQ_EMPTY(head2)) {                                      \
+               *(head1)->tqh_last = (head2)->tqh_first;                \
+               (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+               (head1)->tqh_last = (head2)->tqh_last;                  \
+               TAILQ_INIT((head2));                                    \
+               QMD_TRACE_HEAD(head1);                                  \
+               QMD_TRACE_HEAD(head2);                                  \
+       }                                                               \
+} while (0)
+
+#define        TAILQ_EMPTY(head)       ((head)->tqh_first == NULL)
+
+#define        TAILQ_FIRST(head)       ((head)->tqh_first)
+
+#define        TAILQ_FOREACH(var, head, field)                                 \
+       for ((var) = TAILQ_FIRST((head));                               \
+           (var);                                                      \
+           (var) = TAILQ_NEXT((var), field))
+
+#define        TAILQ_FOREACH_FROM(var, head, field)                            \
+       for ((var) = ((var) ? (var) : TAILQ_FIRST((head)));             \
+           (var);                                                      \
+           (var) = TAILQ_NEXT((var), field))
+
+#define        TAILQ_FOREACH_SAFE(var, head, field, tvar)                      \
+       for ((var) = TAILQ_FIRST((head));                               \
+           (var) && ((tvar) = TAILQ_NEXT((var), field), 1);            \
+           (var) = (tvar))
+
+#define        TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar)                 \
+       for ((var) = ((var) ? (var) : TAILQ_FIRST((head)));             \
+           (var) && ((tvar) = TAILQ_NEXT((var), field), 1);            \
+           (var) = (tvar))
+
+#define        TAILQ_FOREACH_REVERSE(var, head, headname, field)               \
+       for ((var) = TAILQ_LAST((head), headname);                      \
+           (var);                                                      \
+           (var) = TAILQ_PREV((var), headname, field))
+
+#define        TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field)          \
+       for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname));    \
+           (var);                                                      \
+           (var) = TAILQ_PREV((var), headname, field))
+
+#define        TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar)    \
+       for ((var) = TAILQ_LAST((head), headname);                      \
+           (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1);  \
+           (var) = (tvar))
+
+#define        TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
+       for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname));    \
+           (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1);  \
+           (var) = (tvar))
+
+#define        TAILQ_INIT(head) do {                                           \
+       TAILQ_FIRST((head)) = NULL;                                     \
+       (head)->tqh_last = &TAILQ_FIRST((head));                        \
+       QMD_TRACE_HEAD(head);                                           \
+} while (0)
+
+#define        TAILQ_INSERT_AFTER(head, listelm, elm, field) do {              \
+       QMD_TAILQ_CHECK_NEXT(listelm, field);                           \
+       if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+               TAILQ_NEXT((elm), field)->field.tqe_prev =              \
+                   &TAILQ_NEXT((elm), field);                          \
+       else {                                                          \
+               (head)->tqh_last = &TAILQ_NEXT((elm), field);           \
+               QMD_TRACE_HEAD(head);                                   \
+       }                                                               \
+       TAILQ_NEXT((listelm), field) = (elm);                           \
+       (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field);          \
+       QMD_TRACE_ELEM(&(elm)->field);                                  \
+       QMD_TRACE_ELEM(&(listelm)->field);                              \
+} while (0)
+
+#define        TAILQ_INSERT_BEFORE(listelm, elm, field) do {                   \
+       QMD_TAILQ_CHECK_PREV(listelm, field);                           \
+       (elm)->field.tqe_prev = (listelm)->field.tqe_prev;              \
+       TAILQ_NEXT((elm), field) = (listelm);                           \
+       *(listelm)->field.tqe_prev = (elm);                             \
+       (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field);          \
+       QMD_TRACE_ELEM(&(elm)->field);                                  \
+       QMD_TRACE_ELEM(&(listelm)->field);                              \
+} while (0)
+
+#define        TAILQ_INSERT_HEAD(head, elm, field) do {                        \
+       QMD_TAILQ_CHECK_HEAD(head, field);                              \
+       if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL)   \
+               TAILQ_FIRST((head))->field.tqe_prev =                   \
+                   &TAILQ_NEXT((elm), field);                          \
+       else                                                            \
+               (head)->tqh_last = &TAILQ_NEXT((elm), field);           \
+       TAILQ_FIRST((head)) = (elm);                                    \
+       (elm)->field.tqe_prev = &TAILQ_FIRST((head));                   \
+       QMD_TRACE_HEAD(head);                                           \
+       QMD_TRACE_ELEM(&(elm)->field);                                  \
+} while (0)
+
+#define        TAILQ_INSERT_TAIL(head, elm, field) do {                        \
+       QMD_TAILQ_CHECK_TAIL(head, field);                              \
+       TAILQ_NEXT((elm), field) = NULL;                                \
+       (elm)->field.tqe_prev = (head)->tqh_last;                       \
+       *(head)->tqh_last = (elm);                                      \
+       (head)->tqh_last = &TAILQ_NEXT((elm), field);                   \
+       QMD_TRACE_HEAD(head);                                           \
+       QMD_TRACE_ELEM(&(elm)->field);                                  \
+} while (0)
+
+#define        TAILQ_LAST(head, headname)                                      \
+       (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define        TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define        TAILQ_PREV(elm, headname, field)                                \
+       (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define        TAILQ_REMOVE(head, elm, field) do {                             \
+       QMD_SAVELINK(oldnext, (elm)->field.tqe_next);                   \
+       QMD_SAVELINK(oldprev, (elm)->field.tqe_prev);                   \
+       QMD_TAILQ_CHECK_NEXT(elm, field);                               \
+       QMD_TAILQ_CHECK_PREV(elm, field);                               \
+       if ((TAILQ_NEXT((elm), field)) != NULL)                         \
+               TAILQ_NEXT((elm), field)->field.tqe_prev =              \
+                   (elm)->field.tqe_prev;                              \
+       else {                                                          \
+               (head)->tqh_last = (elm)->field.tqe_prev;               \
+               QMD_TRACE_HEAD(head);                                   \
+       }                                                               \
+       *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);              \
+       TRASHIT(*oldnext);                                              \
+       TRASHIT(*oldprev);                                              \
+       QMD_TRACE_ELEM(&(elm)->field);                                  \
+} while (0)
+
+#define TAILQ_SWAP(head1, head2, type, field) do {                     \
+       QUEUE_TYPEOF(type) *swap_first = (head1)->tqh_first;            \
+       QUEUE_TYPEOF(type) **swap_last = (head1)->tqh_last;             \
+       (head1)->tqh_first = (head2)->tqh_first;                        \
+       (head1)->tqh_last = (head2)->tqh_last;                          \
+       (head2)->tqh_first = swap_first;                                \
+       (head2)->tqh_last = swap_last;                                  \
+       if ((swap_first = (head1)->tqh_first) != NULL)                  \
+               swap_first->field.tqe_prev = &(head1)->tqh_first;       \
+       else                                                            \
+               (head1)->tqh_last = &(head1)->tqh_first;                \
+       if ((swap_first = (head2)->tqh_first) != NULL)                  \
+               swap_first->field.tqe_prev = &(head2)->tqh_first;       \
+       else                                                            \
+               (head2)->tqh_last = &(head2)->tqh_first;                \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
index 037622551c73c654c1976bee33375234a39b5b31..d62645304aef654f1940ed8c88fd0efacbce0ac3 100644 (file)
--- a/recvcmd.c
+++ b/recvcmd.c
@@ -741,15 +741,13 @@ static int attach_reply_envelope_defaults(struct Envelope *env, struct AttachPtr
   mutt_make_misc_reply_headers(env, Context, curhdr, curenv);
 
   if (parent)
-    mutt_add_to_reference_headers(env, curenv, NULL, NULL);
+    mutt_add_to_reference_headers(env, curenv);
   else
   {
-    struct List **p = NULL, **q = NULL;
-
     for (short i = 0; i < idxlen; i++)
     {
       if (idx[i]->content->tagged)
-        mutt_add_to_reference_headers(env, idx[i]->content->hdr->env, &p, &q);
+        mutt_add_to_reference_headers(env, idx[i]->content->hdr->env);
     }
   }
 
diff --git a/send.c b/send.c
index c310bc84ef4a4aca382102647af057e9f46aca6c..8ad30c0b2c54179583ad2b069ec4c48565732f25 100644 (file)
--- a/send.c
+++ b/send.c
@@ -655,24 +655,28 @@ int mutt_fetch_recips(struct Envelope *out, struct Envelope *in, int flags)
   return 0;
 }
 
-static struct List *make_references(struct Envelope *e)
+static void add_references(struct STailQHead *head, struct Envelope *e)
 {
-  struct List *t = NULL, *l = NULL;
+  struct STailQHead *src;
+  struct STailQNode *np, *new;
 
-  if (e->references)
-    l = mutt_copy_list(e->references);
-  else
-    l = mutt_copy_list(e->in_reply_to);
+  src = !STAILQ_EMPTY(&e->references) ? &e->references : &e->in_reply_to;
+  STAILQ_FOREACH(np, src, entries)
+  {
+    new = safe_calloc(1, sizeof(struct STailQNode));
+    new->data = safe_strdup(np->data);
+    STAILQ_INSERT_TAIL(head, new, entries);
+  }
+}
 
+static void add_message_id(struct STailQHead *head, struct Envelope *e)
+{
   if (e->message_id)
   {
-    t = mutt_new_list();
-    t->data = safe_strdup(e->message_id);
-    t->next = l;
-    l = t;
+    struct STailQNode *new = safe_calloc(1, sizeof(struct STailQNode));
+    new->data = safe_strdup(e->message_id);
+    STAILQ_INSERT_HEAD(head, new, entries);
   }
-
-  return l;
 }
 
 void mutt_fix_reply_recipients(struct Envelope *env)
@@ -729,38 +733,11 @@ void mutt_make_misc_reply_headers(struct Envelope *env, struct Context *ctx,
     env->subject = safe_strdup(EmptySubject);
 }
 
-void mutt_add_to_reference_headers(struct Envelope *env, struct Envelope *curenv,
-                                   struct List ***pp, struct List ***qq)
+void mutt_add_to_reference_headers(struct Envelope *env, struct Envelope *curenv)
 {
-  struct List **p = NULL, **q = NULL;
-
-  if (pp)
-    p = *pp;
-  if (qq)
-    q = *qq;
-
-  if (!p)
-    p = &env->references;
-  if (!q)
-    q = &env->in_reply_to;
-
-  while (*p)
-    p = &(*p)->next;
-  while (*q)
-    q = &(*q)->next;
-
-  *p = make_references(curenv);
-
-  if (curenv->message_id)
-  {
-    *q = mutt_new_list();
-    (*q)->data = safe_strdup(curenv->message_id);
-  }
-
-  if (pp)
-    *pp = p;
-  if (qq)
-    *qq = q;
+  add_references(&env->references, curenv);
+  add_message_id(&env->references, curenv);
+  add_message_id(&env->in_reply_to, curenv);
 
 #ifdef USE_NNTP
   if (option(OPT_NEWS_SEND) && option(OPT_XCOMMENT_TO) && curenv->from)
@@ -774,28 +751,25 @@ static void make_reference_headers(struct Envelope *curenv,
   if (!env || !ctx)
     return;
 
-  env->references = NULL;
-  env->in_reply_to = NULL;
-
   if (!curenv)
   {
     struct Header *h = NULL;
-    struct List **p = NULL, **q = NULL;
     for (int i = 0; i < ctx->vcount; i++)
     {
       h = ctx->hdrs[ctx->v2r[i]];
       if (h->tagged)
-        mutt_add_to_reference_headers(env, h->env, &p, &q);
+        mutt_add_to_reference_headers(env, h->env);
     }
   }
   else
-    mutt_add_to_reference_headers(env, curenv, NULL, NULL);
+    mutt_add_to_reference_headers(env, curenv);
 
   /* if there's more than entry in In-Reply-To (i.e. message has
      multiple parents), don't generate a References: header as it's
      discouraged by RfC2822, sect. 3.6.4 */
-  if (ctx->tagged > 0 && env->in_reply_to && env->in_reply_to->next)
-    mutt_free_list(&env->references);
+  if (ctx->tagged > 0 && !STAILQ_EMPTY(&env->in_reply_to) &&
+      STAILQ_NEXT(STAILQ_FIRST(&env->in_reply_to), entries))
+    mutt_free_stailq(&env->references);
 }
 
 static int envelope_defaults(struct Envelope *env, struct Context *ctx,
@@ -1293,8 +1267,8 @@ static int is_reply(struct Header *reply, struct Header *orig)
 {
   if (!reply || !reply->env || !orig || !orig->env)
     return 0;
-  return mutt_find_list(orig->env->references, reply->env->message_id) ||
-         mutt_find_list(orig->env->in_reply_to, reply->env->message_id);
+  return mutt_find_stailq(&orig->env->references, reply->env->message_id) ||
+         mutt_find_stailq(&orig->env->in_reply_to, reply->env->message_id);
 }
 
 static int has_recips(struct Address *a)
index ae4af6b1f66149f2fafcdf3ae13a05cea2127292..9ea786805da1524270d80860be69723a29893a4a 100644 (file)
--- a/sendlib.c
+++ b/sendlib.c
@@ -1586,33 +1586,33 @@ void mutt_write_address_list(struct Address *adr, FILE *fp, int linelen, int dis
  * need to write the list in reverse because they are stored in reverse order
  * when parsed to speed up threading
  */
-void mutt_write_references(struct List *r, FILE *f, int trim)
+void mutt_write_references(const struct STailQHead *r, FILE *f, size_t trim)
 {
-  struct List **ref = NULL;
-  int refcnt = 0, refmax = 0;
-  int multiline = 1;
-  int space = 0;
+  struct STailQNode *np;
+  size_t length = 0;
 
-  if (trim < 0)
+  STAILQ_FOREACH(np, r, entries)
   {
-    trim = -trim;
-    multiline = 0;
+    if (++length == trim)
+      break;
   }
 
-  for (; (trim == 0 || refcnt < trim) && r; r = r->next)
+  struct STailQNode **ref = safe_calloc(length, sizeof(struct STailQNode*));
+
+  // store in reverse order
+  STAILQ_FOREACH(np, r, entries)
   {
-    if (refcnt == refmax)
-      safe_realloc(&ref, (refmax += REF_INC) * sizeof(struct List *));
-    ref[refcnt++] = r;
+    ref[--length] = np;
+    if (length == 0)
+      break;
   }
 
-  while (refcnt-- > 0)
+  for (size_t i = 0; i < length; ++i)
   {
-    if (multiline || space)
+    if (i != 0)
       fputc(' ', f);
-    space = 1;
-    fputs(ref[refcnt]->data, f);
-    if (multiline && refcnt >= 1)
+    fputs(ref[i]->data, f);
+    if (i != length - 1)
       fputc('\n', f);
   }
 
@@ -2104,10 +2104,10 @@ int mutt_write_rfc822_header(FILE *fp, struct Envelope *env,
 
   if (mode <= 0)
   {
-    if (env->references)
+    if (!STAILQ_EMPTY(&env->references))
     {
       fputs("References:", fp);
-      mutt_write_references(env->references, fp, 10);
+      mutt_write_references(&env->references, fp, 10);
       fputc('\n', fp);
     }
 
@@ -2116,10 +2116,10 @@ int mutt_write_rfc822_header(FILE *fp, struct Envelope *env,
     mutt_write_mime_header(attach, fp);
   }
 
-  if (env->in_reply_to)
+  if (!STAILQ_EMPTY(&env->in_reply_to))
   {
     fputs("In-Reply-To:", fp);
-    mutt_write_references(env->in_reply_to, fp, 0);
+    mutt_write_references(&env->in_reply_to, fp, 0);
     fputc('\n', fp);
   }
 
index 68a14f9fea8ba29b5d90fad141f5697ede0e823a..ba6bf59b98a556804502f1321fc76259b6326685 100644 (file)
--- a/thread.c
+++ b/thread.c
@@ -799,7 +799,7 @@ void mutt_sort_threads(struct Context *ctx, int init)
   int i, oldsort, using_refs = 0;
   struct MuttThread *thread = NULL, *new = NULL, *tmp = NULL, top;
   memset(&top, 0, sizeof(top));
-  struct List *ref = NULL;
+  struct STailQNode *ref = NULL;
 
   /* set Sort to the secondary method to support the set sort_aux=reverse-*
    * settings.  The sorting functions just look at the value of
@@ -933,11 +933,11 @@ void mutt_sort_threads(struct Context *ctx, int init)
       if (using_refs == 0)
       {
         /* look at the beginning of in-reply-to: */
-        if ((ref = cur->env->in_reply_to) != NULL)
+        if ((ref = STAILQ_FIRST(&cur->env->in_reply_to)) != NULL)
           using_refs = 1;
         else
         {
-          ref = cur->env->references;
+          ref = STAILQ_FIRST(&cur->env->references);
           using_refs = 2;
         }
       }
@@ -949,20 +949,20 @@ void mutt_sort_threads(struct Context *ctx, int init)
          * the second reference (since at least eudora puts the most
          * recent reference in in-reply-to and the rest in references)
          */
-        if (!cur->env->references)
-          ref = ref->next;
+        if (STAILQ_EMPTY(&cur->env->references))
+          ref = STAILQ_NEXT(ref, entries);
         else
         {
-          if (mutt_strcmp(ref->data, cur->env->references->data) != 0)
-            ref = cur->env->references;
+          if (mutt_strcmp(ref->data, STAILQ_FIRST(&cur->env->references)->data) != 0)
+            ref = STAILQ_FIRST(&cur->env->references);
           else
-            ref = cur->env->references->next;
+            ref = STAILQ_NEXT(STAILQ_FIRST(&cur->env->references), entries);
 
           using_refs = 2;
         }
       }
       else
-        ref = ref->next; /* go on with references */
+        ref = STAILQ_NEXT(ref, entries); /* go on with references */
 
       if (!ref)
         break;
@@ -1409,7 +1409,7 @@ struct Hash *mutt_make_id_hash(struct Context *ctx)
 static void clean_references(struct MuttThread *brk, struct MuttThread *cur)
 {
   struct MuttThread *p = NULL;
-  struct List *ref = NULL;
+  struct STailQNode *ref = NULL;
   bool done = false;
 
   for (; cur; cur = cur->next, done = false)
@@ -1424,19 +1424,31 @@ static void clean_references(struct MuttThread *brk, struct MuttThread *cur)
      * Optimal since Mutt stores the references in reverse order, and the
      * first loop should match immediately for mails respecting RFC2822. */
     for (p = brk; !done && p; p = p->parent)
-      for (ref = cur->message->env->references; p->message && ref; ref = ref->next)
+    {
+      for (ref = STAILQ_FIRST(&cur->message->env->references);
+           p->message && ref;
+           ref = STAILQ_NEXT(ref, entries))
+      {
         if (mutt_strcasecmp(ref->data, p->message->env->message_id) == 0)
         {
           done = true;
           break;
         }
+      }
+    }
 
     if (done)
     {
       struct Header *h = cur->message;
 
       /* clearing the References: header from obsolete Message-ID(s) */
-      mutt_free_list(&ref->next);
+      struct STailQNode *np = NULL;
+      while ((np = STAILQ_NEXT(ref, entries)) != NULL)
+      {
+        STAILQ_REMOVE_AFTER(&cur->message->env->references, ref, entries);
+        FREE(&np->data);
+        FREE(&np);
+      }
 
       h->env->refs_changed = h->changed = true;
     }
@@ -1445,8 +1457,8 @@ static void clean_references(struct MuttThread *brk, struct MuttThread *cur)
 
 void mutt_break_thread(struct Header *hdr)
 {
-  mutt_free_list(&hdr->env->in_reply_to);
-  mutt_free_list(&hdr->env->references);
+  mutt_free_stailq(&hdr->env->in_reply_to);
+  mutt_free_stailq(&hdr->env->references);
   hdr->env->irt_changed = hdr->env->refs_changed = hdr->changed = true;
 
   clean_references(hdr->thread, hdr->thread->child);
@@ -1459,8 +1471,10 @@ static bool link_threads(struct Header *parent, struct Header *child, struct Con
 
   mutt_break_thread(child);
 
-  child->env->in_reply_to = mutt_new_list();
-  child->env->in_reply_to->data = safe_strdup(parent->env->message_id);
+  STAILQ_INIT(&child->env->in_reply_to);
+  struct STailQNode *new = safe_calloc(1, sizeof(struct STailQNode));
+  new->data = safe_strdup(parent->env->message_id);
+  STAILQ_INSERT_HEAD(&child->env->in_reply_to, new, entries);
 
   mutt_set_flag(ctx, child, MUTT_TAG, 0);