[PATCH] Add --message-headers flag to notmuch-show

Johan Parin johanparin at gmail.com
Sun Nov 10 04:49:29 PST 2019


Add a new flag --message-headers to notmuch show, in order to let the
user specify displayed headers using `notmuch-message-headers' in the
emacs mua.

The flag will impact which headers are output in
format_headers_sprinter.

By default only the following headers are output by notmuch show with
--format=sexp :

- From
- To
- Subject
- Cc
- Bcc
- Reply-To
- In-reply-to
- References
- Date

`From' is always output regardless of what is specified in
--message-headers.

See this bug report:

  https://notmuchmail.org/pipermail/notmuch/2017/026069.html

This commit does not include documentation updates.
---
 emacs/notmuch-query.el |   4 +-
 emacs/notmuch-tree.el  |   2 +
 notmuch-show.c         | 138 +++++++++++++++++++++++++++++------------
 3 files changed, 104 insertions(+), 40 deletions(-)

diff --git a/emacs/notmuch-query.el b/emacs/notmuch-query.el
index 563e4acf..61c921a4 100644
--- a/emacs/notmuch-query.el
+++ b/emacs/notmuch-query.el
@@ -30,7 +30,9 @@ A thread is a forest or list of trees. A tree is a two element
 list where the first element is a message, and the second element
 is a possibly empty forest of replies.
 "
-  (let ((args '("show" "--format=sexp" "--format-version=4")))
+  (let ((args `("show" "--format=sexp" "--format-version=4"
+		,(concat "--message-headers="
+                         (mapconcat #'identity notmuch-message-headers ",")))))
     (if notmuch-show-process-crypto
 	(setq args (append args '("--decrypt=true"))))
     (setq args (append args search-terms))
diff --git a/emacs/notmuch-tree.el b/emacs/notmuch-tree.el
index c00315e8..1d793ec3 100644
--- a/emacs/notmuch-tree.el
+++ b/emacs/notmuch-tree.el
@@ -922,6 +922,8 @@ the same as for the function notmuch-tree."
     (let ((proc (notmuch-start-notmuch
 		 "notmuch-tree" (current-buffer) #'notmuch-tree-process-sentinel
 		 "show" "--body=false" "--format=sexp" "--format-version=4"
+                 (concat "--message-headers="
+                         (mapconcat #'identity notmuch-message-headers ","))
 		 message-arg search-args))
 	  ;; Use a scratch buffer to accumulate partial output.
 	  ;; This buffer will be killed by the sentinel, which
diff --git a/notmuch-show.c b/notmuch-show.c
index 21792a57..1658b66e 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -18,11 +18,31 @@
  * Author: Carl Worth <cworth at cworth.org>
  */
 
+#include <string.h>
+#include <stdlib.h>
+
 #include "notmuch-client.h"
 #include "gmime-filter-reply.h"
 #include "sprinter.h"
 #include "zlib-extra.h"
 
+/* Max number of headers that can be output from
+ * format_headers_sprinter */
+#define MAX_PRINTABLE_MESSAGE_HEADERS 25
+
+/* Default list of header names to be printed by
+ * format_headers_sprinter */
+static const char *default_message_header_list[] = {
+    "To", "Subject", "Cc", "Bcc", "Reply-To", "In-reply-to",
+    "References", "Date"};
+
+/* List of header names to be printed by format_headers_sprinter */
+static char **message_header_list_p = (char **) default_message_header_list;
+
+static int message_header_list_len =
+    sizeof(default_message_header_list) / sizeof(char *);
+
+
 static const char *
 _get_tags_as_string (const void *ctx, notmuch_message_t *message)
 {
@@ -48,6 +68,26 @@ _get_tags_as_string (const void *ctx, notmuch_message_t *message)
     return result;
 }
 
+/* Extract requested header names from the message-headers command
+ * line argument.
+ */
+static void
+extract_requested_headers (const char *opt_str)
+{
+    int count = 0;
+    char *tofree = strdup (opt_str);
+    char *string = tofree;
+    char *header_name;
+
+    message_header_list_p = malloc(MAX_PRINTABLE_MESSAGE_HEADERS * sizeof(char *));
+    while ((header_name = strsep(&string, ",")) != NULL &&
+	   count < MAX_PRINTABLE_MESSAGE_HEADERS)
+	message_header_list_p[count++] = strdup(header_name);
+
+    message_header_list_len = count;
+    free(tofree);
+}
+
 /* Get a nice, single-line summary of message. */
 static const char *
 _get_one_line_summary (const void *ctx, notmuch_message_t *message)
@@ -202,57 +242,72 @@ format_headers_sprinter (sprinter_t *sp, GMimeMessage *message,
     /* Any changes to the JSON or S-Expression format should be
      * reflected in the file devel/schemata. */
 
-    char *recipients_string;
-    const char *reply_to_string;
     void *local = talloc_new (sp);
+    GMimeHeaderList *header_list;
 
-    sp->begin_map (sp);
+    /* Not currently used */
+    (void) reply;
 
-    sp->map_key (sp, "Subject");
-    if (msg_crypto && msg_crypto->payload_subject) {
-	sp->string (sp, msg_crypto->payload_subject);
-    } else
-	sp->string (sp, g_mime_message_get_subject (message));
+    sp->begin_map (sp);
 
     sp->map_key (sp, "From");
     sp->string (sp, g_mime_message_get_from_string (message));
 
-    recipients_string = g_mime_message_get_address_string (message, GMIME_ADDRESS_TYPE_TO);
-    if (recipients_string) {
-	sp->map_key (sp, "To");
-	sp->string (sp, recipients_string);
-	g_free (recipients_string);
-    }
+    header_list  = g_mime_object_get_header_list (GMIME_OBJECT(message));
 
-    recipients_string = g_mime_message_get_address_string (message, GMIME_ADDRESS_TYPE_CC);
-    if (recipients_string) {
-	sp->map_key (sp, "Cc");
-	sp->string (sp, recipients_string);
-	g_free (recipients_string);
-    }
+    for (int i = 0; i < message_header_list_len; i++) {
+	const char *name = message_header_list_p[i];
 
-    recipients_string = g_mime_message_get_address_string (message, GMIME_ADDRESS_TYPE_BCC);
-    if (recipients_string) {
-	sp->map_key (sp, "Bcc");
-	sp->string (sp, recipients_string);
-	g_free (recipients_string);
-    }
+	if (!STRNCMP_LITERAL (name, "Subject")) {
+	    sp->map_key (sp, "Subject");
+	    if (msg_crypto && msg_crypto->payload_subject) {
+		sp->string (sp, msg_crypto->payload_subject);
+	    } else
+		sp->string (sp, g_mime_message_get_subject (message));
+	}
 
-    reply_to_string = g_mime_message_get_reply_to_string (local, message);
-    if (reply_to_string) {
-	sp->map_key (sp, "Reply-To");
-	sp->string (sp, reply_to_string);
-    }
+	else if (!STRNCMP_LITERAL (name, "To") ||
+		 !STRNCMP_LITERAL (name, "Cc") ||
+		 !STRNCMP_LITERAL (name, "Bcc")) {
+	    GMimeAddressType addr_type;
+	    char *recipients_string;
+
+	    if (!STRNCMP_LITERAL (name, "To"))
+		addr_type = GMIME_ADDRESS_TYPE_TO;
+	    else if (!STRNCMP_LITERAL (name, "Cc"))
+		addr_type = GMIME_ADDRESS_TYPE_CC;
+	    else
+		addr_type = GMIME_ADDRESS_TYPE_BCC;
+
+	    recipients_string = g_mime_message_get_address_string (
+		message, addr_type);
+	    if (recipients_string) {
+		sp->map_key (sp, name);
+		sp->string (sp, recipients_string);
+		g_free (recipients_string);
+	    }
+	}
 
-    if (reply) {
-	sp->map_key (sp, "In-reply-to");
-	sp->string (sp, g_mime_object_get_header (GMIME_OBJECT (message), "In-reply-to"));
+	else if (!STRNCMP_LITERAL (name, "Reply-To")) {
+	    const char *reply_to_string;
 
-	sp->map_key (sp, "References");
-	sp->string (sp, g_mime_object_get_header (GMIME_OBJECT (message), "References"));
-    } else {
-	sp->map_key (sp, "Date");
-	sp->string (sp, g_mime_message_get_date_string (sp, message));
+	    reply_to_string = g_mime_message_get_reply_to_string (local, message);
+	    if (reply_to_string) {
+		sp->map_key (sp, "Reply-To");
+		sp->string (sp, reply_to_string);
+	    }
+	}
+
+	else {
+	    GMimeHeader *header = g_mime_header_list_get_header(
+		header_list, name);
+
+	    if (header == NULL)
+		continue;
+
+	    sp->map_key (sp, g_mime_header_get_name(header));
+	    sp->string (sp, g_mime_header_get_value(header));
+	}
     }
 
     sp->end (sp);
@@ -1168,6 +1223,7 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
     bool exclude = true;
     bool entire_thread_set = false;
     bool single_message;
+    const char *message_header_str = NULL;
 
     notmuch_opt_desc_t options[] = {
 	{ .opt_keyword = &format, .name = "format", .keywords =
@@ -1193,6 +1249,7 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
 	{ .opt_bool = &params.output_body, .name = "body" },
 	{ .opt_bool = &params.include_html, .name = "include-html" },
 	{ .opt_inherit = notmuch_shared_options },
+	{ .opt_string = &message_header_str, .name = "message-headers" },
 	{ }
     };
 
@@ -1202,6 +1259,9 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
 
     notmuch_process_shared_options (argv[0]);
 
+    if (message_header_str)
+	extract_requested_headers(message_header_str);
+
     /* explicit decryption implies verification */
     if (params.crypto.decrypt == NOTMUCH_DECRYPT_NOSTASH ||
 	params.crypto.decrypt == NOTMUCH_DECRYPT_TRUE)
-- 
2.21.0 (Apple Git-122)



More information about the notmuch mailing list