unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
From: Michal Sojka <sojkam1@fel.cvut.cz>
To: notmuch@notmuchmail.org
Subject: [PATCH v4 4/6] cli: search: Add --output={sender,recipients}
Date: Mon, 27 Oct 2014 15:50:53 +0100	[thread overview]
Message-ID: <1414421455-3037-5-git-send-email-sojkam1@fel.cvut.cz> (raw)
In-Reply-To: <1414421455-3037-1-git-send-email-sojkam1@fel.cvut.cz>

The new outputs allow printing senders, recipients or both of matching
messages. To print both, the user can use --output=sender and
--output=recipients simultaneously.

Currently, the same address can appear multiple times in the output. The
next patch will change this. For this reason, the test are introduced in
the next patch as well.

We use mailbox_t rather than InternetAddressMailbox because we will need
to extend it in a following patch.

This code based on a patch from Jani Nikula.
---
 completion/notmuch-completion.bash |   2 +-
 completion/notmuch-completion.zsh  |   3 +-
 doc/man1/notmuch-search.rst        |  22 +++++++-
 notmuch-search.c                   | 112 ++++++++++++++++++++++++++++++++++++-
 4 files changed, 134 insertions(+), 5 deletions(-)

diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash
index 0571dc9..cfbd389 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -294,7 +294,7 @@ _notmuch_search()
 	    return
 	    ;;
 	--output)
-	    COMPREPLY=( $( compgen -W "summary threads messages files tags" -- "${cur}" ) )
+	    COMPREPLY=( $( compgen -W "summary threads messages files tags sender recipients" -- "${cur}" ) )
 	    return
 	    ;;
 	--sort)
diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh
index 67a9aba..3e52a00 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -52,7 +52,8 @@ _notmuch_search()
   _arguments -s : \
     '--max-threads=[display only the first x threads from the search results]:number of threads to show: ' \
     '--first=[omit the first x threads from the search results]:number of threads to omit: ' \
-    '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))'
+    '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \
+    '--output=[select what to output]:output:((summary threads messages files tags sender recipients))'
 }
 
 _notmuch()
diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
index 90160f2..b6607c9 100644
--- a/doc/man1/notmuch-search.rst
+++ b/doc/man1/notmuch-search.rst
@@ -35,7 +35,7 @@ Supported options for **search** include
         intended for programs that invoke **notmuch(1)** internally. If
         omitted, the latest supported version will be used.
 
-    ``--output=(summary|threads|messages|files|tags)``
+    ``--output=(summary|threads|messages|files|tags|sender|recipients)``
 
         **summary**
             Output a summary of each thread with any message matching
@@ -78,6 +78,26 @@ Supported options for **search** include
             by null characters (--format=text0), as a JSON array
             (--format=json), or as an S-Expression list (--format=sexp).
 
+	**sender**
+            Output all addresses from the *From* header that appear on
+            any message matching the search terms, either one per line
+            (--format=text), separated by null characters
+            (--format=text0), as a JSON array (--format=json), or as
+            an S-Expression list (--format=sexp).
+
+	    Note: Searching for **sender** should be much faster than
+	    searching for **recipients**, because sender addresses are
+	    cached directly in the database whereas other addresses
+	    need to be fetched from message files.
+
+	**recipients**
+            Like **sender** but for addresses from *To*, *Cc* and
+	    *Bcc* headers.
+
+	This option can be given multiple times to combine different
+	outputs. Currently, this is only supported for **sender** and
+	**recipients** outputs.
+
     ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
         This option can be used to present results in either
         chronological order (**oldest-first**) or reverse chronological
diff --git a/notmuch-search.c b/notmuch-search.c
index ce46877..ce3bfb2 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -28,8 +28,12 @@ typedef enum {
     OUTPUT_MESSAGES	= 1 << 2,
     OUTPUT_FILES	= 1 << 3,
     OUTPUT_TAGS		= 1 << 4,
+    OUTPUT_SENDER	= 1 << 5,
+    OUTPUT_RECIPIENTS	= 1 << 6,
 } output_t;
 
+#define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS)
+
 typedef struct {
     sprinter_t *format;
     notmuch_query_t *query;
@@ -40,6 +44,11 @@ typedef struct {
     int dupe;
 } search_options_t;
 
+typedef struct {
+    const char *name;
+    const char *addr;
+} mailbox_t;
+
 /* Return two stable query strings that identify exactly the matched
  * and unmatched messages currently in thread.  If there are no
  * matched or unmatched messages, the returned buffers will be
@@ -220,6 +229,84 @@ do_search_threads (search_options_t *opt)
     return 0;
 }
 
+static void
+print_mailbox (const search_options_t *opt, const mailbox_t *mailbox)
+{
+    const char *name = mailbox->name;
+    const char *addr = mailbox->addr;
+
+    if (opt->format->is_text_printer) {
+	char *mailbox_str;
+
+	if (name && *name)
+	    mailbox_str = talloc_asprintf (opt->format, "%s <%s>", name, addr);
+	else
+	    mailbox_str = talloc_strdup (opt->format, addr);
+
+	if (! mailbox_str) {
+	    fprintf (stderr, "Error: out of memory\n");
+	    return;
+	}
+	opt->format->string (opt->format, mailbox_str);
+	opt->format->separator (opt->format);
+
+	talloc_free (mailbox_str);
+    } else {
+	opt->format->begin_map (opt->format);
+	opt->format->map_key (opt->format, "name");
+	opt->format->string (opt->format, name);
+	opt->format->map_key (opt->format, "address");
+	opt->format->string (opt->format, addr);
+	opt->format->end (opt->format);
+	opt->format->separator (opt->format);
+    }
+}
+
+static void
+process_address_list (const search_options_t *opt, 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;
+
+	    process_address_list (opt, group_list);
+	} else {
+	    InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX (address);
+	    mailbox_t mbx = {
+		.name = internet_address_get_name (address),
+		.addr = internet_address_mailbox_get_addr (mailbox),
+	    };
+
+	    print_mailbox (opt, &mbx);
+	}
+    }
+}
+
+static void
+process_address_header (const search_options_t *opt, const char *value)
+{
+    InternetAddressList *list;
+
+    if (value == NULL)
+	return;
+
+    list = internet_address_list_parse_string (value);
+    if (list == NULL)
+	return;
+
+    process_address_list (opt, list);
+}
+
 static int
 do_search_messages (search_options_t *opt)
 {
@@ -266,11 +353,29 @@ do_search_messages (search_options_t *opt)
 	    
 	    notmuch_filenames_destroy( filenames );
 
-	} else { /* output == OUTPUT_MESSAGES */
+	} else if (opt->output == OUTPUT_MESSAGES) {
 	    format->set_prefix (format, "id");
 	    format->string (format,
 			    notmuch_message_get_message_id (message));
 	    format->separator (format);
+	} else {
+	    if (opt->output & OUTPUT_SENDER) {
+		const char *addrs;
+
+		addrs = notmuch_message_get_header (message, "from");
+		process_address_header (opt, addrs);
+	    }
+
+	    if (opt->output & OUTPUT_RECIPIENTS) {
+		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]);
+		    process_address_header (opt, addrs);
+		}
+	    }
 	}
 
 	notmuch_message_destroy (message);
@@ -371,6 +476,8 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 	  (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },
 				  { "threads", OUTPUT_THREADS },
 				  { "messages", OUTPUT_MESSAGES },
+				  { "sender", OUTPUT_SENDER },
+				  { "recipients", OUTPUT_RECIPIENTS },
 				  { "files", OUTPUT_FILES },
 				  { "tags", OUTPUT_TAGS },
 				  { 0, 0 } } },
@@ -462,7 +569,8 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 	opt.output == OUTPUT_THREADS)
 	ret = do_search_threads (&opt);
     else if (opt.output == OUTPUT_MESSAGES ||
-	     opt.output == OUTPUT_FILES)
+	     opt.output == OUTPUT_FILES ||
+	     (opt.output & OUTPUT_ADDRESS_FLAGS && !(opt.output & ~OUTPUT_ADDRESS_FLAGS)))
 	ret = do_search_messages (&opt);
     else if (opt.output == OUTPUT_TAGS)
 	ret = do_search_tags (notmuch, &opt);
-- 
2.1.1

  parent reply	other threads:[~2014-10-27 14:51 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-10-27 14:50 [PATCH v4 0/6] notmuch search --output=sender/recipients Michal Sojka
2014-10-27 14:50 ` [PATCH v4 1/6] cli: search: Refactor passing of command line options Michal Sojka
2014-10-30  7:57   ` Mark Walters
2014-10-30 21:14     ` Michal Sojka
2014-10-27 14:50 ` [PATCH v4 2/6] cli: Add support for parsing keyword-flag arguments Michal Sojka
2014-10-27 14:50 ` [PATCH v4 3/6] cli: search: Convert --output to keyword-flag argument Michal Sojka
2014-10-27 14:50 ` Michal Sojka [this message]
2014-10-30  8:10   ` [PATCH v4 4/6] cli: search: Add --output={sender,recipients} Mark Walters
2014-10-27 14:50 ` [PATCH v4 5/6] cli: search: Add configurable way to filter out duplicate addresses Michal Sojka
2014-10-30  8:16   ` Mark Walters
2014-10-30  8:52     ` Tomi Ollila
2014-10-30  9:00       ` Mark Walters
2014-10-30 21:42       ` Michal Sojka
2014-10-30 21:34     ` Michal Sojka
2014-10-27 14:50 ` [PATCH v4 6/6] cli: search: Add --output=count Michal Sojka
2014-10-28 21:19 ` [PATCH v4 0/6] notmuch search --output=sender/recipients Tomi Ollila

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://notmuchmail.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1414421455-3037-5-git-send-email-sojkam1@fel.cvut.cz \
    --to=sojkam1@fel.cvut.cz \
    --cc=notmuch@notmuchmail.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).