[PATCH v6 7/7] cli: search: Add --filter-by option to configure address filtering

2014-10-31 Thread Michal Sojka
This option allows to configure the criterion for duplicate address
filtering. Without this option, all unique combinations of name and
address parts are printed. This option allows to filter the output
more, for example to only contain unique address parts.
---
 completion/notmuch-completion.bash |  6 +++-
 completion/notmuch-completion.zsh  |  3 +-
 doc/man1/notmuch-search.rst| 39 +++-
 notmuch-search.c   | 53 +--
 test/T095-search-filter-by.sh  | 73 ++
 5 files changed, 169 insertions(+), 5 deletions(-)
 create mode 100755 test/T095-search-filter-by.sh

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index 39cd829..b625b02 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -305,12 +305,16 @@ _notmuch_search()
COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) )
return
;;
+   --filter-by)
+   COMPREPLY=( $( compgen -W "nameaddr name addr addrfold 
nameaddrfold" -- "${cur}" ) )
+   return
+   ;;
 esac

 ! $split &&
 case "${cur}" in
-*)
-   local options="--format= --output= --sort= --offset= --limit= 
--exclude= --duplicate="
+   local options="--format= --output= --sort= --offset= --limit= 
--exclude= --duplicate= --filter-by="
compopt -o nospace
COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
;;
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index d7e5a5e..c1ccc32 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -53,7 +53,8 @@ _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 count))'
+'--output=[select what to output]:output:((summary threads messages files 
tags sender recipients count))' \
+'--filter-by=[filter out duplicate addresses]:filter-by:((nameaddr\:"both 
name and address part" name\:"name part" addr\:"address part" 
addrfold\:"case-insensitive address part" nameaddrfold\:"name and 
case-insensitive address part"))'
 }

 _notmuch()
diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
index ec89200..3a5556b 100644
--- a/doc/man1/notmuch-search.rst
+++ b/doc/man1/notmuch-search.rst
@@ -85,7 +85,8 @@ Supported options for **search** include
 (--format=text0), as a JSON array (--format=json), or as
 an S-Expression list (--format=sexp).

-Duplicate addresses are filtered out.
+Duplicate addresses are filtered out. Filtering can be
+configured with the --filter-by option.

Note: Searching for **sender** should be much faster than
searching for **recipients**, because sender addresses are
@@ -158,6 +159,42 @@ Supported options for **search** include
 prefix. The prefix matches messages based on filenames. This
 option filters filenames of the matching messages.

+``--filter-by=``\ (**nameaddr**\ \|\ **name** \|\ **addr**\ \|\ 
**addrfold**\ \|\ **nameaddrfold**\)
+
+   Can be used with ``--output=sender`` or
+   ``--output=recipients`` to filter out duplicate addresses. The
+   filtering algorithm receives a sequence of email addresses and
+   outputs the same sequence without the addresses that are
+   considered a duplicate of a previously output address. What is
+   considered a duplicate depends on how the two addresses are
+   compared and this can be controlled with the following
+   keywords:
+
+   **nameaddr** means that both name and address parts are
+   compared in case-sensitive manner. Therefore, all same looking
+   addresses strings are considered duplicate. This is the
+   default.
+
+   **name** means that only the name part is compared (in
+   case-sensitive manner). For example, the addresses "John Doe
+   " and "John Doe " will be
+   considered duplicate.
+
+   **addr** means that only the address part is compared (in
+   case-sensitive manner). For example, the addresses "John Doe
+   " and "Dr. John Doe " will
+   be considered duplicate.
+
+   **addrfold** is like **addr**, but comparison is done in
+   canse-insensitive manner. For example, the addresses "John Doe
+   " and "Dr. John Doe " will
+   be considered duplicate.
+
+   **nameaddrfold** is like **nameaddr**, but address comparison
+   is done in canse-insensitive manner. For example, the

[PATCH v6 6/7] cli: search: Add --output=count

2014-10-31 Thread Michal Sojka
This output can be used with --output=recipients or --output=sender
and in addition to the addresses, it prints how many times was each
address encountered during search.
---
 completion/notmuch-completion.bash |  2 +-
 completion/notmuch-completion.zsh  |  2 +-
 doc/man1/notmuch-search.rst|  9 +--
 notmuch-search.c   | 51 --
 4 files changed, 52 insertions(+), 12 deletions(-)

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index cfbd389..39cd829 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -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 
sender recipients count" -- "${cur}" ) )
return
;;
--sort)
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index 3e52a00..d7e5a5e 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -53,7 +53,7 @@ _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 sender recipients count))'
 }

 _notmuch()
diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
index 42f17e4..ec89200 100644
--- a/doc/man1/notmuch-search.rst
+++ b/doc/man1/notmuch-search.rst
@@ -96,9 +96,14 @@ Supported options for **search** include
 Like **sender** but for addresses from *To*, *Cc* and
*Bcc* headers.

+   **count**
+   Can be used in combination with **sender** or
+   **recipients** to print the count of how many times was
+   the address encountered during search.
+
This option can be given multiple times to combine different
-   outputs. Currently, this is only supported for **sender** and
-   **recipients** outputs.
+   outputs. Currently, this is only supported for **sender**,
+   **recipients** and **count** outputs.

 ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
 This option can be used to present results in either
diff --git a/notmuch-search.c b/notmuch-search.c
index 43d42c6..4b39dfc 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -30,9 +30,10 @@ typedef enum {
 OUTPUT_TAGS= 1 << 4,
 OUTPUT_SENDER  = 1 << 5,
 OUTPUT_RECIPIENTS  = 1 << 6,
+OUTPUT_COUNT   = 1 << 7,
 } output_t;

-#define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS)
+#define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS | OUTPUT_COUNT)

 typedef struct {
 sprinter_t *format;
@@ -47,6 +48,7 @@ typedef struct {
 typedef struct {
 const char *name;
 const char *addr;
+int count;
 } mailbox_t;

 /* Return two stable query strings that identify exactly the matched
@@ -235,17 +237,24 @@ is_duplicate (const search_options_t *opt, GHashTable 
*addrs, const char *name,
 {
 notmuch_bool_t duplicate;
 char *key;
+mailbox_t *mailbox;

 key = talloc_asprintf (opt->format, "%s <%s>", name, addr);
 if (! key)
return FALSE;

-duplicate = g_hash_table_lookup_extended (addrs, key, NULL, NULL);
+duplicate = g_hash_table_lookup_extended (addrs, key, NULL, 
(gpointer)&mailbox);

-if (! duplicate)
-   g_hash_table_insert (addrs, key, NULL);
-else
+if (! duplicate) {
+   mailbox = talloc (opt->format, mailbox_t);
+   mailbox->name = talloc_strdup (mailbox, name);
+   mailbox->addr = talloc_strdup (mailbox, addr);
+   mailbox->count = 1;
+   g_hash_table_insert (addrs, key, mailbox);
+} else {
+   mailbox->count++;
talloc_free (key);
+}

 return duplicate;
 }
@@ -255,6 +264,7 @@ print_mailbox (const search_options_t *opt, const mailbox_t 
*mailbox)
 {
 const char *name = mailbox->name;
 const char *addr = mailbox->addr;
+int count = mailbox->count;
 sprinter_t *format = opt->format;
 InternetAddress *ia = internet_address_mailbox_new (name, addr);
 char *name_addr;
@@ -264,6 +274,10 @@ print_mailbox (const search_options_t *opt, const 
mailbox_t *mailbox)
 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 (

[PATCH v6 5/7] cli: search: Do not output duplicate addresses

2014-10-31 Thread Michal Sojka
This filters out duplicate addresses from address outputs (sender,
receivers).

It also also adds tests for the new outputs.

The code here is an extended version of a patch from Jani Nikula.
---
 doc/man1/notmuch-search.rst |  2 ++
 notmuch-search.c| 51 ++
 test/T090-search-output.sh  | 87 +
 3 files changed, 134 insertions(+), 6 deletions(-)

diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
index b6607c9..42f17e4 100644
--- a/doc/man1/notmuch-search.rst
+++ b/doc/man1/notmuch-search.rst
@@ -85,6 +85,8 @@ Supported options for **search** include
 (--format=text0), as a JSON array (--format=json), or as
 an S-Expression list (--format=sexp).

+Duplicate addresses are filtered out.
+
Note: Searching for **sender** should be much faster than
searching for **recipients**, because sender addresses are
cached directly in the database whereas other addresses
diff --git a/notmuch-search.c b/notmuch-search.c
index 671fe41..43d42c6 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -229,6 +229,27 @@ do_search_threads (search_options_t *opt)
 return 0;
 }

+/* Returns TRUE iff name and addr is duplicate. */
+static notmuch_bool_t
+is_duplicate (const search_options_t *opt, GHashTable *addrs, const char 
*name, const char *addr)
+{
+notmuch_bool_t duplicate;
+char *key;
+
+key = talloc_asprintf (opt->format, "%s <%s>", name, addr);
+if (! key)
+   return FALSE;
+
+duplicate = g_hash_table_lookup_extended (addrs, key, NULL, NULL);
+
+if (! duplicate)
+   g_hash_table_insert (addrs, key, NULL);
+else
+   talloc_free (key);
+
+return duplicate;
+}
+
 static void
 print_mailbox (const search_options_t *opt, const mailbox_t *mailbox)
 {
@@ -263,7 +284,8 @@ print_mailbox (const search_options_t *opt, const mailbox_t 
*mailbox)

 /* Print addresses from InternetAddressList.  */
 static void
-process_address_list (const search_options_t *opt, InternetAddressList *list)
+process_address_list (const search_options_t *opt, GHashTable *addrs,
+ InternetAddressList *list)
 {
 InternetAddress *address;
 int i;
@@ -279,7 +301,7 @@ process_address_list (const search_options_t *opt, 
InternetAddressList *list)
if (group_list == NULL)
continue;

-   process_address_list (opt, group_list);
+   process_address_list (opt, addrs, group_list);
} else {
InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX 
(address);
mailbox_t mbx = {
@@ -287,6 +309,9 @@ process_address_list (const search_options_t *opt, 
InternetAddressList *list)
.addr = internet_address_mailbox_get_addr (mailbox),
};

+   if (is_duplicate (opt, addrs, mbx.name, mbx.addr))
+   continue;
+
print_mailbox (opt, &mbx);
}
 }
@@ -294,7 +319,7 @@ process_address_list (const search_options_t *opt, 
InternetAddressList *list)

 /* Print addresses from a message header.  */
 static void
-process_address_header (const search_options_t *opt, const char *value)
+process_address_header (const search_options_t *opt, GHashTable *addrs, const 
char *value)
 {
 InternetAddressList *list;

@@ -305,11 +330,17 @@ process_address_header (const search_options_t *opt, 
const char *value)
 if (list == NULL)
return;

-process_address_list (opt, list);
+process_address_list (opt, addrs, list);

 g_object_unref (list);
 }

+static void
+_my_talloc_free_for_g_hash (void *ptr)
+{
+talloc_free (ptr);
+}
+
 static int
 do_search_messages (search_options_t *opt)
 {
@@ -317,8 +348,13 @@ do_search_messages (search_options_t *opt)
 notmuch_messages_t *messages;
 notmuch_filenames_t *filenames;
 sprinter_t *format = opt->format;
+GHashTable *addresses = NULL;
 int i;

+if (opt->output & OUTPUT_ADDRESS_FLAGS)
+   addresses = g_hash_table_new_full (g_str_hash, g_str_equal,
+  _my_talloc_free_for_g_hash, NULL);
+
 if (opt->offset < 0) {
opt->offset += notmuch_query_count_messages (opt->query);
if (opt->offset < 0)
@@ -366,7 +402,7 @@ do_search_messages (search_options_t *opt)
const char *addrs;

addrs = notmuch_message_get_header (message, "from");
-   process_address_header (opt, addrs);
+   process_address_header (opt, addresses, addrs);
}

if (opt->output & OUTPUT_RECIPIENTS) {
@@ -376,7 +412,7 @@ do_search_messages (search_options_t *opt)

for (j = 0; j < ARRAY_SIZE (hdrs); j++) {
addrs = notmuch_message_get_header (message, hdrs[j]);
-   process_address_header (opt, addrs);
+   process_address_header (opt, addresses, addrs);
}

[PATCH v6 4/7] cli: search: Add --output={sender,recipients}

2014-10-31 Thread Michal Sojka
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 commit will change this. For this reason, tests are
introduced there.

We use mailbox_t rather than InternetAddressMailbox because we will
need to extend it in a following commit.

This code is based on a patch from Jani Nikula.
---
 completion/notmuch-completion.bash |   2 +-
 completion/notmuch-completion.zsh  |   3 +-
 doc/man1/notmuch-search.rst|  22 ++-
 notmuch-search.c   | 115 -
 4 files changed, 137 insertions(+), 5 deletions(-)

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index 0571dc9..cfbd389 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -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)
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index 67a9aba..3e52a00 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -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()
diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
index 90160f2..b6607c9 100644
--- a/doc/man1/notmuch-search.rst
+++ b/doc/man1/notmuch-search.rst
@@ -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
diff --git a/notmuch-search.c b/notmuch-search.c
index ce46877..671fe41 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -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,87 @@ do_search_threads (search_options_t *opt)
 return 0;
 }

+static void
+print_mailbox (const search_options_t *opt, const mailbox

[PATCH v6 3/7] cli: search: Convert --output to keyword-flag argument

2014-10-31 Thread Michal Sojka
This converts "notmuch search" to use the recently introduced
keyword-flag argument parser. At this point, it only makes the code
slightly less readable but following commits that add new --output
keywords will profit from this.
---
 notmuch-search.c | 35 ++-
 1 file changed, 18 insertions(+), 17 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 0c3e972..ce46877 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -23,11 +23,11 @@
 #include "string-util.h"

 typedef enum {
-OUTPUT_SUMMARY,
-OUTPUT_THREADS,
-OUTPUT_MESSAGES,
-OUTPUT_FILES,
-OUTPUT_TAGS
+OUTPUT_SUMMARY = 1 << 0,
+OUTPUT_THREADS = 1 << 1,
+OUTPUT_MESSAGES= 1 << 2,
+OUTPUT_FILES   = 1 << 3,
+OUTPUT_TAGS= 1 << 4,
 } output_t;

 typedef struct {
@@ -338,7 +338,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
 notmuch_database_t *notmuch;
 search_options_t opt = {
.sort = NOTMUCH_SORT_NEWEST_FIRST,
-   .output = OUTPUT_SUMMARY,
+   .output = 0,
.offset = 0,
.limit = -1, /* unlimited */
.dupe = -1,
@@ -367,7 +367,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
  { "text0", NOTMUCH_FORMAT_TEXT0 },
  { 0, 0 } } },
{ NOTMUCH_OPT_INT, ¬much_format_version, "format-version", 0, 0 },
-   { NOTMUCH_OPT_KEYWORD, &opt.output, "output", 'o',
+   { NOTMUCH_OPT_KEYWORD_FLAGS, &opt.output, "output", 'o',
  (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },
  { "threads", OUTPUT_THREADS },
  { "messages", OUTPUT_MESSAGES },
@@ -390,6 +390,9 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
 if (opt_index < 0)
return EXIT_FAILURE;

+if (! opt.output)
+   opt.output = OUTPUT_SUMMARY;
+
 switch (format_sel) {
 case NOTMUCH_FORMAT_TEXT:
opt.format = sprinter_text_create (config, stdout);
@@ -455,19 +458,17 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
notmuch_query_set_omit_excluded (opt.query, exclude);
 }

-switch (opt.output) {
-default:
-case OUTPUT_SUMMARY:
-case OUTPUT_THREADS:
+if (opt.output == OUTPUT_SUMMARY ||
+   opt.output == OUTPUT_THREADS)
ret = do_search_threads (&opt);
-   break;
-case OUTPUT_MESSAGES:
-case OUTPUT_FILES:
+else if (opt.output == OUTPUT_MESSAGES ||
+opt.output == OUTPUT_FILES)
ret = do_search_messages (&opt);
-   break;
-case OUTPUT_TAGS:
+else if (opt.output == OUTPUT_TAGS)
ret = do_search_tags (notmuch, &opt);
-   break;
+else {
+   fprintf (stderr, "Error: the combination of outputs is not 
supported.\n");
+   ret = 1;
 }

 notmuch_query_destroy (opt.query);
-- 
2.1.1



[PATCH v6 2/7] cli: Add support for parsing keyword-flag arguments

2014-10-31 Thread Michal Sojka
From: Jani Nikula 

This allows having multiple --foo=bar --foo=baz options on the command
line, with the corresponding values OR'd together.

[Test added by Michal Sojka]
---
 command-line-arguments.c  | 6 +-
 command-line-arguments.h  | 1 +
 test/T410-argument-parsing.sh | 3 ++-
 test/arg-test.c   | 9 +
 4 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/command-line-arguments.c b/command-line-arguments.c
index 844d6c3..c6f7269 100644
--- a/command-line-arguments.c
+++ b/command-line-arguments.c
@@ -23,7 +23,10 @@ _process_keyword_arg (const notmuch_opt_desc_t *arg_desc, 
char next, const char
 while (keywords->name) {
if (strcmp (arg_str, keywords->name) == 0) {
if (arg_desc->output_var) {
-   *((int *)arg_desc->output_var) = keywords->value;
+   if (arg_desc->opt_type == NOTMUCH_OPT_KEYWORD_FLAGS)
+   *((int *)arg_desc->output_var) |= keywords->value;
+   else
+   *((int *)arg_desc->output_var) = keywords->value;
}
return TRUE;
}
@@ -152,6 +155,7 @@ parse_option (const char *arg,

switch (try->opt_type) {
case NOTMUCH_OPT_KEYWORD:
+   case NOTMUCH_OPT_KEYWORD_FLAGS:
return _process_keyword_arg (try, next, value);
case NOTMUCH_OPT_BOOLEAN:
return _process_boolean_arg (try, next, value);
diff --git a/command-line-arguments.h b/command-line-arguments.h
index de1734a..6444129 100644
--- a/command-line-arguments.h
+++ b/command-line-arguments.h
@@ -8,6 +8,7 @@ enum notmuch_opt_type {
 NOTMUCH_OPT_BOOLEAN,   /* --verbose  */
 NOTMUCH_OPT_INT,   /* --frob=8   */
 NOTMUCH_OPT_KEYWORD,   /* --format=raw|json|text */
+NOTMUCH_OPT_KEYWORD_FLAGS, /* the above with values OR'd together */
 NOTMUCH_OPT_STRING,/* --file=/tmp/gnarf.txt  */
 NOTMUCH_OPT_POSITION   /* notmuch dump pos_arg   */
 };
diff --git a/test/T410-argument-parsing.sh b/test/T410-argument-parsing.sh
index 94e9087..2e5d7ae 100755
--- a/test/T410-argument-parsing.sh
+++ b/test/T410-argument-parsing.sh
@@ -3,9 +3,10 @@ test_description="argument parsing"
 . ./test-lib.sh

 test_begin_subtest "sanity check"
-$TEST_DIRECTORY/arg-test  pos1  --keyword=one --string=foo pos2 --int=7 > 
OUTPUT
+$TEST_DIRECTORY/arg-test  pos1  --keyword=one --string=foo pos2 --int=7 
--flag=one --flag=three > OUTPUT
 cat < EXPECTED
 keyword 1
+flags 5
 int 7
 string foo
 positional arg 1 pos1
diff --git a/test/arg-test.c b/test/arg-test.c
index 6c49eac..736686d 100644
--- a/test/arg-test.c
+++ b/test/arg-test.c
@@ -7,6 +7,7 @@ int main(int argc, char **argv){
 int opt_index=1;

 int kw_val=0;
+int fl_val=0;
 int int_val=0;
 char *pos_arg1=NULL;
 char *pos_arg2=NULL;
@@ -17,6 +18,11 @@ int main(int argc, char **argv){
  (notmuch_keyword_t []){ { "one", 1 },
  { "two", 2 },
  { 0, 0 } } },
+   { NOTMUCH_OPT_KEYWORD_FLAGS, &fl_val, "flag", 'f',
+ (notmuch_keyword_t []){ { "one",   1 << 0},
+ { "two",   1 << 1 },
+ { "three", 1 << 2 },
+ { 0, 0 } } },
{ NOTMUCH_OPT_INT, &int_val, "int", 'i', 0},
{ NOTMUCH_OPT_STRING, &string_val, "string", 's', 0},
{ NOTMUCH_OPT_POSITION, &pos_arg1, 0,0, 0},
@@ -31,6 +37,9 @@ int main(int argc, char **argv){
 if (kw_val)
printf("keyword %d\n", kw_val);

+if (fl_val)
+   printf("flags %d\n", fl_val);
+
 if (int_val)
printf("int %d\n", int_val);

-- 
2.1.1



[PATCH v6 1/7] cli: search: Refactor passing of command line options

2014-10-31 Thread Michal Sojka
Many functions that implement the search command need to access command
line options. Instead of passing each option in a separate variable, put
them in a structure and pass only this structure.

This will become handy in the following commits.
---
 notmuch-search.c | 125 ---
 1 file changed, 64 insertions(+), 61 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index bc9be45..0c3e972 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -30,6 +30,16 @@ typedef enum {
 OUTPUT_TAGS
 } output_t;

+typedef struct {
+sprinter_t *format;
+notmuch_query_t *query;
+notmuch_sort_t sort;
+output_t output;
+int offset;
+int limit;
+int dupe;
+} search_options_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
@@ -70,43 +80,39 @@ get_thread_query (notmuch_thread_t *thread,
 }

 static int
-do_search_threads (sprinter_t *format,
-  notmuch_query_t *query,
-  notmuch_sort_t sort,
-  output_t output,
-  int offset,
-  int limit)
+do_search_threads (search_options_t *opt)
 {
 notmuch_thread_t *thread;
 notmuch_threads_t *threads;
 notmuch_tags_t *tags;
+sprinter_t *format = opt->format;
 time_t date;
 int i;

-if (offset < 0) {
-   offset += notmuch_query_count_threads (query);
-   if (offset < 0)
-   offset = 0;
+if (opt->offset < 0) {
+   opt->offset += notmuch_query_count_threads (opt->query);
+   if (opt->offset < 0)
+   opt->offset = 0;
 }

-threads = notmuch_query_search_threads (query);
+threads = notmuch_query_search_threads (opt->query);
 if (threads == NULL)
return 1;

 format->begin_list (format);

 for (i = 0;
-notmuch_threads_valid (threads) && (limit < 0 || i < offset + limit);
+notmuch_threads_valid (threads) && (opt->limit < 0 || i < opt->offset 
+ opt->limit);
 notmuch_threads_move_to_next (threads), i++)
 {
thread = notmuch_threads_get (threads);

-   if (i < offset) {
+   if (i < opt->offset) {
notmuch_thread_destroy (thread);
continue;
}

-   if (output == OUTPUT_THREADS) {
+   if (opt->output == OUTPUT_THREADS) {
format->set_prefix (format, "thread");
format->string (format,
notmuch_thread_get_thread_id (thread));
@@ -123,7 +129,7 @@ do_search_threads (sprinter_t *format,

format->begin_map (format);

-   if (sort == NOTMUCH_SORT_OLDEST_FIRST)
+   if (opt->sort == NOTMUCH_SORT_OLDEST_FIRST)
date = notmuch_thread_get_oldest_date (thread);
else
date = notmuch_thread_get_newest_date (thread);
@@ -215,40 +221,36 @@ do_search_threads (sprinter_t *format,
 }

 static int
-do_search_messages (sprinter_t *format,
-   notmuch_query_t *query,
-   output_t output,
-   int offset,
-   int limit,
-   int dupe)
+do_search_messages (search_options_t *opt)
 {
 notmuch_message_t *message;
 notmuch_messages_t *messages;
 notmuch_filenames_t *filenames;
+sprinter_t *format = opt->format;
 int i;

-if (offset < 0) {
-   offset += notmuch_query_count_messages (query);
-   if (offset < 0)
-   offset = 0;
+if (opt->offset < 0) {
+   opt->offset += notmuch_query_count_messages (opt->query);
+   if (opt->offset < 0)
+   opt->offset = 0;
 }

-messages = notmuch_query_search_messages (query);
+messages = notmuch_query_search_messages (opt->query);
 if (messages == NULL)
return 1;

 format->begin_list (format);

 for (i = 0;
-notmuch_messages_valid (messages) && (limit < 0 || i < offset + limit);
+notmuch_messages_valid (messages) && (opt->limit < 0 || i < 
opt->offset + opt->limit);
 notmuch_messages_move_to_next (messages), i++)
 {
-   if (i < offset)
+   if (i < opt->offset)
continue;

message = notmuch_messages_get (messages);

-   if (output == OUTPUT_FILES) {
+   if (opt->output == OUTPUT_FILES) {
int j;
filenames = notmuch_message_get_filenames (message);

@@ -256,7 +258,7 @@ do_search_messages (sprinter_t *format,
 notmuch_filenames_valid (filenames);
 notmuch_filenames_move_to_next (filenames), j++)
{
-   if (dupe < 0 || dupe == j) {
+   if (opt->dupe < 0 || opt->dupe == j) {
format->string (format, notmuch_filenames_get (filenames));
format->separator (format);
}
@@ -283,12 +285,13 @@ do_search_messages (sprinter_t *f

[PATCH v6 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Michal Sojka
Hi all,

this is v6 of the search --output=address series. It obsoletes v5
(id:1414713573-21461-1-git-send-email-sojkam1 at fel.cvut.cz).

Changes from v5 (full diff below):

- Added quoting of name parts if that is necessary (pointed out by
  Mark Walters). Structured formats contain both full address
  (possibly with quoted name) and unquoted individual fields.
- Fixed bug in --output=count --filter-by=*fold (reported by Jesse
  Rosenthal). New test was added for this case. Fixing the bug also
  resulted in simpler code :)
- Added missing unreferencing of InternetAddressList.

Changes from v4:

- patch changed to commit in commit messages
- opt->format changed to format
- Added comments to process_* functions
- duplicite changed to duplicate
- check_duplicate changed to is_duplicate
- Deduplication was split into two commits: basic deduplication
  without a command line option and configurable deduplication with
  --fiter-by.

Changes from v3:

- `o' renamed to `opt'.
- Conversion of --output from keyword to keyword-flags is now a
  separate patch.
- Structured output formats print name and address separately.
- Added test for --format=json.
- Changed --filter-by default to nameaddr. In v2, the default was
  addrfold, in v3 the default was no filtering at all. I believe that
  Mark's suggestion to make nameaddr the default is good trade off.
- Added new --output=count
- Minor style fixes
- Few typos fixed
- There is no way to output unfiltered (duplicite) addresses.
  Hopefully, the introduction of --output=count is sufficient
  replacement for this "feature".

Cheers,
-Michal


Jani Nikula (1):
  cli: Add support for parsing keyword-flag arguments

Michal Sojka (6):
  cli: search: Refactor passing of command line options
  cli: search: Convert --output to keyword-flag argument
  cli: search: Add --output={sender,recipients}
  cli: search: Do not output duplicate addresses
  cli: search: Add --output=count
  cli: search: Add --filter-by option to configure address filtering

 command-line-arguments.c   |   6 +-
 command-line-arguments.h   |   1 +
 completion/notmuch-completion.bash |   8 +-
 completion/notmuch-completion.zsh  |   4 +-
 doc/man1/notmuch-search.rst|  66 ++-
 notmuch-search.c   | 390 +
 test/T090-search-output.sh |  87 +
 test/T095-search-filter-by.sh  |  73 +++
 test/T410-argument-parsing.sh  |   3 +-
 test/arg-test.c|   9 +
 10 files changed, 565 insertions(+), 82 deletions(-)
 create mode 100755 test/T095-search-filter-by.sh

-- 
2.1.1

diff --git a/notmuch-search.c b/notmuch-search.c
index 8bc80d3..a350f06 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -246,33 +246,35 @@ is_duplicate (const search_options_t *opt, GHashTable 
*addrs, const char *name,
 {
 notmuch_bool_t duplicate;
 char *key;
+gchar *addrfold = NULL;
 mailbox_t *mailbox;

 if (opt->filter_by == FILTER_BY_ADDRFOLD ||
-   opt->filter_by == FILTER_BY_NAMEADDRFOLD) {
-   gchar *folded = g_utf8_casefold (addr, -1);
-   addr = talloc_strdup (opt->format, folded);
-   g_free (folded);
-}
+   opt->filter_by == FILTER_BY_NAMEADDRFOLD)
+   addrfold = g_utf8_casefold (addr, -1);
+
 switch (opt->filter_by) {
 case FILTER_BY_NAMEADDR:
-case FILTER_BY_NAMEADDRFOLD:
key = talloc_asprintf (opt->format, "%s <%s>", name, addr);
break;
+case FILTER_BY_NAMEADDRFOLD:
+   key = talloc_asprintf (opt->format, "%s <%s>", name, addrfold);
+   break;
 case FILTER_BY_NAME:
key = talloc_strdup (opt->format, name); /* !name results in !key */
break;
 case FILTER_BY_ADDR:
-case FILTER_BY_ADDRFOLD:
key = talloc_strdup (opt->format, addr);
break;
+case FILTER_BY_ADDRFOLD:
+   key = talloc_strdup (opt->format, addrfold);
+   break;
 default:
INTERNAL_ERROR("invalid --filter-by flags");
 }

-if (opt->filter_by == FILTER_BY_ADDRFOLD ||
-   opt->filter_by == FILTER_BY_NAMEADDRFOLD)
-   talloc_free ((char*)addr);
+if (addrfold)
+   g_free (addrfold);

 if (! key)
return FALSE;
@@ -300,33 +302,28 @@ print_mailbox (const search_options_t *opt, const 
mailbox_t *mailbox)
 const char *addr = mailbox->addr;
 int count = mailbox->count;
 sprinter_t *format = opt->format;
+InternetAddress *ia = internet_address_mailbox_new (name, addr);
+char *name_addr;

-if (format->is_text_printer) {
-   char *mailbox_str;
+/* name_addr has the name part quoted if necessary. Compare
+ * 'John Doe ' vs. '"Doe, John" ' */
+name_addr = internet_address_to_string (ia, FALSE);

-   if (name && *name)
-   mailbox_str = talloc_asprintf (format, "%s <%s>", name, addr);
-   else
-   mailbox_str = talloc_strdup (format, addr);
-
-   if (! mailbox_str) {
-   fprintf (stderr, "Error: out of me

[PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Tomi Ollila
On Fri, Oct 31 2014, Michal Sojka  wrote:

> On Fri, Oct 31 2014, Mark Walters wrote:
>> My only query is in the text output: should the name part be printed as
>> a quoted string. For example currently I get a line of the form
>>
>> Bloggs, Fred 
>
> Good point.

There has been some discussion on this issue on IRC channel, and the
opinion of most (seen so far) is that output the parts without quoting
(i.e. just like done in this patch series)...

Taken the other example from Mark's earlyer email:

Bloggs , Fred 

echo '^^^' | sed 's/.*:s) (*)

echo '^^^' | sed 's/ <[^<]*$//' would leave only the name part

and regexp '\(.*\) <\(.*\)>' or pcre-compatible /(.*)\s<(.*)>/
would capture name & address parts...

(all of the above untested, though;)

In case instead of 'name ' there is only 'addr', then above
sed lines return the same (full) string and the regexps just don't
match.

There were some suggestions how the text output could be changed on IRC;
if anyone wishes to bring those forward, please do so :D

So, IMO, this issue is not showstopper in this series (if anything is);
patches 1-6 looks good to me on paper, but I have not tested those yet.

Tomi

(*) echo '^^^' | sed 's/.*.*//' would drop <>'s from first example


>
> On Fri, Oct 31 2014, Mark Walters wrote:
>> Hi
>>
>> I attach a patch which does the quoting for real names but I don't know
>> if we want it: it changes (example taken from the test suite)
>>
>> Fran?ois Boulogne to
>>
>> =?iso-8859-1?q?Fran=E7ois?= Boulogne
>>
>> (which is what was in the original email)
>>
>> Plausibly the best thing is just to leave the series as is, so the
>> text output is readable and parseable in the common cases.
>>
>> Anyway the patch is attached if anyone wants to experiment.
>>
>> Best wishes
>>
>> Mark
>>
>> From 53b1ced2d6a9fbbba93448325f795e6b99faa240 Mon Sep 17 00:00:00 2001
>> From: Mark Walters 
>> Date: Fri, 31 Oct 2014 10:11:40 +
>> Subject: [PATCH] search: quote real names for output=sender/recipient in text
>>  format
>>
>> This quotes the real name (when gmime thinks appropriate) for the text
>> output. For the other outputs the real name is separate from the
>> address so the consumer can do any quoting necessary.
>> ---
>>  notmuch-search.c   |8 
>>  test/T090-search-output.sh |8 
>>  2 files changed, 8 insertions(+), 8 deletions(-)
>>
>> diff --git a/notmuch-search.c b/notmuch-search.c
>> index eae749a..8eac161 100644
>> --- a/notmuch-search.c
>> +++ b/notmuch-search.c
>> @@ -47,6 +47,7 @@ typedef struct {
>>  typedef struct {
>>  const char *name;
>>  const char *addr;
>> +const char *string;
>>  } mailbox_t;
>>  
>>  /* Return two stable query strings that identify exactly the matched
>> @@ -255,15 +256,13 @@ print_mailbox (const search_options_t *opt, const 
>> mailbox_t *mailbox)
>>  {
>>  const char *name = mailbox->name;
>>  const char *addr = mailbox->addr;
>> +const char *string = mailbox->string;
>>  sprinter_t *format = opt->format;
>>  
>>  if (format->is_text_printer) {
>>  char *mailbox_str;
>>  
>> -if (name && *name)
>> -mailbox_str = talloc_asprintf (format, "%s <%s>", name, addr);
>> -else
>> -mailbox_str = talloc_strdup (format, addr);
>> +mailbox_str = talloc_strdup (format, string);
>>  
>>  if (! mailbox_str) {
>>  fprintf (stderr, "Error: out of memory\n");
>> @@ -309,6 +308,7 @@ process_address_list (const search_options_t *opt, 
>> GHashTable *addrs,
>>  mailbox_t mbx = {
>>  .name = internet_address_get_name (address),
>>  .addr = internet_address_mailbox_get_addr (mailbox),
>> +.string = internet_address_to_string (address, TRUE),
>
> I'd prefer having the second parameter (encode) FALSE. This will still
> add quotes when necessary, but does not encode non-ascii characters so
> the result would be human readable.
>
> Another question is whether to add .string to mailbox_t. In this patch
> it doesn't matter, but if --output=count patch will be merged, this
> would mean that memory consumption doubles, because with --output=count
> the addresses are kept in memory and printed only after the search is
> completed. It would be therefore better to construct a new
> InternetAddressMailbox from name and addr in print_mailbox() and perform
> the conversion to string there. Thoughts?
>
> Thanks,
> -Michal
>
>>  };
>>  
>>  if (is_duplicate (opt, addrs, mbx.name, mbx.addr))
>> diff --git a/test/T090-search-output.sh b/test/T090-search-output.sh
>> index 841a721..776e3f4 100755
>> --- a/test/T090-search-output.sh
>> +++ b/test/T090-search-output.sh
>> @@ -390,7 +390,7 @@ test_expect_equal_file OUTPUT EXPECTED
>>  test_begin_subtest "--output=sender"
>>  notmuch search --output=sender '*' >OUTPUT
>>  cat > -Fran?ois Boulogne 
>> +=?iso-8859-1?q?Fran=E7ois?= Boulogne 
>>  Olivier Berger 
>>  Chris Wilson 
>>  Carl Worth 
>> @@ -437,7 +437,7 @@ test_be

Re: [PATCH v6 6/7] cli: search: Add --output=count

2014-10-31 Thread Mark Walters

On Fri, 31 Oct 2014, Michal Sojka  wrote:
> This output can be used with --output=recipients or --output=sender
> and in addition to the addresses, it prints how many times was each
> address encountered during search.

Hi

I have a couple comments on this patch.

> ---
>  completion/notmuch-completion.bash |  2 +-
>  completion/notmuch-completion.zsh  |  2 +-
>  doc/man1/notmuch-search.rst|  9 +--
>  notmuch-search.c   | 51 
> --
>  4 files changed, 52 insertions(+), 12 deletions(-)
>
> diff --git a/completion/notmuch-completion.bash 
> b/completion/notmuch-completion.bash
> index cfbd389..39cd829 100644
> --- a/completion/notmuch-completion.bash
> +++ b/completion/notmuch-completion.bash
> @@ -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 
> sender recipients count" -- "${cur}" ) )
>   return
>   ;;
>   --sort)
> diff --git a/completion/notmuch-completion.zsh 
> b/completion/notmuch-completion.zsh
> index 3e52a00..d7e5a5e 100644
> --- a/completion/notmuch-completion.zsh
> +++ b/completion/notmuch-completion.zsh
> @@ -53,7 +53,7 @@ _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 sender recipients count))'
>  }
>  
>  _notmuch()
> diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
> index 42f17e4..ec89200 100644
> --- a/doc/man1/notmuch-search.rst
> +++ b/doc/man1/notmuch-search.rst
> @@ -96,9 +96,14 @@ Supported options for **search** include
>  Like **sender** but for addresses from *To*, *Cc* and
>   *Bcc* headers.
>  
> + **count**
> + Can be used in combination with **sender** or
> + **recipients** to print the count of how many times was
> + the address encountered during search.
> +
>   This option can be given multiple times to combine different
> - outputs. Currently, this is only supported for **sender** and
> - **recipients** outputs.
> + outputs. Currently, this is only supported for **sender**,
> + **recipients** and **count** outputs.

It might be worth saying that the results will be slower if count is
specified.

>  ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)

I think sort works as expected if count is not specified, but does not
with count. Maybe count can be done by doing two passes? If it is
difficult it might be sufficient to just document that sort has no
effect.

Best wishes

Mark



>  This option can be used to present results in either
> diff --git a/notmuch-search.c b/notmuch-search.c
> index 43d42c6..4b39dfc 100644
> --- a/notmuch-search.c
> +++ b/notmuch-search.c
> @@ -30,9 +30,10 @@ typedef enum {
>  OUTPUT_TAGS  = 1 << 4,
>  OUTPUT_SENDER= 1 << 5,
>  OUTPUT_RECIPIENTS= 1 << 6,
> +OUTPUT_COUNT = 1 << 7,
>  } output_t;
>  
> -#define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS)
> +#define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS | 
> OUTPUT_COUNT)
>  
>  typedef struct {
>  sprinter_t *format;
> @@ -47,6 +48,7 @@ typedef struct {
>  typedef struct {
>  const char *name;
>  const char *addr;
> +int count;
>  } mailbox_t;
>  
>  /* Return two stable query strings that identify exactly the matched
> @@ -235,17 +237,24 @@ is_duplicate (const search_options_t *opt, GHashTable 
> *addrs, const char *name,
>  {
>  notmuch_bool_t duplicate;
>  char *key;
> +mailbox_t *mailbox;
>  
>  key = talloc_asprintf (opt->format, "%s <%s>", name, addr);
>  if (! key)
>   return FALSE;
>  
> -duplicate = g_hash_table_lookup_extended (addrs, key, NULL, NULL);
> +duplicate = g_hash_table_lookup_extended (addrs, key, NULL, 
> (gpointer)&mailbox);
>  
> -if (! duplicate)
> - g_hash_table_insert (addrs, key, NULL);
> -else
> +if (! duplicate) {
> + mailbox = talloc (opt->format, mailbox_t);
> + mailbox->name = talloc_strdup (mailbox, name);
> + mailbox->addr = talloc_strdup (mailbox, addr);
> + mailbox->count = 1;
> + g_hash_table_insert (addrs, key, mailbox);
> +} else {
> + mailbox->count++;
>   talloc_free (key);
> +}
>  
>  return duplicate;
>  }
> @@ -255,6 +264,7 @@ print_mailbox (const search_options_t *opt

[PATCH] test: use LDFLAGS in test/Makefile.local

2014-10-31 Thread David Bremner
Jani Nikula  writes:

> Apparently the test binaries are built with minimal LDFLAGS, only
> adding dependency specific LDFLAGS as needed. However because some of
> the test binaries incorporate notmuch object files, it is necessary to
> use the same link flags as notmuch. For example user provided
> CFLAGS/CXXFLAGS/LDFLAGS with -fsanitize=undefined fails to build the
> test binaries if the flags differ.
>
> ---

pushed

d


[PATCH v2] emacs: Improve the behaviour of the 'q' binding.

2014-10-31 Thread David Bremner
David Edmondson  writes:

> When a user hits 'q' in a notmuch buffer, kill the buffer only if
> there are no other windows currently showing it.

pushed. That probably deserves a NEWS patch.

d


[PATCH v1] NEWS: Improved `q` binding.

2014-10-31 Thread David Edmondson
---
 NEWS | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/NEWS b/NEWS
index 4718838..723bca0 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,12 @@ Expanded default saved search settings
   The default saved searches now include several more common searches,
   as well as shortcut keys for `notmuch-jump`.

+Improved `q` binding in notmuch buffers
+
+  `q` will now bury rather than kill a notmuch search, show or tree
+  buffer if there are multiple windows showing the buffer. If only a
+  single window is showing the buffer, it is killed.
+
 Library changes
 ---

-- 
1.9.3 (Apple Git-50)



[PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Mark Walters

On Fri, 31 Oct 2014, Tomi Ollila  wrote:
> On Fri, Oct 31 2014, Michal Sojka  wrote:
>
>> On Fri, Oct 31 2014, Mark Walters wrote:
>>> My only query is in the text output: should the name part be printed as
>>> a quoted string. For example currently I get a line of the form
>>>
>>> Bloggs, Fred 
>>
>> Good point.
>
> There has been some discussion on this issue on IRC channel, and the
> opinion of most (seen so far) is that output the parts without quoting
> (i.e. just like done in this patch series)...

Just to confirm I am happy either way with the quoting. I think it would
be good not to change the text format after it goes into mainline
though. 

Best wishes

Mark





>
> Taken the other example from Mark's earlyer email:
>
> Bloggs , Fred 
>
> echo '^^^' | sed 's/.*:s) (*)
>
> echo '^^^' | sed 's/ <[^<]*$//' would leave only the name part
>
> and regexp '\(.*\) <\(.*\)>' or pcre-compatible /(.*)\s<(.*)>/
> would capture name & address parts...
>
> (all of the above untested, though;)
>
> In case instead of 'name ' there is only 'addr', then above
> sed lines return the same (full) string and the regexps just don't
> match.
>
> There were some suggestions how the text output could be changed on IRC;
> if anyone wishes to bring those forward, please do so :D
>
> So, IMO, this issue is not showstopper in this series (if anything is);
> patches 1-6 looks good to me on paper, but I have not tested those yet.
>
> Tomi
>
> (*) echo '^^^' | sed 's/.*.*//' would drop <>'s from first example
>
>
>>
>> On Fri, Oct 31 2014, Mark Walters wrote:
>>> Hi
>>>
>>> I attach a patch which does the quoting for real names but I don't know
>>> if we want it: it changes (example taken from the test suite)
>>>
>>> Fran?ois Boulogne to
>>>
>>> =?iso-8859-1?q?Fran=E7ois?= Boulogne
>>>
>>> (which is what was in the original email)
>>>
>>> Plausibly the best thing is just to leave the series as is, so the
>>> text output is readable and parseable in the common cases.
>>>
>>> Anyway the patch is attached if anyone wants to experiment.
>>>
>>> Best wishes
>>>
>>> Mark
>>>
>>> From 53b1ced2d6a9fbbba93448325f795e6b99faa240 Mon Sep 17 00:00:00 2001
>>> From: Mark Walters 
>>> Date: Fri, 31 Oct 2014 10:11:40 +
>>> Subject: [PATCH] search: quote real names for output=sender/recipient in 
>>> text
>>>  format
>>>
>>> This quotes the real name (when gmime thinks appropriate) for the text
>>> output. For the other outputs the real name is separate from the
>>> address so the consumer can do any quoting necessary.
>>> ---
>>>  notmuch-search.c   |8 
>>>  test/T090-search-output.sh |8 
>>>  2 files changed, 8 insertions(+), 8 deletions(-)
>>>
>>> diff --git a/notmuch-search.c b/notmuch-search.c
>>> index eae749a..8eac161 100644
>>> --- a/notmuch-search.c
>>> +++ b/notmuch-search.c
>>> @@ -47,6 +47,7 @@ typedef struct {
>>>  typedef struct {
>>>  const char *name;
>>>  const char *addr;
>>> +const char *string;
>>>  } mailbox_t;
>>>  
>>>  /* Return two stable query strings that identify exactly the matched
>>> @@ -255,15 +256,13 @@ print_mailbox (const search_options_t *opt, const 
>>> mailbox_t *mailbox)
>>>  {
>>>  const char *name = mailbox->name;
>>>  const char *addr = mailbox->addr;
>>> +const char *string = mailbox->string;
>>>  sprinter_t *format = opt->format;
>>>  
>>>  if (format->is_text_printer) {
>>> char *mailbox_str;
>>>  
>>> -   if (name && *name)
>>> -   mailbox_str = talloc_asprintf (format, "%s <%s>", name, addr);
>>> -   else
>>> -   mailbox_str = talloc_strdup (format, addr);
>>> +   mailbox_str = talloc_strdup (format, string);
>>>  
>>> if (! mailbox_str) {
>>> fprintf (stderr, "Error: out of memory\n");
>>> @@ -309,6 +308,7 @@ process_address_list (const search_options_t *opt, 
>>> GHashTable *addrs,
>>> mailbox_t mbx = {
>>> .name = internet_address_get_name (address),
>>> .addr = internet_address_mailbox_get_addr (mailbox),
>>> +   .string = internet_address_to_string (address, TRUE),
>>
>> I'd prefer having the second parameter (encode) FALSE. This will still
>> add quotes when necessary, but does not encode non-ascii characters so
>> the result would be human readable.
>>
>> Another question is whether to add .string to mailbox_t. In this patch
>> it doesn't matter, but if --output=count patch will be merged, this
>> would mean that memory consumption doubles, because with --output=count
>> the addresses are kept in memory and printed only after the search is
>> completed. It would be therefore better to construct a new
>> InternetAddressMailbox from name and addr in print_mailbox() and perform
>> the conversion to string there. Thoughts?
>>
>> Thanks,
>> -Michal
>>
>>> };
>>>  
>>> if (is_duplicate (opt, addrs, mbx.name, mbx.addr))
>>> diff --git a/test/T090-search-output.sh b/test/T090-search-output.sh
>>> index 841a721..776e3f4 100755
>>> --- a/test/T090-search-

[PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Mark Walters

Hi

On Fri, 31 Oct 2014, Michal Sojka  wrote:
> On Fri, Oct 31 2014, Mark Walters wrote:
>> My only query is in the text output: should the name part be printed as
>> a quoted string. For example currently I get a line of the form
>>
>> Bloggs, Fred 
>
> Good point.
>
> On Fri, Oct 31 2014, Mark Walters wrote:
>> Hi
>>
>> I attach a patch which does the quoting for real names but I don't know
>> if we want it: it changes (example taken from the test suite)
>>
>> Fran?ois Boulogne to
>>
>> =?iso-8859-1?q?Fran=E7ois?= Boulogne
>>
>> (which is what was in the original email)
>>
>> Plausibly the best thing is just to leave the series as is, so the
>> text output is readable and parseable in the common cases.
>>
>> Anyway the patch is attached if anyone wants to experiment.
>>
>> Best wishes
>>
>> Mark
>>
>> From 53b1ced2d6a9fbbba93448325f795e6b99faa240 Mon Sep 17 00:00:00 2001
>> From: Mark Walters 
>> Date: Fri, 31 Oct 2014 10:11:40 +
>> Subject: [PATCH] search: quote real names for output=sender/recipient in text
>>  format
>>
>> This quotes the real name (when gmime thinks appropriate) for the text
>> output. For the other outputs the real name is separate from the
>> address so the consumer can do any quoting necessary.
>> ---
>>  notmuch-search.c   |8 
>>  test/T090-search-output.sh |8 
>>  2 files changed, 8 insertions(+), 8 deletions(-)
>>
>> diff --git a/notmuch-search.c b/notmuch-search.c
>> index eae749a..8eac161 100644
>> --- a/notmuch-search.c
>> +++ b/notmuch-search.c
>> @@ -47,6 +47,7 @@ typedef struct {
>>  typedef struct {
>>  const char *name;
>>  const char *addr;
>> +const char *string;
>>  } mailbox_t;
>>  
>>  /* Return two stable query strings that identify exactly the matched
>> @@ -255,15 +256,13 @@ print_mailbox (const search_options_t *opt, const 
>> mailbox_t *mailbox)
>>  {
>>  const char *name = mailbox->name;
>>  const char *addr = mailbox->addr;
>> +const char *string = mailbox->string;
>>  sprinter_t *format = opt->format;
>>  
>>  if (format->is_text_printer) {
>>  char *mailbox_str;
>>  
>> -if (name && *name)
>> -mailbox_str = talloc_asprintf (format, "%s <%s>", name, addr);
>> -else
>> -mailbox_str = talloc_strdup (format, addr);
>> +mailbox_str = talloc_strdup (format, string);
>>  
>>  if (! mailbox_str) {
>>  fprintf (stderr, "Error: out of memory\n");
>> @@ -309,6 +308,7 @@ process_address_list (const search_options_t *opt, 
>> GHashTable *addrs,
>>  mailbox_t mbx = {
>>  .name = internet_address_get_name (address),
>>  .addr = internet_address_mailbox_get_addr (mailbox),
>> +.string = internet_address_to_string (address, TRUE),
>
> I'd prefer having the second parameter (encode) FALSE. This will still
> add quotes when necessary, but does not encode non-ascii characters so
> the result would be human readable.

Yes that is clearly better.
>
> Another question is whether to add .string to mailbox_t. In this patch
> it doesn't matter, but if --output=count patch will be merged, this
> would mean that memory consumption doubles, because with --output=count
> the addresses are kept in memory and printed only after the search is
> completed. It would be therefore better to construct a new
> InternetAddressMailbox from name and addr in print_mailbox() and perform
> the conversion to string there. Thoughts?

Yes I agree here too. Are you happy doing a replacement patch or should
I?

Best wishes

Mark

>
> Thanks,
> -Michal
>
>>  };
>>  
>>  if (is_duplicate (opt, addrs, mbx.name, mbx.addr))
>> diff --git a/test/T090-search-output.sh b/test/T090-search-output.sh
>> index 841a721..776e3f4 100755
>> --- a/test/T090-search-output.sh
>> +++ b/test/T090-search-output.sh
>> @@ -390,7 +390,7 @@ test_expect_equal_file OUTPUT EXPECTED
>>  test_begin_subtest "--output=sender"
>>  notmuch search --output=sender '*' >OUTPUT
>>  cat > -Fran?ois Boulogne 
>> +=?iso-8859-1?q?Fran=E7ois?= Boulogne 
>>  Olivier Berger 
>>  Chris Wilson 
>>  Carl Worth 
>> @@ -437,7 +437,7 @@ test_begin_subtest "--output=recipients"
>>  notmuch search --output=recipients '*' >OUTPUT
>>  cat >  Allan McRae 
>> -Discussion about the Arch User Repository (AUR) > archlinux.org>
>> +"Discussion about the Arch User Repository (AUR)" > archlinux.org>
>>  olivier.berger at it-sudparis.eu
>>  notmuch at notmuchmail.org
>>  notmuch 
>> @@ -449,9 +449,9 @@ test_expect_equal_file OUTPUT EXPECTED
>>  test_begin_subtest "--output=sender --output=recipients"
>>  notmuch search --output=sender --output=recipients '*' >OUTPUT
>>  cat > -Fran?ois Boulogne 
>> +=?iso-8859-1?q?Fran=E7ois?= Boulogne 
>>  Allan McRae 
>> -Discussion about the Arch User Repository (AUR) > archlinux.org>
>> +"Discussion about the Arch User Repository (AUR)" > archlinux.org>
>>  Olivier Berger 
>>  olivier.berger at it-sudparis.eu
>>  Chris Wilson 
>>

[PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Michal Sojka
On Fri, Oct 31 2014, Jesse Rosenthal wrote:
> Dear Michal,
>
> Thanks for all this. The feature looks great! One issue: I'm getting
> corrupt output when using `--output=count` with "fold" filters:
>
> ~~~
> jkr at ladybug [11:01AM] ~ $ notmuch search --output=recipients 
> --output=count "tag:notmuch and date:today.." 
> 2 Jani Nikula 
> 2 Michal Sojka 
> 1 David Edmondson 
> 9 notmuch at notmuchmail.org
>
> jkr at ladybug [11:01AM] ~ $ notmuch search --output=recipients 
> --output=count --filter-by=addrfold "tag:notmuch and date:today.."
> 2 Michal Sojka <0Fam1 at fel.cvutH>
> 9 
> 1 David Edmondson <8dme.org>
> 2 Jani Nikula 
>
> jkr at ladybug [11:01AM] ~ $ notmuch search --output=recipients 
> --output=count --filter-by=nameaddr "tag:notmuch and date:today.."
> 2 Jani Nikula 
> 2 Michal Sojka 
> 1 David Edmondson 
> 9 notmuch at notmuchmail.org
>
> jkr at ladybug [11:01AM] ~ $ notmuch search --output=recipients 
> --output=count --filter-by=nameaddrfold "tag:notmuch and date:today.."
> 2 Jani Nikula 
> 2 Michal Sojka <0V?   am1 at fel.cvutH>
> 1 David Edmondson <8??dme.org>
> 9
> ~~~
>
> Not sure if this is expected behavior with the filtering mechanism in
> its current state, but I wanted to call it to your attention if not.

Definitely not. I'll look into that. Thanks.

-Michal


[PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Michal Sojka
On Fri, Oct 31 2014, Mark Walters wrote:
> My only query is in the text output: should the name part be printed as
> a quoted string. For example currently I get a line of the form
>
> Bloggs, Fred 

Good point.

On Fri, Oct 31 2014, Mark Walters wrote:
> Hi
>
> I attach a patch which does the quoting for real names but I don't know
> if we want it: it changes (example taken from the test suite)
>
> Fran?ois Boulogne to
>
> =?iso-8859-1?q?Fran=E7ois?= Boulogne
>
> (which is what was in the original email)
>
> Plausibly the best thing is just to leave the series as is, so the
> text output is readable and parseable in the common cases.
>
> Anyway the patch is attached if anyone wants to experiment.
>
> Best wishes
>
> Mark
>
> From 53b1ced2d6a9fbbba93448325f795e6b99faa240 Mon Sep 17 00:00:00 2001
> From: Mark Walters 
> Date: Fri, 31 Oct 2014 10:11:40 +
> Subject: [PATCH] search: quote real names for output=sender/recipient in text
>  format
>
> This quotes the real name (when gmime thinks appropriate) for the text
> output. For the other outputs the real name is separate from the
> address so the consumer can do any quoting necessary.
> ---
>  notmuch-search.c   |8 
>  test/T090-search-output.sh |8 
>  2 files changed, 8 insertions(+), 8 deletions(-)
>
> diff --git a/notmuch-search.c b/notmuch-search.c
> index eae749a..8eac161 100644
> --- a/notmuch-search.c
> +++ b/notmuch-search.c
> @@ -47,6 +47,7 @@ typedef struct {
>  typedef struct {
>  const char *name;
>  const char *addr;
> +const char *string;
>  } mailbox_t;
>  
>  /* Return two stable query strings that identify exactly the matched
> @@ -255,15 +256,13 @@ print_mailbox (const search_options_t *opt, const 
> mailbox_t *mailbox)
>  {
>  const char *name = mailbox->name;
>  const char *addr = mailbox->addr;
> +const char *string = mailbox->string;
>  sprinter_t *format = opt->format;
>  
>  if (format->is_text_printer) {
>   char *mailbox_str;
>  
> - if (name && *name)
> - mailbox_str = talloc_asprintf (format, "%s <%s>", name, addr);
> - else
> - mailbox_str = talloc_strdup (format, addr);
> + mailbox_str = talloc_strdup (format, string);
>  
>   if (! mailbox_str) {
>   fprintf (stderr, "Error: out of memory\n");
> @@ -309,6 +308,7 @@ process_address_list (const search_options_t *opt, 
> GHashTable *addrs,
>   mailbox_t mbx = {
>   .name = internet_address_get_name (address),
>   .addr = internet_address_mailbox_get_addr (mailbox),
> + .string = internet_address_to_string (address, TRUE),

I'd prefer having the second parameter (encode) FALSE. This will still
add quotes when necessary, but does not encode non-ascii characters so
the result would be human readable.

Another question is whether to add .string to mailbox_t. In this patch
it doesn't matter, but if --output=count patch will be merged, this
would mean that memory consumption doubles, because with --output=count
the addresses are kept in memory and printed only after the search is
completed. It would be therefore better to construct a new
InternetAddressMailbox from name and addr in print_mailbox() and perform
the conversion to string there. Thoughts?

Thanks,
-Michal

>   };
>  
>   if (is_duplicate (opt, addrs, mbx.name, mbx.addr))
> diff --git a/test/T090-search-output.sh b/test/T090-search-output.sh
> index 841a721..776e3f4 100755
> --- a/test/T090-search-output.sh
> +++ b/test/T090-search-output.sh
> @@ -390,7 +390,7 @@ test_expect_equal_file OUTPUT EXPECTED
>  test_begin_subtest "--output=sender"
>  notmuch search --output=sender '*' >OUTPUT
>  cat  -Fran?ois Boulogne 
> +=?iso-8859-1?q?Fran=E7ois?= Boulogne 
>  Olivier Berger 
>  Chris Wilson 
>  Carl Worth 
> @@ -437,7 +437,7 @@ test_begin_subtest "--output=recipients"
>  notmuch search --output=recipients '*' >OUTPUT
>  cat   Allan McRae 
> -Discussion about the Arch User Repository (AUR)  archlinux.org>
> +"Discussion about the Arch User Repository (AUR)"  archlinux.org>
>  olivier.berger at it-sudparis.eu
>  notmuch at notmuchmail.org
>  notmuch 
> @@ -449,9 +449,9 @@ test_expect_equal_file OUTPUT EXPECTED
>  test_begin_subtest "--output=sender --output=recipients"
>  notmuch search --output=sender --output=recipients '*' >OUTPUT
>  cat  -Fran?ois Boulogne 
> +=?iso-8859-1?q?Fran=E7ois?= Boulogne 
>  Allan McRae 
> -Discussion about the Arch User Repository (AUR)  archlinux.org>
> +"Discussion about the Arch User Repository (AUR)"  archlinux.org>
>  Olivier Berger 
>  olivier.berger at it-sudparis.eu
>  Chris Wilson 
> -- 
> 1.7.10.4


[PATCH v6 7/7] cli: search: Add --filter-by option to configure address filtering

2014-10-31 Thread Michal Sojka
This option allows to configure the criterion for duplicate address
filtering. Without this option, all unique combinations of name and
address parts are printed. This option allows to filter the output
more, for example to only contain unique address parts.
---
 completion/notmuch-completion.bash |  6 +++-
 completion/notmuch-completion.zsh  |  3 +-
 doc/man1/notmuch-search.rst| 39 +++-
 notmuch-search.c   | 53 +--
 test/T095-search-filter-by.sh  | 73 ++
 5 files changed, 169 insertions(+), 5 deletions(-)
 create mode 100755 test/T095-search-filter-by.sh

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index 39cd829..b625b02 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -305,12 +305,16 @@ _notmuch_search()
COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) )
return
;;
+   --filter-by)
+   COMPREPLY=( $( compgen -W "nameaddr name addr addrfold 
nameaddrfold" -- "${cur}" ) )
+   return
+   ;;
 esac
 
 ! $split &&
 case "${cur}" in
-*)
-   local options="--format= --output= --sort= --offset= --limit= 
--exclude= --duplicate="
+   local options="--format= --output= --sort= --offset= --limit= 
--exclude= --duplicate= --filter-by="
compopt -o nospace
COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
;;
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index d7e5a5e..c1ccc32 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -53,7 +53,8 @@ _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 count))'
+'--output=[select what to output]:output:((summary threads messages files 
tags sender recipients count))' \
+'--filter-by=[filter out duplicate addresses]:filter-by:((nameaddr\:"both 
name and address part" name\:"name part" addr\:"address part" 
addrfold\:"case-insensitive address part" nameaddrfold\:"name and 
case-insensitive address part"))'
 }
 
 _notmuch()
diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
index ec89200..3a5556b 100644
--- a/doc/man1/notmuch-search.rst
+++ b/doc/man1/notmuch-search.rst
@@ -85,7 +85,8 @@ Supported options for **search** include
 (--format=text0), as a JSON array (--format=json), or as
 an S-Expression list (--format=sexp).
 
-Duplicate addresses are filtered out.
+Duplicate addresses are filtered out. Filtering can be
+configured with the --filter-by option.
 
Note: Searching for **sender** should be much faster than
searching for **recipients**, because sender addresses are
@@ -158,6 +159,42 @@ Supported options for **search** include
 prefix. The prefix matches messages based on filenames. This
 option filters filenames of the matching messages.
 
+``--filter-by=``\ (**nameaddr**\ \|\ **name** \|\ **addr**\ \|\ 
**addrfold**\ \|\ **nameaddrfold**\)
+
+   Can be used with ``--output=sender`` or
+   ``--output=recipients`` to filter out duplicate addresses. The
+   filtering algorithm receives a sequence of email addresses and
+   outputs the same sequence without the addresses that are
+   considered a duplicate of a previously output address. What is
+   considered a duplicate depends on how the two addresses are
+   compared and this can be controlled with the following
+   keywords:
+
+   **nameaddr** means that both name and address parts are
+   compared in case-sensitive manner. Therefore, all same looking
+   addresses strings are considered duplicate. This is the
+   default.
+
+   **name** means that only the name part is compared (in
+   case-sensitive manner). For example, the addresses "John Doe
+   " and "John Doe " will be
+   considered duplicate.
+
+   **addr** means that only the address part is compared (in
+   case-sensitive manner). For example, the addresses "John Doe
+   " and "Dr. John Doe " will
+   be considered duplicate.
+
+   **addrfold** is like **addr**, but comparison is done in
+   canse-insensitive manner. For example, the addresses "John Doe
+   " and "Dr. John Doe " will
+   be considered duplicate.
+
+   **nameaddrfold** is like **nameaddr**, but address comparison
+   is done in canse-insensitive manner. For example

[PATCH v6 5/7] cli: search: Do not output duplicate addresses

2014-10-31 Thread Michal Sojka
This filters out duplicate addresses from address outputs (sender,
receivers).

It also also adds tests for the new outputs.

The code here is an extended version of a patch from Jani Nikula.
---
 doc/man1/notmuch-search.rst |  2 ++
 notmuch-search.c| 51 ++
 test/T090-search-output.sh  | 87 +
 3 files changed, 134 insertions(+), 6 deletions(-)

diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
index b6607c9..42f17e4 100644
--- a/doc/man1/notmuch-search.rst
+++ b/doc/man1/notmuch-search.rst
@@ -85,6 +85,8 @@ Supported options for **search** include
 (--format=text0), as a JSON array (--format=json), or as
 an S-Expression list (--format=sexp).
 
+Duplicate addresses are filtered out.
+
Note: Searching for **sender** should be much faster than
searching for **recipients**, because sender addresses are
cached directly in the database whereas other addresses
diff --git a/notmuch-search.c b/notmuch-search.c
index 671fe41..43d42c6 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -229,6 +229,27 @@ do_search_threads (search_options_t *opt)
 return 0;
 }
 
+/* Returns TRUE iff name and addr is duplicate. */
+static notmuch_bool_t
+is_duplicate (const search_options_t *opt, GHashTable *addrs, const char 
*name, const char *addr)
+{
+notmuch_bool_t duplicate;
+char *key;
+
+key = talloc_asprintf (opt->format, "%s <%s>", name, addr);
+if (! key)
+   return FALSE;
+
+duplicate = g_hash_table_lookup_extended (addrs, key, NULL, NULL);
+
+if (! duplicate)
+   g_hash_table_insert (addrs, key, NULL);
+else
+   talloc_free (key);
+
+return duplicate;
+}
+
 static void
 print_mailbox (const search_options_t *opt, const mailbox_t *mailbox)
 {
@@ -263,7 +284,8 @@ print_mailbox (const search_options_t *opt, const mailbox_t 
*mailbox)
 
 /* Print addresses from InternetAddressList.  */
 static void
-process_address_list (const search_options_t *opt, InternetAddressList *list)
+process_address_list (const search_options_t *opt, GHashTable *addrs,
+ InternetAddressList *list)
 {
 InternetAddress *address;
 int i;
@@ -279,7 +301,7 @@ process_address_list (const search_options_t *opt, 
InternetAddressList *list)
if (group_list == NULL)
continue;
 
-   process_address_list (opt, group_list);
+   process_address_list (opt, addrs, group_list);
} else {
InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX 
(address);
mailbox_t mbx = {
@@ -287,6 +309,9 @@ process_address_list (const search_options_t *opt, 
InternetAddressList *list)
.addr = internet_address_mailbox_get_addr (mailbox),
};
 
+   if (is_duplicate (opt, addrs, mbx.name, mbx.addr))
+   continue;
+
print_mailbox (opt, &mbx);
}
 }
@@ -294,7 +319,7 @@ process_address_list (const search_options_t *opt, 
InternetAddressList *list)
 
 /* Print addresses from a message header.  */
 static void
-process_address_header (const search_options_t *opt, const char *value)
+process_address_header (const search_options_t *opt, GHashTable *addrs, const 
char *value)
 {
 InternetAddressList *list;
 
@@ -305,11 +330,17 @@ process_address_header (const search_options_t *opt, 
const char *value)
 if (list == NULL)
return;
 
-process_address_list (opt, list);
+process_address_list (opt, addrs, list);
 
 g_object_unref (list);
 }
 
+static void
+_my_talloc_free_for_g_hash (void *ptr)
+{
+talloc_free (ptr);
+}
+
 static int
 do_search_messages (search_options_t *opt)
 {
@@ -317,8 +348,13 @@ do_search_messages (search_options_t *opt)
 notmuch_messages_t *messages;
 notmuch_filenames_t *filenames;
 sprinter_t *format = opt->format;
+GHashTable *addresses = NULL;
 int i;
 
+if (opt->output & OUTPUT_ADDRESS_FLAGS)
+   addresses = g_hash_table_new_full (g_str_hash, g_str_equal,
+  _my_talloc_free_for_g_hash, NULL);
+
 if (opt->offset < 0) {
opt->offset += notmuch_query_count_messages (opt->query);
if (opt->offset < 0)
@@ -366,7 +402,7 @@ do_search_messages (search_options_t *opt)
const char *addrs;
 
addrs = notmuch_message_get_header (message, "from");
-   process_address_header (opt, addrs);
+   process_address_header (opt, addresses, addrs);
}
 
if (opt->output & OUTPUT_RECIPIENTS) {
@@ -376,7 +412,7 @@ do_search_messages (search_options_t *opt)
 
for (j = 0; j < ARRAY_SIZE (hdrs); j++) {
addrs = notmuch_message_get_header (message, hdrs[j]);
-   process_address_header (opt, addrs);
+   process_address_header (opt, addresses, addrs);
   

[PATCH v6 2/7] cli: Add support for parsing keyword-flag arguments

2014-10-31 Thread Michal Sojka
From: Jani Nikula 

This allows having multiple --foo=bar --foo=baz options on the command
line, with the corresponding values OR'd together.

[Test added by Michal Sojka]
---
 command-line-arguments.c  | 6 +-
 command-line-arguments.h  | 1 +
 test/T410-argument-parsing.sh | 3 ++-
 test/arg-test.c   | 9 +
 4 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/command-line-arguments.c b/command-line-arguments.c
index 844d6c3..c6f7269 100644
--- a/command-line-arguments.c
+++ b/command-line-arguments.c
@@ -23,7 +23,10 @@ _process_keyword_arg (const notmuch_opt_desc_t *arg_desc, 
char next, const char
 while (keywords->name) {
if (strcmp (arg_str, keywords->name) == 0) {
if (arg_desc->output_var) {
-   *((int *)arg_desc->output_var) = keywords->value;
+   if (arg_desc->opt_type == NOTMUCH_OPT_KEYWORD_FLAGS)
+   *((int *)arg_desc->output_var) |= keywords->value;
+   else
+   *((int *)arg_desc->output_var) = keywords->value;
}
return TRUE;
}
@@ -152,6 +155,7 @@ parse_option (const char *arg,
 
switch (try->opt_type) {
case NOTMUCH_OPT_KEYWORD:
+   case NOTMUCH_OPT_KEYWORD_FLAGS:
return _process_keyword_arg (try, next, value);
case NOTMUCH_OPT_BOOLEAN:
return _process_boolean_arg (try, next, value);
diff --git a/command-line-arguments.h b/command-line-arguments.h
index de1734a..6444129 100644
--- a/command-line-arguments.h
+++ b/command-line-arguments.h
@@ -8,6 +8,7 @@ enum notmuch_opt_type {
 NOTMUCH_OPT_BOOLEAN,   /* --verbose  */
 NOTMUCH_OPT_INT,   /* --frob=8   */
 NOTMUCH_OPT_KEYWORD,   /* --format=raw|json|text */
+NOTMUCH_OPT_KEYWORD_FLAGS, /* the above with values OR'd together */
 NOTMUCH_OPT_STRING,/* --file=/tmp/gnarf.txt  */
 NOTMUCH_OPT_POSITION   /* notmuch dump pos_arg   */
 };
diff --git a/test/T410-argument-parsing.sh b/test/T410-argument-parsing.sh
index 94e9087..2e5d7ae 100755
--- a/test/T410-argument-parsing.sh
+++ b/test/T410-argument-parsing.sh
@@ -3,9 +3,10 @@ test_description="argument parsing"
 . ./test-lib.sh
 
 test_begin_subtest "sanity check"
-$TEST_DIRECTORY/arg-test  pos1  --keyword=one --string=foo pos2 --int=7 > 
OUTPUT
+$TEST_DIRECTORY/arg-test  pos1  --keyword=one --string=foo pos2 --int=7 
--flag=one --flag=three > OUTPUT
 cat < EXPECTED
 keyword 1
+flags 5
 int 7
 string foo
 positional arg 1 pos1
diff --git a/test/arg-test.c b/test/arg-test.c
index 6c49eac..736686d 100644
--- a/test/arg-test.c
+++ b/test/arg-test.c
@@ -7,6 +7,7 @@ int main(int argc, char **argv){
 int opt_index=1;
 
 int kw_val=0;
+int fl_val=0;
 int int_val=0;
 char *pos_arg1=NULL;
 char *pos_arg2=NULL;
@@ -17,6 +18,11 @@ int main(int argc, char **argv){
  (notmuch_keyword_t []){ { "one", 1 },
  { "two", 2 },
  { 0, 0 } } },
+   { NOTMUCH_OPT_KEYWORD_FLAGS, &fl_val, "flag", 'f',
+ (notmuch_keyword_t []){ { "one",   1 << 0},
+ { "two",   1 << 1 },
+ { "three", 1 << 2 },
+ { 0, 0 } } },
{ NOTMUCH_OPT_INT, &int_val, "int", 'i', 0},
{ NOTMUCH_OPT_STRING, &string_val, "string", 's', 0},
{ NOTMUCH_OPT_POSITION, &pos_arg1, 0,0, 0},
@@ -31,6 +37,9 @@ int main(int argc, char **argv){
 if (kw_val)
printf("keyword %d\n", kw_val);
 
+if (fl_val)
+   printf("flags %d\n", fl_val);
+
 if (int_val)
printf("int %d\n", int_val);
 
-- 
2.1.1

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v6 6/7] cli: search: Add --output=count

2014-10-31 Thread Michal Sojka
This output can be used with --output=recipients or --output=sender
and in addition to the addresses, it prints how many times was each
address encountered during search.
---
 completion/notmuch-completion.bash |  2 +-
 completion/notmuch-completion.zsh  |  2 +-
 doc/man1/notmuch-search.rst|  9 +--
 notmuch-search.c   | 51 --
 4 files changed, 52 insertions(+), 12 deletions(-)

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index cfbd389..39cd829 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -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 
sender recipients count" -- "${cur}" ) )
return
;;
--sort)
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index 3e52a00..d7e5a5e 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -53,7 +53,7 @@ _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 sender recipients count))'
 }
 
 _notmuch()
diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
index 42f17e4..ec89200 100644
--- a/doc/man1/notmuch-search.rst
+++ b/doc/man1/notmuch-search.rst
@@ -96,9 +96,14 @@ Supported options for **search** include
 Like **sender** but for addresses from *To*, *Cc* and
*Bcc* headers.
 
+   **count**
+   Can be used in combination with **sender** or
+   **recipients** to print the count of how many times was
+   the address encountered during search.
+
This option can be given multiple times to combine different
-   outputs. Currently, this is only supported for **sender** and
-   **recipients** outputs.
+   outputs. Currently, this is only supported for **sender**,
+   **recipients** and **count** outputs.
 
 ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
 This option can be used to present results in either
diff --git a/notmuch-search.c b/notmuch-search.c
index 43d42c6..4b39dfc 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -30,9 +30,10 @@ typedef enum {
 OUTPUT_TAGS= 1 << 4,
 OUTPUT_SENDER  = 1 << 5,
 OUTPUT_RECIPIENTS  = 1 << 6,
+OUTPUT_COUNT   = 1 << 7,
 } output_t;
 
-#define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS)
+#define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS | OUTPUT_COUNT)
 
 typedef struct {
 sprinter_t *format;
@@ -47,6 +48,7 @@ typedef struct {
 typedef struct {
 const char *name;
 const char *addr;
+int count;
 } mailbox_t;
 
 /* Return two stable query strings that identify exactly the matched
@@ -235,17 +237,24 @@ is_duplicate (const search_options_t *opt, GHashTable 
*addrs, const char *name,
 {
 notmuch_bool_t duplicate;
 char *key;
+mailbox_t *mailbox;
 
 key = talloc_asprintf (opt->format, "%s <%s>", name, addr);
 if (! key)
return FALSE;
 
-duplicate = g_hash_table_lookup_extended (addrs, key, NULL, NULL);
+duplicate = g_hash_table_lookup_extended (addrs, key, NULL, 
(gpointer)&mailbox);
 
-if (! duplicate)
-   g_hash_table_insert (addrs, key, NULL);
-else
+if (! duplicate) {
+   mailbox = talloc (opt->format, mailbox_t);
+   mailbox->name = talloc_strdup (mailbox, name);
+   mailbox->addr = talloc_strdup (mailbox, addr);
+   mailbox->count = 1;
+   g_hash_table_insert (addrs, key, mailbox);
+} else {
+   mailbox->count++;
talloc_free (key);
+}
 
 return duplicate;
 }
@@ -255,6 +264,7 @@ print_mailbox (const search_options_t *opt, const mailbox_t 
*mailbox)
 {
 const char *name = mailbox->name;
 const char *addr = mailbox->addr;
+int count = mailbox->count;
 sprinter_t *format = opt->format;
 InternetAddress *ia = internet_address_mailbox_new (name, addr);
 char *name_addr;
@@ -264,6 +274,10 @@ print_mailbox (const search_options_t *opt, const 
mailbox_t *mailbox)
 name_addr = internet_address_to_string (ia, FALSE);
 
 if (format->is_text_printer) {
+   if (count > 0) {
+   format->integer (format, count);
+   format->string (format, "\t");
+   }
forma

[PATCH v6 3/7] cli: search: Convert --output to keyword-flag argument

2014-10-31 Thread Michal Sojka
This converts "notmuch search" to use the recently introduced
keyword-flag argument parser. At this point, it only makes the code
slightly less readable but following commits that add new --output
keywords will profit from this.
---
 notmuch-search.c | 35 ++-
 1 file changed, 18 insertions(+), 17 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 0c3e972..ce46877 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -23,11 +23,11 @@
 #include "string-util.h"
 
 typedef enum {
-OUTPUT_SUMMARY,
-OUTPUT_THREADS,
-OUTPUT_MESSAGES,
-OUTPUT_FILES,
-OUTPUT_TAGS
+OUTPUT_SUMMARY = 1 << 0,
+OUTPUT_THREADS = 1 << 1,
+OUTPUT_MESSAGES= 1 << 2,
+OUTPUT_FILES   = 1 << 3,
+OUTPUT_TAGS= 1 << 4,
 } output_t;
 
 typedef struct {
@@ -338,7 +338,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
 notmuch_database_t *notmuch;
 search_options_t opt = {
.sort = NOTMUCH_SORT_NEWEST_FIRST,
-   .output = OUTPUT_SUMMARY,
+   .output = 0,
.offset = 0,
.limit = -1, /* unlimited */
.dupe = -1,
@@ -367,7 +367,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
  { "text0", NOTMUCH_FORMAT_TEXT0 },
  { 0, 0 } } },
{ NOTMUCH_OPT_INT, ¬much_format_version, "format-version", 0, 0 },
-   { NOTMUCH_OPT_KEYWORD, &opt.output, "output", 'o',
+   { NOTMUCH_OPT_KEYWORD_FLAGS, &opt.output, "output", 'o',
  (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },
  { "threads", OUTPUT_THREADS },
  { "messages", OUTPUT_MESSAGES },
@@ -390,6 +390,9 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
 if (opt_index < 0)
return EXIT_FAILURE;
 
+if (! opt.output)
+   opt.output = OUTPUT_SUMMARY;
+
 switch (format_sel) {
 case NOTMUCH_FORMAT_TEXT:
opt.format = sprinter_text_create (config, stdout);
@@ -455,19 +458,17 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
notmuch_query_set_omit_excluded (opt.query, exclude);
 }
 
-switch (opt.output) {
-default:
-case OUTPUT_SUMMARY:
-case OUTPUT_THREADS:
+if (opt.output == OUTPUT_SUMMARY ||
+   opt.output == OUTPUT_THREADS)
ret = do_search_threads (&opt);
-   break;
-case OUTPUT_MESSAGES:
-case OUTPUT_FILES:
+else if (opt.output == OUTPUT_MESSAGES ||
+opt.output == OUTPUT_FILES)
ret = do_search_messages (&opt);
-   break;
-case OUTPUT_TAGS:
+else if (opt.output == OUTPUT_TAGS)
ret = do_search_tags (notmuch, &opt);
-   break;
+else {
+   fprintf (stderr, "Error: the combination of outputs is not 
supported.\n");
+   ret = 1;
 }
 
 notmuch_query_destroy (opt.query);
-- 
2.1.1

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v6 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Michal Sojka
Hi all,

this is v6 of the search --output=address series. It obsoletes v5
(id:1414713573-21461-1-git-send-email-sojk...@fel.cvut.cz).

Changes from v5 (full diff below):

- Added quoting of name parts if that is necessary (pointed out by
  Mark Walters). Structured formats contain both full address
  (possibly with quoted name) and unquoted individual fields.
- Fixed bug in --output=count --filter-by=*fold (reported by Jesse
  Rosenthal). New test was added for this case. Fixing the bug also
  resulted in simpler code :)
- Added missing unreferencing of InternetAddressList.

Changes from v4:

- patch changed to commit in commit messages
- opt->format changed to format
- Added comments to process_* functions
- duplicite changed to duplicate
- check_duplicate changed to is_duplicate
- Deduplication was split into two commits: basic deduplication
  without a command line option and configurable deduplication with
  --fiter-by.

Changes from v3:

- `o' renamed to `opt'.
- Conversion of --output from keyword to keyword-flags is now a
  separate patch.
- Structured output formats print name and address separately.
- Added test for --format=json.
- Changed --filter-by default to nameaddr. In v2, the default was
  addrfold, in v3 the default was no filtering at all. I believe that
  Mark's suggestion to make nameaddr the default is good trade off.
- Added new --output=count
- Minor style fixes
- Few typos fixed
- There is no way to output unfiltered (duplicite) addresses.
  Hopefully, the introduction of --output=count is sufficient
  replacement for this "feature".

Cheers,
-Michal


Jani Nikula (1):
  cli: Add support for parsing keyword-flag arguments

Michal Sojka (6):
  cli: search: Refactor passing of command line options
  cli: search: Convert --output to keyword-flag argument
  cli: search: Add --output={sender,recipients}
  cli: search: Do not output duplicate addresses
  cli: search: Add --output=count
  cli: search: Add --filter-by option to configure address filtering

 command-line-arguments.c   |   6 +-
 command-line-arguments.h   |   1 +
 completion/notmuch-completion.bash |   8 +-
 completion/notmuch-completion.zsh  |   4 +-
 doc/man1/notmuch-search.rst|  66 ++-
 notmuch-search.c   | 390 +
 test/T090-search-output.sh |  87 +
 test/T095-search-filter-by.sh  |  73 +++
 test/T410-argument-parsing.sh  |   3 +-
 test/arg-test.c|   9 +
 10 files changed, 565 insertions(+), 82 deletions(-)
 create mode 100755 test/T095-search-filter-by.sh

-- 
2.1.1

diff --git a/notmuch-search.c b/notmuch-search.c
index 8bc80d3..a350f06 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -246,33 +246,35 @@ is_duplicate (const search_options_t *opt, GHashTable 
*addrs, const char *name,
 {
 notmuch_bool_t duplicate;
 char *key;
+gchar *addrfold = NULL;
 mailbox_t *mailbox;
 
 if (opt->filter_by == FILTER_BY_ADDRFOLD ||
-   opt->filter_by == FILTER_BY_NAMEADDRFOLD) {
-   gchar *folded = g_utf8_casefold (addr, -1);
-   addr = talloc_strdup (opt->format, folded);
-   g_free (folded);
-}
+   opt->filter_by == FILTER_BY_NAMEADDRFOLD)
+   addrfold = g_utf8_casefold (addr, -1);
+
 switch (opt->filter_by) {
 case FILTER_BY_NAMEADDR:
-case FILTER_BY_NAMEADDRFOLD:
key = talloc_asprintf (opt->format, "%s <%s>", name, addr);
break;
+case FILTER_BY_NAMEADDRFOLD:
+   key = talloc_asprintf (opt->format, "%s <%s>", name, addrfold);
+   break;
 case FILTER_BY_NAME:
key = talloc_strdup (opt->format, name); /* !name results in !key */
break;
 case FILTER_BY_ADDR:
-case FILTER_BY_ADDRFOLD:
key = talloc_strdup (opt->format, addr);
break;
+case FILTER_BY_ADDRFOLD:
+   key = talloc_strdup (opt->format, addrfold);
+   break;
 default:
INTERNAL_ERROR("invalid --filter-by flags");
 }
 
-if (opt->filter_by == FILTER_BY_ADDRFOLD ||
-   opt->filter_by == FILTER_BY_NAMEADDRFOLD)
-   talloc_free ((char*)addr);
+if (addrfold)
+   g_free (addrfold);
 
 if (! key)
return FALSE;
@@ -300,33 +302,28 @@ print_mailbox (const search_options_t *opt, const 
mailbox_t *mailbox)
 const char *addr = mailbox->addr;
 int count = mailbox->count;
 sprinter_t *format = opt->format;
+InternetAddress *ia = internet_address_mailbox_new (name, addr);
+char *name_addr;
 
-if (format->is_text_printer) {
-   char *mailbox_str;
+/* name_addr has the name part quoted if necessary. Compare
+ * 'John Doe ' vs. '"Doe, John" ' */
+name_addr = internet_address_to_string (ia, FALSE);
 
-   if (name && *name)
-   mailbox_str = talloc_asprintf (format, "%s <%s>", name, addr);
-   else
-   mailbox_str = talloc_strdup (format, addr);
-
-   if (! mailbox_str) {
-   fprintf (stderr, "Error: out of 

[PATCH v6 4/7] cli: search: Add --output={sender,recipients}

2014-10-31 Thread Michal Sojka
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 commit will change this. For this reason, tests are
introduced there.

We use mailbox_t rather than InternetAddressMailbox because we will
need to extend it in a following commit.

This code is based on a patch from Jani Nikula.
---
 completion/notmuch-completion.bash |   2 +-
 completion/notmuch-completion.zsh  |   3 +-
 doc/man1/notmuch-search.rst|  22 ++-
 notmuch-search.c   | 115 -
 4 files changed, 137 insertions(+), 5 deletions(-)

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index 0571dc9..cfbd389 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -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)
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index 67a9aba..3e52a00 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -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()
diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
index 90160f2..b6607c9 100644
--- a/doc/man1/notmuch-search.rst
+++ b/doc/man1/notmuch-search.rst
@@ -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
diff --git a/notmuch-search.c b/notmuch-search.c
index ce46877..671fe41 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -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,87 @@ do_search_threads (search_options_t *opt)
 return 0;
 }
 
+static void
+print_mailbox (const search_options_t *opt, const 

[PATCH v6 1/7] cli: search: Refactor passing of command line options

2014-10-31 Thread Michal Sojka
Many functions that implement the search command need to access command
line options. Instead of passing each option in a separate variable, put
them in a structure and pass only this structure.

This will become handy in the following commits.
---
 notmuch-search.c | 125 ---
 1 file changed, 64 insertions(+), 61 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index bc9be45..0c3e972 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -30,6 +30,16 @@ typedef enum {
 OUTPUT_TAGS
 } output_t;
 
+typedef struct {
+sprinter_t *format;
+notmuch_query_t *query;
+notmuch_sort_t sort;
+output_t output;
+int offset;
+int limit;
+int dupe;
+} search_options_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
@@ -70,43 +80,39 @@ get_thread_query (notmuch_thread_t *thread,
 }
 
 static int
-do_search_threads (sprinter_t *format,
-  notmuch_query_t *query,
-  notmuch_sort_t sort,
-  output_t output,
-  int offset,
-  int limit)
+do_search_threads (search_options_t *opt)
 {
 notmuch_thread_t *thread;
 notmuch_threads_t *threads;
 notmuch_tags_t *tags;
+sprinter_t *format = opt->format;
 time_t date;
 int i;
 
-if (offset < 0) {
-   offset += notmuch_query_count_threads (query);
-   if (offset < 0)
-   offset = 0;
+if (opt->offset < 0) {
+   opt->offset += notmuch_query_count_threads (opt->query);
+   if (opt->offset < 0)
+   opt->offset = 0;
 }
 
-threads = notmuch_query_search_threads (query);
+threads = notmuch_query_search_threads (opt->query);
 if (threads == NULL)
return 1;
 
 format->begin_list (format);
 
 for (i = 0;
-notmuch_threads_valid (threads) && (limit < 0 || i < offset + limit);
+notmuch_threads_valid (threads) && (opt->limit < 0 || i < opt->offset 
+ opt->limit);
 notmuch_threads_move_to_next (threads), i++)
 {
thread = notmuch_threads_get (threads);
 
-   if (i < offset) {
+   if (i < opt->offset) {
notmuch_thread_destroy (thread);
continue;
}
 
-   if (output == OUTPUT_THREADS) {
+   if (opt->output == OUTPUT_THREADS) {
format->set_prefix (format, "thread");
format->string (format,
notmuch_thread_get_thread_id (thread));
@@ -123,7 +129,7 @@ do_search_threads (sprinter_t *format,
 
format->begin_map (format);
 
-   if (sort == NOTMUCH_SORT_OLDEST_FIRST)
+   if (opt->sort == NOTMUCH_SORT_OLDEST_FIRST)
date = notmuch_thread_get_oldest_date (thread);
else
date = notmuch_thread_get_newest_date (thread);
@@ -215,40 +221,36 @@ do_search_threads (sprinter_t *format,
 }
 
 static int
-do_search_messages (sprinter_t *format,
-   notmuch_query_t *query,
-   output_t output,
-   int offset,
-   int limit,
-   int dupe)
+do_search_messages (search_options_t *opt)
 {
 notmuch_message_t *message;
 notmuch_messages_t *messages;
 notmuch_filenames_t *filenames;
+sprinter_t *format = opt->format;
 int i;
 
-if (offset < 0) {
-   offset += notmuch_query_count_messages (query);
-   if (offset < 0)
-   offset = 0;
+if (opt->offset < 0) {
+   opt->offset += notmuch_query_count_messages (opt->query);
+   if (opt->offset < 0)
+   opt->offset = 0;
 }
 
-messages = notmuch_query_search_messages (query);
+messages = notmuch_query_search_messages (opt->query);
 if (messages == NULL)
return 1;
 
 format->begin_list (format);
 
 for (i = 0;
-notmuch_messages_valid (messages) && (limit < 0 || i < offset + limit);
+notmuch_messages_valid (messages) && (opt->limit < 0 || i < 
opt->offset + opt->limit);
 notmuch_messages_move_to_next (messages), i++)
 {
-   if (i < offset)
+   if (i < opt->offset)
continue;
 
message = notmuch_messages_get (messages);
 
-   if (output == OUTPUT_FILES) {
+   if (opt->output == OUTPUT_FILES) {
int j;
filenames = notmuch_message_get_filenames (message);
 
@@ -256,7 +258,7 @@ do_search_messages (sprinter_t *format,
 notmuch_filenames_valid (filenames);
 notmuch_filenames_move_to_next (filenames), j++)
{
-   if (dupe < 0 || dupe == j) {
+   if (opt->dupe < 0 || opt->dupe == j) {
format->string (format, notmuch_filenames_get (filenames));
format->separator (format);
}
@@ -283,12 +285,13 @@ do_search_messa

[PATCH] test: Make gen-threads work with python3

2014-10-31 Thread Jesse Rosenthal
"W. Trevor King"  writes:

> On Fri, Oct 31, 2014 at 01:33:25PM -0400, Jesse Rosenthal wrote:
>> We instead initalize the dictionary using the dict comprehension and
>> then update it with the values from the tree. This will work with
>> both python2 and python3.
>
> Dict comprehensions are new in 2.7 [1,2], so this drops support for
> systems where ?python? means ?python2.6?.  Personally, I'm fine with
> that, but I thought I'd point it out in case 2.6 users wanted to push
> back ;).

The comprehension was already in the previous version, so I figured that
people were already cool with 2.7+.

>> -
>>  import sys
>
> Why remove this blank line?

Oops -- I had put in a print_function import from __future__ for
testing. Must have lost the line when I took it back out. Is it worth
resubmitting to fix that?


[PATCH] test: Make gen-threads work with python3

2014-10-31 Thread Jesse Rosenthal
python3 doesn't allow dictionaries to be initialized with non-string
keywords. This presents problems on systems in which "python" means
"python3". We instead initalize the dictionary using the dict
comprehension and then update it with the values from the tree. This
will work with both python2 and python3.
---
 test/gen-threads.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/gen-threads.py b/test/gen-threads.py
index 9fbb847..70fb1f6 100644
--- a/test/gen-threads.py
+++ b/test/gen-threads.py
@@ -2,7 +2,6 @@
 # argv[1].  Each output line is a thread structure, where the n'th
 # field is either a number giving the parent of message n or "None"
 # for the root.
-
 import sys
 from itertools import chain, combinations

@@ -28,6 +27,7 @@ while queue:
 else:
 # Expand node to_expand[0] with each possible set of children
 for children in subsets(free):
-ntree = dict(tree, **{child: to_expand[0] for child in children})
+ntree = {child: to_expand[0] for child in children}
+ntree.update(tree)
 nfree = free.difference(children)
 queue.append((ntree, nfree, to_expand[1:] + tuple(children)))
-- 
2.1.2



Re: [PATCH] test: Make gen-threads work with python3

2014-10-31 Thread Jesse Rosenthal
"W. Trevor King"  writes:

> On Fri, Oct 31, 2014 at 01:33:25PM -0400, Jesse Rosenthal wrote:
>> We instead initalize the dictionary using the dict comprehension and
>> then update it with the values from the tree. This will work with
>> both python2 and python3.
>
> Dict comprehensions are new in 2.7 [1,2], so this drops support for
> systems where ‘python’ means ‘python2.6’.  Personally, I'm fine with
> that, but I thought I'd point it out in case 2.6 users wanted to push
> back ;).

The comprehension was already in the previous version, so I figured that
people were already cool with 2.7+.

>> -
>>  import sys
>
> Why remove this blank line?

Oops -- I had put in a print_function import from __future__ for
testing. Must have lost the line when I took it back out. Is it worth
resubmitting to fix that?
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] test: Make gen-threads work with python3

2014-10-31 Thread W. Trevor King
On Fri, Oct 31, 2014 at 02:04:50PM -0400, Jesse Rosenthal wrote:
> W. Trevor King writes:
> > On Fri, Oct 31, 2014 at 01:33:25PM -0400, Jesse Rosenthal wrote:
> >> We instead initalize the dictionary using the dict comprehension
> >> and then update it with the values from the tree. This will work
> >> with both python2 and python3.
> >
> > Dict comprehensions are new in 2.7 [1,2], so this drops support
> > for systems where ‘python’ means ‘python2.6’.  Personally, I'm
> > fine with that, but I thought I'd point it out in case 2.6 users
> > wanted to push back ;).
> 
> The comprehension was already in the previous version, so I figured that
> people were already cool with 2.7+.

Ah, good point :).  Besides looking good to me, I can confirm that I
see a TypeError (and a lot of CPU usage ;) from T260-thread-order.sh
before this patch which is fixed by this patch.

Cheers,
Trevor

-- 
This email may be signed or encrypted with GnuPG (http://www.gnupg.org).
For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy


signature.asc
Description: OpenPGP digital signature
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] test: Make gen-threads work with python3

2014-10-31 Thread W. Trevor King
On Fri, Oct 31, 2014 at 02:04:50PM -0400, Jesse Rosenthal wrote:
> W. Trevor King writes:
> > On Fri, Oct 31, 2014 at 01:33:25PM -0400, Jesse Rosenthal wrote:
> >> We instead initalize the dictionary using the dict comprehension
> >> and then update it with the values from the tree. This will work
> >> with both python2 and python3.
> >
> > Dict comprehensions are new in 2.7 [1,2], so this drops support
> > for systems where ?python? means ?python2.6?.  Personally, I'm
> > fine with that, but I thought I'd point it out in case 2.6 users
> > wanted to push back ;).
> 
> The comprehension was already in the previous version, so I figured that
> people were already cool with 2.7+.

Ah, good point :).  Besides looking good to me, I can confirm that I
see a TypeError (and a lot of CPU usage ;) from T260-thread-order.sh
before this patch which is fixed by this patch.

Cheers,
Trevor

-- 
This email may be signed or encrypted with GnuPG (http://www.gnupg.org).
For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy
-- next part --
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: OpenPGP digital signature
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20141031/4d4eaff3/attachment-0001.pgp>


[PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Jesse Rosenthal
Dear Michal,

Thanks for all this. The feature looks great! One issue: I'm getting
corrupt output when using `--output=count` with "fold" filters:

~~~
jkr at ladybug [11:01AM] ~ $ notmuch search --output=recipients --output=count 
"tag:notmuch and date:today.." 
2   Jani Nikula 
2   Michal Sojka 
1   David Edmondson 
9   notmuch at notmuchmail.org

jkr at ladybug [11:01AM] ~ $ notmuch search --output=recipients --output=count 
--filter-by=addrfold "tag:notmuch and date:today.."
2   Michal Sojka <0Fam1 at fel.cvutH>
9   
1   David Edmondson <8dme.org>
2   Jani Nikula 

jkr at ladybug [11:01AM] ~ $ notmuch search --output=recipients --output=count 
--filter-by=nameaddr "tag:notmuch and date:today.."
2   Jani Nikula 
2   Michal Sojka 
1   David Edmondson 
9   notmuch at notmuchmail.org

jkr at ladybug [11:01AM] ~ $ notmuch search --output=recipients --output=count 
--filter-by=nameaddrfold "tag:notmuch and date:today.."
2   Jani Nikula 
2   Michal Sojka <0V?   am1 at fel.cvutH>
1   David Edmondson <8??dme.org>
9
~~~

Not sure if this is expected behavior with the filtering mechanism in
its current state, but I wanted to call it to your attention if not.

Best,
Jesse


Re: [PATCH] test: Make gen-threads work with python3

2014-10-31 Thread W. Trevor King
On Fri, Oct 31, 2014 at 01:33:25PM -0400, Jesse Rosenthal wrote:
> We instead initalize the dictionary using the dict comprehension and
> then update it with the values from the tree. This will work with
> both python2 and python3.

Dict comprehensions are new in 2.7 [1,2], so this drops support for
systems where ‘python’ means ‘python2.6’.  Personally, I'm fine with
that, but I thought I'd point it out in case 2.6 users wanted to push
back ;).

> diff --git a/test/gen-threads.py b/test/gen-threads.py
> index 9fbb847..70fb1f6 100644
> --- a/test/gen-threads.py
> +++ b/test/gen-threads.py
> @@ -2,7 +2,6 @@
>  # argv[1].  Each output line is a thread structure, where the n'th
>  # field is either a number giving the parent of message n or "None"
>  # for the root.
> -
>  import sys

Why remove this blank line?

>  from itertools import chain, combinations
>  
> @@ -28,6 +27,7 @@ while queue:
>  else:
>  # Expand node to_expand[0] with each possible set of children
>  for children in subsets(free):
> -ntree = dict(tree, **{child: to_expand[0] for child in children})
> +ntree = {child: to_expand[0] for child in children}
> +ntree.update(tree)

This looks good to me.

Cheers,
Trevor

[1]: https://docs.python.org/3/whatsnew/2.7.html#other-language-changes
[2]: http://legacy.python.org/dev/peps/pep-0274/

-- 
This email may be signed or encrypted with GnuPG (http://www.gnupg.org).
For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy


signature.asc
Description: OpenPGP digital signature
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] test: Make gen-threads work with python3

2014-10-31 Thread W. Trevor King
On Fri, Oct 31, 2014 at 01:33:25PM -0400, Jesse Rosenthal wrote:
> We instead initalize the dictionary using the dict comprehension and
> then update it with the values from the tree. This will work with
> both python2 and python3.

Dict comprehensions are new in 2.7 [1,2], so this drops support for
systems where ?python? means ?python2.6?.  Personally, I'm fine with
that, but I thought I'd point it out in case 2.6 users wanted to push
back ;).

> diff --git a/test/gen-threads.py b/test/gen-threads.py
> index 9fbb847..70fb1f6 100644
> --- a/test/gen-threads.py
> +++ b/test/gen-threads.py
> @@ -2,7 +2,6 @@
>  # argv[1].  Each output line is a thread structure, where the n'th
>  # field is either a number giving the parent of message n or "None"
>  # for the root.
> -
>  import sys

Why remove this blank line?

>  from itertools import chain, combinations
>  
> @@ -28,6 +27,7 @@ while queue:
>  else:
>  # Expand node to_expand[0] with each possible set of children
>  for children in subsets(free):
> -ntree = dict(tree, **{child: to_expand[0] for child in children})
> +ntree = {child: to_expand[0] for child in children}
> +ntree.update(tree)

This looks good to me.

Cheers,
Trevor

[1]: https://docs.python.org/3/whatsnew/2.7.html#other-language-changes
[2]: http://legacy.python.org/dev/peps/pep-0274/

-- 
This email may be signed or encrypted with GnuPG (http://www.gnupg.org).
For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy
-- next part --
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: OpenPGP digital signature
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20141031/bb8e1c9b/attachment.pgp>


[PATCH] test: Make gen-threads work with python3

2014-10-31 Thread Jesse Rosenthal
python3 doesn't allow dictionaries to be initialized with non-string
keywords. This presents problems on systems in which "python" means
"python3". We instead initalize the dictionary using the dict
comprehension and then update it with the values from the tree. This
will work with both python2 and python3.
---
 test/gen-threads.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/gen-threads.py b/test/gen-threads.py
index 9fbb847..70fb1f6 100644
--- a/test/gen-threads.py
+++ b/test/gen-threads.py
@@ -2,7 +2,6 @@
 # argv[1].  Each output line is a thread structure, where the n'th
 # field is either a number giving the parent of message n or "None"
 # for the root.
-
 import sys
 from itertools import chain, combinations
 
@@ -28,6 +27,7 @@ while queue:
 else:
 # Expand node to_expand[0] with each possible set of children
 for children in subsets(free):
-ntree = dict(tree, **{child: to_expand[0] for child in children})
+ntree = {child: to_expand[0] for child in children}
+ntree.update(tree)
 nfree = free.difference(children)
 queue.append((ntree, nfree, to_expand[1:] + tuple(children)))
-- 
2.1.2

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v1] NEWS: Improved `q` binding.

2014-10-31 Thread David Edmondson
---
 NEWS | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/NEWS b/NEWS
index 4718838..723bca0 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,12 @@ Expanded default saved search settings
   The default saved searches now include several more common searches,
   as well as shortcut keys for `notmuch-jump`.
 
+Improved `q` binding in notmuch buffers
+
+  `q` will now bury rather than kill a notmuch search, show or tree
+  buffer if there are multiple windows showing the buffer. If only a
+  single window is showing the buffer, it is killed.
+
 Library changes
 ---
 
-- 
1.9.3 (Apple Git-50)

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Mark Walters

Hi

I attach a patch which does the quoting for real names but I don't know
if we want it: it changes (example taken from the test suite)

Fran?ois Boulogne to

=?iso-8859-1?q?Fran=E7ois?= Boulogne

(which is what was in the original email)

Plausibly the best thing is just to leave the series as is, so the
text output is readable and parseable in the common cases.

Anyway the patch is attached if anyone wants to experiment.

Best wishes

Mark

-- next part --
A non-text attachment was scrubbed...
Name: 0001-search-quote-real-names-for-output-sender-recipient-.patch
Type: text/x-diff
Size: 3436 bytes
Desc: not available
URL: 
<http://notmuchmail.org/pipermail/notmuch/attachments/20141031/2f486f4f/attachment-0001.patch>


[RFC] [PATCH v1] emacs: More citation washing.

2014-10-31 Thread David Edmondson
Ensure that a citation is preceded and followed by a blank line,
unless the preceding line looks like a citation leader.
---

Washing changes are always a bit risky, as they are heuristic
based. Testers for this (alleged) improvement welcome!

 emacs/notmuch-wash.el | 14 +-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/emacs/notmuch-wash.el b/emacs/notmuch-wash.el
index 8fe91e1..83621bb 100644
--- a/emacs/notmuch-wash.el
+++ b/emacs/notmuch-wash.el
@@ -276,7 +276,19 @@ Perform several transformations on the message body:
   ;; text.
   (goto-char (point-min))
   (while (re-search-forward "\\(^>[> ]*\n\\)\\(^$\\|^[^>].*\\)" nil t)
-(replace-match "\\2")))
+(replace-match "\\2"))
+
+  ;; Ensure that a citation block has a blank line before it, unless
+  ;; the previous line ends in ':' (because that's probably the
+  ;; citation).
+  (goto-char (point-min))
+  (while (re-search-forward "^[^>].*[^:]\\(\n\\)>.*" nil t)
+(replace-match "\n\n" nil nil nil 1))
+
+  ;; Ensure that a citation block has a blank line after it.
+  (goto-char (point-min))
+  (while (re-search-forward "^>.*\\(\n\\)[^>].*" nil t)
+(replace-match "\n\n" nil nil nil 1)))

 ;;

-- 
1.9.3 (Apple Git-50)



Re: [PATCH] test: use LDFLAGS in test/Makefile.local

2014-10-31 Thread David Bremner
Jani Nikula  writes:

> Apparently the test binaries are built with minimal LDFLAGS, only
> adding dependency specific LDFLAGS as needed. However because some of
> the test binaries incorporate notmuch object files, it is necessary to
> use the same link flags as notmuch. For example user provided
> CFLAGS/CXXFLAGS/LDFLAGS with -fsanitize=undefined fails to build the
> test binaries if the flags differ.
>
> ---

pushed

d
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v2] emacs: Improve the behaviour of the 'q' binding.

2014-10-31 Thread David Bremner
David Edmondson  writes:

> When a user hits 'q' in a notmuch buffer, kill the buffer only if
> there are no other windows currently showing it.

pushed. That probably deserves a NEWS patch.

d
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Mark Walters

On Fri, 31 Oct 2014, Tomi Ollila  wrote:
> On Fri, Oct 31 2014, Michal Sojka  wrote:
>
>> On Fri, Oct 31 2014, Mark Walters wrote:
>>> My only query is in the text output: should the name part be printed as
>>> a quoted string. For example currently I get a line of the form
>>>
>>> Bloggs, Fred 
>>
>> Good point.
>
> There has been some discussion on this issue on IRC channel, and the
> opinion of most (seen so far) is that output the parts without quoting
> (i.e. just like done in this patch series)...

Just to confirm I am happy either way with the quoting. I think it would
be good not to change the text format after it goes into mainline
though. 

Best wishes

Mark





>
> Taken the other example from Mark's earlyer email:
>
> Bloggs , Fred 
>
> echo '^^^' | sed 's/.*:s) (*)
>
> echo '^^^' | sed 's/ <[^<]*$//' would leave only the name part
>
> and regexp '\(.*\) <\(.*\)>' or pcre-compatible /(.*)\s<(.*)>/
> would capture name & address parts...
>
> (all of the above untested, though;)
>
> In case instead of 'name ' there is only 'addr', then above
> sed lines return the same (full) string and the regexps just don't
> match.
>
> There were some suggestions how the text output could be changed on IRC;
> if anyone wishes to bring those forward, please do so :D
>
> So, IMO, this issue is not showstopper in this series (if anything is);
> patches 1-6 looks good to me on paper, but I have not tested those yet.
>
> Tomi
>
> (*) echo '^^^' | sed 's/.*.*//' would drop <>'s from first example
>
>
>>
>> On Fri, Oct 31 2014, Mark Walters wrote:
>>> Hi
>>>
>>> I attach a patch which does the quoting for real names but I don't know
>>> if we want it: it changes (example taken from the test suite)
>>>
>>> François Boulogne to
>>>
>>> =?iso-8859-1?q?Fran=E7ois?= Boulogne
>>>
>>> (which is what was in the original email)
>>>
>>> Plausibly the best thing is just to leave the series as is, so the
>>> text output is readable and parseable in the common cases.
>>>
>>> Anyway the patch is attached if anyone wants to experiment.
>>>
>>> Best wishes
>>>
>>> Mark
>>>
>>> From 53b1ced2d6a9fbbba93448325f795e6b99faa240 Mon Sep 17 00:00:00 2001
>>> From: Mark Walters 
>>> Date: Fri, 31 Oct 2014 10:11:40 +
>>> Subject: [PATCH] search: quote real names for output=sender/recipient in 
>>> text
>>>  format
>>>
>>> This quotes the real name (when gmime thinks appropriate) for the text
>>> output. For the other outputs the real name is separate from the
>>> address so the consumer can do any quoting necessary.
>>> ---
>>>  notmuch-search.c   |8 
>>>  test/T090-search-output.sh |8 
>>>  2 files changed, 8 insertions(+), 8 deletions(-)
>>>
>>> diff --git a/notmuch-search.c b/notmuch-search.c
>>> index eae749a..8eac161 100644
>>> --- a/notmuch-search.c
>>> +++ b/notmuch-search.c
>>> @@ -47,6 +47,7 @@ typedef struct {
>>>  typedef struct {
>>>  const char *name;
>>>  const char *addr;
>>> +const char *string;
>>>  } mailbox_t;
>>>  
>>>  /* Return two stable query strings that identify exactly the matched
>>> @@ -255,15 +256,13 @@ print_mailbox (const search_options_t *opt, const 
>>> mailbox_t *mailbox)
>>>  {
>>>  const char *name = mailbox->name;
>>>  const char *addr = mailbox->addr;
>>> +const char *string = mailbox->string;
>>>  sprinter_t *format = opt->format;
>>>  
>>>  if (format->is_text_printer) {
>>> char *mailbox_str;
>>>  
>>> -   if (name && *name)
>>> -   mailbox_str = talloc_asprintf (format, "%s <%s>", name, addr);
>>> -   else
>>> -   mailbox_str = talloc_strdup (format, addr);
>>> +   mailbox_str = talloc_strdup (format, string);
>>>  
>>> if (! mailbox_str) {
>>> fprintf (stderr, "Error: out of memory\n");
>>> @@ -309,6 +308,7 @@ process_address_list (const search_options_t *opt, 
>>> GHashTable *addrs,
>>> mailbox_t mbx = {
>>> .name = internet_address_get_name (address),
>>> .addr = internet_address_mailbox_get_addr (mailbox),
>>> +   .string = internet_address_to_string (address, TRUE),
>>
>> I'd prefer having the second parameter (encode) FALSE. This will still
>> add quotes when necessary, but does not encode non-ascii characters so
>> the result would be human readable.
>>
>> Another question is whether to add .string to mailbox_t. In this patch
>> it doesn't matter, but if --output=count patch will be merged, this
>> would mean that memory consumption doubles, because with --output=count
>> the addresses are kept in memory and printed only after the search is
>> completed. It would be therefore better to construct a new
>> InternetAddressMailbox from name and addr in print_mailbox() and perform
>> the conversion to string there. Thoughts?
>>
>> Thanks,
>> -Michal
>>
>>> };
>>>  
>>> if (is_duplicate (opt, addrs, mbx.name, mbx.addr))
>>> diff --git a/test/T090-search-output.sh b/test/T090-search-output.sh
>>> index 841a721..776e3f4 100755
>>> --- a/test/T090-search-

Re: [PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Mark Walters

Hi

On Fri, 31 Oct 2014, Michal Sojka  wrote:
> On Fri, Oct 31 2014, Mark Walters wrote:
>> My only query is in the text output: should the name part be printed as
>> a quoted string. For example currently I get a line of the form
>>
>> Bloggs, Fred 
>
> Good point.
>
> On Fri, Oct 31 2014, Mark Walters wrote:
>> Hi
>>
>> I attach a patch which does the quoting for real names but I don't know
>> if we want it: it changes (example taken from the test suite)
>>
>> François Boulogne to
>>
>> =?iso-8859-1?q?Fran=E7ois?= Boulogne
>>
>> (which is what was in the original email)
>>
>> Plausibly the best thing is just to leave the series as is, so the
>> text output is readable and parseable in the common cases.
>>
>> Anyway the patch is attached if anyone wants to experiment.
>>
>> Best wishes
>>
>> Mark
>>
>> From 53b1ced2d6a9fbbba93448325f795e6b99faa240 Mon Sep 17 00:00:00 2001
>> From: Mark Walters 
>> Date: Fri, 31 Oct 2014 10:11:40 +
>> Subject: [PATCH] search: quote real names for output=sender/recipient in text
>>  format
>>
>> This quotes the real name (when gmime thinks appropriate) for the text
>> output. For the other outputs the real name is separate from the
>> address so the consumer can do any quoting necessary.
>> ---
>>  notmuch-search.c   |8 
>>  test/T090-search-output.sh |8 
>>  2 files changed, 8 insertions(+), 8 deletions(-)
>>
>> diff --git a/notmuch-search.c b/notmuch-search.c
>> index eae749a..8eac161 100644
>> --- a/notmuch-search.c
>> +++ b/notmuch-search.c
>> @@ -47,6 +47,7 @@ typedef struct {
>>  typedef struct {
>>  const char *name;
>>  const char *addr;
>> +const char *string;
>>  } mailbox_t;
>>  
>>  /* Return two stable query strings that identify exactly the matched
>> @@ -255,15 +256,13 @@ print_mailbox (const search_options_t *opt, const 
>> mailbox_t *mailbox)
>>  {
>>  const char *name = mailbox->name;
>>  const char *addr = mailbox->addr;
>> +const char *string = mailbox->string;
>>  sprinter_t *format = opt->format;
>>  
>>  if (format->is_text_printer) {
>>  char *mailbox_str;
>>  
>> -if (name && *name)
>> -mailbox_str = talloc_asprintf (format, "%s <%s>", name, addr);
>> -else
>> -mailbox_str = talloc_strdup (format, addr);
>> +mailbox_str = talloc_strdup (format, string);
>>  
>>  if (! mailbox_str) {
>>  fprintf (stderr, "Error: out of memory\n");
>> @@ -309,6 +308,7 @@ process_address_list (const search_options_t *opt, 
>> GHashTable *addrs,
>>  mailbox_t mbx = {
>>  .name = internet_address_get_name (address),
>>  .addr = internet_address_mailbox_get_addr (mailbox),
>> +.string = internet_address_to_string (address, TRUE),
>
> I'd prefer having the second parameter (encode) FALSE. This will still
> add quotes when necessary, but does not encode non-ascii characters so
> the result would be human readable.

Yes that is clearly better.
>
> Another question is whether to add .string to mailbox_t. In this patch
> it doesn't matter, but if --output=count patch will be merged, this
> would mean that memory consumption doubles, because with --output=count
> the addresses are kept in memory and printed only after the search is
> completed. It would be therefore better to construct a new
> InternetAddressMailbox from name and addr in print_mailbox() and perform
> the conversion to string there. Thoughts?

Yes I agree here too. Are you happy doing a replacement patch or should
I?

Best wishes

Mark

>
> Thanks,
> -Michal
>
>>  };
>>  
>>  if (is_duplicate (opt, addrs, mbx.name, mbx.addr))
>> diff --git a/test/T090-search-output.sh b/test/T090-search-output.sh
>> index 841a721..776e3f4 100755
>> --- a/test/T090-search-output.sh
>> +++ b/test/T090-search-output.sh
>> @@ -390,7 +390,7 @@ test_expect_equal_file OUTPUT EXPECTED
>>  test_begin_subtest "--output=sender"
>>  notmuch search --output=sender '*' >OUTPUT
>>  cat > -François Boulogne 
>> +=?iso-8859-1?q?Fran=E7ois?= Boulogne 
>>  Olivier Berger 
>>  Chris Wilson 
>>  Carl Worth 
>> @@ -437,7 +437,7 @@ test_begin_subtest "--output=recipients"
>>  notmuch search --output=recipients '*' >OUTPUT
>>  cat >  Allan McRae 
>> -Discussion about the Arch User Repository (AUR) 
>> +"Discussion about the Arch User Repository (AUR)" 
>> 
>>  olivier.ber...@it-sudparis.eu
>>  notmuch@notmuchmail.org
>>  notmuch 
>> @@ -449,9 +449,9 @@ test_expect_equal_file OUTPUT EXPECTED
>>  test_begin_subtest "--output=sender --output=recipients"
>>  notmuch search --output=sender --output=recipients '*' >OUTPUT
>>  cat > -François Boulogne 
>> +=?iso-8859-1?q?Fran=E7ois?= Boulogne 
>>  Allan McRae 
>> -Discussion about the Arch User Repository (AUR) 
>> +"Discussion about the Arch User Repository (AUR)" 
>> 
>>  Olivier Berger 
>>  olivier.ber...@it-sudparis.eu
>>  Chris Wilson 
>> -- 
>> 1.7.10.4
___

Re: [PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Tomi Ollila
On Fri, Oct 31 2014, Michal Sojka  wrote:

> On Fri, Oct 31 2014, Mark Walters wrote:
>> My only query is in the text output: should the name part be printed as
>> a quoted string. For example currently I get a line of the form
>>
>> Bloggs, Fred 
>
> Good point.

There has been some discussion on this issue on IRC channel, and the
opinion of most (seen so far) is that output the parts without quoting
(i.e. just like done in this patch series)...

Taken the other example from Mark's earlyer email:

Bloggs , Fred 

echo '^^^' | sed 's/.*:s) (*)

echo '^^^' | sed 's/ <[^<]*$//' would leave only the name part

and regexp '\(.*\) <\(.*\)>' or pcre-compatible /(.*)\s<(.*)>/
would capture name & address parts...

(all of the above untested, though;)

In case instead of 'name ' there is only 'addr', then above
sed lines return the same (full) string and the regexps just don't
match.

There were some suggestions how the text output could be changed on IRC;
if anyone wishes to bring those forward, please do so :D

So, IMO, this issue is not showstopper in this series (if anything is);
patches 1-6 looks good to me on paper, but I have not tested those yet.

Tomi

(*) echo '^^^' | sed 's/.*.*//' would drop <>'s from first example


>
> On Fri, Oct 31 2014, Mark Walters wrote:
>> Hi
>>
>> I attach a patch which does the quoting for real names but I don't know
>> if we want it: it changes (example taken from the test suite)
>>
>> François Boulogne to
>>
>> =?iso-8859-1?q?Fran=E7ois?= Boulogne
>>
>> (which is what was in the original email)
>>
>> Plausibly the best thing is just to leave the series as is, so the
>> text output is readable and parseable in the common cases.
>>
>> Anyway the patch is attached if anyone wants to experiment.
>>
>> Best wishes
>>
>> Mark
>>
>> From 53b1ced2d6a9fbbba93448325f795e6b99faa240 Mon Sep 17 00:00:00 2001
>> From: Mark Walters 
>> Date: Fri, 31 Oct 2014 10:11:40 +
>> Subject: [PATCH] search: quote real names for output=sender/recipient in text
>>  format
>>
>> This quotes the real name (when gmime thinks appropriate) for the text
>> output. For the other outputs the real name is separate from the
>> address so the consumer can do any quoting necessary.
>> ---
>>  notmuch-search.c   |8 
>>  test/T090-search-output.sh |8 
>>  2 files changed, 8 insertions(+), 8 deletions(-)
>>
>> diff --git a/notmuch-search.c b/notmuch-search.c
>> index eae749a..8eac161 100644
>> --- a/notmuch-search.c
>> +++ b/notmuch-search.c
>> @@ -47,6 +47,7 @@ typedef struct {
>>  typedef struct {
>>  const char *name;
>>  const char *addr;
>> +const char *string;
>>  } mailbox_t;
>>  
>>  /* Return two stable query strings that identify exactly the matched
>> @@ -255,15 +256,13 @@ print_mailbox (const search_options_t *opt, const 
>> mailbox_t *mailbox)
>>  {
>>  const char *name = mailbox->name;
>>  const char *addr = mailbox->addr;
>> +const char *string = mailbox->string;
>>  sprinter_t *format = opt->format;
>>  
>>  if (format->is_text_printer) {
>>  char *mailbox_str;
>>  
>> -if (name && *name)
>> -mailbox_str = talloc_asprintf (format, "%s <%s>", name, addr);
>> -else
>> -mailbox_str = talloc_strdup (format, addr);
>> +mailbox_str = talloc_strdup (format, string);
>>  
>>  if (! mailbox_str) {
>>  fprintf (stderr, "Error: out of memory\n");
>> @@ -309,6 +308,7 @@ process_address_list (const search_options_t *opt, 
>> GHashTable *addrs,
>>  mailbox_t mbx = {
>>  .name = internet_address_get_name (address),
>>  .addr = internet_address_mailbox_get_addr (mailbox),
>> +.string = internet_address_to_string (address, TRUE),
>
> I'd prefer having the second parameter (encode) FALSE. This will still
> add quotes when necessary, but does not encode non-ascii characters so
> the result would be human readable.
>
> Another question is whether to add .string to mailbox_t. In this patch
> it doesn't matter, but if --output=count patch will be merged, this
> would mean that memory consumption doubles, because with --output=count
> the addresses are kept in memory and printed only after the search is
> completed. It would be therefore better to construct a new
> InternetAddressMailbox from name and addr in print_mailbox() and perform
> the conversion to string there. Thoughts?
>
> Thanks,
> -Michal
>
>>  };
>>  
>>  if (is_duplicate (opt, addrs, mbx.name, mbx.addr))
>> diff --git a/test/T090-search-output.sh b/test/T090-search-output.sh
>> index 841a721..776e3f4 100755
>> --- a/test/T090-search-output.sh
>> +++ b/test/T090-search-output.sh
>> @@ -390,7 +390,7 @@ test_expect_equal_file OUTPUT EXPECTED
>>  test_begin_subtest "--output=sender"
>>  notmuch search --output=sender '*' >OUTPUT
>>  cat > -François Boulogne 
>> +=?iso-8859-1?q?Fran=E7ois?= Boulogne 
>>  Olivier Berger 
>>  Chris Wilson 
>>  Carl Worth 
>> @@ -437,7 +437,7 @@ test_be

Re: [PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Jesse Rosenthal
Dear Michal,

Thanks for all this. The feature looks great! One issue: I'm getting
corrupt output when using `--output=count` with "fold" filters:

~~~
jkr@ladybug [11:01AM] ~ $ notmuch search --output=recipients --output=count 
"tag:notmuch and date:today.." 
2   Jani Nikula 
2   Michal Sojka 
1   David Edmondson 
9   notmuch@notmuchmail.org

jkr@ladybug [11:01AM] ~ $ notmuch search --output=recipients --output=count 
--filter-by=addrfold "tag:notmuch and date:today.."
2   Michal Sojka <0Fam1@fel.cvutH>
9   
1   David Edmondson <8dme.org>
2   Jani Nikula 

jkr@ladybug [11:01AM] ~ $ notmuch search --output=recipients --output=count 
--filter-by=nameaddr "tag:notmuch and date:today.."
2   Jani Nikula 
2   Michal Sojka 
1   David Edmondson 
9   notmuch@notmuchmail.org

jkr@ladybug [11:01AM] ~ $ notmuch search --output=recipients --output=count 
--filter-by=nameaddrfold "tag:notmuch and date:today.."
2   Jani Nikula 
2   Michal Sojka <0V�   am1@fel.cvutH>
1   David Edmondson <8��dme.org>
9
~~~

Not sure if this is expected behavior with the filtering mechanism in
its current state, but I wanted to call it to your attention if not.

Best,
Jesse
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Mark Walters
On Thu, 30 Oct 2014, Michal Sojka  wrote:
> Hi all,
>
> this is v5 of the search --output=address series. It obsoletes v4
> (id:1414421455-3037-1-git-send-email-sojkam1 at fel.cvut.cz).
>
> I addresses comments from Mark and Tomi. Based on the comments to v4
> and earlier versions, patches 1-4 should be ready for merging. Patch 5
> is a non-controversial part of the controversial --filter-by patch and
> could be probably merged after review.

I have looked at Patches 1-5 and tested. These look good to me. +1

My only query is in the text output: should the name part be printed as
a quoted string. For example currently I get a line of the form

Bloggs, Fred 

and I think in theory I could have a real name 

"Fred  Bloggs" which this would print without the quotes.

For the other formats it is much less of a problem because the name and
address are clearly separated.

I am happy with an answer of the form "for robust parseable results use a
structured format" which is what we say for search for example.

I just thought I would mention it in case you thought the quoted form
was more useful for consumers.

Best wishes

Mark


>
> Patch 6 needs at least a review and patch 7 needs more discussion.
>
> Changes from v4:
>
> - patch changed to commit in commit messages
> - opt->format changed to format
> - Added comments to process_* functions
> - duplicite changed to duplicate
> - check_duplicate changed to is_duplicate
> - Deduplication was split into two commits: basic deduplication
>   without a command line option and configurable deduplication with
>   --fiter-by.
>
> Changes from v3:
>
> - `o' renamed to `opt'.
> - Conversion of --output from keyword to keyword-flags is now a
>   separate patch.
> - Structured output formats print name and address separately.
> - Added test for --format=json.
> - Changed --filter-by default to nameaddr. In v2, the default was
>   addrfold, in v3 the default was no filtering at all. I believe that
>   Mark's suggestion to make nameaddr the default is good trade off.
> - Added new --output=count
> - Minor style fixes
> - Few typos fixed
> - There is no way to output unfiltered (duplicite) addresses.
>   Hopefully, the introduction of --output=count is sufficient
>   replacement for this "feature".
>
> Cheers,
> -Michal
>
>
> Jani Nikula (1):
>   cli: Add support for parsing keyword-flag arguments
>
> Michal Sojka (6):
>   cli: search: Refactor passing of command line options
>   cli: search: Convert --output to keyword-flag argument
>   cli: search: Add --output={sender,recipients}
>   cli: search: Do not output duplicate addresses
>   cli: search: Add --output=count
>   cli: search: Add --filter-by option to configure address filtering
>
>  command-line-arguments.c   |   6 +-
>  command-line-arguments.h   |   1 +
>  completion/notmuch-completion.bash |   8 +-
>  completion/notmuch-completion.zsh  |   4 +-
>  doc/man1/notmuch-search.rst|  66 ++-
>  notmuch-search.c   | 388 
> +
>  test/T090-search-output.sh | 137 +
>  test/T095-search-filter-by.sh  |  64 ++
>  test/T410-argument-parsing.sh  |   3 +-
>  test/arg-test.c|   9 +
>  10 files changed, 604 insertions(+), 82 deletions(-)
>  create mode 100755 test/T095-search-filter-by.sh
>
> -- 
> 2.1.1
>
> ___
> notmuch mailing list
> notmuch at notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Michal Sojka
On Fri, Oct 31 2014, Jesse Rosenthal wrote:
> Dear Michal,
>
> Thanks for all this. The feature looks great! One issue: I'm getting
> corrupt output when using `--output=count` with "fold" filters:
>
> ~~~
> jkr@ladybug [11:01AM] ~ $ notmuch search --output=recipients --output=count 
> "tag:notmuch and date:today.." 
> 2 Jani Nikula 
> 2 Michal Sojka 
> 1 David Edmondson 
> 9 notmuch@notmuchmail.org
>
> jkr@ladybug [11:01AM] ~ $ notmuch search --output=recipients --output=count 
> --filter-by=addrfold "tag:notmuch and date:today.."
> 2 Michal Sojka <0Fam1@fel.cvutH>
> 9 
> 1 David Edmondson <8dme.org>
> 2 Jani Nikula 
>
> jkr@ladybug [11:01AM] ~ $ notmuch search --output=recipients --output=count 
> --filter-by=nameaddr "tag:notmuch and date:today.."
> 2 Jani Nikula 
> 2 Michal Sojka 
> 1 David Edmondson 
> 9 notmuch@notmuchmail.org
>
> jkr@ladybug [11:01AM] ~ $ notmuch search --output=recipients --output=count 
> --filter-by=nameaddrfold "tag:notmuch and date:today.."
> 2 Jani Nikula 
> 2 Michal Sojka <0V�   am1@fel.cvutH>
> 1 David Edmondson <8��dme.org>
> 9
> ~~~
>
> Not sure if this is expected behavior with the filtering mechanism in
> its current state, but I wanted to call it to your attention if not.

Definitely not. I'll look into that. Thanks.

-Michal
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


"search --path=directory/" is lame(-ish)

2014-10-31 Thread David Bremner
David Edmondson  writes:

>
> Could we always prune a trailing slash from the path: component of a
> query before using it?
>

As I understand it, this is complicated by the fact that we pass the
whole string to Xapian to be parsed as a query, so we don't really know
where the path: terms are. We could in principle preprocess the string
(more) but that seems to be pretty fragile, and we'd have to minimally
deal with quoting of e.g. paths with spaces in them.

d


Re: [PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Michal Sojka
On Fri, Oct 31 2014, Mark Walters wrote:
> My only query is in the text output: should the name part be printed as
> a quoted string. For example currently I get a line of the form
>
> Bloggs, Fred 

Good point.

On Fri, Oct 31 2014, Mark Walters wrote:
> Hi
>
> I attach a patch which does the quoting for real names but I don't know
> if we want it: it changes (example taken from the test suite)
>
> François Boulogne to
>
> =?iso-8859-1?q?Fran=E7ois?= Boulogne
>
> (which is what was in the original email)
>
> Plausibly the best thing is just to leave the series as is, so the
> text output is readable and parseable in the common cases.
>
> Anyway the patch is attached if anyone wants to experiment.
>
> Best wishes
>
> Mark
>
> From 53b1ced2d6a9fbbba93448325f795e6b99faa240 Mon Sep 17 00:00:00 2001
> From: Mark Walters 
> Date: Fri, 31 Oct 2014 10:11:40 +
> Subject: [PATCH] search: quote real names for output=sender/recipient in text
>  format
>
> This quotes the real name (when gmime thinks appropriate) for the text
> output. For the other outputs the real name is separate from the
> address so the consumer can do any quoting necessary.
> ---
>  notmuch-search.c   |8 
>  test/T090-search-output.sh |8 
>  2 files changed, 8 insertions(+), 8 deletions(-)
>
> diff --git a/notmuch-search.c b/notmuch-search.c
> index eae749a..8eac161 100644
> --- a/notmuch-search.c
> +++ b/notmuch-search.c
> @@ -47,6 +47,7 @@ typedef struct {
>  typedef struct {
>  const char *name;
>  const char *addr;
> +const char *string;
>  } mailbox_t;
>  
>  /* Return two stable query strings that identify exactly the matched
> @@ -255,15 +256,13 @@ print_mailbox (const search_options_t *opt, const 
> mailbox_t *mailbox)
>  {
>  const char *name = mailbox->name;
>  const char *addr = mailbox->addr;
> +const char *string = mailbox->string;
>  sprinter_t *format = opt->format;
>  
>  if (format->is_text_printer) {
>   char *mailbox_str;
>  
> - if (name && *name)
> - mailbox_str = talloc_asprintf (format, "%s <%s>", name, addr);
> - else
> - mailbox_str = talloc_strdup (format, addr);
> + mailbox_str = talloc_strdup (format, string);
>  
>   if (! mailbox_str) {
>   fprintf (stderr, "Error: out of memory\n");
> @@ -309,6 +308,7 @@ process_address_list (const search_options_t *opt, 
> GHashTable *addrs,
>   mailbox_t mbx = {
>   .name = internet_address_get_name (address),
>   .addr = internet_address_mailbox_get_addr (mailbox),
> + .string = internet_address_to_string (address, TRUE),

I'd prefer having the second parameter (encode) FALSE. This will still
add quotes when necessary, but does not encode non-ascii characters so
the result would be human readable.

Another question is whether to add .string to mailbox_t. In this patch
it doesn't matter, but if --output=count patch will be merged, this
would mean that memory consumption doubles, because with --output=count
the addresses are kept in memory and printed only after the search is
completed. It would be therefore better to construct a new
InternetAddressMailbox from name and addr in print_mailbox() and perform
the conversion to string there. Thoughts?

Thanks,
-Michal

>   };
>  
>   if (is_duplicate (opt, addrs, mbx.name, mbx.addr))
> diff --git a/test/T090-search-output.sh b/test/T090-search-output.sh
> index 841a721..776e3f4 100755
> --- a/test/T090-search-output.sh
> +++ b/test/T090-search-output.sh
> @@ -390,7 +390,7 @@ test_expect_equal_file OUTPUT EXPECTED
>  test_begin_subtest "--output=sender"
>  notmuch search --output=sender '*' >OUTPUT
>  cat  -François Boulogne 
> +=?iso-8859-1?q?Fran=E7ois?= Boulogne 
>  Olivier Berger 
>  Chris Wilson 
>  Carl Worth 
> @@ -437,7 +437,7 @@ test_begin_subtest "--output=recipients"
>  notmuch search --output=recipients '*' >OUTPUT
>  cat   Allan McRae 
> -Discussion about the Arch User Repository (AUR) 
> +"Discussion about the Arch User Repository (AUR)" 
>  olivier.ber...@it-sudparis.eu
>  notmuch@notmuchmail.org
>  notmuch 
> @@ -449,9 +449,9 @@ test_expect_equal_file OUTPUT EXPECTED
>  test_begin_subtest "--output=sender --output=recipients"
>  notmuch search --output=sender --output=recipients '*' >OUTPUT
>  cat  -François Boulogne 
> +=?iso-8859-1?q?Fran=E7ois?= Boulogne 
>  Allan McRae 
> -Discussion about the Arch User Repository (AUR) 
> +"Discussion about the Arch User Repository (AUR)" 
>  Olivier Berger 
>  olivier.ber...@it-sudparis.eu
>  Chris Wilson 
> -- 
> 1.7.10.4
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v1 2/2] emacs: Washing should use more `defcustom'.

2014-10-31 Thread David Edmondson
More of the washing variables should be available through the standard
customisation interface.
---
 emacs/notmuch-wash.el | 88 +--
 1 file changed, 57 insertions(+), 31 deletions(-)

diff --git a/emacs/notmuch-wash.el b/emacs/notmuch-wash.el
index 1844400..a76b4f5 100644
--- a/emacs/notmuch-wash.el
+++ b/emacs/notmuch-wash.el
@@ -31,73 +31,99 @@
   "Cleaning up messages for display."
   :group 'notmuch)

-(defvar notmuch-wash-signature-regexp
-  "^\\(-- ?\\|_+\\)$"
-  "Pattern to match a line that separates content from signature.")
-
-(defvar notmuch-wash-citation-regexp
-  "\\(^[[:space:]]*>.*\n\\)+"
-  "Pattern to match citation lines.")
-
-(defvar notmuch-wash-original-regexp "^\\(--+\s?[oO]riginal 
[mM]essage\s?--+\\)$"
-  "Pattern to match a line that separates original message from reply in 
top-posted message.")
-
-(defvar notmuch-wash-button-signature-hidden-format
+(defcustom notmuch-wash-signature-regexp "^\\(-- ?\\|_+\\)$"
+  "Pattern to match a line that separates content from signature."
+  :type 'regexp
+  :group 'notmuch-wash)
+
+(defcustom notmuch-wash-citation-regexp "\\(^[[:space:]]*>.*\n\\)+"
+  "Pattern to match citation lines."
+  :type 'regexp
+  :group 'notmuch-wash)
+
+(defcustom notmuch-wash-original-regexp "^\\(--+\s?[oO]riginal 
[mM]essage\s?--+\\)$"
+  "Pattern to match a line that separates original message from
+reply in top-posted message."
+  :type 'regexp
+  :group 'notmuch-wash)
+
+(defcustom notmuch-wash-button-signature-hidden-format
   "[ %d-line signature. Click/Enter to show. ]"
   "String used to construct button text for hidden signatures.
-Can use up to one integer format parameter, i.e. %d")
+Can use up to one integer format parameter, i.e. %d."
+  :type 'string
+  :group 'notmuch-wash)

-(defvar notmuch-wash-button-signature-visible-format
+(defcustom notmuch-wash-button-signature-visible-format
   "[ %d-line signature. Click/Enter to hide. ]"
   "String used to construct button text for visible signatures.
-Can use up to one integer format parameter, i.e. %d")
+Can use up to one integer format parameter, i.e. %d."
+  :type 'string
+  :group 'notmuch-wash)

-(defvar notmuch-wash-button-citation-hidden-format
+(defcustom notmuch-wash-button-citation-hidden-format
   "[ %d more citation lines. Click/Enter to show. ]"
   "String used to construct button text for hidden citations.
-Can use up to one integer format parameter, i.e. %d")
+Can use up to one integer format parameter, i.e. %d."
+  :type 'string
+  :group 'notmuch-wash)

-(defvar notmuch-wash-button-citation-visible-format
+(defcustom notmuch-wash-button-citation-visible-format
   "[ %d more citation lines. Click/Enter to hide. ]"
   "String used to construct button text for visible citations.
-Can use up to one integer format parameter, i.e. %d")
+Can use up to one integer format parameter, i.e. %d."
+  :type 'string
+  :group 'notmuch-wash)

-(defvar notmuch-wash-button-original-hidden-format
+(defcustom notmuch-wash-button-original-hidden-format
   "[ %d-line hidden original message. Click/Enter to show. ]"
   "String used to construct button text for hidden citations.
-Can use up to one integer format parameter, i.e. %d")
+Can use up to one integer format parameter, i.e. %d."
+  :type 'string
+  :group 'notmuch-wash)

-(defvar notmuch-wash-button-original-visible-format
+(defcustom notmuch-wash-button-original-visible-format
   "[ %d-line original message. Click/Enter to hide. ]"
   "String used to construct button text for visible citations.
-Can use up to one integer format parameter, i.e. %d")
+Can use up to one integer format parameter, i.e. %d."
+  :type 'string
+  :group 'notmuch-wash)

-(defvar notmuch-wash-signature-lines-max 12
-  "Maximum length of signature that will be hidden by default.")
+(defcustom notmuch-wash-signature-lines-max 12
+  "Maximum length of signature that will be hidden by default."
+  :type 'integer
+  :group 'notmuch-wash)

-(defvar notmuch-wash-citation-lines-prefix 3
+(defcustom notmuch-wash-citation-lines-prefix 3
   "Always show at least this many lines from the start of a citation.

 If there is one more line than the sum of
 `notmuch-wash-citation-lines-prefix' and
 `notmuch-wash-citation-lines-suffix', show that, otherwise
-collapse the remaining lines into a button.")
+collapse the remaining lines into a button."
+  :type 'integer
+  :group 'notmuch-wash)

-(defvar notmuch-wash-citation-lines-suffix 3
+(defcustom notmuch-wash-citation-lines-suffix 3
   "Always show at least this many lines from the end of a citation.

 If there is one more line than the sum of
 `notmuch-wash-citation-lines-prefix' and
 `notmuch-wash-citation-lines-suffix', show that, otherwise
-collapse the remaining lines into a button.")
+collapse the remaining lines into a button."
+  :type 'integer
+  :group 'notmuch-wash)

-(defvar notmuch-wash-wrap-lines-length nil
+(defcustom notmuch-wash-wrap-lines-length nil
   "Wrap line after at most thi

[PATCH v1 1/2] emacs: More flexible washed faces.

2014-10-31 Thread David Edmondson
The faces used when washing messages should be notmuch specific and
inherit from the underlying emacs face rather than using it
directly. This allows the washed face to be modified without requiring
the modification of the underlying face.
---
 emacs/notmuch-wash.el | 21 +++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/emacs/notmuch-wash.el b/emacs/notmuch-wash.el
index 8fe91e1..1844400 100644
--- a/emacs/notmuch-wash.el
+++ b/emacs/notmuch-wash.el
@@ -27,6 +27,10 @@

 ;;

+(defgroup notmuch-wash nil
+  "Cleaning up messages for display."
+  :group 'notmuch)
+
 (defvar notmuch-wash-signature-regexp
   "^\\(-- ?\\|_+\\)$"
   "Pattern to match a line that separates content from signature.")
@@ -95,6 +99,19 @@ current window. If this is a number, lines will be wrapped 
after
 this many characters or at the window width (whichever one is
 lower).")

+(defface notmuch-wash-toggle-button
+  '((t (:inherit font-lock-comment-face)))
+  "Face used for buttons toggling the visibility of washed away
+message parts."
+  :group 'notmuch-wash
+  :group 'notmuch-faces)
+
+(defface notmuch-wash-cited-text
+  '((t (:inherit message-cited-text)))
+  "Face used for cited text."
+  :group 'notmuch-wash
+  :group 'notmuch-faces)
+
 (defun notmuch-wash-toggle-invisible-action (cite-button)
   ;; Toggle overlay visibility
   (let ((overlay (button-get cite-button 'overlay)))
@@ -117,7 +134,7 @@ lower).")
 (define-button-type 'notmuch-wash-button-invisibility-toggle-type
   'action 'notmuch-wash-toggle-invisible-action
   'follow-link t
-  'face 'font-lock-comment-face
+  'face 'notmuch-wash-toggle-button
   :supertype 'notmuch-button-type)

 (define-button-type 'notmuch-wash-button-citation-toggle-type
@@ -192,7 +209,7 @@ that PREFIX should not include a newline."
 (let* ((cite-start (match-beginning 0))
   (cite-end (match-end 0))
   (cite-lines (count-lines cite-start cite-end)))
-  (overlay-put (make-overlay cite-start cite-end) 'face 
'message-cited-text)
+  (overlay-put (make-overlay cite-start cite-end) 'face 
'notmuch-wash-cited-text)
   (when (> cite-lines (+ notmuch-wash-citation-lines-prefix
 notmuch-wash-citation-lines-suffix
 1))
-- 
1.9.3 (Apple Git-50)



[PATCH v1 0/2] emacs: Improve notmuch-wash customisation.

2014-10-31 Thread David Edmondson

emacs: Improve notmuch-wash customisation.

Minor updates to the customisation of notmuch-wash features.


David Edmondson (2):
  emacs: More flexible washed faces.
  emacs: Washing should use more `defcustom'.

 emacs/notmuch-wash.el | 109 +++---
 1 file changed, 76 insertions(+), 33 deletions(-)

-- 
1.9.3 (Apple Git-50)



[PATCH] cli: add support for notmuch search --duplicate=N with --output=messages

2014-10-31 Thread David Edmondson
On Thu, Oct 30 2014, Jani Nikula wrote:
> Print the message IDs of all messages matching the search terms that
> have at least N files associated with them.

Briefly tested as working. I commend this patch to the masters of push.

Thanks Jani!

> ---
>  doc/man1/notmuch-search.rst | 12 
>  notmuch-search.c| 34 ++
>  2 files changed, 38 insertions(+), 8 deletions(-)
>
> diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
> index 90160f21e23c..aeba4bf604f6 100644
> --- a/doc/man1/notmuch-search.rst
> +++ b/doc/man1/notmuch-search.rst
> @@ -122,10 +122,14 @@ Supported options for **search** include
>  rather than the number of matching messages.
>  
>  ``--duplicate=N``
> -Effective with ``--output=files``, output the Nth filename
> -associated with each message matching the query (N is 1-based).
> -If N is greater than the number of files associated with the
> -message, don't print anything.
> +For ``--output=files``, output the Nth filename associated
> +with each message matching the query (N is 1-based). If N is
> +greater than the number of files associated with the message,
> +don't print anything.
> +
> +For ``--output=messages``, only output message IDs of messages
> +matching the search terms that have at least N filenames
> +associated with them.
>  
>  Note that this option is orthogonal with the **folder:** search
>  prefix. The prefix matches messages based on filenames. This
> diff --git a/notmuch-search.c b/notmuch-search.c
> index bc9be4593ecc..2bf876fd5abf 100644
> --- a/notmuch-search.c
> +++ b/notmuch-search.c
> @@ -215,6 +215,24 @@ do_search_threads (sprinter_t *format,
>  }
>  
>  static int
> +_count_filenames (notmuch_message_t *message)
> +{
> +notmuch_filenames_t *filenames;
> +int i = 0;
> +
> +filenames = notmuch_message_get_filenames (message);
> +
> +while (notmuch_filenames_valid (filenames)) {
> + notmuch_filenames_move_to_next (filenames);
> + i++;
> +}
> +
> +notmuch_filenames_destroy (filenames);
> +
> +return i;
> +}
> +
> +static int
>  do_search_messages (sprinter_t *format,
>   notmuch_query_t *query,
>   output_t output,
> @@ -265,10 +283,13 @@ do_search_messages (sprinter_t *format,
>   notmuch_filenames_destroy( filenames );
>  
>   } else { /* output == OUTPUT_MESSAGES */
> - format->set_prefix (format, "id");
> - format->string (format,
> - notmuch_message_get_message_id (message));
> - format->separator (format);
> + /* special case 1 for speed */
> + if (dupe <= 1 || dupe <= _count_filenames (message)) {
> + format->set_prefix (format, "id");
> + format->string (format,
> + notmuch_message_get_message_id (message));
> + format->separator (format);
> + }
>   }
>  
>   notmuch_message_destroy (message);
> @@ -387,6 +408,11 @@ notmuch_search_command (notmuch_config_t *config, int 
> argc, char *argv[])
>  if (opt_index < 0)
>   return EXIT_FAILURE;
>  
> +if (output != OUTPUT_FILES && output != OUTPUT_MESSAGES && dupe != -1) {
> + fprintf (stderr, "Error: --duplicate=N is only supported with 
> --output=files and --output=messages.\n");
> + return EXIT_FAILURE;
> +}
> +
>  switch (format_sel) {
>  case NOTMUCH_FORMAT_TEXT:
>   format = sprinter_text_create (config, stdout);
> -- 
> 2.1.1


[PATCH v1] emacs: Improved header display.

2014-10-31 Thread David Edmondson
On Thu, Oct 30 2014, Jani Nikula wrote:
> On Thu, 30 Oct 2014, David Edmondson  wrote:
>> Truncate the displayed headers to the window width. Show an ellipsis
>> if the displayed header is truncated. Add a binding 'T' to toggle the
>> truncation of headers. Add the not-displayed section of the header as
>> a tooltip to the displayed section.
>
> Thanks for your efforts, David - I hate it that I'm going to sound
> ungrateful since I asked for something like this. But not quite like
> this...
>
> I think more header lines than just one should be displayed untruncated
> by default. I think it's okay to show, say, five lines of To: or Cc: and
> that'll probably cover most emails without truncation. And when the
> header does get truncated, I'd really like to see the indication more
> predominantly displayed than just ellipsis.
>
> I'm thinking of something like this, similar to notmuch-wash:
>
> ---
> To: user at example.com, user at example.com, user at example.com,
> user at example.com, user at example.com, user at example.com, user at 
> example.com,
> user at example.com, user at example.com, user at example.com, user at 
> example.com,
> user at example.com, user at example.com, user at example.com, user at 
> example.com,
> [ 42 more header lines. Click/Enter to show. ]
> Cc: user at example.com
> ---

Hmm. That seems pretty ugly to me :-)

As you mentioned in #notmuch, the implementation below is also not quite
correct - it assumes that it is splitting addresses but is also used on
the non-address headers. There are also some oddities when the header
lines of collapsed messages are compressed.

If anyone else is particularly interested then I may come back to it,
but otherwise will leave it alone.

> BR,
> Jani.
>
>
>> ---
>>  emacs/notmuch-show.el | 54 
>> ---
>>  1 file changed, 51 insertions(+), 3 deletions(-)
>>
>> diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el
>> index a997482..523cef5 100644
>> --- a/emacs/notmuch-show.el
>> +++ b/emacs/notmuch-show.el
>> @@ -443,9 +443,56 @@ message at DEPTH in the current thread."
>>  ")\n")
>>  (overlay-put (make-overlay start (point)) 'face 
>> 'notmuch-message-summary-face)))
>>  
>> +(defun notmuch-truncate-nicely (addresses target-length)
>> +  ;; If it fits, everything is easy.
>> +  (if (< (length addresses) target-length)
>> +  (cons addresses nil)
>> +(let* ((visible-length (- target-length (length "...")))
>> +   (visible (substring addresses 0 visible-length))
>> +   (invisible (substring addresses visible-length)))
>> +  ;; Try to terminate the visible string at a good break point.
>> +  (when (string-match "\\(.+\\),\\([^,]*\\)" visible)
>> +;; Order is important (second clause is destructive on
>> +;; `visible'.
>> +(setq invisible (concat (match-string 2 visible) invisible)
>> +  visible (match-string 1 visible)))
>> +  ;; `invisible' can end up with a leading space or
>> +  ;; comma-space, because the list of addresses is
>> +  ;; seperated with ", ", but we split on ",".
>> +  (setq invisible (replace-regexp-in-string "^[, ]*\\(.*\\)$" "\\1" 
>> invisible))
>> +  (cons visible invisible
>> +
>> +(defun notmuch-show-toggle-header-truncation ()
>> +  (interactive)
>> +  (let ((invisibility-spec-member (cons 'notmuch-show-mode t)))
>> +(if (member invisibility-spec-member buffer-invisibility-spec)
>> +(remove-from-invisibility-spec invisibility-spec-member)
>> +  (add-to-invisibility-spec invisibility-spec-member)))
>> +  ;; Required to have the change in visibility take effect.
>> +  (force-window-update))
>> +
>>  (defun notmuch-show-insert-header (header header-value)
>>"Insert a single header."
>> -  (insert header ": " (notmuch-sanitize header-value) "\n"))
>> +  (let* ((header-value (notmuch-sanitize header-value))
>> + (header-colon (concat header ": "))
>> + (available-width (- (window-width) (length header-colon)))
>> + (v-i (notmuch-truncate-nicely header-value available-width)))
>> +
>> +(insert header-colon)
>> +(let ((visible (car v-i))
>> +  (invisible (cdr v-i)))
>> +  (when invisible
>> +(setq visible (propertize visible 'help-echo (concat "..." invisible
>> +  (insert visible)
>> +  (when invisible
>> +(insert ", ")
>> +(let ((start (point))
>> +  overlay)
>> +  (insert invisible)
>> +  (setq overlay (make-overlay start (point)))
>> +  (overlay-put overlay 'invisible 'notmuch-show-mode)
>> +  (overlay-put overlay 'isearch-open-invisible #'delete-overlay
>> +(insert "\n")))
>>  
>>  (defun notmuch-show-insert-headers (headers)
>>"Insert the headers of the current message."
>> @@ -1328,6 +1375,7 @@ reset based on the original query."
>>  (define-key map "$" 'notmuch-show-toggle-process-crypto)
>>  (define-key map "<" 'notmuch-show-toggle-thread-indentation)
>>  (define-key map "t" 

Re: [PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Mark Walters

Hi

I attach a patch which does the quoting for real names but I don't know
if we want it: it changes (example taken from the test suite)

François Boulogne to

=?iso-8859-1?q?Fran=E7ois?= Boulogne

(which is what was in the original email)

Plausibly the best thing is just to leave the series as is, so the
text output is readable and parseable in the common cases.

Anyway the patch is attached if anyone wants to experiment.

Best wishes

Mark

>From 53b1ced2d6a9fbbba93448325f795e6b99faa240 Mon Sep 17 00:00:00 2001
From: Mark Walters 
Date: Fri, 31 Oct 2014 10:11:40 +
Subject: [PATCH] search: quote real names for output=sender/recipient in text
 format

This quotes the real name (when gmime thinks appropriate) for the text
output. For the other outputs the real name is separate from the
address so the consumer can do any quoting necessary.
---
 notmuch-search.c   |8 
 test/T090-search-output.sh |8 
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index eae749a..8eac161 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -47,6 +47,7 @@ typedef struct {
 typedef struct {
 const char *name;
 const char *addr;
+const char *string;
 } mailbox_t;
 
 /* Return two stable query strings that identify exactly the matched
@@ -255,15 +256,13 @@ print_mailbox (const search_options_t *opt, const mailbox_t *mailbox)
 {
 const char *name = mailbox->name;
 const char *addr = mailbox->addr;
+const char *string = mailbox->string;
 sprinter_t *format = opt->format;
 
 if (format->is_text_printer) {
 	char *mailbox_str;
 
-	if (name && *name)
-	mailbox_str = talloc_asprintf (format, "%s <%s>", name, addr);
-	else
-	mailbox_str = talloc_strdup (format, addr);
+	mailbox_str = talloc_strdup (format, string);
 
 	if (! mailbox_str) {
 	fprintf (stderr, "Error: out of memory\n");
@@ -309,6 +308,7 @@ process_address_list (const search_options_t *opt, GHashTable *addrs,
 	mailbox_t mbx = {
 		.name = internet_address_get_name (address),
 		.addr = internet_address_mailbox_get_addr (mailbox),
+		.string = internet_address_to_string (address, TRUE),
 	};
 
 	if (is_duplicate (opt, addrs, mbx.name, mbx.addr))
diff --git a/test/T090-search-output.sh b/test/T090-search-output.sh
index 841a721..776e3f4 100755
--- a/test/T090-search-output.sh
+++ b/test/T090-search-output.sh
@@ -390,7 +390,7 @@ test_expect_equal_file OUTPUT EXPECTED
 test_begin_subtest "--output=sender"
 notmuch search --output=sender '*' >OUTPUT
 cat OUTPUT
 cat OUTPUT
 cat 

[RFC] [PATCH v1] emacs: More citation washing.

2014-10-31 Thread David Edmondson
Ensure that a citation is preceded and followed by a blank line,
unless the preceding line looks like a citation leader.
---

Washing changes are always a bit risky, as they are heuristic
based. Testers for this (alleged) improvement welcome!

 emacs/notmuch-wash.el | 14 +-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/emacs/notmuch-wash.el b/emacs/notmuch-wash.el
index 8fe91e1..83621bb 100644
--- a/emacs/notmuch-wash.el
+++ b/emacs/notmuch-wash.el
@@ -276,7 +276,19 @@ Perform several transformations on the message body:
   ;; text.
   (goto-char (point-min))
   (while (re-search-forward "\\(^>[> ]*\n\\)\\(^$\\|^[^>].*\\)" nil t)
-(replace-match "\\2")))
+(replace-match "\\2"))
+
+  ;; Ensure that a citation block has a blank line before it, unless
+  ;; the previous line ends in ':' (because that's probably the
+  ;; citation).
+  (goto-char (point-min))
+  (while (re-search-forward "^[^>].*[^:]\\(\n\\)>.*" nil t)
+(replace-match "\n\n" nil nil nil 1))
+
+  ;; Ensure that a citation block has a blank line after it.
+  (goto-char (point-min))
+  (while (re-search-forward "^>.*\\(\n\\)[^>].*" nil t)
+(replace-match "\n\n" nil nil nil 1)))
 
 ;;
 
-- 
1.9.3 (Apple Git-50)

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Mark Walters
On Thu, 30 Oct 2014, Michal Sojka  wrote:
> Hi all,
>
> this is v5 of the search --output=address series. It obsoletes v4
> (id:1414421455-3037-1-git-send-email-sojk...@fel.cvut.cz).
>
> I addresses comments from Mark and Tomi. Based on the comments to v4
> and earlier versions, patches 1-4 should be ready for merging. Patch 5
> is a non-controversial part of the controversial --filter-by patch and
> could be probably merged after review.

I have looked at Patches 1-5 and tested. These look good to me. +1

My only query is in the text output: should the name part be printed as
a quoted string. For example currently I get a line of the form

Bloggs, Fred 

and I think in theory I could have a real name 

"Fred  Bloggs" which this would print without the quotes.

For the other formats it is much less of a problem because the name and
address are clearly separated.

I am happy with an answer of the form "for robust parseable results use a
structured format" which is what we say for search for example.

I just thought I would mention it in case you thought the quoted form
was more useful for consumers.

Best wishes

Mark


>
> Patch 6 needs at least a review and patch 7 needs more discussion.
>
> Changes from v4:
>
> - patch changed to commit in commit messages
> - opt->format changed to format
> - Added comments to process_* functions
> - duplicite changed to duplicate
> - check_duplicate changed to is_duplicate
> - Deduplication was split into two commits: basic deduplication
>   without a command line option and configurable deduplication with
>   --fiter-by.
>
> Changes from v3:
>
> - `o' renamed to `opt'.
> - Conversion of --output from keyword to keyword-flags is now a
>   separate patch.
> - Structured output formats print name and address separately.
> - Added test for --format=json.
> - Changed --filter-by default to nameaddr. In v2, the default was
>   addrfold, in v3 the default was no filtering at all. I believe that
>   Mark's suggestion to make nameaddr the default is good trade off.
> - Added new --output=count
> - Minor style fixes
> - Few typos fixed
> - There is no way to output unfiltered (duplicite) addresses.
>   Hopefully, the introduction of --output=count is sufficient
>   replacement for this "feature".
>
> Cheers,
> -Michal
>
>
> Jani Nikula (1):
>   cli: Add support for parsing keyword-flag arguments
>
> Michal Sojka (6):
>   cli: search: Refactor passing of command line options
>   cli: search: Convert --output to keyword-flag argument
>   cli: search: Add --output={sender,recipients}
>   cli: search: Do not output duplicate addresses
>   cli: search: Add --output=count
>   cli: search: Add --filter-by option to configure address filtering
>
>  command-line-arguments.c   |   6 +-
>  command-line-arguments.h   |   1 +
>  completion/notmuch-completion.bash |   8 +-
>  completion/notmuch-completion.zsh  |   4 +-
>  doc/man1/notmuch-search.rst|  66 ++-
>  notmuch-search.c   | 388 
> +
>  test/T090-search-output.sh | 137 +
>  test/T095-search-filter-by.sh  |  64 ++
>  test/T410-argument-parsing.sh  |   3 +-
>  test/arg-test.c|   9 +
>  10 files changed, 604 insertions(+), 82 deletions(-)
>  create mode 100755 test/T095-search-filter-by.sh
>
> -- 
> 2.1.1
>
> ___
> notmuch mailing list
> notmuch@notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v1 2/2] emacs: Washing should use more `defcustom'.

2014-10-31 Thread David Edmondson
More of the washing variables should be available through the standard
customisation interface.
---
 emacs/notmuch-wash.el | 88 +--
 1 file changed, 57 insertions(+), 31 deletions(-)

diff --git a/emacs/notmuch-wash.el b/emacs/notmuch-wash.el
index 1844400..a76b4f5 100644
--- a/emacs/notmuch-wash.el
+++ b/emacs/notmuch-wash.el
@@ -31,73 +31,99 @@
   "Cleaning up messages for display."
   :group 'notmuch)
 
-(defvar notmuch-wash-signature-regexp
-  "^\\(-- ?\\|_+\\)$"
-  "Pattern to match a line that separates content from signature.")
-
-(defvar notmuch-wash-citation-regexp
-  "\\(^[[:space:]]*>.*\n\\)+"
-  "Pattern to match citation lines.")
-
-(defvar notmuch-wash-original-regexp "^\\(--+\s?[oO]riginal 
[mM]essage\s?--+\\)$"
-  "Pattern to match a line that separates original message from reply in 
top-posted message.")
-
-(defvar notmuch-wash-button-signature-hidden-format
+(defcustom notmuch-wash-signature-regexp "^\\(-- ?\\|_+\\)$"
+  "Pattern to match a line that separates content from signature."
+  :type 'regexp
+  :group 'notmuch-wash)
+
+(defcustom notmuch-wash-citation-regexp "\\(^[[:space:]]*>.*\n\\)+"
+  "Pattern to match citation lines."
+  :type 'regexp
+  :group 'notmuch-wash)
+
+(defcustom notmuch-wash-original-regexp "^\\(--+\s?[oO]riginal 
[mM]essage\s?--+\\)$"
+  "Pattern to match a line that separates original message from
+reply in top-posted message."
+  :type 'regexp
+  :group 'notmuch-wash)
+
+(defcustom notmuch-wash-button-signature-hidden-format
   "[ %d-line signature. Click/Enter to show. ]"
   "String used to construct button text for hidden signatures.
-Can use up to one integer format parameter, i.e. %d")
+Can use up to one integer format parameter, i.e. %d."
+  :type 'string
+  :group 'notmuch-wash)
 
-(defvar notmuch-wash-button-signature-visible-format
+(defcustom notmuch-wash-button-signature-visible-format
   "[ %d-line signature. Click/Enter to hide. ]"
   "String used to construct button text for visible signatures.
-Can use up to one integer format parameter, i.e. %d")
+Can use up to one integer format parameter, i.e. %d."
+  :type 'string
+  :group 'notmuch-wash)
 
-(defvar notmuch-wash-button-citation-hidden-format
+(defcustom notmuch-wash-button-citation-hidden-format
   "[ %d more citation lines. Click/Enter to show. ]"
   "String used to construct button text for hidden citations.
-Can use up to one integer format parameter, i.e. %d")
+Can use up to one integer format parameter, i.e. %d."
+  :type 'string
+  :group 'notmuch-wash)
 
-(defvar notmuch-wash-button-citation-visible-format
+(defcustom notmuch-wash-button-citation-visible-format
   "[ %d more citation lines. Click/Enter to hide. ]"
   "String used to construct button text for visible citations.
-Can use up to one integer format parameter, i.e. %d")
+Can use up to one integer format parameter, i.e. %d."
+  :type 'string
+  :group 'notmuch-wash)
 
-(defvar notmuch-wash-button-original-hidden-format
+(defcustom notmuch-wash-button-original-hidden-format
   "[ %d-line hidden original message. Click/Enter to show. ]"
   "String used to construct button text for hidden citations.
-Can use up to one integer format parameter, i.e. %d")
+Can use up to one integer format parameter, i.e. %d."
+  :type 'string
+  :group 'notmuch-wash)
 
-(defvar notmuch-wash-button-original-visible-format
+(defcustom notmuch-wash-button-original-visible-format
   "[ %d-line original message. Click/Enter to hide. ]"
   "String used to construct button text for visible citations.
-Can use up to one integer format parameter, i.e. %d")
+Can use up to one integer format parameter, i.e. %d."
+  :type 'string
+  :group 'notmuch-wash)
 
-(defvar notmuch-wash-signature-lines-max 12
-  "Maximum length of signature that will be hidden by default.")
+(defcustom notmuch-wash-signature-lines-max 12
+  "Maximum length of signature that will be hidden by default."
+  :type 'integer
+  :group 'notmuch-wash)
 
-(defvar notmuch-wash-citation-lines-prefix 3
+(defcustom notmuch-wash-citation-lines-prefix 3
   "Always show at least this many lines from the start of a citation.
 
 If there is one more line than the sum of
 `notmuch-wash-citation-lines-prefix' and
 `notmuch-wash-citation-lines-suffix', show that, otherwise
-collapse the remaining lines into a button.")
+collapse the remaining lines into a button."
+  :type 'integer
+  :group 'notmuch-wash)
 
-(defvar notmuch-wash-citation-lines-suffix 3
+(defcustom notmuch-wash-citation-lines-suffix 3
   "Always show at least this many lines from the end of a citation.
 
 If there is one more line than the sum of
 `notmuch-wash-citation-lines-prefix' and
 `notmuch-wash-citation-lines-suffix', show that, otherwise
-collapse the remaining lines into a button.")
+collapse the remaining lines into a button."
+  :type 'integer
+  :group 'notmuch-wash)
 
-(defvar notmuch-wash-wrap-lines-length nil
+(defcustom notmuch-wash-wrap-lines-length nil
   "Wrap line after

[PATCH v1 0/2] emacs: Improve notmuch-wash customisation.

2014-10-31 Thread David Edmondson

emacs: Improve notmuch-wash customisation.

Minor updates to the customisation of notmuch-wash features.


David Edmondson (2):
  emacs: More flexible washed faces.
  emacs: Washing should use more `defcustom'.

 emacs/notmuch-wash.el | 109 +++---
 1 file changed, 76 insertions(+), 33 deletions(-)

-- 
1.9.3 (Apple Git-50)

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v1 1/2] emacs: More flexible washed faces.

2014-10-31 Thread David Edmondson
The faces used when washing messages should be notmuch specific and
inherit from the underlying emacs face rather than using it
directly. This allows the washed face to be modified without requiring
the modification of the underlying face.
---
 emacs/notmuch-wash.el | 21 +++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/emacs/notmuch-wash.el b/emacs/notmuch-wash.el
index 8fe91e1..1844400 100644
--- a/emacs/notmuch-wash.el
+++ b/emacs/notmuch-wash.el
@@ -27,6 +27,10 @@
 
 ;;
 
+(defgroup notmuch-wash nil
+  "Cleaning up messages for display."
+  :group 'notmuch)
+
 (defvar notmuch-wash-signature-regexp
   "^\\(-- ?\\|_+\\)$"
   "Pattern to match a line that separates content from signature.")
@@ -95,6 +99,19 @@ current window. If this is a number, lines will be wrapped 
after
 this many characters or at the window width (whichever one is
 lower).")
 
+(defface notmuch-wash-toggle-button
+  '((t (:inherit font-lock-comment-face)))
+  "Face used for buttons toggling the visibility of washed away
+message parts."
+  :group 'notmuch-wash
+  :group 'notmuch-faces)
+
+(defface notmuch-wash-cited-text
+  '((t (:inherit message-cited-text)))
+  "Face used for cited text."
+  :group 'notmuch-wash
+  :group 'notmuch-faces)
+
 (defun notmuch-wash-toggle-invisible-action (cite-button)
   ;; Toggle overlay visibility
   (let ((overlay (button-get cite-button 'overlay)))
@@ -117,7 +134,7 @@ lower).")
 (define-button-type 'notmuch-wash-button-invisibility-toggle-type
   'action 'notmuch-wash-toggle-invisible-action
   'follow-link t
-  'face 'font-lock-comment-face
+  'face 'notmuch-wash-toggle-button
   :supertype 'notmuch-button-type)
 
 (define-button-type 'notmuch-wash-button-citation-toggle-type
@@ -192,7 +209,7 @@ that PREFIX should not include a newline."
 (let* ((cite-start (match-beginning 0))
   (cite-end (match-end 0))
   (cite-lines (count-lines cite-start cite-end)))
-  (overlay-put (make-overlay cite-start cite-end) 'face 
'message-cited-text)
+  (overlay-put (make-overlay cite-start cite-end) 'face 
'notmuch-wash-cited-text)
   (when (> cite-lines (+ notmuch-wash-citation-lines-prefix
 notmuch-wash-citation-lines-suffix
 1))
-- 
1.9.3 (Apple Git-50)

___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v5 7/7] cli: search: Add --filter-by option to configure address filtering

2014-10-31 Thread Michal Sojka
This option allows to configure the criterion for duplicate address
filtering. Without this option, all unique combinations of name and
address parts are printed. This option allows to filter the output
more, for example to only contain unique address parts.
---
 completion/notmuch-completion.bash |  6 +++-
 completion/notmuch-completion.zsh  |  3 +-
 doc/man1/notmuch-search.rst| 39 ++-
 notmuch-search.c   | 51 --
 test/T095-search-filter-by.sh  | 64 ++
 5 files changed, 158 insertions(+), 5 deletions(-)
 create mode 100755 test/T095-search-filter-by.sh

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index 39cd829..b625b02 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -305,12 +305,16 @@ _notmuch_search()
COMPREPLY=( $( compgen -W "true false flag all" -- "${cur}" ) )
return
;;
+   --filter-by)
+   COMPREPLY=( $( compgen -W "nameaddr name addr addrfold 
nameaddrfold" -- "${cur}" ) )
+   return
+   ;;
 esac

 ! $split &&
 case "${cur}" in
-*)
-   local options="--format= --output= --sort= --offset= --limit= 
--exclude= --duplicate="
+   local options="--format= --output= --sort= --offset= --limit= 
--exclude= --duplicate= --filter-by="
compopt -o nospace
COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
;;
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index d7e5a5e..c1ccc32 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -53,7 +53,8 @@ _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 count))'
+'--output=[select what to output]:output:((summary threads messages files 
tags sender recipients count))' \
+'--filter-by=[filter out duplicate addresses]:filter-by:((nameaddr\:"both 
name and address part" name\:"name part" addr\:"address part" 
addrfold\:"case-insensitive address part" nameaddrfold\:"name and 
case-insensitive address part"))'
 }

 _notmuch()
diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
index ec89200..3a5556b 100644
--- a/doc/man1/notmuch-search.rst
+++ b/doc/man1/notmuch-search.rst
@@ -85,7 +85,8 @@ Supported options for **search** include
 (--format=text0), as a JSON array (--format=json), or as
 an S-Expression list (--format=sexp).

-Duplicate addresses are filtered out.
+Duplicate addresses are filtered out. Filtering can be
+configured with the --filter-by option.

Note: Searching for **sender** should be much faster than
searching for **recipients**, because sender addresses are
@@ -158,6 +159,42 @@ Supported options for **search** include
 prefix. The prefix matches messages based on filenames. This
 option filters filenames of the matching messages.

+``--filter-by=``\ (**nameaddr**\ \|\ **name** \|\ **addr**\ \|\ 
**addrfold**\ \|\ **nameaddrfold**\)
+
+   Can be used with ``--output=sender`` or
+   ``--output=recipients`` to filter out duplicate addresses. The
+   filtering algorithm receives a sequence of email addresses and
+   outputs the same sequence without the addresses that are
+   considered a duplicate of a previously output address. What is
+   considered a duplicate depends on how the two addresses are
+   compared and this can be controlled with the following
+   keywords:
+
+   **nameaddr** means that both name and address parts are
+   compared in case-sensitive manner. Therefore, all same looking
+   addresses strings are considered duplicate. This is the
+   default.
+
+   **name** means that only the name part is compared (in
+   case-sensitive manner). For example, the addresses "John Doe
+   " and "John Doe " will be
+   considered duplicate.
+
+   **addr** means that only the address part is compared (in
+   case-sensitive manner). For example, the addresses "John Doe
+   " and "Dr. John Doe " will
+   be considered duplicate.
+
+   **addrfold** is like **addr**, but comparison is done in
+   canse-insensitive manner. For example, the addresses "John Doe
+   " and "Dr. John Doe " will
+   be considered duplicate.
+
+   **nameaddrfold** is like **nameaddr**, but address comparison
+   is done in canse-insensitive manner. For exampl

[PATCH v5 6/7] cli: search: Add --output=count

2014-10-31 Thread Michal Sojka
This output can be used with --output=recipients or --output=sender
and in addition to the addresses, it prints how many times was each
address encountered during search.
---
 completion/notmuch-completion.bash |  2 +-
 completion/notmuch-completion.zsh  |  2 +-
 doc/man1/notmuch-search.rst|  9 +--
 notmuch-search.c   | 51 --
 test/T090-search-output.sh | 50 +
 5 files changed, 102 insertions(+), 12 deletions(-)

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index cfbd389..39cd829 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -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 
sender recipients count" -- "${cur}" ) )
return
;;
--sort)
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index 3e52a00..d7e5a5e 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -53,7 +53,7 @@ _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 sender recipients count))'
 }

 _notmuch()
diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
index 42f17e4..ec89200 100644
--- a/doc/man1/notmuch-search.rst
+++ b/doc/man1/notmuch-search.rst
@@ -96,9 +96,14 @@ Supported options for **search** include
 Like **sender** but for addresses from *To*, *Cc* and
*Bcc* headers.

+   **count**
+   Can be used in combination with **sender** or
+   **recipients** to print the count of how many times was
+   the address encountered during search.
+
This option can be given multiple times to combine different
-   outputs. Currently, this is only supported for **sender** and
-   **recipients** outputs.
+   outputs. Currently, this is only supported for **sender**,
+   **recipients** and **count** outputs.

 ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
 This option can be used to present results in either
diff --git a/notmuch-search.c b/notmuch-search.c
index eae749a..15527c4 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -30,9 +30,10 @@ typedef enum {
 OUTPUT_TAGS= 1 << 4,
 OUTPUT_SENDER  = 1 << 5,
 OUTPUT_RECIPIENTS  = 1 << 6,
+OUTPUT_COUNT   = 1 << 7,
 } output_t;

-#define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS)
+#define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS | OUTPUT_COUNT)

 typedef struct {
 sprinter_t *format;
@@ -47,6 +48,7 @@ typedef struct {
 typedef struct {
 const char *name;
 const char *addr;
+int count;
 } mailbox_t;

 /* Return two stable query strings that identify exactly the matched
@@ -235,17 +237,24 @@ is_duplicate (const search_options_t *opt, GHashTable 
*addrs, const char *name,
 {
 notmuch_bool_t duplicate;
 char *key;
+mailbox_t *mailbox;

 key = talloc_asprintf (opt->format, "%s <%s>", name, addr);
 if (! key)
return FALSE;

-duplicate = g_hash_table_lookup_extended (addrs, key, NULL, NULL);
+duplicate = g_hash_table_lookup_extended (addrs, key, NULL, 
(gpointer)&mailbox);

-if (! duplicate)
-   g_hash_table_insert (addrs, key, NULL);
-else
+if (! duplicate) {
+   mailbox = talloc (opt->format, mailbox_t);
+   mailbox->name = talloc_strdup (mailbox, name);
+   mailbox->addr = talloc_strdup (mailbox, addr);
+   mailbox->count = 1;
+   g_hash_table_insert (addrs, key, mailbox);
+} else {
+   mailbox->count++;
talloc_free (key);
+}

 return duplicate;
 }
@@ -255,6 +264,7 @@ print_mailbox (const search_options_t *opt, const mailbox_t 
*mailbox)
 {
 const char *name = mailbox->name;
 const char *addr = mailbox->addr;
+int count = mailbox->count;
 sprinter_t *format = opt->format;

 if (format->is_text_printer) {
@@ -269,6 +279,10 @@ print_mailbox (const search_options_t *opt, const 
mailbox_t *mailbox)
fprintf (stderr, "Error: out of memory\n");
return;
}
+   if (count > 0) {
+   format->integer (format, count);
+   format->string (format, "\t");
+   }
   

[PATCH v5 5/7] cli: search: Do not output duplicate addresses

2014-10-31 Thread Michal Sojka
This filters out duplicate addresses from address outputs (sender,
receivers).

It also also adds tests for the new outputs.

The code here is an extended version of a patch from Jani Nikula.
---
 doc/man1/notmuch-search.rst |  2 ++
 notmuch-search.c| 51 ++
 test/T090-search-output.sh  | 87 +
 3 files changed, 134 insertions(+), 6 deletions(-)

diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
index b6607c9..42f17e4 100644
--- a/doc/man1/notmuch-search.rst
+++ b/doc/man1/notmuch-search.rst
@@ -85,6 +85,8 @@ Supported options for **search** include
 (--format=text0), as a JSON array (--format=json), or as
 an S-Expression list (--format=sexp).

+Duplicate addresses are filtered out.
+
Note: Searching for **sender** should be much faster than
searching for **recipients**, because sender addresses are
cached directly in the database whereas other addresses
diff --git a/notmuch-search.c b/notmuch-search.c
index 31e4221..eae749a 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -229,6 +229,27 @@ do_search_threads (search_options_t *opt)
 return 0;
 }

+/* Returns TRUE iff name and addr is duplicate. */
+static notmuch_bool_t
+is_duplicate (const search_options_t *opt, GHashTable *addrs, const char 
*name, const char *addr)
+{
+notmuch_bool_t duplicate;
+char *key;
+
+key = talloc_asprintf (opt->format, "%s <%s>", name, addr);
+if (! key)
+   return FALSE;
+
+duplicate = g_hash_table_lookup_extended (addrs, key, NULL, NULL);
+
+if (! duplicate)
+   g_hash_table_insert (addrs, key, NULL);
+else
+   talloc_free (key);
+
+return duplicate;
+}
+
 static void
 print_mailbox (const search_options_t *opt, const mailbox_t *mailbox)
 {
@@ -265,7 +286,8 @@ print_mailbox (const search_options_t *opt, const mailbox_t 
*mailbox)

 /* Print addresses from InternetAddressList.  */
 static void
-process_address_list (const search_options_t *opt, InternetAddressList *list)
+process_address_list (const search_options_t *opt, GHashTable *addrs,
+ InternetAddressList *list)
 {
 InternetAddress *address;
 int i;
@@ -281,7 +303,7 @@ process_address_list (const search_options_t *opt, 
InternetAddressList *list)
if (group_list == NULL)
continue;

-   process_address_list (opt, group_list);
+   process_address_list (opt, addrs, group_list);
} else {
InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX 
(address);
mailbox_t mbx = {
@@ -289,6 +311,9 @@ process_address_list (const search_options_t *opt, 
InternetAddressList *list)
.addr = internet_address_mailbox_get_addr (mailbox),
};

+   if (is_duplicate (opt, addrs, mbx.name, mbx.addr))
+   continue;
+
print_mailbox (opt, &mbx);
}
 }
@@ -296,7 +321,7 @@ process_address_list (const search_options_t *opt, 
InternetAddressList *list)

 /* Print addresses from a message header.  */
 static void
-process_address_header (const search_options_t *opt, const char *value)
+process_address_header (const search_options_t *opt, GHashTable *addrs, const 
char *value)
 {
 InternetAddressList *list;

@@ -307,7 +332,13 @@ process_address_header (const search_options_t *opt, const 
char *value)
 if (list == NULL)
return;

-process_address_list (opt, list);
+process_address_list (opt, addrs, list);
+}
+
+static void
+_my_talloc_free_for_g_hash (void *ptr)
+{
+talloc_free (ptr);
 }

 static int
@@ -317,8 +348,13 @@ do_search_messages (search_options_t *opt)
 notmuch_messages_t *messages;
 notmuch_filenames_t *filenames;
 sprinter_t *format = opt->format;
+GHashTable *addresses = NULL;
 int i;

+if (opt->output & OUTPUT_ADDRESS_FLAGS)
+   addresses = g_hash_table_new_full (g_str_hash, g_str_equal,
+  _my_talloc_free_for_g_hash, NULL);
+
 if (opt->offset < 0) {
opt->offset += notmuch_query_count_messages (opt->query);
if (opt->offset < 0)
@@ -366,7 +402,7 @@ do_search_messages (search_options_t *opt)
const char *addrs;

addrs = notmuch_message_get_header (message, "from");
-   process_address_header (opt, addrs);
+   process_address_header (opt, addresses, addrs);
}

if (opt->output & OUTPUT_RECIPIENTS) {
@@ -376,7 +412,7 @@ do_search_messages (search_options_t *opt)

for (j = 0; j < ARRAY_SIZE (hdrs); j++) {
addrs = notmuch_message_get_header (message, hdrs[j]);
-   process_address_header (opt, addrs);
+   process_address_header (opt, addresses, addrs);
}
}
}
@@ -384,6 +420,9 @@ do_search_messages (search_optio

[PATCH v5 4/7] cli: search: Add --output={sender,recipients}

2014-10-31 Thread Michal Sojka
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 commit will change this. For this reason, tests are
introduced there.

We use mailbox_t rather than InternetAddressMailbox because we will
need to extend it in a following commit.

This code is based on a patch from Jani Nikula.
---
 completion/notmuch-completion.bash |   2 +-
 completion/notmuch-completion.zsh  |   3 +-
 doc/man1/notmuch-search.rst|  22 ++-
 notmuch-search.c   | 115 -
 4 files changed, 137 insertions(+), 5 deletions(-)

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index 0571dc9..cfbd389 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -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)
diff --git a/completion/notmuch-completion.zsh 
b/completion/notmuch-completion.zsh
index 67a9aba..3e52a00 100644
--- a/completion/notmuch-completion.zsh
+++ b/completion/notmuch-completion.zsh
@@ -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()
diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
index 90160f2..b6607c9 100644
--- a/doc/man1/notmuch-search.rst
+++ b/doc/man1/notmuch-search.rst
@@ -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
diff --git a/notmuch-search.c b/notmuch-search.c
index ce46877..31e4221 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -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,87 @@ do_search_threads (search_options_t *opt)
 return 0;
 }

+static void
+print_mailbox (const search_options_t *opt, const mailbox

[PATCH v5 3/7] cli: search: Convert --output to keyword-flag argument

2014-10-31 Thread Michal Sojka
This converts "notmuch search" to use the recently introduced
keyword-flag argument parser. At this point, it only makes the code
slightly less readable but following commits that add new --output
keywords will profit from this.
---
 notmuch-search.c | 35 ++-
 1 file changed, 18 insertions(+), 17 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index 0c3e972..ce46877 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -23,11 +23,11 @@
 #include "string-util.h"

 typedef enum {
-OUTPUT_SUMMARY,
-OUTPUT_THREADS,
-OUTPUT_MESSAGES,
-OUTPUT_FILES,
-OUTPUT_TAGS
+OUTPUT_SUMMARY = 1 << 0,
+OUTPUT_THREADS = 1 << 1,
+OUTPUT_MESSAGES= 1 << 2,
+OUTPUT_FILES   = 1 << 3,
+OUTPUT_TAGS= 1 << 4,
 } output_t;

 typedef struct {
@@ -338,7 +338,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
 notmuch_database_t *notmuch;
 search_options_t opt = {
.sort = NOTMUCH_SORT_NEWEST_FIRST,
-   .output = OUTPUT_SUMMARY,
+   .output = 0,
.offset = 0,
.limit = -1, /* unlimited */
.dupe = -1,
@@ -367,7 +367,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
  { "text0", NOTMUCH_FORMAT_TEXT0 },
  { 0, 0 } } },
{ NOTMUCH_OPT_INT, ¬much_format_version, "format-version", 0, 0 },
-   { NOTMUCH_OPT_KEYWORD, &opt.output, "output", 'o',
+   { NOTMUCH_OPT_KEYWORD_FLAGS, &opt.output, "output", 'o',
  (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },
  { "threads", OUTPUT_THREADS },
  { "messages", OUTPUT_MESSAGES },
@@ -390,6 +390,9 @@ notmuch_search_command (notmuch_config_t *config, int argc, 
char *argv[])
 if (opt_index < 0)
return EXIT_FAILURE;

+if (! opt.output)
+   opt.output = OUTPUT_SUMMARY;
+
 switch (format_sel) {
 case NOTMUCH_FORMAT_TEXT:
opt.format = sprinter_text_create (config, stdout);
@@ -455,19 +458,17 @@ notmuch_search_command (notmuch_config_t *config, int 
argc, char *argv[])
notmuch_query_set_omit_excluded (opt.query, exclude);
 }

-switch (opt.output) {
-default:
-case OUTPUT_SUMMARY:
-case OUTPUT_THREADS:
+if (opt.output == OUTPUT_SUMMARY ||
+   opt.output == OUTPUT_THREADS)
ret = do_search_threads (&opt);
-   break;
-case OUTPUT_MESSAGES:
-case OUTPUT_FILES:
+else if (opt.output == OUTPUT_MESSAGES ||
+opt.output == OUTPUT_FILES)
ret = do_search_messages (&opt);
-   break;
-case OUTPUT_TAGS:
+else if (opt.output == OUTPUT_TAGS)
ret = do_search_tags (notmuch, &opt);
-   break;
+else {
+   fprintf (stderr, "Error: the combination of outputs is not 
supported.\n");
+   ret = 1;
 }

 notmuch_query_destroy (opt.query);
-- 
2.1.1



[PATCH v5 2/7] cli: Add support for parsing keyword-flag arguments

2014-10-31 Thread Michal Sojka
From: Jani Nikula 

This allows having multiple --foo=bar --foo=baz options on the command
line, with the corresponding values OR'd together.

[Test added by Michal Sojka]
---
 command-line-arguments.c  | 6 +-
 command-line-arguments.h  | 1 +
 test/T410-argument-parsing.sh | 3 ++-
 test/arg-test.c   | 9 +
 4 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/command-line-arguments.c b/command-line-arguments.c
index 844d6c3..c6f7269 100644
--- a/command-line-arguments.c
+++ b/command-line-arguments.c
@@ -23,7 +23,10 @@ _process_keyword_arg (const notmuch_opt_desc_t *arg_desc, 
char next, const char
 while (keywords->name) {
if (strcmp (arg_str, keywords->name) == 0) {
if (arg_desc->output_var) {
-   *((int *)arg_desc->output_var) = keywords->value;
+   if (arg_desc->opt_type == NOTMUCH_OPT_KEYWORD_FLAGS)
+   *((int *)arg_desc->output_var) |= keywords->value;
+   else
+   *((int *)arg_desc->output_var) = keywords->value;
}
return TRUE;
}
@@ -152,6 +155,7 @@ parse_option (const char *arg,

switch (try->opt_type) {
case NOTMUCH_OPT_KEYWORD:
+   case NOTMUCH_OPT_KEYWORD_FLAGS:
return _process_keyword_arg (try, next, value);
case NOTMUCH_OPT_BOOLEAN:
return _process_boolean_arg (try, next, value);
diff --git a/command-line-arguments.h b/command-line-arguments.h
index de1734a..6444129 100644
--- a/command-line-arguments.h
+++ b/command-line-arguments.h
@@ -8,6 +8,7 @@ enum notmuch_opt_type {
 NOTMUCH_OPT_BOOLEAN,   /* --verbose  */
 NOTMUCH_OPT_INT,   /* --frob=8   */
 NOTMUCH_OPT_KEYWORD,   /* --format=raw|json|text */
+NOTMUCH_OPT_KEYWORD_FLAGS, /* the above with values OR'd together */
 NOTMUCH_OPT_STRING,/* --file=/tmp/gnarf.txt  */
 NOTMUCH_OPT_POSITION   /* notmuch dump pos_arg   */
 };
diff --git a/test/T410-argument-parsing.sh b/test/T410-argument-parsing.sh
index 94e9087..2e5d7ae 100755
--- a/test/T410-argument-parsing.sh
+++ b/test/T410-argument-parsing.sh
@@ -3,9 +3,10 @@ test_description="argument parsing"
 . ./test-lib.sh

 test_begin_subtest "sanity check"
-$TEST_DIRECTORY/arg-test  pos1  --keyword=one --string=foo pos2 --int=7 > 
OUTPUT
+$TEST_DIRECTORY/arg-test  pos1  --keyword=one --string=foo pos2 --int=7 
--flag=one --flag=three > OUTPUT
 cat < EXPECTED
 keyword 1
+flags 5
 int 7
 string foo
 positional arg 1 pos1
diff --git a/test/arg-test.c b/test/arg-test.c
index 6c49eac..736686d 100644
--- a/test/arg-test.c
+++ b/test/arg-test.c
@@ -7,6 +7,7 @@ int main(int argc, char **argv){
 int opt_index=1;

 int kw_val=0;
+int fl_val=0;
 int int_val=0;
 char *pos_arg1=NULL;
 char *pos_arg2=NULL;
@@ -17,6 +18,11 @@ int main(int argc, char **argv){
  (notmuch_keyword_t []){ { "one", 1 },
  { "two", 2 },
  { 0, 0 } } },
+   { NOTMUCH_OPT_KEYWORD_FLAGS, &fl_val, "flag", 'f',
+ (notmuch_keyword_t []){ { "one",   1 << 0},
+ { "two",   1 << 1 },
+ { "three", 1 << 2 },
+ { 0, 0 } } },
{ NOTMUCH_OPT_INT, &int_val, "int", 'i', 0},
{ NOTMUCH_OPT_STRING, &string_val, "string", 's', 0},
{ NOTMUCH_OPT_POSITION, &pos_arg1, 0,0, 0},
@@ -31,6 +37,9 @@ int main(int argc, char **argv){
 if (kw_val)
printf("keyword %d\n", kw_val);

+if (fl_val)
+   printf("flags %d\n", fl_val);
+
 if (int_val)
printf("int %d\n", int_val);

-- 
2.1.1



[PATCH v5 1/7] cli: search: Refactor passing of command line options

2014-10-31 Thread Michal Sojka
Many functions that implement the search command need to access command
line options. Instead of passing each option in a separate variable, put
them in a structure and pass only this structure.

This will become handy in the following commits.
---
 notmuch-search.c | 125 ---
 1 file changed, 64 insertions(+), 61 deletions(-)

diff --git a/notmuch-search.c b/notmuch-search.c
index bc9be45..0c3e972 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -30,6 +30,16 @@ typedef enum {
 OUTPUT_TAGS
 } output_t;

+typedef struct {
+sprinter_t *format;
+notmuch_query_t *query;
+notmuch_sort_t sort;
+output_t output;
+int offset;
+int limit;
+int dupe;
+} search_options_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
@@ -70,43 +80,39 @@ get_thread_query (notmuch_thread_t *thread,
 }

 static int
-do_search_threads (sprinter_t *format,
-  notmuch_query_t *query,
-  notmuch_sort_t sort,
-  output_t output,
-  int offset,
-  int limit)
+do_search_threads (search_options_t *opt)
 {
 notmuch_thread_t *thread;
 notmuch_threads_t *threads;
 notmuch_tags_t *tags;
+sprinter_t *format = opt->format;
 time_t date;
 int i;

-if (offset < 0) {
-   offset += notmuch_query_count_threads (query);
-   if (offset < 0)
-   offset = 0;
+if (opt->offset < 0) {
+   opt->offset += notmuch_query_count_threads (opt->query);
+   if (opt->offset < 0)
+   opt->offset = 0;
 }

-threads = notmuch_query_search_threads (query);
+threads = notmuch_query_search_threads (opt->query);
 if (threads == NULL)
return 1;

 format->begin_list (format);

 for (i = 0;
-notmuch_threads_valid (threads) && (limit < 0 || i < offset + limit);
+notmuch_threads_valid (threads) && (opt->limit < 0 || i < opt->offset 
+ opt->limit);
 notmuch_threads_move_to_next (threads), i++)
 {
thread = notmuch_threads_get (threads);

-   if (i < offset) {
+   if (i < opt->offset) {
notmuch_thread_destroy (thread);
continue;
}

-   if (output == OUTPUT_THREADS) {
+   if (opt->output == OUTPUT_THREADS) {
format->set_prefix (format, "thread");
format->string (format,
notmuch_thread_get_thread_id (thread));
@@ -123,7 +129,7 @@ do_search_threads (sprinter_t *format,

format->begin_map (format);

-   if (sort == NOTMUCH_SORT_OLDEST_FIRST)
+   if (opt->sort == NOTMUCH_SORT_OLDEST_FIRST)
date = notmuch_thread_get_oldest_date (thread);
else
date = notmuch_thread_get_newest_date (thread);
@@ -215,40 +221,36 @@ do_search_threads (sprinter_t *format,
 }

 static int
-do_search_messages (sprinter_t *format,
-   notmuch_query_t *query,
-   output_t output,
-   int offset,
-   int limit,
-   int dupe)
+do_search_messages (search_options_t *opt)
 {
 notmuch_message_t *message;
 notmuch_messages_t *messages;
 notmuch_filenames_t *filenames;
+sprinter_t *format = opt->format;
 int i;

-if (offset < 0) {
-   offset += notmuch_query_count_messages (query);
-   if (offset < 0)
-   offset = 0;
+if (opt->offset < 0) {
+   opt->offset += notmuch_query_count_messages (opt->query);
+   if (opt->offset < 0)
+   opt->offset = 0;
 }

-messages = notmuch_query_search_messages (query);
+messages = notmuch_query_search_messages (opt->query);
 if (messages == NULL)
return 1;

 format->begin_list (format);

 for (i = 0;
-notmuch_messages_valid (messages) && (limit < 0 || i < offset + limit);
+notmuch_messages_valid (messages) && (opt->limit < 0 || i < 
opt->offset + opt->limit);
 notmuch_messages_move_to_next (messages), i++)
 {
-   if (i < offset)
+   if (i < opt->offset)
continue;

message = notmuch_messages_get (messages);

-   if (output == OUTPUT_FILES) {
+   if (opt->output == OUTPUT_FILES) {
int j;
filenames = notmuch_message_get_filenames (message);

@@ -256,7 +258,7 @@ do_search_messages (sprinter_t *format,
 notmuch_filenames_valid (filenames);
 notmuch_filenames_move_to_next (filenames), j++)
{
-   if (dupe < 0 || dupe == j) {
+   if (opt->dupe < 0 || opt->dupe == j) {
format->string (format, notmuch_filenames_get (filenames));
format->separator (format);
}
@@ -283,12 +285,13 @@ do_search_messages (sprinter_t *f

[PATCH v5 0/7] notmuch search --output=sender/recipients

2014-10-31 Thread Michal Sojka
Hi all,

this is v5 of the search --output=address series. It obsoletes v4
(id:1414421455-3037-1-git-send-email-sojkam1 at fel.cvut.cz).

I addresses comments from Mark and Tomi. Based on the comments to v4
and earlier versions, patches 1-4 should be ready for merging. Patch 5
is a non-controversial part of the controversial --filter-by patch and
could be probably merged after review.

Patch 6 needs at least a review and patch 7 needs more discussion.

Changes from v4:

- patch changed to commit in commit messages
- opt->format changed to format
- Added comments to process_* functions
- duplicite changed to duplicate
- check_duplicate changed to is_duplicate
- Deduplication was split into two commits: basic deduplication
  without a command line option and configurable deduplication with
  --fiter-by.

Changes from v3:

- `o' renamed to `opt'.
- Conversion of --output from keyword to keyword-flags is now a
  separate patch.
- Structured output formats print name and address separately.
- Added test for --format=json.
- Changed --filter-by default to nameaddr. In v2, the default was
  addrfold, in v3 the default was no filtering at all. I believe that
  Mark's suggestion to make nameaddr the default is good trade off.
- Added new --output=count
- Minor style fixes
- Few typos fixed
- There is no way to output unfiltered (duplicite) addresses.
  Hopefully, the introduction of --output=count is sufficient
  replacement for this "feature".

Cheers,
-Michal


Jani Nikula (1):
  cli: Add support for parsing keyword-flag arguments

Michal Sojka (6):
  cli: search: Refactor passing of command line options
  cli: search: Convert --output to keyword-flag argument
  cli: search: Add --output={sender,recipients}
  cli: search: Do not output duplicate addresses
  cli: search: Add --output=count
  cli: search: Add --filter-by option to configure address filtering

 command-line-arguments.c   |   6 +-
 command-line-arguments.h   |   1 +
 completion/notmuch-completion.bash |   8 +-
 completion/notmuch-completion.zsh  |   4 +-
 doc/man1/notmuch-search.rst|  66 ++-
 notmuch-search.c   | 388 +
 test/T090-search-output.sh | 137 +
 test/T095-search-filter-by.sh  |  64 ++
 test/T410-argument-parsing.sh  |   3 +-
 test/arg-test.c|   9 +
 10 files changed, 604 insertions(+), 82 deletions(-)
 create mode 100755 test/T095-search-filter-by.sh

-- 
2.1.1



Re: "search --path=directory/" is lame(-ish)

2014-10-31 Thread David Bremner
David Edmondson  writes:

>
> Could we always prune a trailing slash from the path: component of a
> query before using it?
>

As I understand it, this is complicated by the fact that we pass the
whole string to Xapian to be parsed as a query, so we don't really know
where the path: terms are. We could in principle preprocess the string
(more) but that seems to be pretty fragile, and we'd have to minimally
deal with quoting of e.g. paths with spaces in them.

d
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] cli: add support for notmuch search --duplicate=N with --output=messages

2014-10-31 Thread David Edmondson
On Thu, Oct 30 2014, Jani Nikula wrote:
> Print the message IDs of all messages matching the search terms that
> have at least N files associated with them.

Briefly tested as working. I commend this patch to the masters of push.

Thanks Jani!

> ---
>  doc/man1/notmuch-search.rst | 12 
>  notmuch-search.c| 34 ++
>  2 files changed, 38 insertions(+), 8 deletions(-)
>
> diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst
> index 90160f21e23c..aeba4bf604f6 100644
> --- a/doc/man1/notmuch-search.rst
> +++ b/doc/man1/notmuch-search.rst
> @@ -122,10 +122,14 @@ Supported options for **search** include
>  rather than the number of matching messages.
>  
>  ``--duplicate=N``
> -Effective with ``--output=files``, output the Nth filename
> -associated with each message matching the query (N is 1-based).
> -If N is greater than the number of files associated with the
> -message, don't print anything.
> +For ``--output=files``, output the Nth filename associated
> +with each message matching the query (N is 1-based). If N is
> +greater than the number of files associated with the message,
> +don't print anything.
> +
> +For ``--output=messages``, only output message IDs of messages
> +matching the search terms that have at least N filenames
> +associated with them.
>  
>  Note that this option is orthogonal with the **folder:** search
>  prefix. The prefix matches messages based on filenames. This
> diff --git a/notmuch-search.c b/notmuch-search.c
> index bc9be4593ecc..2bf876fd5abf 100644
> --- a/notmuch-search.c
> +++ b/notmuch-search.c
> @@ -215,6 +215,24 @@ do_search_threads (sprinter_t *format,
>  }
>  
>  static int
> +_count_filenames (notmuch_message_t *message)
> +{
> +notmuch_filenames_t *filenames;
> +int i = 0;
> +
> +filenames = notmuch_message_get_filenames (message);
> +
> +while (notmuch_filenames_valid (filenames)) {
> + notmuch_filenames_move_to_next (filenames);
> + i++;
> +}
> +
> +notmuch_filenames_destroy (filenames);
> +
> +return i;
> +}
> +
> +static int
>  do_search_messages (sprinter_t *format,
>   notmuch_query_t *query,
>   output_t output,
> @@ -265,10 +283,13 @@ do_search_messages (sprinter_t *format,
>   notmuch_filenames_destroy( filenames );
>  
>   } else { /* output == OUTPUT_MESSAGES */
> - format->set_prefix (format, "id");
> - format->string (format,
> - notmuch_message_get_message_id (message));
> - format->separator (format);
> + /* special case 1 for speed */
> + if (dupe <= 1 || dupe <= _count_filenames (message)) {
> + format->set_prefix (format, "id");
> + format->string (format,
> + notmuch_message_get_message_id (message));
> + format->separator (format);
> + }
>   }
>  
>   notmuch_message_destroy (message);
> @@ -387,6 +408,11 @@ notmuch_search_command (notmuch_config_t *config, int 
> argc, char *argv[])
>  if (opt_index < 0)
>   return EXIT_FAILURE;
>  
> +if (output != OUTPUT_FILES && output != OUTPUT_MESSAGES && dupe != -1) {
> + fprintf (stderr, "Error: --duplicate=N is only supported with 
> --output=files and --output=messages.\n");
> + return EXIT_FAILURE;
> +}
> +
>  switch (format_sel) {
>  case NOTMUCH_FORMAT_TEXT:
>   format = sprinter_text_create (config, stdout);
> -- 
> 2.1.1
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v1] emacs: Improved header display.

2014-10-31 Thread David Edmondson
On Thu, Oct 30 2014, Jani Nikula wrote:
> On Thu, 30 Oct 2014, David Edmondson  wrote:
>> Truncate the displayed headers to the window width. Show an ellipsis
>> if the displayed header is truncated. Add a binding 'T' to toggle the
>> truncation of headers. Add the not-displayed section of the header as
>> a tooltip to the displayed section.
>
> Thanks for your efforts, David - I hate it that I'm going to sound
> ungrateful since I asked for something like this. But not quite like
> this...
>
> I think more header lines than just one should be displayed untruncated
> by default. I think it's okay to show, say, five lines of To: or Cc: and
> that'll probably cover most emails without truncation. And when the
> header does get truncated, I'd really like to see the indication more
> predominantly displayed than just ellipsis.
>
> I'm thinking of something like this, similar to notmuch-wash:
>
> ---
> To: u...@example.com, u...@example.com, u...@example.com,
> u...@example.com, u...@example.com, u...@example.com, u...@example.com,
> u...@example.com, u...@example.com, u...@example.com, u...@example.com,
> u...@example.com, u...@example.com, u...@example.com, u...@example.com,
> [ 42 more header lines. Click/Enter to show. ]
> Cc: u...@example.com
> ---

Hmm. That seems pretty ugly to me :-)

As you mentioned in #notmuch, the implementation below is also not quite
correct - it assumes that it is splitting addresses but is also used on
the non-address headers. There are also some oddities when the header
lines of collapsed messages are compressed.

If anyone else is particularly interested then I may come back to it,
but otherwise will leave it alone.

> BR,
> Jani.
>
>
>> ---
>>  emacs/notmuch-show.el | 54 
>> ---
>>  1 file changed, 51 insertions(+), 3 deletions(-)
>>
>> diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el
>> index a997482..523cef5 100644
>> --- a/emacs/notmuch-show.el
>> +++ b/emacs/notmuch-show.el
>> @@ -443,9 +443,56 @@ message at DEPTH in the current thread."
>>  ")\n")
>>  (overlay-put (make-overlay start (point)) 'face 
>> 'notmuch-message-summary-face)))
>>  
>> +(defun notmuch-truncate-nicely (addresses target-length)
>> +  ;; If it fits, everything is easy.
>> +  (if (< (length addresses) target-length)
>> +  (cons addresses nil)
>> +(let* ((visible-length (- target-length (length "...")))
>> +   (visible (substring addresses 0 visible-length))
>> +   (invisible (substring addresses visible-length)))
>> +  ;; Try to terminate the visible string at a good break point.
>> +  (when (string-match "\\(.+\\),\\([^,]*\\)" visible)
>> +;; Order is important (second clause is destructive on
>> +;; `visible'.
>> +(setq invisible (concat (match-string 2 visible) invisible)
>> +  visible (match-string 1 visible)))
>> +  ;; `invisible' can end up with a leading space or
>> +  ;; comma-space, because the list of addresses is
>> +  ;; seperated with ", ", but we split on ",".
>> +  (setq invisible (replace-regexp-in-string "^[, ]*\\(.*\\)$" "\\1" 
>> invisible))
>> +  (cons visible invisible
>> +
>> +(defun notmuch-show-toggle-header-truncation ()
>> +  (interactive)
>> +  (let ((invisibility-spec-member (cons 'notmuch-show-mode t)))
>> +(if (member invisibility-spec-member buffer-invisibility-spec)
>> +(remove-from-invisibility-spec invisibility-spec-member)
>> +  (add-to-invisibility-spec invisibility-spec-member)))
>> +  ;; Required to have the change in visibility take effect.
>> +  (force-window-update))
>> +
>>  (defun notmuch-show-insert-header (header header-value)
>>"Insert a single header."
>> -  (insert header ": " (notmuch-sanitize header-value) "\n"))
>> +  (let* ((header-value (notmuch-sanitize header-value))
>> + (header-colon (concat header ": "))
>> + (available-width (- (window-width) (length header-colon)))
>> + (v-i (notmuch-truncate-nicely header-value available-width)))
>> +
>> +(insert header-colon)
>> +(let ((visible (car v-i))
>> +  (invisible (cdr v-i)))
>> +  (when invisible
>> +(setq visible (propertize visible 'help-echo (concat "..." invisible
>> +  (insert visible)
>> +  (when invisible
>> +(insert ", ")
>> +(let ((start (point))
>> +  overlay)
>> +  (insert invisible)
>> +  (setq overlay (make-overlay start (point)))
>> +  (overlay-put overlay 'invisible 'notmuch-show-mode)
>> +  (overlay-put overlay 'isearch-open-invisible #'delete-overlay
>> +(insert "\n")))
>>  
>>  (defun notmuch-show-insert-headers (headers)
>>"Insert the headers of the current message."
>> @@ -1328,6 +1375,7 @@ reset based on the original query."
>>  (define-key map "$" 'notmuch-show-toggle-process-crypto)
>>  (define-key map "<" 'notmuch-show-toggle-thread-indentation)
>>  (define-key map "t" 'toggle-truncate-lines)
>> +(define-key map "T" 'notm