[PATCH] cli: add --output=address-{from,to,all} to notmuch search

Subject: [PATCH] cli: add --output=address-{from,to,all} to notmuch search

Date: Sat, 6 Sep 2014 11:14:27 +0300

To: notmuch@notmuchmail.org, David Edmondson, Mark Walters

Cc:

From: Jani Nikula


address-from prints reply-to or from, address-to prints to, cc, and
bcc, and address-all prints all of them.

---

Mark, David -

I wrote most of this almost two years ago, but wasn't really happy
with it. There's address deduplication, but for large result sets that
might use lots of memory. Maybe the --duplicate option could be
overloaded for doing or not doing deduplication. I'd like to have some
way of picking the prettiest (that's subjective too) name part to go
with the address, now it's just the first encountered. And so on.

But maybe this will be useful for you, and you can pick some ideas. I
won't have the time to do much on this.

Cheers,
Jani.
---
 notmuch-search.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 113 insertions(+), 1 deletion(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index bc9be4593ecc..33da90eaceec 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -26,6 +26,9 @@ typedef enum {
     OUTPUT_SUMMARY,
     OUTPUT_THREADS,
     OUTPUT_MESSAGES,
+    OUTPUT_ADDRESS_FROM,
+    OUTPUT_ADDRESS_TO,
+    OUTPUT_ADDRESS_ALL,
     OUTPUT_FILES,
     OUTPUT_TAGS
 } output_t;
@@ -214,6 +217,78 @@ do_search_threads (sprinter_t *format,
     return 0;
 }
 
+static void
+print_address_list (sprinter_t *format, GHashTable *addrs,
+		    InternetAddressList *list)
+{
+    InternetAddress *address;
+    int i;
+
+    for (i = 0; i < internet_address_list_length (list); i++) {
+	address = internet_address_list_get_address (list, i);
+	if (INTERNET_ADDRESS_IS_GROUP (address)) {
+	    InternetAddressGroup *group;
+	    InternetAddressList *group_list;
+
+	    group = INTERNET_ADDRESS_GROUP (address);
+	    group_list = internet_address_group_get_members (group);
+	    if (group_list == NULL)
+		continue;
+
+	    print_address_list (format, addrs, group_list);
+	} else {
+	    InternetAddressMailbox *mailbox;
+	    const char *name;
+	    const char *addr;
+	    char *full_address;
+
+	    mailbox = INTERNET_ADDRESS_MAILBOX (address);
+
+	    name = internet_address_get_name (address);
+	    addr = internet_address_mailbox_get_addr (mailbox);
+
+	    if (g_hash_table_lookup_extended (addrs, addr, NULL, NULL))
+		continue;
+
+	    g_hash_table_insert (addrs, talloc_strdup (NULL, addr), NULL);
+
+	    if (name && *name)
+		full_address = talloc_asprintf (NULL, "%s <%s>", name, addr);
+	    else
+		full_address = talloc_asprintf (NULL, "<%s>", addr);
+
+	    if (!full_address)
+		break;
+
+	    format->string (format, full_address);
+	    format->separator (format);
+
+	    talloc_free (full_address);
+	}
+    }
+}
+
+static void
+print_address_string (sprinter_t *format, GHashTable *addrs, const char *recipients)
+{
+    InternetAddressList *list;
+
+    if (recipients == NULL)
+	return;
+
+    list = internet_address_list_parse_string (recipients);
+    if (list == NULL)
+	return;
+
+    print_address_list (format, addrs, list);
+}
+
+static void
+_my_talloc_free_for_g_hash (void *ptr)
+{
+    talloc_free (ptr);
+}
+
 static int
 do_search_messages (sprinter_t *format,
 		    notmuch_query_t *query,
@@ -225,8 +300,14 @@ do_search_messages (sprinter_t *format,
     notmuch_message_t *message;
     notmuch_messages_t *messages;
     notmuch_filenames_t *filenames;
+    GHashTable *addresses = NULL;
     int i;
 
+    if (output == OUTPUT_ADDRESS_FROM || output == OUTPUT_ADDRESS_TO ||
+	output == OUTPUT_ADDRESS_ALL)
+	addresses = g_hash_table_new_full (g_str_hash, g_str_equal,
+				       _my_talloc_free_for_g_hash, NULL);
+
     if (offset < 0) {
 	offset += notmuch_query_count_messages (query);
 	if (offset < 0)
@@ -264,16 +345,41 @@ do_search_messages (sprinter_t *format,
 	    
 	    notmuch_filenames_destroy( filenames );
 
-	} else { /* output == OUTPUT_MESSAGES */
+	} else if (output == OUTPUT_MESSAGES) {
 	    format->set_prefix (format, "id");
 	    format->string (format,
 			    notmuch_message_get_message_id (message));
 	    format->separator (format);
+	} else {
+	    if (output == OUTPUT_ADDRESS_FROM || output == OUTPUT_ADDRESS_ALL) {
+		const char *addrs;
+
+		addrs = notmuch_message_get_header (message, "reply-to");
+
+		if (addrs == NULL || *addrs == '\0')
+		    addrs = notmuch_message_get_header (message, "from");
+
+		print_address_string (format, addresses, addrs);
+	    }
+
+	    if (output == OUTPUT_ADDRESS_TO || output == OUTPUT_ADDRESS_ALL) {
+		const char *hdrs[] = { "to", "cc", "bcc" };
+		const char *addrs;
+		size_t j;
+
+		for (j = 0; j < ARRAY_SIZE (hdrs); j++) {
+		    addrs = notmuch_message_get_header (message, hdrs[j]);
+		    print_address_string (format, addresses, addrs);
+		}
+	    }
 	}
 
 	notmuch_message_destroy (message);
     }
 
+    if (addresses)
+	g_hash_table_unref (addresses);
+
     notmuch_messages_destroy (messages);
 
     format->end (format);
@@ -368,6 +474,9 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 	  (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },
 				  { "threads", OUTPUT_THREADS },
 				  { "messages", OUTPUT_MESSAGES },
+				  { "address-from", OUTPUT_ADDRESS_FROM },
+				  { "address-to", OUTPUT_ADDRESS_TO },
+				  { "address-all", OUTPUT_ADDRESS_ALL },
 				  { "files", OUTPUT_FILES },
 				  { "tags", OUTPUT_TAGS },
 				  { 0, 0 } } },
@@ -459,6 +568,9 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 	ret = do_search_threads (format, query, sort, output, offset, limit);
 	break;
     case OUTPUT_MESSAGES:
+    case OUTPUT_ADDRESS_FROM:
+    case OUTPUT_ADDRESS_TO:
+    case OUTPUT_ADDRESS_ALL:
     case OUTPUT_FILES:
 	ret = do_search_messages (format, query, output, offset, limit, dupe);
 	break;
-- 
2.1.0


Thread: