--- /dev/null
+Return-Path: <amthrax@awakening.csail.mit.edu>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+ by olra.theworths.org (Postfix) with ESMTP id 871B1431FB6\r
+ for <notmuch@notmuchmail.org>; Thu, 9 Dec 2010 13:00:30 -0800 (PST)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]\r
+ autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+ by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+ with ESMTP id qs2mtyM5j5os for <notmuch@notmuchmail.org>;\r
+ Thu, 9 Dec 2010 13:00:28 -0800 (PST)\r
+Received: from dmz-mailsec-scanner-5.mit.edu (DMZ-MAILSEC-SCANNER-5.MIT.EDU\r
+ [18.7.68.34])\r
+ by olra.theworths.org (Postfix) with ESMTP id 917D3431FB5\r
+ for <notmuch@notmuchmail.org>; Thu, 9 Dec 2010 13:00:28 -0800 (PST)\r
+X-AuditID: 12074422-b7c3eae000000a70-ea-4d01436cb68a\r
+Received: from mailhub-auth-1.mit.edu ( [18.9.21.35])\r
+ by dmz-mailsec-scanner-5.mit.edu (Symantec Brightmail Gateway) with\r
+ SMTP id BC.D7.02672.C63410D4; Thu, 9 Dec 2010 16:00:28 -0500 (EST)\r
+Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])\r
+ by mailhub-auth-1.mit.edu (8.13.8/8.9.2) with ESMTP id oB9L0Sij011352; \r
+ Thu, 9 Dec 2010 16:00:28 -0500\r
+Received: from awakening.csail.mit.edu (awakening.csail.mit.edu [18.26.4.91])\r
+ (authenticated bits=0)\r
+ (User authenticated as amdragon@ATHENA.MIT.EDU)\r
+ by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id oB9L0QXa010074\r
+ (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
+ Thu, 9 Dec 2010 16:00:27 -0500 (EST)\r
+Received: from amthrax by awakening.csail.mit.edu with local (Exim 4.72)\r
+ (envelope-from <amthrax@awakening.csail.mit.edu>)\r
+ id 1PQnbG-0007Hp-SX; Thu, 09 Dec 2010 16:00:26 -0500\r
+From: Austin Clements <amdragon@MIT.EDU>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH 2/5] Implement an internal generic string list and use it.\r
+Date: Thu, 9 Dec 2010 15:59:53 -0500\r
+Message-Id: <1291928396-27937-3-git-send-email-amdragon@mit.edu>\r
+X-Mailer: git-send-email 1.7.2.3\r
+In-Reply-To: <1291928396-27937-1-git-send-email-amdragon@mit.edu>\r
+References: <1291928396-27937-1-git-send-email-amdragon@mit.edu>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=UTF-8\r
+Content-Transfer-Encoding: 8bit\r
+X-Brightmail-Tracker: AAAAAhbjYfcW42MD\r
+Cc: Austin Clements <amdragon@mit.edu>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+ <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Thu, 09 Dec 2010 21:00:30 -0000\r
+\r
+This replaces the guts of the filename list and tag list, making those\r
+interfaces simple iterators over the generic string list. The\r
+directory, message filename, and tags-related code now build generic\r
+string lists and then wraps them in specific iterators. The real wins\r
+come in later patches, when we use these for even more generic\r
+functionality.\r
+\r
+As a nice side-effect, this also eliminates the annoying dependency on\r
+GList in the tag list.\r
+---\r
+ lib/Makefile.local | 1 +\r
+ lib/database.cc | 12 +++---\r
+ lib/directory.cc | 7 ++--\r
+ lib/filenames.c | 52 +++------------------------\r
+ lib/message.cc | 15 ++++----\r
+ lib/messages.c | 11 +++---\r
+ lib/notmuch-private.h | 68 +++++++++++++++++++++--------------\r
+ lib/strings.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++\r
+ lib/tags.c | 63 ++++++--------------------------\r
+ lib/thread.cc | 10 +++---\r
+ 10 files changed, 179 insertions(+), 154 deletions(-)\r
+ create mode 100644 lib/strings.c\r
+\r
+diff --git a/lib/Makefile.local b/lib/Makefile.local\r
+index 5233ea6..37d1c0d 100644\r
+--- a/lib/Makefile.local\r
++++ b/lib/Makefile.local\r
+@@ -50,6 +50,7 @@ extra_cflags += -I$(dir) -fPIC\r
+ libnotmuch_c_srcs = \\r
+ $(notmuch_compat_srcs) \\r
+ $(dir)/filenames.c \\r
++ $(dir)/strings.c \\r
+ $(dir)/libsha1.c \\r
+ $(dir)/message-file.c \\r
+ $(dir)/messages.c \\r
+diff --git a/lib/database.cc b/lib/database.cc\r
+index 7a00917..45613bd 100644\r
+--- a/lib/database.cc\r
++++ b/lib/database.cc\r
+@@ -1752,15 +1752,15 @@ _notmuch_convert_tags (void *ctx, Xapian::TermIterator &i,\r
+ Xapian::TermIterator &end)\r
+ {\r
+ const char *prefix = _find_prefix ("tag");\r
+- notmuch_tags_t *tags;\r
++ notmuch_string_list_t *list;\r
+ std::string tag;\r
+ \r
+ /* Currently this iteration is written with the assumption that\r
+ * "tag" has a single-character prefix. */\r
+ assert (strlen (prefix) == 1);\r
+ \r
+- tags = _notmuch_tags_create (ctx);\r
+- if (unlikely (tags == NULL))\r
++ list = _notmuch_string_list_create (ctx);\r
++ if (unlikely (list == NULL))\r
+ return NULL;\r
+ \r
+ i.skip_to (prefix);\r
+@@ -1771,14 +1771,14 @@ _notmuch_convert_tags (void *ctx, Xapian::TermIterator &i,\r
+ if (tag.empty () || tag[0] != *prefix)\r
+ break;\r
+ \r
+- _notmuch_tags_add_tag (tags, tag.c_str () + 1);\r
++ _notmuch_string_list_append (list, tag.c_str () + 1);\r
+ \r
+ i++;\r
+ }\r
+ \r
+- _notmuch_tags_prepare_iterator (tags);\r
++ _notmuch_string_list_sort (list);\r
+ \r
+- return tags;\r
++ return _notmuch_tags_create (ctx, list, TRUE);\r
+ }\r
+ \r
+ notmuch_tags_t *\r
+diff --git a/lib/directory.cc b/lib/directory.cc\r
+index 946be4f..aeee9ca 100644\r
+--- a/lib/directory.cc\r
++++ b/lib/directory.cc\r
+@@ -33,11 +33,11 @@ _create_filenames_for_terms_with_prefix (void *ctx,\r
+ notmuch_database_t *notmuch,\r
+ const char *prefix)\r
+ {\r
+- notmuch_filename_list_t *filename_list;\r
++ notmuch_string_list_t *filename_list;\r
+ Xapian::TermIterator i, end;\r
+ int prefix_len = strlen (prefix);\r
+ \r
+- filename_list = _notmuch_filename_list_create (ctx);\r
++ filename_list = _notmuch_string_list_create (ctx);\r
+ if (unlikely (filename_list == NULL))\r
+ return NULL;\r
+ \r
+@@ -47,8 +47,7 @@ _create_filenames_for_terms_with_prefix (void *ctx,\r
+ {\r
+ std::string term = *i;\r
+ \r
+- _notmuch_filename_list_add_filename (filename_list, term.c_str () +\r
+- prefix_len);\r
++ _notmuch_string_list_append (filename_list, term.c_str () + prefix_len);\r
+ }\r
+ \r
+ return _notmuch_filenames_create (ctx, filename_list);\r
+diff --git a/lib/filenames.c b/lib/filenames.c\r
+index f078c95..f1ea243 100644\r
+--- a/lib/filenames.c\r
++++ b/lib/filenames.c\r
+@@ -21,56 +21,14 @@\r
+ #include "notmuch-private.h"\r
+ \r
+ struct _notmuch_filenames {\r
+- notmuch_filename_node_t *iterator;\r
++ notmuch_string_node_t *iterator;\r
+ };\r
+ \r
+-/* Create a new notmuch_filename_list_t object, with 'ctx' as its\r
+- * talloc owner.\r
+- *\r
+- * This function can return NULL in case of out-of-memory.\r
+- */\r
+-notmuch_filename_list_t *\r
+-_notmuch_filename_list_create (const void *ctx)\r
+-{\r
+- notmuch_filename_list_t *list;\r
+-\r
+- list = talloc (ctx, notmuch_filename_list_t);\r
+- if (unlikely (list == NULL))\r
+- return NULL;\r
+-\r
+- list->head = NULL;\r
+- list->tail = &list->head;\r
+-\r
+- return list;\r
+-}\r
+-\r
+-void\r
+-_notmuch_filename_list_add_filename (notmuch_filename_list_t *list,\r
+- const char *filename)\r
+-{\r
+- /* Create and initialize new node. */\r
+- notmuch_filename_node_t *node = talloc (list,\r
+- notmuch_filename_node_t);\r
+-\r
+- node->filename = talloc_strdup (node, filename);\r
+- node->next = NULL;\r
+-\r
+- /* Append the node to the list. */\r
+- *(list->tail) = node;\r
+- list->tail = &node->next;\r
+-}\r
+-\r
+-void\r
+-_notmuch_filename_list_destroy (notmuch_filename_list_t *list)\r
+-{\r
+- talloc_free (list);\r
+-}\r
+-\r
+-/* The notmuch_filenames_t is an iterator object for a\r
+- * notmuch_filename_list_t */\r
++/* The notmuch_filenames_t iterates over a notmuch_string_list_t of\r
++ * file names */\r
+ notmuch_filenames_t *\r
+ _notmuch_filenames_create (const void *ctx,\r
+- notmuch_filename_list_t *list)\r
++ notmuch_string_list_t *list)\r
+ {\r
+ notmuch_filenames_t *filenames;\r
+ \r
+@@ -99,7 +57,7 @@ notmuch_filenames_get (notmuch_filenames_t *filenames)\r
+ if (filenames->iterator == NULL)\r
+ return NULL;\r
+ \r
+- return filenames->iterator->filename;\r
++ return filenames->iterator->string;\r
+ }\r
+ \r
+ void\r
+diff --git a/lib/message.cc b/lib/message.cc\r
+index d6ab636..031eda5 100644\r
+--- a/lib/message.cc\r
++++ b/lib/message.cc\r
+@@ -32,7 +32,7 @@ struct _notmuch_message {\r
+ char *message_id;\r
+ char *thread_id;\r
+ char *in_reply_to;\r
+- notmuch_filename_list_t *filename_list;\r
++ notmuch_string_list_t *filename_list;\r
+ char *author;\r
+ notmuch_message_file_t *message_file;\r
+ notmuch_message_list_t *replies;\r
+@@ -434,7 +434,7 @@ _notmuch_message_add_filename (notmuch_message_t *message,\r
+ char *direntry;\r
+ \r
+ if (message->filename_list) {\r
+- _notmuch_filename_list_destroy (message->filename_list);\r
++ talloc_free (message->filename_list);\r
+ message->filename_list = NULL;\r
+ }\r
+ \r
+@@ -511,7 +511,7 @@ _notmuch_message_ensure_filename_list (notmuch_message_t *message)\r
+ if (message->filename_list)\r
+ return;\r
+ \r
+- message->filename_list = _notmuch_filename_list_create (message);\r
++ message->filename_list = _notmuch_string_list_create (message);\r
+ \r
+ i = message->doc.termlist_begin ();\r
+ i.skip_to (prefix);\r
+@@ -532,7 +532,7 @@ _notmuch_message_ensure_filename_list (notmuch_message_t *message)\r
+ if (data == NULL)\r
+ INTERNAL_ERROR ("message with no filename");\r
+ \r
+- _notmuch_filename_list_add_filename (message->filename_list, data);\r
++ _notmuch_string_list_append (message->filename_list, data);\r
+ \r
+ return;\r
+ }\r
+@@ -573,8 +573,7 @@ _notmuch_message_ensure_filename_list (notmuch_message_t *message)\r
+ filename = talloc_asprintf (message, "%s/%s",\r
+ db_path, basename);\r
+ \r
+- _notmuch_filename_list_add_filename (message->filename_list,\r
+- filename);\r
++ _notmuch_string_list_append (message->filename_list, filename);\r
+ \r
+ talloc_free (local);\r
+ }\r
+@@ -589,12 +588,12 @@ notmuch_message_get_filename (notmuch_message_t *message)\r
+ return NULL;\r
+ \r
+ if (message->filename_list->head == NULL ||\r
+- message->filename_list->head->filename == NULL)\r
++ message->filename_list->head->string == NULL)\r
+ {\r
+ INTERNAL_ERROR ("message with no filename");\r
+ }\r
+ \r
+- return message->filename_list->head->filename;\r
++ return message->filename_list->head->string;\r
+ }\r
+ \r
+ notmuch_filenames_t *\r
+diff --git a/lib/messages.c b/lib/messages.c\r
+index 120a48a..404e8b3 100644\r
+--- a/lib/messages.c\r
++++ b/lib/messages.c\r
+@@ -146,13 +146,14 @@ notmuch_messages_destroy (notmuch_messages_t *messages)\r
+ notmuch_tags_t *\r
+ notmuch_messages_collect_tags (notmuch_messages_t *messages)\r
+ {\r
+- notmuch_tags_t *tags, *msg_tags;\r
++ notmuch_string_list_t *tags;\r
++ notmuch_tags_t *msg_tags;\r
+ notmuch_message_t *msg;\r
+ GHashTable *htable;\r
+ GList *keys, *l;\r
+ const char *tag;\r
+ \r
+- tags = _notmuch_tags_create (messages);\r
++ tags = _notmuch_string_list_create (messages);\r
+ if (tags == NULL) return NULL;\r
+ \r
+ htable = g_hash_table_new_full (g_str_hash, g_str_equal, free, NULL);\r
+@@ -170,12 +171,12 @@ notmuch_messages_collect_tags (notmuch_messages_t *messages)\r
+ \r
+ keys = g_hash_table_get_keys (htable);\r
+ for (l = keys; l; l = l->next) {\r
+- _notmuch_tags_add_tag (tags, (char *)l->data);\r
++ _notmuch_string_list_append (tags, (char *)l->data);\r
+ }\r
+ \r
+ g_list_free (keys);\r
+ g_hash_table_destroy (htable);\r
+ \r
+- _notmuch_tags_prepare_iterator (tags);\r
+- return tags;\r
++ _notmuch_string_list_sort (tags);\r
++ return _notmuch_tags_create (messages, tags, TRUE);\r
+ }\r
+diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h\r
+index 303aeb3..b6f1095 100644\r
+--- a/lib/notmuch-private.h\r
++++ b/lib/notmuch-private.h\r
+@@ -453,48 +453,60 @@ notmuch_sha1_of_string (const char *str);\r
+ char *\r
+ notmuch_sha1_of_file (const char *filename);\r
+ \r
+-/* tags.c */\r
++/* strings.c */\r
+ \r
+-notmuch_tags_t *\r
+-_notmuch_tags_create (void *ctx);\r
++typedef struct _notmuch_string_node {\r
++ char *string;\r
++ struct _notmuch_string_node *next;\r
++} notmuch_string_node_t;\r
++\r
++typedef struct _notmuch_string_list {\r
++ int length;\r
++ notmuch_string_node_t *head;\r
++ notmuch_string_node_t **tail;\r
++} notmuch_string_list_t;\r
+ \r
++notmuch_string_list_t *\r
++_notmuch_string_list_create (const void *ctx);\r
++\r
++/* Add 'string' to 'list'.\r
++ *\r
++ * The list will create its own talloced copy of 'string'.\r
++ */\r
+ void\r
+-_notmuch_tags_add_tag (notmuch_tags_t *tags, const char *tag);\r
++_notmuch_string_list_append (notmuch_string_list_t *list,\r
++ const char *string);\r
+ \r
+ void\r
+-_notmuch_tags_prepare_iterator (notmuch_tags_t *tags);\r
++_notmuch_string_list_sort (notmuch_string_list_t *list);\r
+ \r
+-/* filenames.c */\r
++/* tags.c */\r
++\r
++notmuch_tags_t *\r
++_notmuch_tags_create (const void *ctx, notmuch_string_list_t *list,\r
++ notmuch_bool_t steal);\r
+ \r
+-typedef struct _notmuch_filename_node {\r
+- char *filename;\r
+- struct _notmuch_filename_node *next;\r
+-} notmuch_filename_node_t;\r
++/* filenames.c */\r
+ \r
+-typedef struct _notmuch_filename_list {\r
+- notmuch_filename_node_t *head;\r
+- notmuch_filename_node_t **tail;\r
+-} notmuch_filename_list_t;\r
++/* The notmuch_filenames_t iterates over a notmuch_string_list_t of\r
++ * file names */\r
++notmuch_filenames_t *\r
++_notmuch_filenames_create (const void *ctx,\r
++ notmuch_string_list_t *list);\r
+ \r
+-notmuch_filename_list_t *\r
+-_notmuch_filename_list_create (const void *ctx);\r
++/* tags.c */\r
+ \r
+-/* Add 'filename' to 'list'.\r
+- *\r
+- * The list will create its own talloced copy of 'filename'.\r
+- */\r
+-void\r
+-_notmuch_filename_list_add_filename (notmuch_filename_list_t *list,\r
+- const char *filename);\r
++notmuch_tags_t *\r
++_notmuch_tags_create (const void *ctx, notmuch_string_list_t *list,\r
++ notmuch_bool_t steal);\r
+ \r
+-void\r
+-_notmuch_filename_list_destroy (notmuch_filename_list_t *list);\r
++/* filenames.c */\r
+ \r
+-/* The notmuch_filenames_t is an iterator object for a\r
+- * notmuch_filename_list_t */\r
++/* The notmuch_filenames_t iterates over a notmuch_string_list_t of\r
++ * file names */\r
+ notmuch_filenames_t *\r
+ _notmuch_filenames_create (const void *ctx,\r
+- notmuch_filename_list_t *list);\r
++ notmuch_string_list_t *list);\r
+ \r
+ #pragma GCC visibility pop\r
+ \r
+diff --git a/lib/strings.c b/lib/strings.c\r
+new file mode 100644\r
+index 0000000..d92a0ba\r
+--- /dev/null\r
++++ b/lib/strings.c\r
+@@ -0,0 +1,94 @@\r
++/* strings.c - Iterator for a list of strings\r
++ *\r
++ * Copyright © 2010 Intel Corporation\r
++ *\r
++ * This program is free software: you can redistribute it and/or modify\r
++ * it under the terms of the GNU General Public License as published by\r
++ * the Free Software Foundation, either version 3 of the License, or\r
++ * (at your option) any later version.\r
++ *\r
++ * This program is distributed in the hope that it will be useful,\r
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
++ * GNU General Public License for more details.\r
++ *\r
++ * You should have received a copy of the GNU General Public License\r
++ * along with this program. If not, see http://www.gnu.org/licenses/ .\r
++ *\r
++ * Author: Carl Worth <cworth@cworth.org>\r
++ */\r
++\r
++#include "notmuch-private.h"\r
++\r
++/* Create a new notmuch_string_list_t object, with 'ctx' as its\r
++ * talloc owner.\r
++ *\r
++ * This function can return NULL in case of out-of-memory.\r
++ */\r
++notmuch_string_list_t *\r
++_notmuch_string_list_create (const void *ctx)\r
++{\r
++ notmuch_string_list_t *list;\r
++\r
++ list = talloc (ctx, notmuch_string_list_t);\r
++ if (unlikely (list == NULL))\r
++ return NULL;\r
++\r
++ list->length = 0;\r
++ list->head = NULL;\r
++ list->tail = &list->head;\r
++\r
++ return list;\r
++}\r
++\r
++void\r
++_notmuch_string_list_append (notmuch_string_list_t *list,\r
++ const char *string)\r
++{\r
++ /* Create and initialize new node. */\r
++ notmuch_string_node_t *node = talloc (list, notmuch_string_node_t);\r
++\r
++ node->string = talloc_strdup (node, string);\r
++ node->next = NULL;\r
++\r
++ /* Append the node to the list. */\r
++ *(list->tail) = node;\r
++ list->tail = &node->next;\r
++ list->length++;\r
++}\r
++\r
++static int\r
++cmpnode (const void *pa, const void *pb)\r
++{\r
++ notmuch_string_node_t *a = *(notmuch_string_node_t * const *)pa;\r
++ notmuch_string_node_t *b = *(notmuch_string_node_t * const *)pb;\r
++\r
++ return strcmp (a->string, b->string);\r
++}\r
++\r
++void\r
++_notmuch_string_list_sort (notmuch_string_list_t *list)\r
++{\r
++ notmuch_string_node_t **nodes, *node;\r
++ int i;\r
++\r
++ if (list->length == 0)\r
++ return;\r
++\r
++ nodes = talloc_array (list, notmuch_string_node_t *, list->length);\r
++ if (unlikely (nodes == NULL))\r
++ INTERNAL_ERROR ("Could not allocate memory for list sort");\r
++\r
++ for (i = 0, node = list->head; node; i++, node = node->next)\r
++ nodes[i] = node;\r
++\r
++ qsort (nodes, list->length, sizeof (*nodes), cmpnode);\r
++\r
++ for (i = 0; i < list->length - 1; ++i)\r
++ nodes[i]->next = nodes[i+1];\r
++ nodes[i]->next = NULL;\r
++ list->head = nodes[0];\r
++ list->tail = &nodes[i]->next;\r
++\r
++ talloc_free (nodes);\r
++}\r
+diff --git a/lib/tags.c b/lib/tags.c\r
+index 8fe4a3f..8392ef6 100644\r
+--- a/lib/tags.c\r
++++ b/lib/tags.c\r
+@@ -20,30 +20,20 @@\r
+ \r
+ #include "notmuch-private.h"\r
+ \r
+-#include <glib.h> /* GList */\r
+-\r
+ struct _notmuch_tags {\r
+- int sorted;\r
+- GList *tags;\r
+- GList *iterator;\r
++ notmuch_string_node_t *iterator;\r
+ };\r
+ \r
+-/* XXX: Should write some talloc-friendly list to avoid the need for\r
+- * this. */\r
+-static int\r
+-_notmuch_tags_destructor (notmuch_tags_t *tags)\r
+-{\r
+- g_list_free (tags->tags);\r
+-\r
+- return 0;\r
+-}\r
+-\r
+ /* Create a new notmuch_tags_t object, with 'ctx' as its talloc owner.\r
++ * If steal is true, then the iterator will steal the reference to the\r
++ * list (useful for one-shot iterators); otherwise it will add a\r
++ * reference.\r
+ *\r
+ * This function can return NULL in case of out-of-memory.\r
+ */\r
+ notmuch_tags_t *\r
+-_notmuch_tags_create (void *ctx)\r
++_notmuch_tags_create (const void *ctx, notmuch_string_list_t *list,\r
++ notmuch_bool_t steal)\r
+ {\r
+ notmuch_tags_t *tags;\r
+ \r
+@@ -51,44 +41,15 @@ _notmuch_tags_create (void *ctx)\r
+ if (unlikely (tags == NULL))\r
+ return NULL;\r
+ \r
+- talloc_set_destructor (tags, _notmuch_tags_destructor);\r
+-\r
+- tags->sorted = 1;\r
+- tags->tags = NULL;\r
+- tags->iterator = NULL;\r
++ tags->iterator = list->head;\r
++ if (steal)\r
++ talloc_steal (tags, list);\r
++ else\r
++ (void) talloc_reference (tags, list);\r
+ \r
+ return tags;\r
+ }\r
+ \r
+-/* Add a new tag to 'tags'. The tags object will create its own copy\r
+- * of the string.\r
+- *\r
+- * Note: The tags object will not do anything to prevent duplicate\r
+- * tags being stored, so the caller really shouldn't pass\r
+- * duplicates. */\r
+-void\r
+-_notmuch_tags_add_tag (notmuch_tags_t *tags, const char *tag)\r
+-{\r
+- tags->tags = g_list_prepend (tags->tags, talloc_strdup (tags, tag));\r
+- tags->sorted = 0;\r
+-}\r
+-\r
+-/* Prepare 'tag' for iteration.\r
+- *\r
+- * The internal creator of 'tags' should call this function before\r
+- * returning 'tags' to the user to call the public functions such as\r
+- * notmuch_tags_valid, notmuch_tags_get, and\r
+- * notmuch_tags_move_to_next. */\r
+-void\r
+-_notmuch_tags_prepare_iterator (notmuch_tags_t *tags)\r
+-{\r
+- if (! tags->sorted)\r
+- tags->tags = g_list_sort (tags->tags, (GCompareFunc) strcmp);\r
+- tags->sorted = 1;\r
+-\r
+- tags->iterator = tags->tags;\r
+-}\r
+-\r
+ notmuch_bool_t\r
+ notmuch_tags_valid (notmuch_tags_t *tags)\r
+ {\r
+@@ -101,7 +62,7 @@ notmuch_tags_get (notmuch_tags_t *tags)\r
+ if (tags->iterator == NULL)\r
+ return NULL;\r
+ \r
+- return (char *) tags->iterator->data;\r
++ return (char *) tags->iterator->string;\r
+ }\r
+ \r
+ void\r
+diff --git a/lib/thread.cc b/lib/thread.cc\r
+index b29f2c9..5b024fd 100644\r
+--- a/lib/thread.cc\r
++++ b/lib/thread.cc\r
+@@ -535,23 +535,23 @@ notmuch_thread_get_newest_date (notmuch_thread_t *thread)\r
+ notmuch_tags_t *\r
+ notmuch_thread_get_tags (notmuch_thread_t *thread)\r
+ {\r
+- notmuch_tags_t *tags;\r
++ notmuch_string_list_t *tags;\r
+ GList *keys, *l;\r
+ \r
+- tags = _notmuch_tags_create (thread);\r
++ tags = _notmuch_string_list_create (thread);\r
+ if (unlikely (tags == NULL))\r
+ return NULL;\r
+ \r
+ keys = g_hash_table_get_keys (thread->tags);\r
+ \r
+ for (l = keys; l; l = l->next)\r
+- _notmuch_tags_add_tag (tags, (char *) l->data);\r
++ _notmuch_string_list_append (tags, (char *) l->data);\r
+ \r
+ g_list_free (keys);\r
+ \r
+- _notmuch_tags_prepare_iterator (tags);\r
++ _notmuch_string_list_sort (tags);\r
+ \r
+- return tags;\r
++ return _notmuch_tags_create (thread, tags, true);\r
+ }\r
+ \r
+ void\r
+-- \r
+1.7.2.3\r
+\r