From c12823648ee84b4748e0e9f0cd97f7264911b589 Mon Sep 17 00:00:00 2001
From: Carl Worth <cworth@cworth.org>
Date: Mon, 26 Oct 2009 17:35:31 -0700
Subject: [PATCH] Add public notmuch_thread_get_subject

And use this in "notmuch search" to display subject line as well as
thread ID.
---
 message-file.c    | 49 +++++++++++++++++++++++++++++++----------------
 message.cc        | 32 ++++++++++++++++++++++++++++---
 notmuch-private.h | 27 ++++++++++++++++++++------
 notmuch.c         |  6 ++++--
 notmuch.h         | 14 ++++++++++++++
 query.cc          |  8 ++++++++
 thread.cc         | 14 ++++++++++++++
 7 files changed, 122 insertions(+), 28 deletions(-)

diff --git a/message-file.c b/message-file.c
index a4c08606..18275fbf 100644
--- a/message-file.c
+++ b/message-file.c
@@ -70,12 +70,36 @@ strcase_hash (const void *ptr)
     return hash;
 }
 
+static int
+_notmuch_message_file_destructor (notmuch_message_file_t *message)
+{
+    if (message->line)
+	free (message->line);
+
+    if (message->value.size)
+	free (message->value.str);
+
+    if (message->headers)
+	g_hash_table_destroy (message->headers);
+
+    if (message->file)
+	fclose (message->file);
+
+    return 0;
+}
+
+/* Create a new notmuch_message_file_t for 'filename' with 'ctx' as
+ * the talloc owner. */
 notmuch_message_file_t *
-notmuch_message_file_open (const char *filename)
+_notmuch_message_file_open_ctx (void *ctx, const char *filename)
 {
     notmuch_message_file_t *message;
 
-    message = talloc_zero (NULL, notmuch_message_file_t);
+    message = talloc_zero (ctx, notmuch_message_file_t);
+    if (unlikely (message == NULL))
+	return NULL;
+
+    talloc_set_destructor (message, _notmuch_message_file_destructor);
 
     message->file = fopen (filename, "r");
     if (message->file == NULL)
@@ -98,24 +122,15 @@ notmuch_message_file_open (const char *filename)
     return NULL;
 }
 
+notmuch_message_file_t *
+notmuch_message_file_open (const char *filename)
+{
+    return _notmuch_message_file_open_ctx (NULL, filename);
+}
+
 void
 notmuch_message_file_close (notmuch_message_file_t *message)
 {
-    if (message == NULL)
-	return;
-
-    if (message->line)
-	free (message->line);
-
-    if (message->value.size)
-	free (message->value.str);
-
-    if (message->headers)
-	g_hash_table_destroy (message->headers);
-
-    if (message->file)
-	fclose (message->file);
-
     talloc_free (message);
 }
 
diff --git a/message.cc b/message.cc
index 66dfc806..6b6141f3 100644
--- a/message.cc
+++ b/message.cc
@@ -29,6 +29,7 @@ struct _notmuch_message {
     char *message_id;
     char *thread_id;
     char *filename;
+    notmuch_message_file_t *message_file;
     Xapian::Document doc;
 };
 
@@ -98,9 +99,12 @@ _notmuch_message_create (const void *talloc_owner,
 
     message->notmuch = notmuch;
     message->doc_id = doc_id;
-    message->message_id = NULL; /* lazily created */
-    message->thread_id = NULL; /* lazily created */
-    message->filename = NULL; /* lazily created */
+
+    /* Each of these will be lazily created as needed. */
+    message->message_id = NULL;
+    message->thread_id = NULL;
+    message->filename = NULL;
+    message->message_file = NULL;
 
     /* This is C++'s creepy "placement new", which is really just an
      * ugly way to call a constructor for a pre-allocated object. So
@@ -224,6 +228,28 @@ notmuch_message_get_message_id (notmuch_message_t *message)
     return message->message_id;
 }
 
+const char *
+_notmuch_message_get_subject (notmuch_message_t *message)
+{
+    if (! message->message_file) {
+	notmuch_message_file_t *message_file;
+	const char *filename;
+
+	filename = notmuch_message_get_filename (message);
+	if (unlikely (filename == NULL))
+	    return NULL;
+
+	message_file = _notmuch_message_file_open_ctx (message, filename);
+	if (unlikely (message_file == NULL))
+	    return NULL;
+
+	message->message_file = message_file;
+    }
+
+    return notmuch_message_file_get_header (message->message_file,
+					    "subject");
+}
+
 const char *
 notmuch_message_get_thread_id (notmuch_message_t *message)
 {
diff --git a/notmuch-private.h b/notmuch-private.h
index ddc59b4c..42f8d275 100644
--- a/notmuch-private.h
+++ b/notmuch-private.h
@@ -137,6 +137,16 @@ typedef enum _notmuch_private_status {
      :									\
      (notmuch_status_t) private_status)
 
+/* database.cc */
+
+/* Lookup a prefix value by name.
+ *
+ * XXX: This should really be static inside of message.cc, and we can
+ * do that once we convert database.cc to use the
+ * _notmuch_message_add/remove_term functions. */
+const char *
+_find_prefix (const char *name);
+
 /* thread.cc */
 
 notmuch_thread_t *
@@ -147,6 +157,12 @@ _notmuch_thread_create (const void *talloc_owner,
 void
 _notmuch_thread_add_tag (notmuch_thread_t *thread, const char *tag);
 
+void
+_notmuch_thread_set_subject (notmuch_thread_t *thread, const char *subject);
+
+const char *
+_notmuch_thread_get_subject (notmuch_thread_t *thread);
+
 /* message.cc */
 
 notmuch_message_t *
@@ -161,13 +177,8 @@ _notmuch_message_create_for_message_id (const void *talloc_owner,
 					const char *message_id,
 					notmuch_status_t *status);
 
-/* Lookup a prefix value by name.
- *
- * XXX: This should really be static inside of message.cc, and we can
- * do that once we convert database.cc to use the
- * _notmuch_message_add/remove_term functions. */
 const char *
-_find_prefix (const char *name);
+_notmuch_message_get_subject (notmuch_message_t *message);
 
 notmuch_private_status_t
 _notmuch_message_add_term (notmuch_message_t *message,
@@ -214,6 +225,10 @@ typedef struct _notmuch_message_file notmuch_message_file_t;
 notmuch_message_file_t *
 notmuch_message_file_open (const char *filename);
 
+/* Like notmuch_message_file_open but with 'ctx' as the talloc owner. */
+notmuch_message_file_t *
+_notmuch_message_file_open_ctx (void *ctx, const char *filename);
+
 /* Close a notmuch message preivously opened with notmuch_message_open. */
 void
 notmuch_message_file_close (notmuch_message_file_t *message);
diff --git a/notmuch.c b/notmuch.c
index fd0e0b25..ae1d5976 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -651,8 +651,11 @@ search_command (int argc, char *argv[])
 
 	thread = notmuch_thread_results_get (results);
 
-	printf ("%s (", notmuch_thread_get_thread_id (thread));
+	printf ("%s %s",
+		notmuch_thread_get_thread_id (thread),
+		_notmuch_thread_get_subject (thread));
 
+	printf (" (");
 	for (tags = notmuch_thread_get_tags (thread);
 	     notmuch_tags_has_more (tags);
 	     notmuch_tags_advance (tags))
@@ -662,7 +665,6 @@ search_command (int argc, char *argv[])
 	    printf ("%s", notmuch_tags_get (tags));
 	    first = 0;
 	}
-
 	printf (")\n");
 
 	notmuch_thread_destroy (thread);
diff --git a/notmuch.h b/notmuch.h
index b4ff53e3..bf390bea 100644
--- a/notmuch.h
+++ b/notmuch.h
@@ -458,6 +458,20 @@ notmuch_thread_results_destroy (notmuch_thread_results_t *results);
 const char *
 notmuch_thread_get_thread_id (notmuch_thread_t *thread);
 
+/* Get the subject of 'thread'
+ *
+ * The subject is taken from the first message (according to the query
+ * order---see notmuch_query_set_sort) in the query results that
+ * belongs to this thread.
+ *
+ * The returned string belongs to 'thread' and as such, should not be
+ * modified by the caller and will only be valid for as long as the
+ * thread is valid, (which is until notmuch_thread_destroy or until
+ * the query from which it derived is destroyed).
+ */
+const char *
+notmuch_thread_get_subject (notmuch_thread_t *thread);
+
 /* Get the tags for 'thread', returning a notmuch_tags_t object which
  * can be used to iterate over all tags.
  *
diff --git a/query.cc b/query.cc
index c8a91491..5fac024e 100644
--- a/query.cc
+++ b/query.cc
@@ -207,9 +207,15 @@ notmuch_query_search_threads (notmuch_query_t *query)
 					    thread_id, NULL,
 					    (void **) &thread))
 	{
+	    const char *subject;
+
 	    thread = _notmuch_thread_create (query, query->notmuch,
 					     thread_id);
 
+	    subject = _notmuch_message_get_subject (message);
+
+	    _notmuch_thread_set_subject (thread, subject);
+
 	    g_hash_table_insert (seen, xstrdup (thread_id), thread);
 
 	    g_ptr_array_add (thread_results->threads, thread);
@@ -222,6 +228,8 @@ notmuch_query_search_threads (notmuch_query_t *query)
 	    tag = notmuch_tags_get (tags);
 	    _notmuch_thread_add_tag (thread, tag);
 	}
+
+	notmuch_message_destroy (message);
     }
 
     g_hash_table_unref (seen);
diff --git a/thread.cc b/thread.cc
index 73d17722..9b49c550 100644
--- a/thread.cc
+++ b/thread.cc
@@ -28,6 +28,7 @@
 struct _notmuch_thread {
     notmuch_database_t *notmuch;
     char *thread_id;
+    char *subject;
     GHashTable *tags;
 };
 
@@ -69,6 +70,7 @@ _notmuch_thread_create (const void *talloc_owner,
 
     thread->notmuch = notmuch;
     thread->thread_id = talloc_strdup (thread, thread_id);
+    thread->subject = NULL;
     thread->tags = g_hash_table_new_full (g_str_hash, g_str_equal,
 					  free, NULL);
 
@@ -87,6 +89,18 @@ _notmuch_thread_add_tag (notmuch_thread_t *thread, const char *tag)
     g_hash_table_insert (thread->tags, xstrdup (tag), NULL);
 }
 
+void
+_notmuch_thread_set_subject (notmuch_thread_t *thread, const char *subject)
+{
+    thread->subject = talloc_strdup (thread, subject);
+}
+
+const char *
+_notmuch_thread_get_subject (notmuch_thread_t *thread)
+{
+    return thread->subject;
+}
+
 notmuch_tags_t *
 notmuch_thread_get_tags (notmuch_thread_t *thread)
 {
-- 
2.45.2