]> git.notmuchmail.org Git - notmuch/commitdiff
cli: address: Add --output=count
authorMichal Sojka <sojkam1@fel.cvut.cz>
Wed, 5 Nov 2014 00:25:58 +0000 (01:25 +0100)
committerDavid Bremner <david@tethera.net>
Wed, 5 Nov 2014 22:25:05 +0000 (23:25 +0100)
This output prints how many times was each address encountered during
search.

completion/notmuch-completion.bash
completion/notmuch-completion.zsh
doc/man1/notmuch-address.rst
notmuch-search.c
test/T095-address.sh

index 94ea2d56943e28afc65056ea93f5c3b45fee38a1..db152f358bc3bebc7a6a659332255b5b6c09f42e 100644 (file)
@@ -332,7 +332,7 @@ _notmuch_address()
            return
            ;;
        --output)
            return
            ;;
        --output)
-           COMPREPLY=( $( compgen -W "sender recipients" -- "${cur}" ) )
+           COMPREPLY=( $( compgen -W "sender recipients count" -- "${cur}" ) )
            return
            ;;
        --sort)
            return
            ;;
        --sort)
index c606b75172e8361522e8bb70cb7e883811c4a652..896856201b46ba772adf74f6027de7cc7d8bd488 100644 (file)
@@ -61,7 +61,7 @@ _notmuch_address()
 {
   _arguments -s : \
     '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \
 {
   _arguments -s : \
     '--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()
index 01eb811e3d29c9b4b4040acfa287051048045243..359616e0dc5f70215794a448a388a04075ced8b9 100644 (file)
@@ -29,7 +29,7 @@ Supported options for **address** include
         intended for programs that invoke **notmuch(1)** internally. If
         omitted, the latest supported version will be used.
 
         intended for programs that invoke **notmuch(1)** internally. If
         omitted, the latest supported version will be used.
 
-    ``--output=(sender|recipients)``
+    ``--output=(sender|recipients|count)``
 
         Controls which information appears in the output. This option
        can be given multiple times to combine different outputs.
 
         Controls which information appears in the output. This option
        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*
             headers.
 
             Output all addresses from the *To*, *Cc* and *Bcc*
             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**)
         This option can be used to present results in either
         chronological order (**oldest-first**) or reverse chronological
     ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
         This option can be used to present results in either
         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
         order, (that is, the newest results will be displayed first).
 
         By default, results will be displayed in reverse chronological
         order, (that is, the newest results will be displayed first).
 
+       This option has no effect when used with --output=count.
+
     ``--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
     ``--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
index 86d54ba044357001c6e46f10d2fb2b76a3f00eba..5036d8e44005532f9f525eb6f87c18234dccb1a4 100644 (file)
@@ -33,6 +33,7 @@ typedef enum {
     /* Address command */
     OUTPUT_SENDER      = 1 << 5,
     OUTPUT_RECIPIENTS  = 1 << 6,
     /* Address command */
     OUTPUT_SENDER      = 1 << 5,
     OUTPUT_RECIPIENTS  = 1 << 6,
+    OUTPUT_COUNT       = 1 << 7,
 } output_t;
 
 typedef enum {
 } output_t;
 
 typedef enum {
@@ -59,6 +60,7 @@ typedef struct {
 typedef struct {
     const char *name;
     const char *addr;
 typedef struct {
     const char *name;
     const char *addr;
+    int count;
 } mailbox_t;
 
 /* Return two stable query strings that identify exactly the matched
 } mailbox_t;
 
 /* 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;
     char *key;
 {
     notmuch_bool_t duplicate;
     char *key;
+    mailbox_t *mailbox;
 
     key = talloc_asprintf (ctx->format, "%s <%s>", name, addr);
     if (! key)
        return FALSE;
 
 
     key = talloc_asprintf (ctx->format, "%s <%s>", name, addr);
     if (! key)
        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)
-       g_hash_table_insert (ctx->addresses, key, NULL);
-    else
+    if (! duplicate) {
+       mailbox = talloc (ctx->format, mailbox_t);
+       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 *addr = mailbox->addr;
 {
     const char *name = mailbox->name;
     const char *addr = mailbox->addr;
+    int count = mailbox->count;
     sprinter_t *format = ctx->format;
     InternetAddress *ia = internet_address_mailbox_new (name, addr);
     char *name_addr;
     sprinter_t *format = ctx->format;
     InternetAddress *ia = internet_address_mailbox_new (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);
 
     if (format->is_text_printer) {
     name_addr = internet_address_to_string (ia, FALSE);
 
     if (format->is_text_printer) {
+       if (count > 0) {
+           format->integer (format, count);
+           format->string (format, "\t");
+       }
        format->string (format, name_addr);
        format->separator (format);
     } else {
        format->string (format, name_addr);
        format->separator (format);
     } else {
@@ -287,6 +301,10 @@ print_mailbox (const search_context_t *ctx, const mailbox_t *mailbox)
        format->string (format, addr);
        format->map_key (format, "name-addr");
        format->string (format, name_addr);
        format->string (format, addr);
        format->map_key (format, "name-addr");
        format->string (format, name_addr);
+       if (count > 0) {
+           format->map_key (format, "count");
+           format->integer (format, count);
+       }
        format->end (format);
        format->separator (format);
     }
        format->end (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
 process_address_list (const search_context_t *ctx,
                      InternetAddressList *list)
 static void
 process_address_list (const search_context_t *ctx,
                      InternetAddressList *list)
@@ -320,17 +338,21 @@ process_address_list (const search_context_t *ctx,
            mailbox_t mbx = {
                .name = internet_address_get_name (address),
                .addr = internet_address_mailbox_get_addr (mailbox),
            mailbox_t mbx = {
                .name = internet_address_get_name (address),
                .addr = internet_address_mailbox_get_addr (mailbox),
+               .count = 0,
            };
 
            if (is_duplicate (ctx, mbx.name, mbx.addr))
                continue;
 
            };
 
            if (is_duplicate (ctx, mbx.name, mbx.addr))
                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
 process_address_header (const search_context_t *ctx, const char *value)
 {
 static void
 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
 _count_filenames (notmuch_message_t *message)
 {
 static int
 _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);
 
     format->end (format);
     notmuch_messages_destroy (messages);
 
     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_keyword_t []){ { "sender", OUTPUT_SENDER },
                                  { "recipients", OUTPUT_RECIPIENTS },
        { NOTMUCH_OPT_KEYWORD_FLAGS, &ctx->output, "output", 'o',
          (notmuch_keyword_t []){ { "sender", OUTPUT_SENDER },
                                  { "recipients", OUTPUT_RECIPIENTS },
+                                 { "count", OUTPUT_COUNT },
                                  { 0, 0 } } },
        { NOTMUCH_OPT_KEYWORD, &ctx->exclude, "exclude", 'x',
          (notmuch_keyword_t []){ { "true", NOTMUCH_EXCLUDE_TRUE },
                                  { 0, 0 } } },
        { NOTMUCH_OPT_KEYWORD, &ctx->exclude, "exclude", 'x',
          (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;
 
     ctx->addresses = g_hash_table_new_full (g_str_hash, g_str_equal,
        return EXIT_FAILURE;
 
     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);
 
index 0d47c0d970a9713f28f1d3b55fe1c5ca9aa7b8e4..033d0f4fd68cffbae01e2473af5b08924239172f 100755 (executable)
@@ -96,4 +96,53 @@ notmuch address '*' >OUTPUT
 # Use EXPECTED from previous subtest
 test_expect_equal_file OUTPUT EXPECTED
 
 # Use EXPECTED from previous subtest
 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