]> rtime.felk.cvut.cz Git - notmuch.git/commitdiff
cli: search: Add --output={sender,recipients}
authorMichal Sojka <sojkam1@fel.cvut.cz>
Mon, 27 Oct 2014 14:25:32 +0000 (15:25 +0100)
committerMichal Sojka <sojkam1@fel.cvut.cz>
Mon, 27 Oct 2014 14:29:58 +0000 (15:29 +0100)
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
completion/notmuch-completion.zsh
doc/man1/notmuch-search.rst
notmuch-search.c

index 0571dc9da8e332b3fa3c24ab27e9fa61a3c637f4..cfbd3890e09179ae8a1966c24c21dd46a114b31f 100644 (file)
@@ -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)
index 67a9aba85f694f0fe652f1e92a27b5202c39de32..3e52a004d8ee9530b2082ad72cd329513f6657d2 100644 (file)
@@ -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()
index 90160f21e23c12161876d019b07ca9a0c8dc7bfc..b6607c922cc083c35b9add629c771d6d3927da1b 100644 (file)
@@ -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
index ce46877895701e07d6e899daf187dd2c7eaeb37c..ce3bfb229655a877e6eaf5431fc825f461588998 100644 (file)
@@ -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);