cli: Introduce "notmuch address" command
authorMichal Sojka <sojkam1@fel.cvut.cz>
Wed, 5 Nov 2014 00:25:55 +0000 (01:25 +0100)
committerDavid Bremner <david@tethera.net>
Wed, 5 Nov 2014 22:19:12 +0000 (23:19 +0100)
This moves address-related functionality from search command to the
new address command. The implementation shares almost all code and
some command line options.

Options --offset and --limit were intentionally not included in the
address command, because they refer to messages numbers, which users
do not see in the output. This could confuse users because, for
example, they could see more addresses in the output that what was
specified with --limit. This functionality can be correctly
reimplemented for address subcommand later.

Also useless values of --exclude flag were not included in the address
command.

This was inspired by a patch from Jani Nikula.

completion/notmuch-completion.bash
completion/notmuch-completion.zsh
doc/man1/notmuch-address.rst [new file with mode: 0644]
doc/man1/notmuch-search.rst
doc/man1/notmuch.rst
notmuch-client.h
notmuch-search.c
notmuch.c

index cfbd3890e09179ae8a1966c24c21dd46a114b31f..94ea2d56943e28afc65056ea93f5c3b45fee38a1 100644 (file)
@@ -294,7 +294,7 @@ _notmuch_search()
            return
            ;;
        --output)
-           COMPREPLY=( $( compgen -W "summary threads messages files tags sender recipients" -- "${cur}" ) )
+           COMPREPLY=( $( compgen -W "summary threads messages files tags" -- "${cur}" ) )
            return
            ;;
        --sort)
@@ -320,6 +320,44 @@ _notmuch_search()
     esac
 }
 
+_notmuch_address()
+{
+    local cur prev words cword split
+    _init_completion -s || return
+
+    $split &&
+    case "${prev}" in
+       --format)
+           COMPREPLY=( $( compgen -W "json sexp text text0" -- "${cur}" ) )
+           return
+           ;;
+       --output)
+           COMPREPLY=( $( compgen -W "sender recipients" -- "${cur}" ) )
+           return
+           ;;
+       --sort)
+           COMPREPLY=( $( compgen -W "newest-first oldest-first" -- "${cur}" ) )
+           return
+           ;;
+       --exclude)
+           COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) )
+           return
+           ;;
+    esac
+
+    ! $split &&
+    case "${cur}" in
+       -*)
+           local options="--format= --output= --sort= --exclude="
+           compopt -o nospace
+           COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
+           ;;
+       *)
+           _notmuch_search_terms
+           ;;
+    esac
+}
+
 _notmuch_show()
 {
     local cur prev words cword split
@@ -393,7 +431,7 @@ _notmuch_tag()
 
 _notmuch()
 {
-    local _notmuch_commands="compact config count dump help insert new reply restore search setup show tag"
+    local _notmuch_commands="compact config count dump help insert new reply restore search address setup show tag"
     local arg cur prev words cword split
 
     # require bash-completion with _init_completion
index 3e52a004d8ee9530b2082ad72cd329513f6657d2..c606b75172e8361522e8bb70cb7e883811c4a652 100644 (file)
@@ -10,6 +10,7 @@ _notmuch_commands()
     'setup:interactively set up notmuch for first use'
     'new:find and import any new message to the database'
     'search:search for messages matching the search terms, display matching threads as results'
+    'address:get addresses from messages matching the given search terms'
     'reply:constructs a reply template for a set of messages'
     'show:show all messages matching the search terms'
     'tag:add or remove tags for all messages matching the search terms'
@@ -53,7 +54,14 @@ _notmuch_search()
     '--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"))' \
-    '--output=[select what to output]:output:((summary threads messages files tags sender recipients))'
+    '--output=[select what to output]:output:((summary threads messages files tags))'
+}
+
+_notmuch_address()
+{
+  _arguments -s : \
+    '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \
+    '--output=[select what to output]:output:((sender recipients))'
 }
 
 _notmuch()
diff --git a/doc/man1/notmuch-address.rst b/doc/man1/notmuch-address.rst
new file mode 100644 (file)
index 0000000..d349237
--- /dev/null
@@ -0,0 +1,89 @@
+===============
+notmuch-address
+===============
+
+SYNOPSIS
+========
+
+**notmuch** **address** [*option* ...] <*search-term*> ...
+
+DESCRIPTION
+===========
+
+Search for messages matching the given search terms, and display the
+addresses from them.
+
+See **notmuch-search-terms(7)** for details of the supported syntax for
+<search-terms>.
+
+Supported options for **address** include
+
+    ``--format=``\ (**json**\ \|\ **sexp**\ \|\ **text**\ \|\ **text0**)
+        Presents the results in either JSON, S-Expressions, newline
+        character separated plain-text (default), or null character
+        separated plain-text (compatible with **xargs(1)** -0 option
+        where available).
+
+    ``--format-version=N``
+        Use the specified structured output format version. This is
+        intended for programs that invoke **notmuch(1)** internally. If
+        omitted, the latest supported version will be used.
+
+    ``--output=(sender|recipients)``
+
+        Controls which information appears in the output. This option
+       can be given multiple times to combine different outputs.
+       Omitting this option is equivalent to
+       --output=sender --output=recipients.
+
+       **sender**
+            Output all addresses from the *From* header.
+
+           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**
+            Output all addresses from the *To*, *Cc* and *Bcc*
+            headers.
+
+    ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
+        This option can be used to present results in either
+        chronological order (**oldest-first**) or reverse chronological
+        order (**newest-first**).
+
+        By default, results will be displayed in reverse chronological
+        order, (that is, the newest results will be displayed first).
+
+    ``--exclude=(true|false)``
+        A message is called "excluded" if it matches at least one tag in
+        search.tag\_exclude that does not appear explicitly in the
+        search terms. This option specifies whether to omit excluded
+        messages in the search process.
+
+        The default value, **true**, prevents excluded messages from
+        matching the search terms.
+
+        **false** allows excluded messages to match search terms and
+        appear in displayed results.
+
+EXIT STATUS
+===========
+
+This command supports the following special exit status codes
+
+``20``
+    The requested format version is too old.
+
+``21``
+    The requested format version is too new.
+
+SEE ALSO
+========
+
+**notmuch(1)**, **notmuch-config(1)**, **notmuch-count(1)**,
+**notmuch-dump(1)**, **notmuch-hooks(5)**, **notmuch-insert(1)**,
+**notmuch-new(1)**, **notmuch-reply(1)**, **notmuch-restore(1)**,
+**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**,
+***notmuch-search(1)**
index 8110086eff4fa29953c10d7e18f84bc3cecc4e28..65df28878d6124294a17860ca8bf3b3448ff5876 100644 (file)
@@ -78,25 +78,8 @@ 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.
+       outputs.
 
     ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
         This option can be used to present results in either
@@ -173,3 +156,4 @@ SEE ALSO
 **notmuch-dump(1)**, **notmuch-hooks(5)**, **notmuch-insert(1)**,
 **notmuch-new(1)**, **notmuch-reply(1)**, **notmuch-restore(1)**,
 **notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**
+***notmuch-address(1)**
index 971029433f5daf6c46b9ea1b44e909708506220d..98590a40969a644d1af968d4cee3366915926b25 100644 (file)
@@ -88,8 +88,8 @@ Several of the notmuch commands accept search terms with a common
 syntax. See **notmuch-search-terms**\ (7) for more details on the
 supported syntax.
 
-The **search**, **show** and **count** commands are used to query the
-email database.
+The **search**, **show**, **address** and **count** commands are used
+to query the email database.
 
 The **reply** command is useful for preparing a template for an email
 reply.
@@ -128,7 +128,8 @@ SEE ALSO
 **notmuch-config(1)**, **notmuch-count(1)**, **notmuch-dump(1)**,
 **notmuch-hooks(5)**, **notmuch-insert(1)**, **notmuch-new(1)**,
 **notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-search(1)**,
-**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**
+**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**,
+***notmuch-address(1)**
 
 The notmuch website: **http://notmuchmail.org**
 
index e1efbe0c82520e1fc186c40f4a1eb3b9ef2634b3..5e0d47508c6af495c4c5097a29e9d5b5cc72742b 100644 (file)
@@ -198,6 +198,9 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[]);
 int
 notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]);
 
+int
+notmuch_address_command (notmuch_config_t *config, int argc, char *argv[]);
+
 int
 notmuch_setup_command (notmuch_config_t *config, int argc, char *argv[]);
 
index f11535942d566e4e386008b2adcbefa2cc318ec7..69938d67be154302df04481329a8ba9124c7ac30 100644 (file)
 #include "string-util.h"
 
 typedef enum {
+    /* Search command */
     OUTPUT_SUMMARY     = 1 << 0,
     OUTPUT_THREADS     = 1 << 1,
     OUTPUT_MESSAGES    = 1 << 2,
     OUTPUT_FILES       = 1 << 3,
     OUTPUT_TAGS                = 1 << 4,
+
+    /* Address command */
     OUTPUT_SENDER      = 1 << 5,
     OUTPUT_RECIPIENTS  = 1 << 6,
 } output_t;
 
-#define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS)
-
 typedef enum {
     NOTMUCH_FORMAT_JSON,
     NOTMUCH_FORMAT_TEXT,
@@ -554,39 +555,42 @@ _notmuch_search_cleanup (search_context_t *ctx)
     talloc_free (ctx->format);
 }
 
+static search_context_t search_context = {
+    .format_sel = NOTMUCH_FORMAT_TEXT,
+    .exclude = NOTMUCH_EXCLUDE_TRUE,
+    .sort = NOTMUCH_SORT_NEWEST_FIRST,
+    .output = 0,
+    .offset = 0,
+    .limit = -1, /* unlimited */
+    .dupe = -1,
+};
+
+static const notmuch_opt_desc_t common_options[] = {
+    { NOTMUCH_OPT_KEYWORD, &search_context.sort, "sort", 's',
+      (notmuch_keyword_t []){ { "oldest-first", NOTMUCH_SORT_OLDEST_FIRST },
+                             { "newest-first", NOTMUCH_SORT_NEWEST_FIRST },
+                             { 0, 0 } } },
+    { NOTMUCH_OPT_KEYWORD, &search_context.format_sel, "format", 'f',
+      (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON },
+                             { "sexp", NOTMUCH_FORMAT_SEXP },
+                             { "text", NOTMUCH_FORMAT_TEXT },
+                             { "text0", NOTMUCH_FORMAT_TEXT0 },
+                             { 0, 0 } } },
+    { NOTMUCH_OPT_INT, &notmuch_format_version, "format-version", 0, 0 },
+    { 0, 0, 0, 0, 0 }
+};
+
 int
 notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 {
-    search_context_t search_context = {
-       .format_sel = NOTMUCH_FORMAT_TEXT,
-       .exclude = NOTMUCH_EXCLUDE_TRUE,
-       .sort = NOTMUCH_SORT_NEWEST_FIRST,
-       .output = 0,
-       .offset = 0,
-       .limit = -1, /* unlimited */
-       .dupe = -1,
-    };
     search_context_t *ctx = &search_context;
     int opt_index, ret;
 
     notmuch_opt_desc_t options[] = {
-       { NOTMUCH_OPT_KEYWORD, &ctx->sort, "sort", 's',
-         (notmuch_keyword_t []){ { "oldest-first", NOTMUCH_SORT_OLDEST_FIRST },
-                                 { "newest-first", NOTMUCH_SORT_NEWEST_FIRST },
-                                 { 0, 0 } } },
-       { NOTMUCH_OPT_KEYWORD, &ctx->format_sel, "format", 'f',
-         (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON },
-                                 { "sexp", NOTMUCH_FORMAT_SEXP },
-                                 { "text", NOTMUCH_FORMAT_TEXT },
-                                 { "text0", NOTMUCH_FORMAT_TEXT0 },
-                                 { 0, 0 } } },
-       { NOTMUCH_OPT_INT, &notmuch_format_version, "format-version", 0, 0 },
        { NOTMUCH_OPT_KEYWORD_FLAGS, &ctx->output, "output", 'o',
          (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 } } },
@@ -599,6 +603,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
        { NOTMUCH_OPT_INT, &ctx->offset, "offset", 'O', 0 },
        { NOTMUCH_OPT_INT, &ctx->limit, "limit", 'L', 0  },
        { NOTMUCH_OPT_INT, &ctx->dupe, "duplicate", 'D', 0  },
+       { NOTMUCH_OPT_INHERIT, &common_options, NULL, 0, 0 },
        { 0, 0, 0, 0, 0 }
     };
 
@@ -623,8 +628,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
        ctx->output == OUTPUT_THREADS)
        ret = do_search_threads (ctx);
     else if (ctx->output == OUTPUT_MESSAGES ||
-            ctx->output == OUTPUT_FILES ||
-            (ctx->output & OUTPUT_ADDRESS_FLAGS && !(ctx->output & ~OUTPUT_ADDRESS_FLAGS)))
+            ctx->output == OUTPUT_FILES)
        ret = do_search_messages (ctx);
     else if (ctx->output == OUTPUT_TAGS)
        ret = do_search_tags (ctx);
@@ -637,3 +641,40 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])
 
     return ret ? EXIT_FAILURE : EXIT_SUCCESS;
 }
+
+int
+notmuch_address_command (notmuch_config_t *config, int argc, char *argv[])
+{
+    search_context_t *ctx = &search_context;
+    int opt_index, ret;
+
+    notmuch_opt_desc_t options[] = {
+       { NOTMUCH_OPT_KEYWORD_FLAGS, &ctx->output, "output", 'o',
+         (notmuch_keyword_t []){ { "sender", OUTPUT_SENDER },
+                                 { "recipients", OUTPUT_RECIPIENTS },
+                                 { 0, 0 } } },
+       { NOTMUCH_OPT_KEYWORD, &ctx->exclude, "exclude", 'x',
+         (notmuch_keyword_t []){ { "true", NOTMUCH_EXCLUDE_TRUE },
+                                 { "false", NOTMUCH_EXCLUDE_FALSE },
+                                 { 0, 0 } } },
+       { NOTMUCH_OPT_INHERIT, &common_options, NULL, 0, 0 },
+       { 0, 0, 0, 0, 0 }
+    };
+
+    opt_index = parse_arguments (argc, argv, options, 1);
+    if (opt_index < 0)
+       return EXIT_FAILURE;
+
+    if (! ctx->output)
+       ctx->output = OUTPUT_SENDER | OUTPUT_RECIPIENTS;
+
+    if (_notmuch_search_prepare (ctx, config,
+                                argc - opt_index, argv + opt_index))
+       return EXIT_FAILURE;
+
+    ret = do_search_messages (ctx);
+
+    _notmuch_search_cleanup (ctx);
+
+    return ret ? EXIT_FAILURE : EXIT_SUCCESS;
+}
index dcda0392a0945123f735399f72308c8653a3b1f4..0fac0997865e6acd8622707c49d2d29999d488e3 100644 (file)
--- a/notmuch.c
+++ b/notmuch.c
@@ -54,6 +54,8 @@ static command_t commands[] = {
       "Add a new message into the maildir and notmuch database." },
     { "search", notmuch_search_command, FALSE,
       "Search for messages matching the given search terms." },
+    { "address", notmuch_address_command, FALSE,
+      "Get addresses from messages matching the given search terms." },
     { "show", notmuch_show_command, FALSE,
       "Show all messages matching the search terms." },
     { "count", notmuch_count_command, FALSE,