cli: Introduce "notmuch address" command

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.
This commit is contained in:
Michal Sojka 2014-11-05 01:25:55 +01:00 committed by David Bremner
parent 4387112de0
commit 5c27136e64
8 changed files with 216 additions and 50 deletions

View file

@ -294,7 +294,7 @@ _notmuch_search()
return return
;; ;;
--output) --output)
COMPREPLY=( $( compgen -W "summary threads messages files tags sender recipients" -- "${cur}" ) ) COMPREPLY=( $( compgen -W "summary threads messages files tags" -- "${cur}" ) )
return return
;; ;;
--sort) --sort)
@ -320,6 +320,44 @@ _notmuch_search()
esac 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() _notmuch_show()
{ {
local cur prev words cword split local cur prev words cword split
@ -393,7 +431,7 @@ _notmuch_tag()
_notmuch() _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 local arg cur prev words cword split
# require bash-completion with _init_completion # require bash-completion with _init_completion

View file

@ -10,6 +10,7 @@ _notmuch_commands()
'setup:interactively set up notmuch for first use' 'setup:interactively set up notmuch for first use'
'new:find and import any new message to the database' 'new:find and import any new message to the database'
'search:search for messages matching the search terms, display matching threads as results' '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' 'reply:constructs a reply template for a set of messages'
'show:show all messages matching the search terms' 'show:show all messages matching the search terms'
'tag:add or remove tags for 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: ' \ '--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: ' \ '--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))' '--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() _notmuch()

View file

@ -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)**

View file

@ -78,25 +78,8 @@ Supported options for **search** include
by null characters (--format=text0), as a JSON array by null characters (--format=text0), as a JSON array
(--format=json), or as an S-Expression list (--format=sexp). (--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 This option can be given multiple times to combine different
outputs. Currently, this is only supported for **sender** and outputs.
**recipients** outputs.
``--sort=``\ (**newest-first**\ \|\ **oldest-first**) ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
This option can be used to present results in either 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-dump(1)**, **notmuch-hooks(5)**, **notmuch-insert(1)**,
**notmuch-new(1)**, **notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-new(1)**, **notmuch-reply(1)**, **notmuch-restore(1)**,
**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)** **notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**
***notmuch-address(1)**

View 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 syntax. See **notmuch-search-terms**\ (7) for more details on the
supported syntax. supported syntax.
The **search**, **show** and **count** commands are used to query the The **search**, **show**, **address** and **count** commands are used
email database. to query the email database.
The **reply** command is useful for preparing a template for an email The **reply** command is useful for preparing a template for an email
reply. reply.
@ -128,7 +128,8 @@ SEE ALSO
**notmuch-config(1)**, **notmuch-count(1)**, **notmuch-dump(1)**, **notmuch-config(1)**, **notmuch-count(1)**, **notmuch-dump(1)**,
**notmuch-hooks(5)**, **notmuch-insert(1)**, **notmuch-new(1)**, **notmuch-hooks(5)**, **notmuch-insert(1)**, **notmuch-new(1)**,
**notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-search(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** The notmuch website: **http://notmuchmail.org**

View file

@ -198,6 +198,9 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[]);
int int
notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]); notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]);
int
notmuch_address_command (notmuch_config_t *config, int argc, char *argv[]);
int int
notmuch_setup_command (notmuch_config_t *config, int argc, char *argv[]); notmuch_setup_command (notmuch_config_t *config, int argc, char *argv[]);

View file

@ -23,17 +23,18 @@
#include "string-util.h" #include "string-util.h"
typedef enum { typedef enum {
/* Search command */
OUTPUT_SUMMARY = 1 << 0, OUTPUT_SUMMARY = 1 << 0,
OUTPUT_THREADS = 1 << 1, OUTPUT_THREADS = 1 << 1,
OUTPUT_MESSAGES = 1 << 2, OUTPUT_MESSAGES = 1 << 2,
OUTPUT_FILES = 1 << 3, OUTPUT_FILES = 1 << 3,
OUTPUT_TAGS = 1 << 4, OUTPUT_TAGS = 1 << 4,
/* Address command */
OUTPUT_SENDER = 1 << 5, OUTPUT_SENDER = 1 << 5,
OUTPUT_RECIPIENTS = 1 << 6, OUTPUT_RECIPIENTS = 1 << 6,
} output_t; } output_t;
#define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS)
typedef enum { typedef enum {
NOTMUCH_FORMAT_JSON, NOTMUCH_FORMAT_JSON,
NOTMUCH_FORMAT_TEXT, NOTMUCH_FORMAT_TEXT,
@ -554,39 +555,42 @@ _notmuch_search_cleanup (search_context_t *ctx)
talloc_free (ctx->format); 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 int
notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) 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; search_context_t *ctx = &search_context;
int opt_index, ret; int opt_index, ret;
notmuch_opt_desc_t options[] = { 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_OPT_KEYWORD_FLAGS, &ctx->output, "output", 'o',
(notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY }, (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },
{ "threads", OUTPUT_THREADS }, { "threads", OUTPUT_THREADS },
{ "messages", OUTPUT_MESSAGES }, { "messages", OUTPUT_MESSAGES },
{ "sender", OUTPUT_SENDER },
{ "recipients", OUTPUT_RECIPIENTS },
{ "files", OUTPUT_FILES }, { "files", OUTPUT_FILES },
{ "tags", OUTPUT_TAGS }, { "tags", OUTPUT_TAGS },
{ 0, 0 } } }, { 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->offset, "offset", 'O', 0 },
{ NOTMUCH_OPT_INT, &ctx->limit, "limit", 'L', 0 }, { NOTMUCH_OPT_INT, &ctx->limit, "limit", 'L', 0 },
{ NOTMUCH_OPT_INT, &ctx->dupe, "duplicate", 'D', 0 }, { NOTMUCH_OPT_INT, &ctx->dupe, "duplicate", 'D', 0 },
{ NOTMUCH_OPT_INHERIT, &common_options, NULL, 0, 0 },
{ 0, 0, 0, 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) ctx->output == OUTPUT_THREADS)
ret = do_search_threads (ctx); ret = do_search_threads (ctx);
else if (ctx->output == OUTPUT_MESSAGES || else if (ctx->output == OUTPUT_MESSAGES ||
ctx->output == OUTPUT_FILES || ctx->output == OUTPUT_FILES)
(ctx->output & OUTPUT_ADDRESS_FLAGS && !(ctx->output & ~OUTPUT_ADDRESS_FLAGS)))
ret = do_search_messages (ctx); ret = do_search_messages (ctx);
else if (ctx->output == OUTPUT_TAGS) else if (ctx->output == OUTPUT_TAGS)
ret = do_search_tags (ctx); 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; 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;
}

View file

@ -54,6 +54,8 @@ static command_t commands[] = {
"Add a new message into the maildir and notmuch database." }, "Add a new message into the maildir and notmuch database." },
{ "search", notmuch_search_command, FALSE, { "search", notmuch_search_command, FALSE,
"Search for messages matching the given search terms." }, "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", notmuch_show_command, FALSE,
"Show all messages matching the search terms." }, "Show all messages matching the search terms." },
{ "count", notmuch_count_command, FALSE, { "count", notmuch_count_command, FALSE,