cli: address: Add --output=count

This output prints how many times was each address encountered during
search.
This commit is contained in:
Michal Sojka 2014-11-05 01:25:58 +01:00 committed by David Bremner
parent 4176e527fc
commit a5a6859197
5 changed files with 103 additions and 10 deletions

View file

@ -332,7 +332,7 @@ _notmuch_address()
return return
;; ;;
--output) --output)
COMPREPLY=( $( compgen -W "sender recipients" -- "${cur}" ) ) COMPREPLY=( $( compgen -W "sender recipients count" -- "${cur}" ) )
return return
;; ;;
--sort) --sort)

View file

@ -61,7 +61,7 @@ _notmuch_address()
{ {
_arguments -s : \ _arguments -s : \
'--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:((sender recipients))' '--output=[select what to output]:output:((sender recipients count))'
} }
_notmuch() _notmuch()

View file

@ -29,7 +29,7 @@ Supported options for **address** include
intended for programs that invoke **notmuch(1)** internally. If intended for programs that invoke **notmuch(1)** internally. If
omitted, the latest supported version will be used. omitted, the latest supported version will be used.
``--output=(sender|recipients)`` ``--output=(sender|recipients|count)``
Controls which information appears in the output. This option Controls which information appears in the output. This option
can be given multiple times to combine different outputs. can be given multiple times to combine different outputs.
@ -48,6 +48,13 @@ Supported options for **address** include
Output all addresses from the *To*, *Cc* and *Bcc* Output all addresses from the *To*, *Cc* and *Bcc*
headers. headers.
**count**
Print the count of how many times was the address
encountered during search.
Note: With this option, addresses are printed only after
the whole search is finished. This may take long time.
``--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
chronological order (**oldest-first**) or reverse chronological chronological order (**oldest-first**) or reverse chronological
@ -56,6 +63,8 @@ Supported options for **address** include
By default, results will be displayed in reverse chronological By default, results will be displayed in reverse chronological
order, (that is, the newest results will be displayed first). order, (that is, the newest results will be displayed first).
This option has no effect when used with --output=count.
``--exclude=(true|false)`` ``--exclude=(true|false)``
A message is called "excluded" if it matches at least one tag in A message is called "excluded" if it matches at least one tag in
search.tag\_exclude that does not appear explicitly in the search.tag\_exclude that does not appear explicitly in the

View file

@ -33,6 +33,7 @@ typedef enum {
/* Address command */ /* Address command */
OUTPUT_SENDER = 1 << 5, OUTPUT_SENDER = 1 << 5,
OUTPUT_RECIPIENTS = 1 << 6, OUTPUT_RECIPIENTS = 1 << 6,
OUTPUT_COUNT = 1 << 7,
} output_t; } output_t;
typedef enum { typedef enum {
@ -59,6 +60,7 @@ typedef struct {
typedef struct { typedef struct {
const char *name; const char *name;
const char *addr; const char *addr;
int count;
} mailbox_t; } mailbox_t;
/* Return two stable query strings that identify exactly the matched /* Return two stable query strings that identify exactly the matched
@ -248,17 +250,24 @@ is_duplicate (const search_context_t *ctx, const char *name, const char *addr)
{ {
notmuch_bool_t duplicate; notmuch_bool_t duplicate;
char *key; char *key;
mailbox_t *mailbox;
key = talloc_asprintf (ctx->format, "%s <%s>", name, addr); key = talloc_asprintf (ctx->format, "%s <%s>", name, addr);
if (! key) if (! key)
return FALSE; return FALSE;
duplicate = g_hash_table_lookup_extended (ctx->addresses, key, NULL, NULL); duplicate = g_hash_table_lookup_extended (ctx->addresses, key, NULL, (gpointer)&mailbox);
if (! duplicate) if (! duplicate) {
g_hash_table_insert (ctx->addresses, key, NULL); mailbox = talloc (ctx->format, mailbox_t);
else mailbox->name = talloc_strdup (mailbox, name);
mailbox->addr = talloc_strdup (mailbox, addr);
mailbox->count = 1;
g_hash_table_insert (ctx->addresses, key, mailbox);
} else {
mailbox->count++;
talloc_free (key); talloc_free (key);
}
return duplicate; return duplicate;
} }
@ -268,6 +277,7 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox)
{ {
const char *name = mailbox->name; const char *name = mailbox->name;
const char *addr = mailbox->addr; const char *addr = mailbox->addr;
int count = mailbox->count;
sprinter_t *format = ctx->format; sprinter_t *format = ctx->format;
InternetAddress *ia = internet_address_mailbox_new (name, addr); InternetAddress *ia = internet_address_mailbox_new (name, addr);
char *name_addr; char *name_addr;
@ -277,6 +287,10 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox)
name_addr = internet_address_to_string (ia, FALSE); name_addr = internet_address_to_string (ia, FALSE);
if (format->is_text_printer) { if (format->is_text_printer) {
if (count > 0) {
format->integer (format, count);
format->string (format, "\t");
}
format->string (format, name_addr); format->string (format, name_addr);
format->separator (format); format->separator (format);
} else { } else {
@ -287,6 +301,10 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox)
format->string (format, addr); format->string (format, addr);
format->map_key (format, "name-addr"); format->map_key (format, "name-addr");
format->string (format, name_addr); format->string (format, name_addr);
if (count > 0) {
format->map_key (format, "count");
format->integer (format, count);
}
format->end (format); format->end (format);
format->separator (format); format->separator (format);
} }
@ -295,7 +313,7 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox)
g_free (name_addr); g_free (name_addr);
} }
/* Print addresses from InternetAddressList. */ /* Print or prepare for printing addresses from InternetAddressList. */
static void static void
process_address_list (const search_context_t *ctx, process_address_list (const search_context_t *ctx,
InternetAddressList *list) InternetAddressList *list)
@ -320,17 +338,21 @@ process_address_list (const search_context_t *ctx,
mailbox_t mbx = { mailbox_t mbx = {
.name = internet_address_get_name (address), .name = internet_address_get_name (address),
.addr = internet_address_mailbox_get_addr (mailbox), .addr = internet_address_mailbox_get_addr (mailbox),
.count = 0,
}; };
if (is_duplicate (ctx, mbx.name, mbx.addr)) if (is_duplicate (ctx, mbx.name, mbx.addr))
continue; continue;
if (ctx->output & OUTPUT_COUNT)
continue;
print_mailbox (ctx, &mbx); print_mailbox (ctx, &mbx);
} }
} }
} }
/* Print addresses from a message header. */ /* Print or prepare for printing addresses from a message header. */
static void static void
process_address_header (const search_context_t *ctx, const char *value) process_address_header (const search_context_t *ctx, const char *value)
{ {
@ -355,6 +377,15 @@ _talloc_free_for_g_hash (void *ptr)
talloc_free (ptr); talloc_free (ptr);
} }
static void
print_hash_value (unused (gpointer key), gpointer value, gpointer user_data)
{
const mailbox_t *mailbox = value;
search_context_t *ctx = user_data;
print_mailbox (ctx, mailbox);
}
static int static int
_count_filenames (notmuch_message_t *message) _count_filenames (notmuch_message_t *message)
{ {
@ -450,6 +481,9 @@ do_search_messages (search_context_t *ctx)
notmuch_message_destroy (message); notmuch_message_destroy (message);
} }
if (ctx->addresses && ctx->output & OUTPUT_COUNT)
g_hash_table_foreach (ctx->addresses, print_hash_value, ctx);
notmuch_messages_destroy (messages); notmuch_messages_destroy (messages);
format->end (format); format->end (format);
@ -687,6 +721,7 @@ notmuch_address_command (notmuch_config_t *config, int argc, char *argv[])
{ NOTMUCH_OPT_KEYWORD_FLAGS, &ctx->output, "output", 'o', { NOTMUCH_OPT_KEYWORD_FLAGS, &ctx->output, "output", 'o',
(notmuch_keyword_t []){ { "sender", OUTPUT_SENDER }, (notmuch_keyword_t []){ { "sender", OUTPUT_SENDER },
{ "recipients", OUTPUT_RECIPIENTS }, { "recipients", OUTPUT_RECIPIENTS },
{ "count", OUTPUT_COUNT },
{ 0, 0 } } }, { 0, 0 } } },
{ NOTMUCH_OPT_KEYWORD, &ctx->exclude, "exclude", 'x', { NOTMUCH_OPT_KEYWORD, &ctx->exclude, "exclude", 'x',
(notmuch_keyword_t []){ { "true", NOTMUCH_EXCLUDE_TRUE }, (notmuch_keyword_t []){ { "true", NOTMUCH_EXCLUDE_TRUE },
@ -708,7 +743,7 @@ notmuch_address_command (notmuch_config_t *config, int argc, char *argv[])
return EXIT_FAILURE; return EXIT_FAILURE;
ctx->addresses = g_hash_table_new_full (g_str_hash, g_str_equal, ctx->addresses = g_hash_table_new_full (g_str_hash, g_str_equal,
_talloc_free_for_g_hash, NULL); _talloc_free_for_g_hash, _talloc_free_for_g_hash);
ret = do_search_messages (ctx); ret = do_search_messages (ctx);

View file

@ -96,4 +96,53 @@ notmuch address '*' >OUTPUT
# Use EXPECTED from previous subtest # Use EXPECTED from previous subtest
test_expect_equal_file OUTPUT EXPECTED test_expect_equal_file OUTPUT EXPECTED
test_begin_subtest "--output=sender --output=count"
notmuch address --output=sender --output=count '*' | sort -n >OUTPUT
cat <<EOF >EXPECTED
1 Adrian Perez de Castro <aperez@igalia.com>
1 Aron Griffis <agriffis@n01se.net>
1 Chris Wilson <chris@chris-wilson.co.uk>
1 François Boulogne <boulogne.f@gmail.com>
1 Ingmar Vanhassel <ingmar@exherbo.org>
1 Israel Herraiz <isra@herraiz.org>
1 Olivier Berger <olivier.berger@it-sudparis.eu>
1 Rolland Santimano <rollandsantimano@yahoo.com>
2 Alex Botero-Lowry <alex.boterolowry@gmail.com>
2 Jjgod Jiang <gzjjgod@gmail.com>
3 Stewart Smith <stewart@flamingspork.com>
4 Alexander Botero-Lowry <alex.boterolowry@gmail.com>
4 Jan Janak <jan@ryngle.com>
5 Lars Kellogg-Stedman <lars@seas.harvard.edu>
5 Mikhail Gusarov <dottedmag@dottedmag.net>
7 Keith Packard <keithp@keithp.com>
12 Carl Worth <cworth@cworth.org>
EOF
test_expect_equal_file OUTPUT EXPECTED
test_begin_subtest "--output=sender --output=count --format=json"
# Since the iteration order of GHashTable is not specified, we
# preprocess and sort the results to keep the order stable here.
notmuch address --output=sender --output=count --format=json '*' | \
sed -e 's/^\[//' -e 's/]$//' -e 's/,$//' | sort >OUTPUT
cat <<EOF >EXPECTED
{"name": "Adrian Perez de Castro", "address": "aperez@igalia.com", "name-addr": "Adrian Perez de Castro <aperez@igalia.com>", "count": 1}
{"name": "Alex Botero-Lowry", "address": "alex.boterolowry@gmail.com", "name-addr": "Alex Botero-Lowry <alex.boterolowry@gmail.com>", "count": 2}
{"name": "Alexander Botero-Lowry", "address": "alex.boterolowry@gmail.com", "name-addr": "Alexander Botero-Lowry <alex.boterolowry@gmail.com>", "count": 4}
{"name": "Aron Griffis", "address": "agriffis@n01se.net", "name-addr": "Aron Griffis <agriffis@n01se.net>", "count": 1}
{"name": "Carl Worth", "address": "cworth@cworth.org", "name-addr": "Carl Worth <cworth@cworth.org>", "count": 12}
{"name": "Chris Wilson", "address": "chris@chris-wilson.co.uk", "name-addr": "Chris Wilson <chris@chris-wilson.co.uk>", "count": 1}
{"name": "François Boulogne", "address": "boulogne.f@gmail.com", "name-addr": "François Boulogne <boulogne.f@gmail.com>", "count": 1}
{"name": "Ingmar Vanhassel", "address": "ingmar@exherbo.org", "name-addr": "Ingmar Vanhassel <ingmar@exherbo.org>", "count": 1}
{"name": "Israel Herraiz", "address": "isra@herraiz.org", "name-addr": "Israel Herraiz <isra@herraiz.org>", "count": 1}
{"name": "Jan Janak", "address": "jan@ryngle.com", "name-addr": "Jan Janak <jan@ryngle.com>", "count": 4}
{"name": "Jjgod Jiang", "address": "gzjjgod@gmail.com", "name-addr": "Jjgod Jiang <gzjjgod@gmail.com>", "count": 2}
{"name": "Keith Packard", "address": "keithp@keithp.com", "name-addr": "Keith Packard <keithp@keithp.com>", "count": 7}
{"name": "Lars Kellogg-Stedman", "address": "lars@seas.harvard.edu", "name-addr": "Lars Kellogg-Stedman <lars@seas.harvard.edu>", "count": 5}
{"name": "Mikhail Gusarov", "address": "dottedmag@dottedmag.net", "name-addr": "Mikhail Gusarov <dottedmag@dottedmag.net>", "count": 5}
{"name": "Olivier Berger", "address": "olivier.berger@it-sudparis.eu", "name-addr": "Olivier Berger <olivier.berger@it-sudparis.eu>", "count": 1}
{"name": "Rolland Santimano", "address": "rollandsantimano@yahoo.com", "name-addr": "Rolland Santimano <rollandsantimano@yahoo.com>", "count": 1}
{"name": "Stewart Smith", "address": "stewart@flamingspork.com", "name-addr": "Stewart Smith <stewart@flamingspork.com>", "count": 3}
EOF
test_expect_equal_file OUTPUT EXPECTED
test_done test_done