[Patch v2 9/9] test/tagging: add test for naked punctuation in tags; compare with quoting spaces.

2013-01-06 Thread da...@tethera.net
From: David Bremner 

This test also serves as documentation of the quoting
requirements. The comment lines are so that it exactly matches the man
page. Nothing more embarrassing than having an example in the man page
fail.
---
 test/tagging |   25 +
 1 file changed, 25 insertions(+)

diff --git a/test/tagging b/test/tagging
index 1717e72..1f5632c 100755
--- a/test/tagging
+++ b/test/tagging
@@ -198,6 +198,31 @@ notmuch dump --format=batch-tag | sort > OUTPUT
 notmuch restore --format=batch-tag < BACKUP
 test_expect_equal_file EXPECTED OUTPUT

+test_begin_subtest "--batch: only space and % needs to be encoded."
+notmuch dump --format=batch-tag > BACKUP
+
+notmuch tag --batch < EXPECTED
++%23possible%5c +%26are +%28tags%29 +crazy%7b +inbox +match%2acrazy 
+space%20in%20tags +tag4 +tag5 +unread +winner -- id:msg-002 at 
notmuch-test-suite
++foo%3a%3abar%25 +found%3a%3ait +inbox +tag5 +unread +winner -- id:msg-001 at 
notmuch-test-suite
+EOF
+
+notmuch dump --format=batch-tag | sort > OUTPUT
+notmuch restore --format=batch-tag < BACKUP
+test_expect_equal_file EXPECTED OUTPUT
+
 test_begin_subtest '--batch: unicode message-ids'

 ${TEST_DIRECTORY}/random-corpus --config-path=${NOTMUCH_CONFIG} \
-- 
1.7.10.4



[Patch v2 8/9] man: document notmuch tag --batch, --input options

2013-01-06 Thread da...@tethera.net
From: Jani Nikula 

---
 man/man1/notmuch-tag.1 |   92 
 1 file changed, 92 insertions(+)

diff --git a/man/man1/notmuch-tag.1 b/man/man1/notmuch-tag.1
index 9444aa4..3aa2fa5 100644
--- a/man/man1/notmuch-tag.1
+++ b/man/man1/notmuch-tag.1
@@ -6,6 +6,11 @@ notmuch-tag \- add/remove tags for all messages matching the 
search terms
 .B notmuch tag
 .RI "+<" tag ">|\-<" tag "> [...] [\-\-] <" search-term "> [...]"

+.B notmuch tag
+.RI "--batch"
+.RI "[ --input=<" filename "> ]"
+
+
 .SH DESCRIPTION

 Add/remove tags for all messages matching the search terms.
@@ -30,6 +35,93 @@ updates the maildir flags according to tag changes if the
 configuration option is enabled. See \fBnotmuch-config\fR(1) for
 details.

+Supported options for
+.B tag
+include
+.RS 4
+.TP 4
+.BR \-\-batch
+
+Read batch tagging operations from a file (stdin by default). This is more
+efficient than repeated
+.B notmuch tag
+invocations. See
+.B TAG FILE FORMAT
+below for the input format. This option is not compatible with
+specifying tagging on the command line.
+.RE
+
+.RS 4
+.TP 4
+.BR "\-\-input=" 
+
+Read input from given file, instead of from stdin. Implies
+.BR --batch .
+
+.SH TAG FILE FORMAT
+
+The input must consist of lines of the format:
+
+.RI "+<" tag ">|\-<" tag "> [...] [\-\-] <" query ">"
+
+Each line is interpreted similarly to
+.B notmuch tag
+command line arguments. The delimiter is one or more spaces ' '. Any
+characters in
+.RI < tag >
+.B may
+be hex-encoded with %NN where NN is the hexadecimal value of the
+character. To hex-encode a character with a multi-byte UTF-8 encoding,
+hex-encode each byte.
+Any spaces in 
+.B must
+be hex-encoded as %20. Any characters that are not
+part of
+.RI  < tag >
+.B must not
+be hex-encoded.
+
+In the future tag:"tag with spaces" style quoting may be supported for
+.RI < tag >
+as well;
+for this reason all double quote characters in
+.RI < tag >
+.B should
+be hex-encoded.
+
+The
+.RI < query >
+should be quoted using Xapian boolean term quoting rules: if a term
+contains whitespace or a close paren or starts with a double quote, it
+must be enclosed in double quotes (not including any prefix) and
+double quotes inside the term must be doubled (see below for
+examples).
+
+Leading and trailing space ' ' is ignored. Empty lines and lines
+beginning with '#' are ignored.
+
+.SS EXAMPLE
+
+The following shows a valid input to batch tagging. Note that only the
+isolated '*' acts as a wildcard. Also note the two different quotings
+of the tag
+.B space in tags
+.
+.RS
+.nf
++winner *
++foo::bar%25 -- (One and Two) or (One and tag:winner)
++found::it -- tag:foo::bar%
+# ignore this line and the next
+
++space%20in%20tags -- Two
+# add tag '(tags)', among other stunts.
++crazy{ +(tags) +&are +#possible\ -- tag:"space in tags"
++match*crazy -- tag:crazy{
++some_tag -- id:"this is ""nauty)"""
+.fi
+.RE
+
 .SH SEE ALSO

 \fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1),
-- 
1.7.10.4



[Patch v2 7/9] test/tagging: add test for exotic message-ids and batch tagging

2013-01-06 Thread da...@tethera.net
From: David Bremner 

The (now fixed) bug that this test revealed is that unquoted
message-ids with whitespace or other control characters in them are
split into several tokens by the Xapian query parser.
---
 test/tagging |   18 ++
 1 file changed, 18 insertions(+)

diff --git a/test/tagging b/test/tagging
index 417112b..1717e72 100755
--- a/test/tagging
+++ b/test/tagging
@@ -198,6 +198,24 @@ notmuch dump --format=batch-tag | sort > OUTPUT
 notmuch restore --format=batch-tag < BACKUP
 test_expect_equal_file EXPECTED OUTPUT

+test_begin_subtest '--batch: unicode message-ids'
+
+${TEST_DIRECTORY}/random-corpus --config-path=${NOTMUCH_CONFIG} \
+ --num-messages=100
+
+notmuch dump --format=batch-tag | sed 's/^.* -- /+common_tag -- /' | \
+sort > EXPECTED
+
+notmuch dump --format=batch-tag | sed 's/^.* -- /  -- /' | \
+notmuch restore --format=batch-tag
+
+notmuch tag --batch < EXPECTED
+
+notmuch dump --format=batch-tag| \
+sort > OUTPUT
+
+test_expect_equal_file EXPECTED OUTPUT
+
 test_expect_code 1 "Empty tag names" 'notmuch tag + One'

 test_expect_code 1 "Tag name beginning with -" 'notmuch tag +- One'
-- 
1.7.10.4



[Patch v2 6/9] test/tagging: add tests for exotic tags

2013-01-06 Thread da...@tethera.net
From: David Bremner 

We test quotes seperately because they matter to the query escaper.
---
 test/tagging |   66 ++
 1 file changed, 66 insertions(+)

diff --git a/test/tagging b/test/tagging
index 405ad7c..417112b 100755
--- a/test/tagging
+++ b/test/tagging
@@ -132,6 +132,72 @@ EOF
 notmuch restore --format=batch-tag < BACKUP
 test_expect_equal_file EXPECTED OUTPUT

+test_begin_subtest '--batch: tags with quotes'
+notmuch dump --format=batch-tag > BACKUP
+
+notmuch tag --batch < EXPECTED
++%22%27%22%22%22%27 +inbox +tag5 +unread -- id:msg-001 at notmuch-test-suite
++%22%27%22%27%22%22%27%27 +inbox +tag4 +tag5 +unread -- id:msg-002 at 
notmuch-test-suite
+EOF
+
+notmuch dump --format=batch-tag | sort > OUTPUT
+notmuch restore --format=batch-tag < BACKUP
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest '--batch: tags with punctuation and space'
+notmuch dump --format=batch-tag > BACKUP
+
+notmuch tag --batch < EXPECTED
++%21@%23%20%24%25%5e%26%2a%29-_=+%5b%7b%5c%20%7c%3b%3a%27%20%22,.%3c%60%7e 
+inbox +tag4 +tag5 +unread -- id:msg-002 at notmuch-test-suite
++%21@%23%20%24%25%5e%26%2a%29-_=+%5b%7b%5c%20%7c%3b%3a%27%20%22,.%3c%60%7e 
+inbox +tag5 +unread -- id:msg-001 at notmuch-test-suite
+EOF
+
+notmuch dump --format=batch-tag | sort > OUTPUT
+notmuch restore --format=batch-tag < BACKUP
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest '--batch: unicode tags'
+notmuch dump --format=batch-tag > BACKUP
+
+notmuch tag --batch < EXPECTED
++%2a@%7d%cf%b5%f4%85%80%adO3%da%a7 
+=%e0%ac%95%c8%b3+%ef%aa%95%c8%a64w%c7%9d%c9%a2%cf%b3%d6%82%24B%c4%a9%c5%a1UX%ee%99%b0%27E7%ca%a4%d0%8b%5d
 
+A%e1%a0%bc%de%8b%d5%b2V%d9%9b%f3%b5%a2%a3M%d8%a1u@%f0%a0%ac%948%7e%f0%ab%86%af%27
 +L%df%85%ef%a1%a5m@%d3%96%c2%ab%d4%9f%ca%b8%f3%b3%a2%bf%c7%b1_u%d7%b4%c7%b1 
+P%c4%98%2f +R +inbox +tag4 +tag5 +unread 
+%7e%d1%8b%25%ec%a0%ae%d1%a0M%3b%e3%b6%b7%e9%a4%87%3c%db%9a%cc%a8%e1%96%9d 
+%c4%bf7%c7%ab9H%c4%99k%ea%91%bd%c3%8ck%e2%b3%8dk%c5%952V%e4%99%b2%d9%b3%e4%8b%bda%5b%24%c7%9b
 +%da%88=f%cc%b9I%ce%af%7b%c9%97%e3%b9%8bH%cb%92X%d2%8c6 
+%dc%9crh%d2%86B%e5%97%a2%22t%ed%99%82d -- id:msg-002 at notmuch-test-suite
++%2a@%7d%cf%b5%f4%85%80%adO3%da%a7 
+=%e0%ac%95%c8%b3+%ef%aa%95%c8%a64w%c7%9d%c9%a2%cf%b3%d6%82%24B%c4%a9%c5%a1UX%ee%99%b0%27E7%ca%a4%d0%8b%5d
 
+A%e1%a0%bc%de%8b%d5%b2V%d9%9b%f3%b5%a2%a3M%d8%a1u@%f0%a0%ac%948%7e%f0%ab%86%af%27
 +L%df%85%ef%a1%a5m@%d3%96%c2%ab%d4%9f%ca%b8%f3%b3%a2%bf%c7%b1_u%d7%b4%c7%b1 
+P%c4%98%2f +R +inbox +tag5 +unread 
+%7e%d1%8b%25%ec%a0%ae%d1%a0M%3b%e3%b6%b7%e9%a4%87%3c%db%9a%cc%a8%e1%96%9d 
+%c4%bf7%c7%ab9H%c4%99k%ea%91%bd%c3%8ck%e2%b3%8dk%c5%952V%e4%99%b2%d9%b3%e4%8b%bda%5b%24%c7%9b
 +%da%88=f%cc%b9I%ce%af%7b%c9%97%e3%b9%8bH%cb%92X%d2%8c6 
+%dc%9crh%d2%86B%e5%97%a2%22t%ed%99%82d -- id:msg-001 at notmuch-test-suite
+EOF
+
+notmuch dump --format=batch-tag | sort > OUTPUT
+notmuch restore --format=batch-tag < BACKUP
+test_expect_equal_file EXPECTED OUTPUT
+
 test_expect_code 1 "Empty tag names" 'notmuch tag + One'

 test_expect_code 1 "Tag name beginning with -" 'notmuch tag +- One'
-- 
1.7.10.4



[Patch v2 5/9] test/tagging: add basic tests for batch tagging functionality

2013-01-06 Thread da...@tethera.net
From: David Bremner 

This tests argument parsing, blank lines and comments, and basic hex
decoding functionality.
---
 test/tagging |   51 +++
 1 file changed, 51 insertions(+)

diff --git a/test/tagging b/test/tagging
index cd16585..405ad7c 100755
--- a/test/tagging
+++ b/test/tagging
@@ -46,6 +46,57 @@ test_expect_equal "$output" "\
 thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (:\"  inbox tag1 unread)
 thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 tag4 unread)"

+test_begin_subtest "--batch"
+notmuch tag --batch < batch.in  < batch.expected < backup.tags
+notmuch tag --input=batch.in
+notmuch search \* | notmuch_search_sanitize > OUTPUT
+notmuch restore --format=batch-tag < backup.tags
+test_expect_equal_file batch.expected OUTPUT
+
+test_begin_subtest "--batch --input"
+notmuch dump --format=batch-tag > backup.tags
+notmuch tag --batch --input=batch.in
+notmuch search \* | notmuch_search_sanitize > OUTPUT
+notmuch restore --format=batch-tag < backup.tags
+test_expect_equal_file batch.expected OUTPUT
+
+test_begin_subtest "--batch, blank lines and comments"
+notmuch dump | sort > EXPECTED
+notmuch tag --batch < OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
 test_begin_subtest '--batch: checking error messages'
 notmuch dump --format=batch-tag > BACKUP
 notmuch tag --batch 

[Patch v2 4/9] test/tagging: add test for error messages of tag --batch

2013-01-06 Thread da...@tethera.net
From: David Bremner 

This is based on the similar test for notmuch restore, but the parser
in batch tagging mode is less tolerant of a few cases, in particular
those tested by illegal_tag.
---
 test/tagging |   35 +++
 1 file changed, 35 insertions(+)

diff --git a/test/tagging b/test/tagging
index 980ff92..cd16585 100755
--- a/test/tagging
+++ b/test/tagging
@@ -46,6 +46,41 @@ test_expect_equal "$output" "\
 thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (:\"  inbox tag1 unread)
 thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 tag4 unread)"

+test_begin_subtest '--batch: checking error messages'
+notmuch dump --format=batch-tag > BACKUP
+notmuch tag --batch 

[Patch v2 3/9] cli: add support for batch tagging operations to "notmuch tag"

2013-01-06 Thread da...@tethera.net
From: Jani Nikula 

Add support for batch tagging operations through stdin to "notmuch
tag". This can be enabled with the new --batch command line option to
"notmuch tag". The input must consist of lines of the format:

+|- [...] [--]  [...]

Each line is interpreted similarly to "notmuch tag" command line
arguments. The delimiter is one or more spaces ' '. Any characters in
 MAY be hex encoded with %NN where NN is the hexadecimal value of
the character. Any ' ' and '%' characters in  and MUST be hex
encoded (using %20 and %25, respectively). For future-proofing, any
'"' characters in  SHOULD be hex-encoded.

Any characters that are not part of  or
MUST NOT be hex encoded.

 is passed verbatim to Xapian

Leading and trailing space ' ' is ignored. Empty lines and lines
beginning with '#' are ignored.

Signed-off-by: Jani Nikula 

Hacked-like-crazy-by: David Bremner 
---
 notmuch-tag.c |   94 -
 1 file changed, 86 insertions(+), 8 deletions(-)

diff --git a/notmuch-tag.c b/notmuch-tag.c
index 8129912..7fc614d 100644
--- a/notmuch-tag.c
+++ b/notmuch-tag.c
@@ -128,6 +128,46 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const 
char *query_string,
 return interrupted;
 }

+static int
+tag_file (void *ctx, notmuch_database_t *notmuch, tag_op_flag_t flags,
+ FILE *input)
+{
+char *line = NULL;
+char *query_string = NULL;
+size_t line_size = 0;
+ssize_t line_len;
+int ret = 0;
+tag_op_list_t *tag_ops;
+
+tag_ops = tag_op_list_create (ctx);
+if (tag_ops == NULL) {
+   fprintf (stderr, "Out of memory.\n");
+   return 1;
+}
+
+while ((line_len = getline (&line, &line_size, input)) != -1 &&
+  ! interrupted) {
+
+   ret = parse_tag_line (ctx, line, TAG_FLAG_NONE,
+ &query_string, tag_ops);
+
+   if (ret > 0)
+   continue;
+
+   if (ret < 0)
+   break;
+
+   ret = tag_query (ctx, notmuch, query_string, tag_ops, flags);
+   if (ret)
+   break;
+}
+
+if (line)
+   free (line);
+
+return ret;
+}
+
 int
 notmuch_tag_command (void *ctx, int argc, char *argv[])
 {
@@ -137,6 +177,10 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])
 notmuch_database_t *notmuch;
 struct sigaction action;
 tag_op_flag_t tag_flags = TAG_FLAG_NONE;
+notmuch_bool_t batch = FALSE;
+FILE *input = stdin;
+char *input_file_name = NULL;
+int opt_index;
 int ret = 0;

 /* Setup our handler for SIGINT */
@@ -146,15 +190,43 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])
 action.sa_flags = SA_RESTART;
 sigaction (SIGINT, &action, NULL);

-tag_ops = tag_op_list_create (ctx);
-if (tag_ops == NULL) {
-   fprintf (stderr, "Out of memory.\n");
+notmuch_opt_desc_t options[] = {
+   { NOTMUCH_OPT_BOOLEAN, &batch, "batch", 0, 0 },
+   { NOTMUCH_OPT_STRING, &input_file_name, "input", 'i', 0 },
+   { 0, 0, 0, 0, 0 }
+};
+
+opt_index = parse_arguments (argc, argv, options, 1);
+if (opt_index < 0)
return 1;
+
+if (input_file_name) {
+   batch = TRUE;
+   input = fopen (input_file_name, "r");
+   if (input == NULL) {
+   fprintf (stderr, "Error opening %s for reading: %s\n",
+input_file_name, strerror (errno));
+   return 1;
+   }
 }

-if (parse_tag_command_line (ctx, argc - 1, argv + 1,
-   &query_string, tag_ops))
-   return 1;
+if (batch) {
+   if (opt_index != argc) {
+   fprintf (stderr, "Can't specify both cmdline and stdin!\n");
+   return 1;
+   }
+} else {
+
+   tag_ops = tag_op_list_create (ctx);
+   if (tag_ops == NULL) {
+   fprintf (stderr, "Out of memory.\n");
+   return 1;
+   }
+
+   if (parse_tag_command_line (ctx, argc - opt_index, argv + opt_index,
+   &query_string, tag_ops))
+   return 1;
+}

 config = notmuch_config_open (ctx, NULL, NULL);
 if (config == NULL)
@@ -167,9 +239,15 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])
 if (notmuch_config_get_maildir_synchronize_flags (config))
tag_flags |= TAG_FLAG_MAILDIR_SYNC;

-ret = tag_query (ctx, notmuch, query_string, tag_ops, tag_flags);
+if (batch)
+   ret = tag_file (ctx, notmuch, tag_flags, input);
+else
+   ret = tag_query (ctx, notmuch, query_string, tag_ops, tag_flags);

 notmuch_database_destroy (notmuch);

-return ret;
+if (input != stdin)
+   fclose (input);
+
+return ret || interrupted;
 }
-- 
1.7.10.4



[Patch v2 2/9] notmuch-tag.c: convert to use tag-utils

2013-01-06 Thread da...@tethera.net
From: David Bremner 

Command line parsing is factored out into a function
parse_tag_command_line in tag-utils.c.

There is some duplicated code eliminated in tag_query, and a bunch of
translation from using the bare tag_op structs to using that tag-utils
API.
---
 notmuch-tag.c |   99 -
 tag-util.c|   51 +++--
 tag-util.h|   15 +
 3 files changed, 84 insertions(+), 81 deletions(-)

diff --git a/notmuch-tag.c b/notmuch-tag.c
index fc9d43a..8129912 100644
--- a/notmuch-tag.c
+++ b/notmuch-tag.c
@@ -19,6 +19,7 @@
  */

 #include "notmuch-client.h"
+#include "tag-util.h"
 #include "string-util.h"

 static volatile sig_atomic_t interrupted;
@@ -36,14 +37,10 @@ handle_sigint (unused (int sig))
 interrupted = 1;
 }

-typedef struct {
-const char *tag;
-notmuch_bool_t remove;
-} tag_operation_t;

 static char *
 _optimize_tag_query (void *ctx, const char *orig_query_string,
-const tag_operation_t *tag_ops)
+const tag_op_list_t *list)
 {
 /* This is subtler than it looks.  Xapian ignores the '-' operator
  * at the beginning both queries and parenthesized groups and,
@@ -60,7 +57,7 @@ _optimize_tag_query (void *ctx, const char *orig_query_string,
 size_t i;

 /* Don't optimize if there are no tag changes. */
-if (tag_ops[0].tag == NULL)
+if (tag_op_list_size (list) == 0)
return talloc_strdup (ctx, orig_query_string);

 /* Build the new query string */
@@ -69,17 +66,17 @@ _optimize_tag_query (void *ctx, const char 
*orig_query_string,
 else
query_string = talloc_asprintf (ctx, "( %s ) and (", orig_query_string);

-for (i = 0; tag_ops[i].tag && query_string; i++) {
+for (i = 0; i < tag_op_list_size (list) && query_string; i++) {
/* XXX in case of OOM, query_string will be deallocated when
 * ctx is, which might be at shutdown */
if (make_boolean_term (ctx,
-  "tag", tag_ops[i].tag,
+  "tag", tag_op_list_tag (list, i),
   &escaped, &escaped_len))
return NULL;

query_string = talloc_asprintf_append_buffer (
query_string, "%s%s%s", join,
-   tag_ops[i].remove ? "" : "not ",
+   tag_op_list_isremove (list, i) ? "" : "not ",
escaped);
join = " or ";
 }
@@ -91,17 +88,15 @@ _optimize_tag_query (void *ctx, const char 
*orig_query_string,
 return query_string;
 }

-/* Tag messages matching 'query_string' according to 'tag_ops', which
- * must be an array of tagging operations terminated with an empty
- * element. */
+/* Tag messages matching 'query_string' according to 'tag_ops'
+ */
 static int
 tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,
-  tag_operation_t *tag_ops, notmuch_bool_t synchronize_flags)
+  tag_op_list_t *tag_ops, tag_op_flag_t flags)
 {
 notmuch_query_t *query;
 notmuch_messages_t *messages;
 notmuch_message_t *message;
-int i;

 /* Optimize the query so it excludes messages that already have
  * the specified set of tags. */
@@ -124,21 +119,7 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const 
char *query_string,
 notmuch_messages_valid (messages) && ! interrupted;
 notmuch_messages_move_to_next (messages)) {
message = notmuch_messages_get (messages);
-
-   notmuch_message_freeze (message);
-
-   for (i = 0; tag_ops[i].tag; i++) {
-   if (tag_ops[i].remove)
-   notmuch_message_remove_tag (message, tag_ops[i].tag);
-   else
-   notmuch_message_add_tag (message, tag_ops[i].tag);
-   }
-
-   notmuch_message_thaw (message);
-
-   if (synchronize_flags)
-   notmuch_message_tags_to_maildir_flags (message);
-
+   tag_op_list_apply (message, tag_ops, flags | TAG_FLAG_PRE_OPTIMIZED);
notmuch_message_destroy (message);
 }

@@ -150,15 +131,13 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const 
char *query_string,
 int
 notmuch_tag_command (void *ctx, int argc, char *argv[])
 {
-tag_operation_t *tag_ops;
-int tag_ops_count = 0;
-char *query_string;
+tag_op_list_t *tag_ops = NULL;
+char *query_string = NULL;
 notmuch_config_t *config;
 notmuch_database_t *notmuch;
 struct sigaction action;
-notmuch_bool_t synchronize_flags;
-int i;
-int ret;
+tag_op_flag_t tag_flags = TAG_FLAG_NONE;
+int ret = 0;

 /* Setup our handler for SIGINT */
 memset (&action, 0, sizeof (struct sigaction));
@@ -167,54 +146,15 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])
 action.sa_flags = SA_RESTART;
 sigaction (SIGINT, &action, NULL);

-argc--; argv++; /* skip subcommand argument */
-
-/* Array of tagging operations (add or remove), terminated with an
- * empty element. */
-t

[Patch v2 1/9] tag-util: factor out rules for illegal tags, use in parse_tag_line

2013-01-06 Thread da...@tethera.net
From: David Bremner 

This will allow us to be consistent between batch tagging and command
line tagging as far as what is an illegal tag.
---
 tag-util.c |   36 +++-
 1 file changed, 31 insertions(+), 5 deletions(-)

diff --git a/tag-util.c b/tag-util.c
index ca12b3b..0a4fe78 100644
--- a/tag-util.c
+++ b/tag-util.c
@@ -31,6 +31,30 @@ line_error (tag_parse_status_t status,
 return status;
 }

+/*
+ * Test tags for some forbidden cases.
+ *
+ * return: NULL if OK,
+ *explanatory message otherwise.
+ */
+
+static const char *
+illegal_tag (const char *tag, notmuch_bool_t remove)
+{
+
+if (*tag == '\0' && ! remove)
+   return "empty tag forbidden";
+
+/* This disallows adding the non-removable tag "-" and
+ * enables notmuch tag to take long options more easily.
+ */
+
+if (*tag == '-' && ! remove)
+   return "tag starting with '-' forbidden";
+
+return NULL;
+}
+
 tag_parse_status_t
 parse_tag_line (void *ctx, char *line,
tag_op_flag_t flags,
@@ -95,11 +119,13 @@ parse_tag_line (void *ctx, char *line,
remove = (*tok == '-');
tag = tok + 1;

-   /* Maybe refuse empty tags. */
-   if (! (flags & TAG_FLAG_BE_GENEROUS) && *tag == '\0') {
-   ret = line_error (TAG_PARSE_INVALID, line_for_error,
- "empty tag");
-   goto DONE;
+   /* Maybe refuse illegal tags. */
+   if (! (flags & TAG_FLAG_BE_GENEROUS)) {
+   const char *msg = illegal_tag (tag, remove);
+   if (msg) {
+   ret = line_error (TAG_PARSE_INVALID, line_for_error, msg);
+   goto DONE;
+   }
}

/* Decode tag. */
-- 
1.7.10.4



Xapian-quoting based batch tagging.

2013-01-06 Thread da...@tethera.net
This is essentially just a rebase of 

 id:1356464567-21779-1-git-send-email-david at tethera.net

with some commit-message fixups for 

[Patch v2 3/9] cli: add support for batch tagging operations to

as suggested by Jani


Re: [PATCH] emacs: show: make id links respect window

2013-01-06 Thread Austin Clements
On Wed, 19 Dec 2012, Mark Walters  wrote:
> There is a bug in current notmuch: if you have multiple windows in one
> frame and click an id button link in a show buffer that does not
> contain point then the link is opened in the window containing point.
>
> This reads the mouse event to make sure that the correct window is
> used for the link.

Personally I prefer that point move to the clicked window, as this patch
does.  The fact that it's even possible for point not to move to the
clicked window I find strange.

> ---
>
> I think this is a bug but that could be debated. It is particularly
> easy to trigger with notmuch pick because that uses the split pane
> while focus usually remains in the `pick' pane rather than the `show'
> pane.
>
> The lisp is not pretty but seems to work.
>
> Best wishes
>
> Mark
>
>
>
>
>  emacs/notmuch-show.el |5 +
>  1 files changed, 5 insertions(+), 0 deletions(-)
>
> diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el
> index 5751d98..5664ea3 100644
> --- a/emacs/notmuch-show.el
> +++ b/emacs/notmuch-show.el
> @@ -1077,6 +1077,11 @@ buttons for a corresponding notmuch search."
>   (make-text-button (first link) (second link)
> 'action `(lambda (arg)
>(notmuch-show ,(third link)))
> +   'mouse-action `(lambda (arg)
> +(let* ((event last-input-event)
> +   (window (car (cadr event

Better would be (posn-window (event-start event)).  If you use the
accessors, I don't think the let bindings are really necessary since the
code becomes self-documenting.

> +  (select-window window)
> +  (notmuch-show ,(third link

Would it be better to (button-activate arg) so you don't have to
duplicate the action code?  (Then you also wouldn't need the
quasiquoting and unquoting.)

> 'follow-link t
> 'help-echo "Mouse-1, RET: search for this message"
> 'face goto-address-mail-face)
> -- 
> 1.7.9.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 v3] emacs: Use the minibuffer for CLI error reporting

2013-01-06 Thread David Bremner
Austin Clements  writes:

> We recently switched to popping up a buffer to report CLI errors, but
> this was too intrusive, especially for transient errors and especially
> since we made fewer things ignore errors.  This patch changes this to
> display a basic error message in the minibuffer (using Emacs' usual
> error handling path) and, if there are additional details, to log
> these to a separate error buffer and reference the error buffer from
> the minibuffer message.  This is more in line with how Emacs typically
> handles errors, but makes the details available to the user without
> flooding them with the details.

pushed.

d


[PATCH 1/1] fix line breaks in one comment paragraph in generated .notmuch-config file

2013-01-06 Thread David Bremner
Tomi Ollila  writes:

> While one comment in generated .notmuch-config file looked good in the
> source file notmuch-config.c, the generated output was inconsistently
> wide -- even breaking the 80-column boundary.

pushed, after deleting one character.

d


[PATCH 1/2] test/dump-restore: new tests for empty files and leading comments/whitespace.

2013-01-06 Thread David Bremner
david at tethera.net writes:

> From: David Bremner 
>
> Three of these are marked broken; the third is a regression test,
> since it passes by virtue of batch-tag being the default input format.

pushed, with v2 of 2/2.

d


[PATCH v5 0/6] Use Xapian query syntax for batch-tag dump/restore

2013-01-06 Thread David Bremner
Austin Clements  writes:

> This obsoletes
>
>   id:1356936162-2589-1-git-send-email-amdragon at mit.edu
>
> v5 should address all of the comments on v4 except those I
> specifically replied to (via the ML or IRC).  It also adds a new patch
> at the beginning that makes missing message IDs non-fatal in restore,
> like they were in 0.14.  This patch can be pushed separately; it's in
> this series because later tests rely on it.

pushed.

d


[PATCH v5 0/6] Use Xapian query syntax for batch-tag dump/restore

2013-01-06 Thread Mark Walters

LGTM too +1

Mark

On Sun, 06 Jan 2013, Jani Nikula  wrote:
> On Sun, 06 Jan 2013, Austin Clements  wrote:
>> This obsoletes
>>
>>   id:1356936162-2589-1-git-send-email-amdragon at mit.edu
>>
>> v5 should address all of the comments on v4 except those I
>> specifically replied to (via the ML or IRC).  It also adds a new patch
>> at the beginning that makes missing message IDs non-fatal in restore,
>> like they were in 0.14.  This patch can be pushed separately; it's in
>> this series because later tests rely on it.
>
> The series LGMT,
> Jani.
>
>>
>> The diff from v4 follows.
>>
>> diff --git a/notmuch-dump.c b/notmuch-dump.c
>> index bf01a39..a3244e0 100644
>> --- a/notmuch-dump.c
>> +++ b/notmuch-dump.c
>> @@ -103,6 +103,18 @@ notmuch_dump_command (unused (void *ctx), int argc, 
>> char *argv[])
>>  message = notmuch_messages_get (messages);
>>  message_id = notmuch_message_get_message_id (message);
>>  
>> +if (output_format == DUMP_FORMAT_BATCH_TAG &&
>> +strchr (message_id, '\n')) {
>> +/* This will produce a line break in the output, which
>> + * would be difficult to handle in tools.  However, it's
>> + * also impossible to produce an email containing a line
>> + * break in a message ID because of unfolding, so we can
>> + * safely disallow it. */
>> +fprintf (stderr, "Warning: skipping message id containing line 
>> break: \"%s\"\n", message_id);
>> +notmuch_message_destroy (message);
>> +continue;
>> +}
>> +
>>  if (output_format == DUMP_FORMAT_SUP) {
>>  fprintf (output, "%s (", message_id);
>>  }
>> @@ -133,19 +145,10 @@ notmuch_dump_command (unused (void *ctx), int argc, 
>> char *argv[])
>>  if (output_format == DUMP_FORMAT_SUP) {
>>  fputs (")\n", output);
>>  } else {
>> -if (strchr (message_id, '\n')) {
>> -/* This will produce a line break in the output, which
>> - * would be difficult to handle in tools.  However,
>> - * it's also impossible to produce an email containing
>> - * a line break in a message ID because of unfolding,
>> - * so we can safely disallow it. */
>> -fprintf (stderr, "Error: cannot dump message id containing line 
>> break: %s\n", message_id);
>> -return 1;
>> -}
>>  if (make_boolean_term (notmuch, "id", message_id,
>> &buffer, &buffer_size)) {
>> -fprintf (stderr, "Error: failed to quote message id %s\n",
>> - message_id);
>> +fprintf (stderr, "Error quoting message id %s: %s\n",
>> + message_id, strerror (errno));
>>  return 1;
>>  }
>>  fprintf (output, " -- %s\n", buffer);
>> diff --git a/notmuch-restore.c b/notmuch-restore.c
>> index 77a4c27..81d4d98 100644
>> --- a/notmuch-restore.c
>> +++ b/notmuch-restore.c
>> @@ -26,7 +26,8 @@
>>  static regex_t regex;
>>  
>>  /* Non-zero return indicates an error in retrieving the message,
>> - * or in applying the tags.
>> + * or in applying the tags.  Missing messages are reported, but not
>> + * considered errors.
>>   */
>>  static int
>>  tag_message (unused (void *ctx),
>> @@ -40,13 +41,17 @@ tag_message (unused (void *ctx),
>>  int ret = 0;
>>  
>>  status = notmuch_database_find_message (notmuch, message_id, &message);
>> -if (status || message == NULL) {
>> -fprintf (stderr, "Warning: cannot apply tags to %smessage: %s\n",
>> - message ? "" : "missing ", message_id);
>> -if (status)
>> -fprintf (stderr, "%s\n", notmuch_status_to_string (status));
>> +if (status) {
>> +fprintf (stderr, "Error applying tags to message %s: %s\n",
>> + message_id, notmuch_status_to_string (status));
>>  return 1;
>>  }
>> +if (message == NULL) {
>> +fprintf (stderr, "Warning: cannot apply tags to missing message: %s\n",
>> + message_id);
>> +/* We consider this a non-fatal error. */
>> +return 0;
>> +}
>>  
>>  /* In order to detect missing messages, this check/optimization is
>>   * intentionally done *after* first finding the message. */
>> @@ -222,12 +227,17 @@ notmuch_restore_command (unused (void *ctx), int argc, 
>> char *argv[])
>>  if (ret == 0) {
>>  ret = parse_boolean_term (line_ctx, query_string,
>>&prefix, &term);
>> -if (ret) {
>> -fprintf (stderr, "Warning: cannot parse query: %s\n",
>> - query_string);
>> +if (ret && errno == EINVAL) {
>> +fprintf (stderr, "Warning: cannot parse query: %s 
>> (skipping)\n", query_string);
>>  continue;
>> +} else if (ret) {
>> +/* This is more fatal (e.g., out of memory) */
>> +fprintf (stderr, "Error parsing query: 

Xapian-quoting based batch tagging.

2013-01-06 Thread david
This is essentially just a rebase of 

 id:1356464567-21779-1-git-send-email-da...@tethera.net

with some commit-message fixups for 

[Patch v2 3/9] cli: add support for batch tagging operations to

as suggested by Jani
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[Patch v2 4/9] test/tagging: add test for error messages of tag --batch

2013-01-06 Thread david
From: David Bremner 

This is based on the similar test for notmuch restore, but the parser
in batch tagging mode is less tolerant of a few cases, in particular
those tested by illegal_tag.
---
 test/tagging |   35 +++
 1 file changed, 35 insertions(+)

diff --git a/test/tagging b/test/tagging
index 980ff92..cd16585 100755
--- a/test/tagging
+++ b/test/tagging
@@ -46,6 +46,41 @@ test_expect_equal "$output" "\
 thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (:\"  inbox tag1 unread)
 thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 tag4 unread)"
 
+test_begin_subtest '--batch: checking error messages'
+notmuch dump --format=batch-tag > BACKUP
+notmuch tag --batch 

[Patch v2 5/9] test/tagging: add basic tests for batch tagging functionality

2013-01-06 Thread david
From: David Bremner 

This tests argument parsing, blank lines and comments, and basic hex
decoding functionality.
---
 test/tagging |   51 +++
 1 file changed, 51 insertions(+)

diff --git a/test/tagging b/test/tagging
index cd16585..405ad7c 100755
--- a/test/tagging
+++ b/test/tagging
@@ -46,6 +46,57 @@ test_expect_equal "$output" "\
 thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (:\"  inbox tag1 unread)
 thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 tag4 unread)"
 
+test_begin_subtest "--batch"
+notmuch tag --batch < batch.in  < batch.expected < backup.tags
+notmuch tag --input=batch.in
+notmuch search \* | notmuch_search_sanitize > OUTPUT
+notmuch restore --format=batch-tag < backup.tags
+test_expect_equal_file batch.expected OUTPUT
+
+test_begin_subtest "--batch --input"
+notmuch dump --format=batch-tag > backup.tags
+notmuch tag --batch --input=batch.in
+notmuch search \* | notmuch_search_sanitize > OUTPUT
+notmuch restore --format=batch-tag < backup.tags
+test_expect_equal_file batch.expected OUTPUT
+
+test_begin_subtest "--batch, blank lines and comments"
+notmuch dump | sort > EXPECTED
+notmuch tag --batch < OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
 test_begin_subtest '--batch: checking error messages'
 notmuch dump --format=batch-tag > BACKUP
 notmuch tag --batch 

[Patch v2 6/9] test/tagging: add tests for exotic tags

2013-01-06 Thread david
From: David Bremner 

We test quotes seperately because they matter to the query escaper.
---
 test/tagging |   66 ++
 1 file changed, 66 insertions(+)

diff --git a/test/tagging b/test/tagging
index 405ad7c..417112b 100755
--- a/test/tagging
+++ b/test/tagging
@@ -132,6 +132,72 @@ EOF
 notmuch restore --format=batch-tag < BACKUP
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest '--batch: tags with quotes'
+notmuch dump --format=batch-tag > BACKUP
+
+notmuch tag --batch < EXPECTED
++%22%27%22%22%22%27 +inbox +tag5 +unread -- id:msg-001@notmuch-test-suite
++%22%27%22%27%22%22%27%27 +inbox +tag4 +tag5 +unread -- 
id:msg-002@notmuch-test-suite
+EOF
+
+notmuch dump --format=batch-tag | sort > OUTPUT
+notmuch restore --format=batch-tag < BACKUP
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest '--batch: tags with punctuation and space'
+notmuch dump --format=batch-tag > BACKUP
+
+notmuch tag --batch < EXPECTED
++%21@%23%20%24%25%5e%26%2a%29-_=+%5b%7b%5c%20%7c%3b%3a%27%20%22,.%3c%60%7e 
+inbox +tag4 +tag5 +unread -- id:msg-002@notmuch-test-suite
++%21@%23%20%24%25%5e%26%2a%29-_=+%5b%7b%5c%20%7c%3b%3a%27%20%22,.%3c%60%7e 
+inbox +tag5 +unread -- id:msg-001@notmuch-test-suite
+EOF
+
+notmuch dump --format=batch-tag | sort > OUTPUT
+notmuch restore --format=batch-tag < BACKUP
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest '--batch: unicode tags'
+notmuch dump --format=batch-tag > BACKUP
+
+notmuch tag --batch < EXPECTED
++%2a@%7d%cf%b5%f4%85%80%adO3%da%a7 
+=%e0%ac%95%c8%b3+%ef%aa%95%c8%a64w%c7%9d%c9%a2%cf%b3%d6%82%24B%c4%a9%c5%a1UX%ee%99%b0%27E7%ca%a4%d0%8b%5d
 
+A%e1%a0%bc%de%8b%d5%b2V%d9%9b%f3%b5%a2%a3M%d8%a1u@%f0%a0%ac%948%7e%f0%ab%86%af%27
 +L%df%85%ef%a1%a5m@%d3%96%c2%ab%d4%9f%ca%b8%f3%b3%a2%bf%c7%b1_u%d7%b4%c7%b1 
+P%c4%98%2f +R +inbox +tag4 +tag5 +unread 
+%7e%d1%8b%25%ec%a0%ae%d1%a0M%3b%e3%b6%b7%e9%a4%87%3c%db%9a%cc%a8%e1%96%9d 
+%c4%bf7%c7%ab9H%c4%99k%ea%91%bd%c3%8ck%e2%b3%8dk%c5%952V%e4%99%b2%d9%b3%e4%8b%bda%5b%24%c7%9b
 +%da%88=f%cc%b9I%ce%af%7b%c9%97%e3%b9%8bH%cb%92X%d2%8c6 
+%dc%9crh%d2%86B%e5%97%a2%22t%ed%99%82d -- id:msg-002@notmuch-test-suite
++%2a@%7d%cf%b5%f4%85%80%adO3%da%a7 
+=%e0%ac%95%c8%b3+%ef%aa%95%c8%a64w%c7%9d%c9%a2%cf%b3%d6%82%24B%c4%a9%c5%a1UX%ee%99%b0%27E7%ca%a4%d0%8b%5d
 
+A%e1%a0%bc%de%8b%d5%b2V%d9%9b%f3%b5%a2%a3M%d8%a1u@%f0%a0%ac%948%7e%f0%ab%86%af%27
 +L%df%85%ef%a1%a5m@%d3%96%c2%ab%d4%9f%ca%b8%f3%b3%a2%bf%c7%b1_u%d7%b4%c7%b1 
+P%c4%98%2f +R +inbox +tag5 +unread 
+%7e%d1%8b%25%ec%a0%ae%d1%a0M%3b%e3%b6%b7%e9%a4%87%3c%db%9a%cc%a8%e1%96%9d 
+%c4%bf7%c7%ab9H%c4%99k%ea%91%bd%c3%8ck%e2%b3%8dk%c5%952V%e4%99%b2%d9%b3%e4%8b%bda%5b%24%c7%9b
 +%da%88=f%cc%b9I%ce%af%7b%c9%97%e3%b9%8bH%cb%92X%d2%8c6 
+%dc%9crh%d2%86B%e5%97%a2%22t%ed%99%82d -- id:msg-001@notmuch-test-suite
+EOF
+
+notmuch dump --format=batch-tag | sort > OUTPUT
+notmuch restore --format=batch-tag < BACKUP
+test_expect_equal_file EXPECTED OUTPUT
+
 test_expect_code 1 "Empty tag names" 'notmuch tag + One'
 
 test_expect_code 1 "Tag name beginning with -" 'notmuch tag +- One'
-- 
1.7.10.4

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


[Patch v2 2/9] notmuch-tag.c: convert to use tag-utils

2013-01-06 Thread david
From: David Bremner 

Command line parsing is factored out into a function
parse_tag_command_line in tag-utils.c.

There is some duplicated code eliminated in tag_query, and a bunch of
translation from using the bare tag_op structs to using that tag-utils
API.
---
 notmuch-tag.c |   99 -
 tag-util.c|   51 +++--
 tag-util.h|   15 +
 3 files changed, 84 insertions(+), 81 deletions(-)

diff --git a/notmuch-tag.c b/notmuch-tag.c
index fc9d43a..8129912 100644
--- a/notmuch-tag.c
+++ b/notmuch-tag.c
@@ -19,6 +19,7 @@
  */
 
 #include "notmuch-client.h"
+#include "tag-util.h"
 #include "string-util.h"
 
 static volatile sig_atomic_t interrupted;
@@ -36,14 +37,10 @@ handle_sigint (unused (int sig))
 interrupted = 1;
 }
 
-typedef struct {
-const char *tag;
-notmuch_bool_t remove;
-} tag_operation_t;
 
 static char *
 _optimize_tag_query (void *ctx, const char *orig_query_string,
-const tag_operation_t *tag_ops)
+const tag_op_list_t *list)
 {
 /* This is subtler than it looks.  Xapian ignores the '-' operator
  * at the beginning both queries and parenthesized groups and,
@@ -60,7 +57,7 @@ _optimize_tag_query (void *ctx, const char *orig_query_string,
 size_t i;
 
 /* Don't optimize if there are no tag changes. */
-if (tag_ops[0].tag == NULL)
+if (tag_op_list_size (list) == 0)
return talloc_strdup (ctx, orig_query_string);
 
 /* Build the new query string */
@@ -69,17 +66,17 @@ _optimize_tag_query (void *ctx, const char 
*orig_query_string,
 else
query_string = talloc_asprintf (ctx, "( %s ) and (", orig_query_string);
 
-for (i = 0; tag_ops[i].tag && query_string; i++) {
+for (i = 0; i < tag_op_list_size (list) && query_string; i++) {
/* XXX in case of OOM, query_string will be deallocated when
 * ctx is, which might be at shutdown */
if (make_boolean_term (ctx,
-  "tag", tag_ops[i].tag,
+  "tag", tag_op_list_tag (list, i),
   &escaped, &escaped_len))
return NULL;
 
query_string = talloc_asprintf_append_buffer (
query_string, "%s%s%s", join,
-   tag_ops[i].remove ? "" : "not ",
+   tag_op_list_isremove (list, i) ? "" : "not ",
escaped);
join = " or ";
 }
@@ -91,17 +88,15 @@ _optimize_tag_query (void *ctx, const char 
*orig_query_string,
 return query_string;
 }
 
-/* Tag messages matching 'query_string' according to 'tag_ops', which
- * must be an array of tagging operations terminated with an empty
- * element. */
+/* Tag messages matching 'query_string' according to 'tag_ops'
+ */
 static int
 tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,
-  tag_operation_t *tag_ops, notmuch_bool_t synchronize_flags)
+  tag_op_list_t *tag_ops, tag_op_flag_t flags)
 {
 notmuch_query_t *query;
 notmuch_messages_t *messages;
 notmuch_message_t *message;
-int i;
 
 /* Optimize the query so it excludes messages that already have
  * the specified set of tags. */
@@ -124,21 +119,7 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const 
char *query_string,
 notmuch_messages_valid (messages) && ! interrupted;
 notmuch_messages_move_to_next (messages)) {
message = notmuch_messages_get (messages);
-
-   notmuch_message_freeze (message);
-
-   for (i = 0; tag_ops[i].tag; i++) {
-   if (tag_ops[i].remove)
-   notmuch_message_remove_tag (message, tag_ops[i].tag);
-   else
-   notmuch_message_add_tag (message, tag_ops[i].tag);
-   }
-
-   notmuch_message_thaw (message);
-
-   if (synchronize_flags)
-   notmuch_message_tags_to_maildir_flags (message);
-
+   tag_op_list_apply (message, tag_ops, flags | TAG_FLAG_PRE_OPTIMIZED);
notmuch_message_destroy (message);
 }
 
@@ -150,15 +131,13 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const 
char *query_string,
 int
 notmuch_tag_command (void *ctx, int argc, char *argv[])
 {
-tag_operation_t *tag_ops;
-int tag_ops_count = 0;
-char *query_string;
+tag_op_list_t *tag_ops = NULL;
+char *query_string = NULL;
 notmuch_config_t *config;
 notmuch_database_t *notmuch;
 struct sigaction action;
-notmuch_bool_t synchronize_flags;
-int i;
-int ret;
+tag_op_flag_t tag_flags = TAG_FLAG_NONE;
+int ret = 0;
 
 /* Setup our handler for SIGINT */
 memset (&action, 0, sizeof (struct sigaction));
@@ -167,54 +146,15 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])
 action.sa_flags = SA_RESTART;
 sigaction (SIGINT, &action, NULL);
 
-argc--; argv++; /* skip subcommand argument */
-
-/* Array of tagging operations (add or remove), terminated with an
- * empty eleme

[Patch v2 1/9] tag-util: factor out rules for illegal tags, use in parse_tag_line

2013-01-06 Thread david
From: David Bremner 

This will allow us to be consistent between batch tagging and command
line tagging as far as what is an illegal tag.
---
 tag-util.c |   36 +++-
 1 file changed, 31 insertions(+), 5 deletions(-)

diff --git a/tag-util.c b/tag-util.c
index ca12b3b..0a4fe78 100644
--- a/tag-util.c
+++ b/tag-util.c
@@ -31,6 +31,30 @@ line_error (tag_parse_status_t status,
 return status;
 }
 
+/*
+ * Test tags for some forbidden cases.
+ *
+ * return: NULL if OK,
+ *explanatory message otherwise.
+ */
+
+static const char *
+illegal_tag (const char *tag, notmuch_bool_t remove)
+{
+
+if (*tag == '\0' && ! remove)
+   return "empty tag forbidden";
+
+/* This disallows adding the non-removable tag "-" and
+ * enables notmuch tag to take long options more easily.
+ */
+
+if (*tag == '-' && ! remove)
+   return "tag starting with '-' forbidden";
+
+return NULL;
+}
+
 tag_parse_status_t
 parse_tag_line (void *ctx, char *line,
tag_op_flag_t flags,
@@ -95,11 +119,13 @@ parse_tag_line (void *ctx, char *line,
remove = (*tok == '-');
tag = tok + 1;
 
-   /* Maybe refuse empty tags. */
-   if (! (flags & TAG_FLAG_BE_GENEROUS) && *tag == '\0') {
-   ret = line_error (TAG_PARSE_INVALID, line_for_error,
- "empty tag");
-   goto DONE;
+   /* Maybe refuse illegal tags. */
+   if (! (flags & TAG_FLAG_BE_GENEROUS)) {
+   const char *msg = illegal_tag (tag, remove);
+   if (msg) {
+   ret = line_error (TAG_PARSE_INVALID, line_for_error, msg);
+   goto DONE;
+   }
}
 
/* Decode tag. */
-- 
1.7.10.4

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


[Patch v2 7/9] test/tagging: add test for exotic message-ids and batch tagging

2013-01-06 Thread david
From: David Bremner 

The (now fixed) bug that this test revealed is that unquoted
message-ids with whitespace or other control characters in them are
split into several tokens by the Xapian query parser.
---
 test/tagging |   18 ++
 1 file changed, 18 insertions(+)

diff --git a/test/tagging b/test/tagging
index 417112b..1717e72 100755
--- a/test/tagging
+++ b/test/tagging
@@ -198,6 +198,24 @@ notmuch dump --format=batch-tag | sort > OUTPUT
 notmuch restore --format=batch-tag < BACKUP
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest '--batch: unicode message-ids'
+
+${TEST_DIRECTORY}/random-corpus --config-path=${NOTMUCH_CONFIG} \
+ --num-messages=100
+
+notmuch dump --format=batch-tag | sed 's/^.* -- /+common_tag -- /' | \
+sort > EXPECTED
+
+notmuch dump --format=batch-tag | sed 's/^.* -- /  -- /' | \
+notmuch restore --format=batch-tag
+
+notmuch tag --batch < EXPECTED
+
+notmuch dump --format=batch-tag| \
+sort > OUTPUT
+
+test_expect_equal_file EXPECTED OUTPUT
+
 test_expect_code 1 "Empty tag names" 'notmuch tag + One'
 
 test_expect_code 1 "Tag name beginning with -" 'notmuch tag +- One'
-- 
1.7.10.4

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


[Patch v2 9/9] test/tagging: add test for naked punctuation in tags; compare with quoting spaces.

2013-01-06 Thread david
From: David Bremner 

This test also serves as documentation of the quoting
requirements. The comment lines are so that it exactly matches the man
page. Nothing more embarrassing than having an example in the man page
fail.
---
 test/tagging |   25 +
 1 file changed, 25 insertions(+)

diff --git a/test/tagging b/test/tagging
index 1717e72..1f5632c 100755
--- a/test/tagging
+++ b/test/tagging
@@ -198,6 +198,31 @@ notmuch dump --format=batch-tag | sort > OUTPUT
 notmuch restore --format=batch-tag < BACKUP
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "--batch: only space and % needs to be encoded."
+notmuch dump --format=batch-tag > BACKUP
+
+notmuch tag --batch < EXPECTED
++%23possible%5c +%26are +%28tags%29 +crazy%7b +inbox +match%2acrazy 
+space%20in%20tags +tag4 +tag5 +unread +winner -- id:msg-002@notmuch-test-suite
++foo%3a%3abar%25 +found%3a%3ait +inbox +tag5 +unread +winner -- 
id:msg-001@notmuch-test-suite
+EOF
+
+notmuch dump --format=batch-tag | sort > OUTPUT
+notmuch restore --format=batch-tag < BACKUP
+test_expect_equal_file EXPECTED OUTPUT
+
 test_begin_subtest '--batch: unicode message-ids'
 
 ${TEST_DIRECTORY}/random-corpus --config-path=${NOTMUCH_CONFIG} \
-- 
1.7.10.4

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


[Patch v2 3/9] cli: add support for batch tagging operations to "notmuch tag"

2013-01-06 Thread david
From: Jani Nikula 

Add support for batch tagging operations through stdin to "notmuch
tag". This can be enabled with the new --batch command line option to
"notmuch tag". The input must consist of lines of the format:

+|- [...] [--]  [...]

Each line is interpreted similarly to "notmuch tag" command line
arguments. The delimiter is one or more spaces ' '. Any characters in
 MAY be hex encoded with %NN where NN is the hexadecimal value of
the character. Any ' ' and '%' characters in  and MUST be hex
encoded (using %20 and %25, respectively). For future-proofing, any
'"' characters in  SHOULD be hex-encoded.

Any characters that are not part of  or
MUST NOT be hex encoded.

 is passed verbatim to Xapian

Leading and trailing space ' ' is ignored. Empty lines and lines
beginning with '#' are ignored.

Signed-off-by: Jani Nikula 

Hacked-like-crazy-by: David Bremner 
---
 notmuch-tag.c |   94 -
 1 file changed, 86 insertions(+), 8 deletions(-)

diff --git a/notmuch-tag.c b/notmuch-tag.c
index 8129912..7fc614d 100644
--- a/notmuch-tag.c
+++ b/notmuch-tag.c
@@ -128,6 +128,46 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const 
char *query_string,
 return interrupted;
 }
 
+static int
+tag_file (void *ctx, notmuch_database_t *notmuch, tag_op_flag_t flags,
+ FILE *input)
+{
+char *line = NULL;
+char *query_string = NULL;
+size_t line_size = 0;
+ssize_t line_len;
+int ret = 0;
+tag_op_list_t *tag_ops;
+
+tag_ops = tag_op_list_create (ctx);
+if (tag_ops == NULL) {
+   fprintf (stderr, "Out of memory.\n");
+   return 1;
+}
+
+while ((line_len = getline (&line, &line_size, input)) != -1 &&
+  ! interrupted) {
+
+   ret = parse_tag_line (ctx, line, TAG_FLAG_NONE,
+ &query_string, tag_ops);
+
+   if (ret > 0)
+   continue;
+
+   if (ret < 0)
+   break;
+
+   ret = tag_query (ctx, notmuch, query_string, tag_ops, flags);
+   if (ret)
+   break;
+}
+
+if (line)
+   free (line);
+
+return ret;
+}
+
 int
 notmuch_tag_command (void *ctx, int argc, char *argv[])
 {
@@ -137,6 +177,10 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])
 notmuch_database_t *notmuch;
 struct sigaction action;
 tag_op_flag_t tag_flags = TAG_FLAG_NONE;
+notmuch_bool_t batch = FALSE;
+FILE *input = stdin;
+char *input_file_name = NULL;
+int opt_index;
 int ret = 0;
 
 /* Setup our handler for SIGINT */
@@ -146,15 +190,43 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])
 action.sa_flags = SA_RESTART;
 sigaction (SIGINT, &action, NULL);
 
-tag_ops = tag_op_list_create (ctx);
-if (tag_ops == NULL) {
-   fprintf (stderr, "Out of memory.\n");
+notmuch_opt_desc_t options[] = {
+   { NOTMUCH_OPT_BOOLEAN, &batch, "batch", 0, 0 },
+   { NOTMUCH_OPT_STRING, &input_file_name, "input", 'i', 0 },
+   { 0, 0, 0, 0, 0 }
+};
+
+opt_index = parse_arguments (argc, argv, options, 1);
+if (opt_index < 0)
return 1;
+
+if (input_file_name) {
+   batch = TRUE;
+   input = fopen (input_file_name, "r");
+   if (input == NULL) {
+   fprintf (stderr, "Error opening %s for reading: %s\n",
+input_file_name, strerror (errno));
+   return 1;
+   }
 }
 
-if (parse_tag_command_line (ctx, argc - 1, argv + 1,
-   &query_string, tag_ops))
-   return 1;
+if (batch) {
+   if (opt_index != argc) {
+   fprintf (stderr, "Can't specify both cmdline and stdin!\n");
+   return 1;
+   }
+} else {
+
+   tag_ops = tag_op_list_create (ctx);
+   if (tag_ops == NULL) {
+   fprintf (stderr, "Out of memory.\n");
+   return 1;
+   }
+
+   if (parse_tag_command_line (ctx, argc - opt_index, argv + opt_index,
+   &query_string, tag_ops))
+   return 1;
+}
 
 config = notmuch_config_open (ctx, NULL, NULL);
 if (config == NULL)
@@ -167,9 +239,15 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])
 if (notmuch_config_get_maildir_synchronize_flags (config))
tag_flags |= TAG_FLAG_MAILDIR_SYNC;
 
-ret = tag_query (ctx, notmuch, query_string, tag_ops, tag_flags);
+if (batch)
+   ret = tag_file (ctx, notmuch, tag_flags, input);
+else
+   ret = tag_query (ctx, notmuch, query_string, tag_ops, tag_flags);
 
 notmuch_database_destroy (notmuch);
 
-return ret;
+if (input != stdin)
+   fclose (input);
+
+return ret || interrupted;
 }
-- 
1.7.10.4

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


[Patch v2 8/9] man: document notmuch tag --batch, --input options

2013-01-06 Thread david
From: Jani Nikula 

---
 man/man1/notmuch-tag.1 |   92 
 1 file changed, 92 insertions(+)

diff --git a/man/man1/notmuch-tag.1 b/man/man1/notmuch-tag.1
index 9444aa4..3aa2fa5 100644
--- a/man/man1/notmuch-tag.1
+++ b/man/man1/notmuch-tag.1
@@ -6,6 +6,11 @@ notmuch-tag \- add/remove tags for all messages matching the 
search terms
 .B notmuch tag
 .RI "+<" tag ">|\-<" tag "> [...] [\-\-] <" search-term "> [...]"
 
+.B notmuch tag
+.RI "--batch"
+.RI "[ --input=<" filename "> ]"
+
+
 .SH DESCRIPTION
 
 Add/remove tags for all messages matching the search terms.
@@ -30,6 +35,93 @@ updates the maildir flags according to tag changes if the
 configuration option is enabled. See \fBnotmuch-config\fR(1) for
 details.
 
+Supported options for
+.B tag
+include
+.RS 4
+.TP 4
+.BR \-\-batch
+
+Read batch tagging operations from a file (stdin by default). This is more
+efficient than repeated
+.B notmuch tag
+invocations. See
+.B TAG FILE FORMAT
+below for the input format. This option is not compatible with
+specifying tagging on the command line.
+.RE
+
+.RS 4
+.TP 4
+.BR "\-\-input=" 
+
+Read input from given file, instead of from stdin. Implies
+.BR --batch .
+
+.SH TAG FILE FORMAT
+
+The input must consist of lines of the format:
+
+.RI "+<" tag ">|\-<" tag "> [...] [\-\-] <" query ">"
+
+Each line is interpreted similarly to
+.B notmuch tag
+command line arguments. The delimiter is one or more spaces ' '. Any
+characters in
+.RI < tag >
+.B may
+be hex-encoded with %NN where NN is the hexadecimal value of the
+character. To hex-encode a character with a multi-byte UTF-8 encoding,
+hex-encode each byte.
+Any spaces in 
+.B must
+be hex-encoded as %20. Any characters that are not
+part of
+.RI  < tag >
+.B must not
+be hex-encoded.
+
+In the future tag:"tag with spaces" style quoting may be supported for
+.RI < tag >
+as well;
+for this reason all double quote characters in
+.RI < tag >
+.B should
+be hex-encoded.
+
+The
+.RI < query >
+should be quoted using Xapian boolean term quoting rules: if a term
+contains whitespace or a close paren or starts with a double quote, it
+must be enclosed in double quotes (not including any prefix) and
+double quotes inside the term must be doubled (see below for
+examples).
+
+Leading and trailing space ' ' is ignored. Empty lines and lines
+beginning with '#' are ignored.
+
+.SS EXAMPLE
+
+The following shows a valid input to batch tagging. Note that only the
+isolated '*' acts as a wildcard. Also note the two different quotings
+of the tag
+.B space in tags
+.
+.RS
+.nf
++winner *
++foo::bar%25 -- (One and Two) or (One and tag:winner)
++found::it -- tag:foo::bar%
+# ignore this line and the next
+
++space%20in%20tags -- Two
+# add tag '(tags)', among other stunts.
++crazy{ +(tags) +&are +#possible\ -- tag:"space in tags"
++match*crazy -- tag:crazy{
++some_tag -- id:"this is ""nauty)"""
+.fi
+.RE
+
 .SH SEE ALSO
 
 \fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1),
-- 
1.7.10.4

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


Re: [PATCH v3] emacs: Use the minibuffer for CLI error reporting

2013-01-06 Thread David Bremner
Austin Clements  writes:

> We recently switched to popping up a buffer to report CLI errors, but
> this was too intrusive, especially for transient errors and especially
> since we made fewer things ignore errors.  This patch changes this to
> display a basic error message in the minibuffer (using Emacs' usual
> error handling path) and, if there are additional details, to log
> these to a separate error buffer and reference the error buffer from
> the minibuffer message.  This is more in line with how Emacs typically
> handles errors, but makes the details available to the user without
> flooding them with the details.

pushed.

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


Re: [PATCH 1/1] fix line breaks in one comment paragraph in generated .notmuch-config file

2013-01-06 Thread David Bremner
Tomi Ollila  writes:

> While one comment in generated .notmuch-config file looked good in the
> source file notmuch-config.c, the generated output was inconsistently
> wide -- even breaking the 80-column boundary.

pushed, after deleting one character.

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


Re: [PATCH 1/2] test/dump-restore: new tests for empty files and leading comments/whitespace.

2013-01-06 Thread David Bremner
da...@tethera.net writes:

> From: David Bremner 
>
> Three of these are marked broken; the third is a regression test,
> since it passes by virtue of batch-tag being the default input format.

pushed, with v2 of 2/2.

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


Re: [PATCH v5 0/6] Use Xapian query syntax for batch-tag dump/restore

2013-01-06 Thread David Bremner
Austin Clements  writes:

> This obsoletes
>
>   id:1356936162-2589-1-git-send-email-amdra...@mit.edu
>
> v5 should address all of the comments on v4 except those I
> specifically replied to (via the ML or IRC).  It also adds a new patch
> at the beginning that makes missing message IDs non-fatal in restore,
> like they were in 0.14.  This patch can be pushed separately; it's in
> this series because later tests rely on it.

pushed.

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


[PATCH] emacs: show: make id links respect window

2013-01-06 Thread Tomi Ollila
On Sun, Jan 06 2013, Mark Walters  wrote:

> On Sat, 05 Jan 2013, Tomi Ollila  wrote:
>> On Wed, Dec 26 2012, David Bremner  wrote:
>>
>>> Mark Walters  writes:
>>>
> I can imagine that people would want/like the "open in other window" 
> effect of the current code, even if the reason is a bug.

 That's definitely possible. I generally expect a mouse click to select
 the window I click and this feels counter intuitive. I think that some
 people might like an option "open this link in a new window" but I would
 guess that would like that whether they clicked or pressed RET on the
 button.
>>>
>>> I don't care much either way myself, but before we change notmuch-show
>>> behaviour (effectively) to accomodate notmuch-pick, I'd like a bit more
>>> feedback from other people.
>>
>> I tested the old behaviour -- split frame to 2 windows, one containing
>> *scratch* and one *notmuch-hello*. Then I searched for 'obsoletes',
>> chose last message (id:1356936162-2589-1-git-send-email-amdragon at mit.edu)
>> moved point to *scratch* buffer and clicked the id: link -- and indeed,
>> the *scratch* window was replaced.
>>
>> I didn't look or test Mark's patch as he stated:
>> "The lisp is not pretty but seems to work." ;)
>> ... well, not entirely because of that but I trust it opens the message
>> in window where the clicked link were and keeps point where it used to
>> be before clicking (in case point was in different window).
>
> It doesn't quite do this: point moves to the window that was clicked. It
> is just as easy to do as Tomi says (patch below but I should resend if
> people like it so the commit message gets picked up).

It seems to be the default (and iniuitively expected) behaviour that
point moves to the window that was clicked... so that's ok :)

> Incidentally, I would be interested to know what people expect the
> following to do: go to notmuch hello and then search. Now display the
> results in two windows simultaneously (either split the frame into two
> windows (c-x 2) or use 2 frames (C-x 5 2)) and then press q.
>
> What actually happens is that q runs kill buffer so it disappears in
> both windows: one of which will fall back to notmuch-hello and one of
> which will fall back to some other window (eg scratch)

I think that is just expected behaviour, there are e.g.. c-x b and c-x 0
which should be familiar to every emacs users...

> Best wishes
>
> Mark

Tomi

PS: did you change anything in the patch below ?

>
>  emacs/notmuch-show.el |5 +
>  1 files changed, 5 insertions(+), 0 deletions(-)
>
> diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el
> index 5751d98..00b9b56 100644
> --- a/emacs/notmuch-show.el
> +++ b/emacs/notmuch-show.el
> @@ -1077,6 +1077,11 @@ buttons for a corresponding notmuch search."
>   (make-text-button (first link) (second link)
> 'action `(lambda (arg)
>(notmuch-show ,(third link)))
> +   'mouse-action `(lambda (arg)
> +(let* ((event last-input-event)
> +   (window (car (cadr event
> +  (with-selected-window window
> +(notmuch-show ,(third link)
> 'follow-link t
> 'help-echo "Mouse-1, RET: search for this message"
> 'face goto-address-mail-face)
> -- 
> 1.7.9.1
>
> ___
> notmuch mailing list
> notmuch at notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch


[Patch v2] notmuch-restore: handle empty input file, leading blank lines and comments.

2013-01-06 Thread Tomi Ollila
On Sun, Jan 06 2013, david at tethera.net wrote:

> From: David Bremner 
>
> This patch corrects several undesirable behaviours:
>
> 1) Empty files were not detected, leading to buffer read overrun.
>
> 2) An initial blank line cause restore to silently abort
>
> 3) Initial comment line caused format detection to fail
> ---

LGTM.

Tomi

>  notmuch-restore.c |   18 +-
>  test/dump-restore |3 ---
>  2 files changed, 13 insertions(+), 8 deletions(-)
>
> diff --git a/notmuch-restore.c b/notmuch-restore.c
> index d43546d..f436989 100644
> --- a/notmuch-restore.c
> +++ b/notmuch-restore.c
> @@ -181,11 +181,6 @@ notmuch_restore_command (unused (void *ctx), int argc, 
> char *argv[])
>argv[opt_index]);
>   return 1;
>  }
> -char *p;
> -
> -line_len = getline (&line, &line_size, input);
> -if (line_len == 0)
> - return 0;
>  
>  tag_ops = tag_op_list_create (ctx);
>  if (tag_ops == NULL) {
> @@ -193,6 +188,19 @@ notmuch_restore_command (unused (void *ctx), int argc, 
> char *argv[])
>   return 1;
>  }
>  
> +do {
> + line_len = getline (&line, &line_size, input);
> +
> + /* empty input file not considered an error */
> + if (line_len < 0)
> + return 0;
> +
> +} while ((line_len == 0) ||
> +  (line[0] == '#') ||
> +  /* the cast is safe because we checked about for line_len < 0 */
> +  (strspn (line, " \t\n") == (unsigned)line_len));
> +
> +char *p;
>  for (p = line; (input_format == DUMP_FORMAT_AUTO) && *p; p++) {
>   if (*p == '(')
>   input_format = DUMP_FORMAT_SUP;
> diff --git a/test/dump-restore b/test/dump-restore
> index c2ddb92..ae30cd1 100755
> --- a/test/dump-restore
> +++ b/test/dump-restore
> @@ -146,13 +146,11 @@ cat < comments-and-blanks
>  EOF
>  
>  test_begin_subtest 'restoring empty file is not an error'
> -test_subtest_known_broken
>  notmuch restore < /dev/null 2>OUTPUT.$test_count
>  cp /dev/null EXPECTED
>  test_expect_equal_file EXPECTED OUTPUT.$test_count
>  
>  test_begin_subtest 'file of comments and blank lines is not an error'
> -test_subtest_known_broken
>  notmuch restore --input=comments-and-blanks
>  ret_val=$?
>  test_expect_equal "$ret_val" "0"
> @@ -172,7 +170,6 @@ echo "yun1vjwegii.fsf at aiko.keithp.com (another_tag)" \
>  >> leading-comments-blanks-sup
>  
>  test_begin_subtest 'detect format=sup with leading comments and blanks'
> -test_subtest_known_broken
>  notmuch restore --input=leading-comments-blanks-sup
>  notmuch search --output=tags id:yun1vjwegii.fsf at aiko.keithp.com > 
> OUTPUT.$test_count
>  echo "another_tag" > EXPECTED
> -- 
> 1.7.10.4
>
> ___
> notmuch mailing list
> notmuch at notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch


gui client

2013-01-06 Thread Rainer M Krug
-BEGIN PGP SIGNED MESSAGE-
Hash: SHA1

On 23/12/12 17:08, Jameson Graef Rollins wrote:
> On Fri, Dec 21 2012, Sergei Shilovsky 
> wrote:
>> Is there a GUI notmuch client? Or is somebody working on it?
>> 
>> I'm thinking of writing one myself, so I want to know current situation.
> 
> Hi, Sergei.  There are some curses front-ends [0], but I haven't heard of any 
> GUI ones.  Good
> luck!

I think it would be a good idea to have a GUI for searching emails and, 
although I am using emacs,
would use it quite regularly.

Cheers,

Rainer


> 
> jamie.
> 
> [0] http://notmuchmail.org/frontends/
> 
> 
> 

-BEGIN PGP SIGNATURE-
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iEYEARECAAYFAlDpnaMACgkQoYgNqgF2egpbIACfUYOOj6H5zbSWFu8ilHW1t0o4
J4QAoIhgDQT7HDTRjhzOXsyfivWUA2NW
=g94w
-END PGP SIGNATURE-



[PATCH v3] emacs: Use the minibuffer for CLI error reporting

2013-01-06 Thread Austin Clements
On Thu, 03 Jan 2013, Mark Walters  wrote:
> On Thu, 03 Jan 2013, Austin Clements  wrote:
>> We recently switched to popping up a buffer to report CLI errors, but
>> this was too intrusive, especially for transient errors and especially
>> since we made fewer things ignore errors.  This patch changes this to
>> display a basic error message in the minibuffer (using Emacs' usual
>> error handling path) and, if there are additional details, to log
>> these to a separate error buffer and reference the error buffer from
>> the minibuffer message.  This is more in line with how Emacs typically
>> handles errors, but makes the details available to the user without
>> flooding them with the details.
>>
>> Given this split, we pare down the basic message and make it more
>> user-friendly, and also make the verbose message even more detailed
>> (and more debugging-oriented).
>> ---
>>
>> This version fixes two hard-coded paths in the tests.
>
>
> This version looks good to me but I have one query we may like to
> consider. 
>
> At the moment notmuch-call-notmuch-process returns the stderr mixed with
> stdout and we might like to separate that out (particularly as the error
> message lists stderr and stdout separately and in this case it all gets
> called stdout).

This sounds like a good idea to me, though I'd rather do it as a
separate patch.  Your patch looks good to me (modulo commit message,
obviously).  Care to roll an official patch?

As I mentioned elsewhere, there are several direct calls to notmuch that
don't go through any of this error handling (for example,
`notmuch-call-notmuch-process' is only used in 'notmuch-tag').  It would
be great if Someone (TM) cleaned this up.

> I attach a patch that does this: I am not really familiar with this so I
> just took Austin's code from notmuch-call-notmuch-json.
>
> Austin: obviously feel free to fold this into your patch if you think
> appropriate.
>
> Best wishes
>
> Mark
>
> From b73395c8efb57111bd4de281797004747de6c2ed Mon Sep 17 00:00:00 2001
> From: Mark Walters 
> Date: Thu, 3 Jan 2013 22:25:02 +
> Subject: [PATCH] tweak notmuch-call-notmuch-process
>
> ---
>  emacs/notmuch.el |   10 +++---
>  1 files changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/emacs/notmuch.el b/emacs/notmuch.el
> index c98a4fe..4f7ee2c 100644
> --- a/emacs/notmuch.el
> +++ b/emacs/notmuch.el
> @@ -540,9 +540,13 @@ If notmuch exits with a non-zero status, output from the 
> process
>  will appear in a buffer named \"*Notmuch errors*\" and an error
>  will be signaled."
>(with-temp-buffer
> -(let ((status (apply #'call-process notmuch-command nil t nil args)))
> -  (notmuch-check-exit-status status (cons notmuch-command args)
> -  (buffer-string)
> +(let ((err-file (make-temp-file "nmerr")))
> +  (unwind-protect
> +   (let ((status (apply #'call-process
> +notmuch-command nil (list t err-file) nil args)))
> + (notmuch-check-exit-status status (cons notmuch-command args)
> +(buffer-string) err-file))
> + (delete-file err-file)
>  
>  (defun notmuch-search-set-tags (tags &optional pos)
>(let ((new-result (plist-put (notmuch-search-get-result pos) :tags tags)))
> -- 
> 1.7.9.1


[PATCH] emacs: show: make id links respect window

2013-01-06 Thread Mark Walters

On Sun, 06 Jan 2013, Tomi Ollila  wrote:
> On Sun, Jan 06 2013, Mark Walters  wrote:
>
>> On Sat, 05 Jan 2013, Tomi Ollila  wrote:
>>> On Wed, Dec 26 2012, David Bremner  wrote:
>>>
 Mark Walters  writes:

>> I can imagine that people would want/like the "open in other window" 
>> effect of the current code, even if the reason is a bug.
>
> That's definitely possible. I generally expect a mouse click to select
> the window I click and this feels counter intuitive. I think that some
> people might like an option "open this link in a new window" but I would
> guess that would like that whether they clicked or pressed RET on the
> button.

 I don't care much either way myself, but before we change notmuch-show
 behaviour (effectively) to accomodate notmuch-pick, I'd like a bit more
 feedback from other people.
>>>
>>> I tested the old behaviour -- split frame to 2 windows, one containing
>>> *scratch* and one *notmuch-hello*. Then I searched for 'obsoletes',
>>> chose last message (id:1356936162-2589-1-git-send-email-amdragon at mit.edu)
>>> moved point to *scratch* buffer and clicked the id: link -- and indeed,
>>> the *scratch* window was replaced.
>>>
>>> I didn't look or test Mark's patch as he stated:
>>> "The lisp is not pretty but seems to work." ;)
>>> ... well, not entirely because of that but I trust it opens the message
>>> in window where the clicked link were and keeps point where it used to
>>> be before clicking (in case point was in different window).
>>
>> It doesn't quite do this: point moves to the window that was clicked. It
>> is just as easy to do as Tomi says (patch below but I should resend if
>> people like it so the commit message gets picked up).
>
> It seems to be the default (and iniuitively expected) behaviour that
> point moves to the window that was clicked... so that's ok :)
>
>> Incidentally, I would be interested to know what people expect the
>> following to do: go to notmuch hello and then search. Now display the
>> results in two windows simultaneously (either split the frame into two
>> windows (c-x 2) or use 2 frames (C-x 5 2)) and then press q.
>>
>> What actually happens is that q runs kill buffer so it disappears in
>> both windows: one of which will fall back to notmuch-hello and one of
>> which will fall back to some other window (eg scratch)
>
> I think that is just expected behaviour, there are e.g.. c-x b and c-x 0
> which should be familiar to every emacs users...

My instinct would be that the window I press q in should go to the
previous buffer in that window but the other window should stay where it
was. But if others are happy as it that is obviously fine.


>> Best wishes
>>
>> Mark
>
> Tomi
>
> PS: did you change anything in the patch below ?

Yes: the new version uses (with-selected-window window ...) compared
with (select-window window) (...)

So the old version switches point to the clicked window, the new version
does not. I am happy either way: the click does normally move point, but
other buttons such as invisibility buttons do not move point when
mouse-clicked so

Best wishes


Mark


>>
>>  emacs/notmuch-show.el |5 +
>>  1 files changed, 5 insertions(+), 0 deletions(-)
>>
>> diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el
>> index 5751d98..00b9b56 100644
>> --- a/emacs/notmuch-show.el
>> +++ b/emacs/notmuch-show.el
>> @@ -1077,6 +1077,11 @@ buttons for a corresponding notmuch search."
>>  (make-text-button (first link) (second link)
>>'action `(lambda (arg)
>>   (notmuch-show ,(third link)))
>> +  'mouse-action `(lambda (arg)
>> +   (let* ((event last-input-event)
>> +  (window (car (cadr event
>> + (with-selected-window window
>> +   (notmuch-show ,(third link)
>>'follow-link t
>>'help-echo "Mouse-1, RET: search for this message"
>>'face goto-address-mail-face)
>> -- 
>> 1.7.9.1
>>
>> ___
>> notmuch mailing list
>> notmuch at notmuchmail.org
>> http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] emacs: show: make id links respect window

2013-01-06 Thread Mark Walters

On Sat, 05 Jan 2013, Tomi Ollila  wrote:
> On Wed, Dec 26 2012, David Bremner  wrote:
>
>> Mark Walters  writes:
>>
 I can imagine that people would want/like the "open in other window" 
 effect of the current code, even if the reason is a bug.
>>>
>>> That's definitely possible. I generally expect a mouse click to select
>>> the window I click and this feels counter intuitive. I think that some
>>> people might like an option "open this link in a new window" but I would
>>> guess that would like that whether they clicked or pressed RET on the
>>> button.
>>
>> I don't care much either way myself, but before we change notmuch-show
>> behaviour (effectively) to accomodate notmuch-pick, I'd like a bit more
>> feedback from other people.
>
> I tested the old behaviour -- split frame to 2 windows, one containing
> *scratch* and one *notmuch-hello*. Then I searched for 'obsoletes',
> chose last message (id:1356936162-2589-1-git-send-email-amdragon at mit.edu)
> moved point to *scratch* buffer and clicked the id: link -- and indeed,
> the *scratch* window was replaced.
>
> I didn't look or test Mark's patch as he stated:
> "The lisp is not pretty but seems to work." ;)
> ... well, not entirely because of that but I trust it opens the message
> in window where the clicked link were and keeps point where it used to
> be before clicking (in case point was in different window).

It doesn't quite do this: point moves to the window that was clicked. It
is just as easy to do as Tomi says (patch below but I should resend if
people like it so the commit message gets picked up).

Incidentally, I would be interested to know what people expect the
following to do: go to notmuch hello and then search. Now display the
results in two windows simultaneously (either split the frame into two
windows (c-x 2) or use 2 frames (C-x 5 2)) and then press q.

What actually happens is that q runs kill buffer so it disappears in
both windows: one of which will fall back to notmuch-hello and one of
which will fall back to some other window (eg scratch)

Best wishes

Mark

 emacs/notmuch-show.el |5 +
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el
index 5751d98..00b9b56 100644
--- a/emacs/notmuch-show.el
+++ b/emacs/notmuch-show.el
@@ -1077,6 +1077,11 @@ buttons for a corresponding notmuch search."
(make-text-button (first link) (second link)
  'action `(lambda (arg)
 (notmuch-show ,(third link)))
+ 'mouse-action `(lambda (arg)
+  (let* ((event last-input-event)
+ (window (car (cadr event
+(with-selected-window window
+  (notmuch-show ,(third link)
  'follow-link t
  'help-echo "Mouse-1, RET: search for this message"
  'face goto-address-mail-face)
-- 
1.7.9.1



[PATCH v5 6/6] man: Update notmuch-dump(1) and notmuch-restore(1)

2013-01-06 Thread Austin Clements
Describe the new batch-tag format.  For notmuch-restore, rather than
half-heartedly duplicating the description, we now cite notmuch-dump.
---
 man/man1/notmuch-dump.1|   11 +++
 man/man1/notmuch-restore.1 |6 ++
 2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/man/man1/notmuch-dump.1 b/man/man1/notmuch-dump.1
index 770b00f..7bd6def 100644
--- a/man/man1/notmuch-dump.1
+++ b/man/man1/notmuch-dump.1
@@ -64,13 +64,16 @@ and tags containing whitespace or non-\fBascii\fR(7) 
characters.
 Each line has the form

 .RS 4
-.RI "+<" "encoded-tag" "> " "" "+<" "encoded-tag" "> ... -- " "" " id:<" 
encoded-message-id >
+.RI "+<" "encoded-tag" "> " "" "+<" "encoded-tag" "> ... -- " "" " id:<" 
quoted-message-id >

-where encoded means that every byte not matching the regex
+Tags are hex-encoded by replacing every byte not matching the regex
 .B [A-Za-z0-9@=.,_+-]
-is replace by
+with
 .B %nn
-where nn is the two digit hex encoding.
+where nn is the two digit hex encoding.  The message ID is a valid Xapian
+query, quoted using Xapian boolean term quoting rules: if the ID contains
+whitespace or a close paren or starts with a double quote, it must be
+enclosed in double quotes and double quotes inside the ID must be doubled.
 The astute reader will notice this is a special case of the batch input
 format for \fBnotmuch-tag\fR(1); note that the single message-id query is
 mandatory for \fBnotmuch-restore\fR(1).
diff --git a/man/man1/notmuch-restore.1 b/man/man1/notmuch-restore.1
index 6bba628..78fef52 100644
--- a/man/man1/notmuch-restore.1
+++ b/man/man1/notmuch-restore.1
@@ -57,10 +57,8 @@ sup calls them).
 The
 .B batch-tag
 dump format is intended to more robust against malformed message-ids
-and tags containing whitespace or non-\fBascii\fR(7) characters.  This
-format hex-escapes all characters those outside of a small character
-set, intended to be suitable for e.g. pathnames in most UNIX-like
-systems.
+and tags containing whitespace or non-\fBascii\fR(7) characters.  See
+\fBnotmuch-dump\fR(1) for details on this format.

 .B "notmuch restore"
 updates the maildir flags according to tag changes if the
-- 
1.7.10.4



[PATCH v5 5/6] dump/restore: Use Xapian queries for batch-tag format

2013-01-06 Thread Austin Clements
This switches the new batch-tag format away from using a home-grown
hex-encoding scheme for message IDs in the dump to simply using Xapian
queries with Xapian quoting syntax.

This has a variety of advantages beyond presenting a cleaner and more
consistent interface.  Foremost is that it will dramatically simplify
the quoting for batch tagging, which shares the same input format.
While the hex-encoding is no better or worse for the simple ID queries
used by dump/restore, it becomes onerous for general-purpose queries
used in batch tagging.  It also better handles strange cases like
"id:foo and bar", since this is no longer syntactically valid.
---
 notmuch-dump.c|   11 ++-
 notmuch-restore.c |   27 +++
 tag-util.c|6 --
 test/dump-restore |   28 ++--
 4 files changed, 43 insertions(+), 29 deletions(-)

diff --git a/notmuch-dump.c b/notmuch-dump.c
index 5bbda36..a3244e0 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -20,6 +20,7 @@

 #include "notmuch-client.h"
 #include "dump-restore-private.h"
+#include "string-util.h"

 int
 notmuch_dump_command (unused (void *ctx), int argc, char *argv[])
@@ -144,13 +145,13 @@ notmuch_dump_command (unused (void *ctx), int argc, char 
*argv[])
if (output_format == DUMP_FORMAT_SUP) {
fputs (")\n", output);
} else {
-   if (hex_encode (notmuch, message_id,
-   &buffer, &buffer_size) != HEX_SUCCESS) {
-   fprintf (stderr, "Error: failed to hex-encode msg-id %s\n",
-message_id);
+   if (make_boolean_term (notmuch, "id", message_id,
+  &buffer, &buffer_size)) {
+   fprintf (stderr, "Error quoting message id %s: %s\n",
+message_id, strerror (errno));
return 1;
}
-   fprintf (output, " -- id:%s\n", buffer);
+   fprintf (output, " -- %s\n", buffer);
}

notmuch_message_destroy (message);
diff --git a/notmuch-restore.c b/notmuch-restore.c
index 96834c0..81d4d98 100644
--- a/notmuch-restore.c
+++ b/notmuch-restore.c
@@ -212,7 +212,7 @@ notmuch_restore_command (unused (void *ctx), int argc, char 
*argv[])
INTERNAL_ERROR ("compile time constant regex failed.");

 do {
-   char *query_string;
+   char *query_string, *prefix, *term;

if (line_ctx != NULL)
talloc_free (line_ctx);
@@ -225,19 +225,22 @@ notmuch_restore_command (unused (void *ctx), int argc, 
char *argv[])
  &query_string, tag_ops);

if (ret == 0) {
-   if (strncmp ("id:", query_string, 3) != 0) {
-   fprintf (stderr, "Warning: unsupported query: %s\n", 
query_string);
+   ret = parse_boolean_term (line_ctx, query_string,
+ &prefix, &term);
+   if (ret && errno == EINVAL) {
+   fprintf (stderr, "Warning: cannot parse query: %s 
(skipping)\n", query_string);
+   continue;
+   } else if (ret) {
+   /* This is more fatal (e.g., out of memory) */
+   fprintf (stderr, "Error parsing query: %s\n",
+strerror (errno));
+   ret = 1;
+   break;
+   } else if (strcmp ("id", prefix) != 0) {
+   fprintf (stderr, "Warning: not an id query: %s 
(skipping)\n", query_string);
continue;
}
-   /* delete id: from front of string; tag_message
-* expects a raw message-id.
-*
-* XXX: Note that query string id:foo and bar will be
-* interpreted as a message id "foo and bar". This
-* should eventually be fixed to give a better error
-* message.
-*/
-   query_string = query_string + 3;
+   query_string = term;
}
}

diff --git a/tag-util.c b/tag-util.c
index 705b7ba..e4e5dda 100644
--- a/tag-util.c
+++ b/tag-util.c
@@ -124,12 +124,6 @@ parse_tag_line (void *ctx, char *line,
 }

 /* tok now points to the query string */
-if (hex_decode_inplace (tok) != HEX_SUCCESS) {
-   ret = line_error (TAG_PARSE_INVALID, line_for_error,
- "hex decoding of query %s failed", tok);
-   goto DONE;
-}
-
 *query_string = tok;

   DONE:
diff --git a/test/dump-restore b/test/dump-restore
index 6a989b6..f076c12 100755
--- a/test/dump-restore
+++ b/test/dump-restore
@@ -195,23 +195,39 @@ a

 # the previous line was blank; also no yelling please
 +%zz -- id:whatever
-+e +f id:%yy
++e +f id:"
++e +f tag:abc
 # the next non-comment line should report an an empty tag error for
 # batch tagging, but not for restore
 + +e -- id:20091117232137.GA7669 at griffis1.net
-

[PATCH v5 4/6] dump: Disallow \n in message IDs

2013-01-06 Thread Austin Clements
When we switch to using regular Xapian queries in the dump format, \n
will cause problems, so we disallow it.  Specially, while Xapian can
quote and parse queries containing \n without difficultly, quoted
queries containing \n still span multiple lines, which breaks the
line-orientedness of the dump format.  Strictly speaking, we could
still round-trip these, but it would significantly complicate restore
as well as scripts that deal with tag dumps.  This complexity would
come at absolutely no benefit: because of the RFC 2822 unfolding
rules, no amount of standards negligence can produce a message with a
message ID containing a line break (not even Outlook can do it!).

Hence, we simply disallow it.
---
 notmuch-dump.c   |   12 
 test/random-corpus.c |4 +++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/notmuch-dump.c b/notmuch-dump.c
index d2dad40..5bbda36 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -102,6 +102,18 @@ notmuch_dump_command (unused (void *ctx), int argc, char 
*argv[])
message = notmuch_messages_get (messages);
message_id = notmuch_message_get_message_id (message);

+   if (output_format == DUMP_FORMAT_BATCH_TAG &&
+   strchr (message_id, '\n')) {
+   /* This will produce a line break in the output, which
+* would be difficult to handle in tools.  However, it's
+* also impossible to produce an email containing a line
+* break in a message ID because of unfolding, so we can
+* safely disallow it. */
+   fprintf (stderr, "Warning: skipping message id containing line 
break: \"%s\"\n", message_id);
+   notmuch_message_destroy (message);
+   continue;
+   }
+
if (output_format == DUMP_FORMAT_SUP) {
fprintf (output, "%s (", message_id);
}
diff --git a/test/random-corpus.c b/test/random-corpus.c
index f354d4b..8b7748e 100644
--- a/test/random-corpus.c
+++ b/test/random-corpus.c
@@ -96,7 +96,9 @@ random_utf8_string (void *ctx, size_t char_count)
buf = talloc_realloc (ctx, buf, gchar, buf_size);
}

-   randomchar = random_unichar ();
+   do {
+   randomchar = random_unichar ();
+   } while (randomchar == '\n');

written = g_unichar_to_utf8 (randomchar, buf + offset);

-- 
1.7.10.4



[PATCH v5 3/6] util: Function to parse boolean term queries

2013-01-06 Thread Austin Clements
This parses the subset of Xapian's boolean term quoting rules that are
used by make_boolean_term.  This is provided as a generic string
utility, but will be used shortly in notmuch restore to parse and
optimize for ID queries.
---
 util/string-util.c |   82 
 util/string-util.h |   16 ++
 2 files changed, 98 insertions(+)

diff --git a/util/string-util.c b/util/string-util.c
index 7a71049..aba9aa8 100644
--- a/util/string-util.c
+++ b/util/string-util.c
@@ -22,6 +22,7 @@
 #include "string-util.h"
 #include "talloc.h"

+#include 
 #include 

 char *
@@ -107,3 +108,84 @@ make_boolean_term (void *ctx, const char *prefix, const 
char *term,

 return 0;
 }
+
+static const char*
+skip_space (const char *str)
+{
+while (*str && isspace ((unsigned char) *str))
+   ++str;
+return str;
+}
+
+int
+parse_boolean_term (void *ctx, const char *str,
+   char **prefix_out, char **term_out)
+{
+int err = EINVAL;
+*prefix_out = *term_out = NULL;
+
+/* Parse prefix */
+str = skip_space (str);
+const char *pos = strchr (str, ':');
+if (! pos)
+   goto FAIL;
+*prefix_out = talloc_strndup (ctx, str, pos - str);
+if (! *prefix_out) {
+   err = ENOMEM;
+   goto FAIL;
+}
+++pos;
+
+/* Implement de-quoting compatible with make_boolean_term. */
+if (*pos == '"') {
+   char *out = talloc_array (ctx, char, strlen (pos));
+   int closed = 0;
+   if (! out) {
+   err = ENOMEM;
+   goto FAIL;
+   }
+   *term_out = out;
+   /* Skip the opening quote, find the closing quote, and
+* un-double doubled internal quotes. */
+   for (++pos; *pos; ) {
+   if (*pos == '"') {
+   ++pos;
+   if (*pos != '"') {
+   /* Found the closing quote. */
+   closed = 1;
+   pos = skip_space (pos);
+   break;
+   }
+   }
+   *out++ = *pos++;
+   }
+   /* Did the term terminate without a closing quote or is there
+* trailing text after the closing quote? */
+   if (!closed || *pos)
+   goto FAIL;
+   *out = '\0';
+} else {
+   const char *start = pos;
+   /* Check for text after the boolean term. */
+   while (! is_unquoted_terminator (*pos))
+   ++pos;
+   if (*skip_space (pos)) {
+   err = EINVAL;
+   goto FAIL;
+   }
+   /* No trailing text; dup the string so the caller can free
+* it. */
+   *term_out = talloc_strndup (ctx, start, pos - start);
+   if (! *term_out) {
+   err = ENOMEM;
+   goto FAIL;
+   }
+}
+return 0;
+
+ FAIL:
+talloc_free (*prefix_out);
+talloc_free (*term_out);
+errno = err;
+return -1;
+}
diff --git a/util/string-util.h b/util/string-util.h
index 719c276..0194607 100644
--- a/util/string-util.h
+++ b/util/string-util.h
@@ -34,4 +34,20 @@ char *strtok_len (char *s, const char *delim, size_t *len);
 int make_boolean_term (void *talloc_ctx, const char *prefix, const char *term,
   char **buf, size_t *len);

+/* Parse a boolean term query consisting of a prefix, a colon, and a
+ * term that may be quoted as described for make_boolean_term.  If the
+ * term is not quoted, then it ends at the first whitespace or close
+ * parenthesis.  str may containing leading or trailing whitespace,
+ * but anything else is considered a parse error.  This is compatible
+ * with anything produced by make_boolean_term, and supports a subset
+ * of the quoting styles supported by Xapian (and hence notmuch).
+ * *prefix_out and *term_out will be talloc'd with context ctx.
+ *
+ * Return: 0 on success, -1 on error.  errno will be set to EINVAL if
+ * there is a parse error or ENOMEM if there is an allocation failure.
+ */
+int
+parse_boolean_term (void *ctx, const char *str,
+   char **prefix_out, char **term_out);
+
 #endif
-- 
1.7.10.4



[PATCH v5 2/6] util: Factor out boolean term quoting routine

2013-01-06 Thread Austin Clements
From: Austin Clements 

This is now a generic boolean term quoting function.  It performs
minimal quoting to produce user-friendly queries.

This could live in tag-util as well, but it is really nothing specific
to tags (although the conventions are specific to Xapian).

The API is changed from "caller-allocates" to "readline-like".  The
scan for max tag length is pushed down into the quoting routine.
Furthermore, this now combines the term prefix with the quoted term;
arguably this is just as easy to do in the caller, but this will
nicely parallel the boolean term parsing function to be introduced
shortly.

This is an amalgamation of code written by David Bremner and myself.
---
 notmuch-tag.c  |   48 ++---
 util/string-util.c |   75 
 util/string-util.h |   15 +++
 3 files changed, 104 insertions(+), 34 deletions(-)

diff --git a/notmuch-tag.c b/notmuch-tag.c
index 88d559b..fc9d43a 100644
--- a/notmuch-tag.c
+++ b/notmuch-tag.c
@@ -19,6 +19,7 @@
  */

 #include "notmuch-client.h"
+#include "string-util.h"

 static volatile sig_atomic_t interrupted;

@@ -35,25 +36,6 @@ handle_sigint (unused (int sig))
 interrupted = 1;
 }

-static char *
-_escape_tag (char *buf, const char *tag)
-{
-const char *in = tag;
-char *out = buf;
-
-/* Boolean terms surrounded by double quotes can contain any
- * character.  Double quotes are quoted by doubling them. */
-*out++ = '"';
-while (*in) {
-   if (*in == '"')
-   *out++ = '"';
-   *out++ = *in++;
-}
-*out++ = '"';
-*out = 0;
-return buf;
-}
-
 typedef struct {
 const char *tag;
 notmuch_bool_t remove;
@@ -71,25 +53,16 @@ _optimize_tag_query (void *ctx, const char 
*orig_query_string,
  * parenthesize and the exclusion part of the query must not use
  * the '-' operator (though the NOT operator is fine). */

-char *escaped, *query_string;
+char *escaped = NULL;
+size_t escaped_len = 0;
+char *query_string;
 const char *join = "";
-int i;
-unsigned int max_tag_len = 0;
+size_t i;

 /* Don't optimize if there are no tag changes. */
 if (tag_ops[0].tag == NULL)
return talloc_strdup (ctx, orig_query_string);

-/* Allocate a buffer for escaping tags.  This is large enough to
- * hold a fully escaped tag with every character doubled plus
- * enclosing quotes and a NUL. */
-for (i = 0; tag_ops[i].tag; i++)
-   if (strlen (tag_ops[i].tag) > max_tag_len)
-   max_tag_len = strlen (tag_ops[i].tag);
-escaped = talloc_array (ctx, char, max_tag_len * 2 + 3);
-if (! escaped)
-   return NULL;
-
 /* Build the new query string */
 if (strcmp (orig_query_string, "*") == 0)
query_string = talloc_strdup (ctx, "(");
@@ -97,10 +70,17 @@ _optimize_tag_query (void *ctx, const char 
*orig_query_string,
query_string = talloc_asprintf (ctx, "( %s ) and (", orig_query_string);

 for (i = 0; tag_ops[i].tag && query_string; i++) {
+   /* XXX in case of OOM, query_string will be deallocated when
+* ctx is, which might be at shutdown */
+   if (make_boolean_term (ctx,
+  "tag", tag_ops[i].tag,
+  &escaped, &escaped_len))
+   return NULL;
+
query_string = talloc_asprintf_append_buffer (
-   query_string, "%s%stag:%s", join,
+   query_string, "%s%s%s", join,
tag_ops[i].remove ? "" : "not ",
-   _escape_tag (escaped, tag_ops[i].tag));
+   escaped);
join = " or ";
 }

diff --git a/util/string-util.c b/util/string-util.c
index 44f8cd3..7a71049 100644
--- a/util/string-util.c
+++ b/util/string-util.c
@@ -20,6 +20,9 @@


 #include "string-util.h"
+#include "talloc.h"
+
+#include 

 char *
 strtok_len (char *s, const char *delim, size_t *len)
@@ -32,3 +35,75 @@ strtok_len (char *s, const char *delim, size_t *len)

 return *len ? s : NULL;
 }
+
+static int
+is_unquoted_terminator (unsigned char c)
+{
+return c == 0 || c <= ' ' || c == ')';
+}
+
+int
+make_boolean_term (void *ctx, const char *prefix, const char *term,
+  char **buf, size_t *len)
+{
+const char *in;
+char *out;
+size_t needed = 3;
+int need_quoting = 0;
+
+/* Do we need quoting?  To be paranoid, we quote anything
+ * containing a quote, even though it only matters at the
+ * beginning, and anything containing non-ASCII text. */
+for (in = term; *in && !need_quoting; in++)
+   if (is_unquoted_terminator (*in) || *in == '"'
+   || (unsigned char)*in > 127)
+   need_quoting = 1;
+
+if (need_quoting)
+   for (in = term; *in; in++)
+   needed += (*in == '"') ? 2 : 1;
+else
+   needed = strlen (term) + 1;
+
+/* Reserve space for the prefix */
+if (prefix)
+   needed += strlen (prefix) + 1;
+
+if ((*buf == NULL) || (need

[PATCH v5 1/6] restore: Make missing messages non-fatal (again)

2013-01-06 Thread Austin Clements
Previously, restore would abort if a message ID in the dump was
missing.  Furthermore, it would only report this as a warning.  This
patch makes it distinguish abort-worthy lookup failures like
out-of-memory from non-fatal failure to find a message ID.  The former
is reported as an error and causes restore to abort, while the latter
is reported as a warning and does not cause an abort.

This restores 0.14's non-fatal handling of missing message IDs in
restore (though 0.14 also considered serious errors non-fatal; we
retain the new and better handling of serious errors).
---
 notmuch-restore.c |   17 +++--
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/notmuch-restore.c b/notmuch-restore.c
index 9ed9b51..96834c0 100644
--- a/notmuch-restore.c
+++ b/notmuch-restore.c
@@ -26,7 +26,8 @@
 static regex_t regex;

 /* Non-zero return indicates an error in retrieving the message,
- * or in applying the tags.
+ * or in applying the tags.  Missing messages are reported, but not
+ * considered errors.
  */
 static int
 tag_message (unused (void *ctx),
@@ -40,13 +41,17 @@ tag_message (unused (void *ctx),
 int ret = 0;

 status = notmuch_database_find_message (notmuch, message_id, &message);
-if (status || message == NULL) {
-   fprintf (stderr, "Warning: cannot apply tags to %smessage: %s\n",
-message ? "" : "missing ", message_id);
-   if (status)
-   fprintf (stderr, "%s\n", notmuch_status_to_string (status));
+if (status) {
+   fprintf (stderr, "Error applying tags to message %s: %s\n",
+message_id, notmuch_status_to_string (status));
return 1;
 }
+if (message == NULL) {
+   fprintf (stderr, "Warning: cannot apply tags to missing message: %s\n",
+message_id);
+   /* We consider this a non-fatal error. */
+   return 0;
+}

 /* In order to detect missing messages, this check/optimization is
  * intentionally done *after* first finding the message. */
-- 
1.7.10.4



[PATCH v5 0/6] Use Xapian query syntax for batch-tag dump/restore

2013-01-06 Thread Austin Clements
This obsoletes

  id:1356936162-2589-1-git-send-email-amdragon at mit.edu

v5 should address all of the comments on v4 except those I
specifically replied to (via the ML or IRC).  It also adds a new patch
at the beginning that makes missing message IDs non-fatal in restore,
like they were in 0.14.  This patch can be pushed separately; it's in
this series because later tests rely on it.

The diff from v4 follows.

diff --git a/notmuch-dump.c b/notmuch-dump.c
index bf01a39..a3244e0 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -103,6 +103,18 @@ notmuch_dump_command (unused (void *ctx), int argc, char 
*argv[])
message = notmuch_messages_get (messages);
message_id = notmuch_message_get_message_id (message);

+   if (output_format == DUMP_FORMAT_BATCH_TAG &&
+   strchr (message_id, '\n')) {
+   /* This will produce a line break in the output, which
+* would be difficult to handle in tools.  However, it's
+* also impossible to produce an email containing a line
+* break in a message ID because of unfolding, so we can
+* safely disallow it. */
+   fprintf (stderr, "Warning: skipping message id containing line 
break: \"%s\"\n", message_id);
+   notmuch_message_destroy (message);
+   continue;
+   }
+
if (output_format == DUMP_FORMAT_SUP) {
fprintf (output, "%s (", message_id);
}
@@ -133,19 +145,10 @@ notmuch_dump_command (unused (void *ctx), int argc, char 
*argv[])
if (output_format == DUMP_FORMAT_SUP) {
fputs (")\n", output);
} else {
-   if (strchr (message_id, '\n')) {
-   /* This will produce a line break in the output, which
-* would be difficult to handle in tools.  However,
-* it's also impossible to produce an email containing
-* a line break in a message ID because of unfolding,
-* so we can safely disallow it. */
-   fprintf (stderr, "Error: cannot dump message id containing line 
break: %s\n", message_id);
-   return 1;
-   }
if (make_boolean_term (notmuch, "id", message_id,
   &buffer, &buffer_size)) {
-   fprintf (stderr, "Error: failed to quote message id %s\n",
-message_id);
+   fprintf (stderr, "Error quoting message id %s: %s\n",
+message_id, strerror (errno));
return 1;
}
fprintf (output, " -- %s\n", buffer);
diff --git a/notmuch-restore.c b/notmuch-restore.c
index 77a4c27..81d4d98 100644
--- a/notmuch-restore.c
+++ b/notmuch-restore.c
@@ -26,7 +26,8 @@
 static regex_t regex;

 /* Non-zero return indicates an error in retrieving the message,
- * or in applying the tags.
+ * or in applying the tags.  Missing messages are reported, but not
+ * considered errors.
  */
 static int
 tag_message (unused (void *ctx),
@@ -40,13 +41,17 @@ tag_message (unused (void *ctx),
 int ret = 0;

 status = notmuch_database_find_message (notmuch, message_id, &message);
-if (status || message == NULL) {
-   fprintf (stderr, "Warning: cannot apply tags to %smessage: %s\n",
-message ? "" : "missing ", message_id);
-   if (status)
-   fprintf (stderr, "%s\n", notmuch_status_to_string (status));
+if (status) {
+   fprintf (stderr, "Error applying tags to message %s: %s\n",
+message_id, notmuch_status_to_string (status));
return 1;
 }
+if (message == NULL) {
+   fprintf (stderr, "Warning: cannot apply tags to missing message: %s\n",
+message_id);
+   /* We consider this a non-fatal error. */
+   return 0;
+}

 /* In order to detect missing messages, this check/optimization is
  * intentionally done *after* first finding the message. */
@@ -222,12 +227,17 @@ notmuch_restore_command (unused (void *ctx), int argc, 
char *argv[])
if (ret == 0) {
ret = parse_boolean_term (line_ctx, query_string,
  &prefix, &term);
-   if (ret) {
-   fprintf (stderr, "Warning: cannot parse query: %s\n",
-query_string);
+   if (ret && errno == EINVAL) {
+   fprintf (stderr, "Warning: cannot parse query: %s 
(skipping)\n", query_string);
continue;
+   } else if (ret) {
+   /* This is more fatal (e.g., out of memory) */
+   fprintf (stderr, "Error parsing query: %s\n",
+strerror (errno));
+   ret = 1;
+   break;
} else if (strcmp ("id", prefix) != 0) {
-   fprintf (stderr, "Warning: not an id query: %s\n", 
query_string);
+   fprintf (stderr, "Warni

Re: [PATCH v5 0/6] Use Xapian query syntax for batch-tag dump/restore

2013-01-06 Thread Mark Walters

LGTM too +1

Mark

On Sun, 06 Jan 2013, Jani Nikula  wrote:
> On Sun, 06 Jan 2013, Austin Clements  wrote:
>> This obsoletes
>>
>>   id:1356936162-2589-1-git-send-email-amdra...@mit.edu
>>
>> v5 should address all of the comments on v4 except those I
>> specifically replied to (via the ML or IRC).  It also adds a new patch
>> at the beginning that makes missing message IDs non-fatal in restore,
>> like they were in 0.14.  This patch can be pushed separately; it's in
>> this series because later tests rely on it.
>
> The series LGMT,
> Jani.
>
>>
>> The diff from v4 follows.
>>
>> diff --git a/notmuch-dump.c b/notmuch-dump.c
>> index bf01a39..a3244e0 100644
>> --- a/notmuch-dump.c
>> +++ b/notmuch-dump.c
>> @@ -103,6 +103,18 @@ notmuch_dump_command (unused (void *ctx), int argc, 
>> char *argv[])
>>  message = notmuch_messages_get (messages);
>>  message_id = notmuch_message_get_message_id (message);
>>  
>> +if (output_format == DUMP_FORMAT_BATCH_TAG &&
>> +strchr (message_id, '\n')) {
>> +/* This will produce a line break in the output, which
>> + * would be difficult to handle in tools.  However, it's
>> + * also impossible to produce an email containing a line
>> + * break in a message ID because of unfolding, so we can
>> + * safely disallow it. */
>> +fprintf (stderr, "Warning: skipping message id containing line 
>> break: \"%s\"\n", message_id);
>> +notmuch_message_destroy (message);
>> +continue;
>> +}
>> +
>>  if (output_format == DUMP_FORMAT_SUP) {
>>  fprintf (output, "%s (", message_id);
>>  }
>> @@ -133,19 +145,10 @@ notmuch_dump_command (unused (void *ctx), int argc, 
>> char *argv[])
>>  if (output_format == DUMP_FORMAT_SUP) {
>>  fputs (")\n", output);
>>  } else {
>> -if (strchr (message_id, '\n')) {
>> -/* This will produce a line break in the output, which
>> - * would be difficult to handle in tools.  However,
>> - * it's also impossible to produce an email containing
>> - * a line break in a message ID because of unfolding,
>> - * so we can safely disallow it. */
>> -fprintf (stderr, "Error: cannot dump message id containing line 
>> break: %s\n", message_id);
>> -return 1;
>> -}
>>  if (make_boolean_term (notmuch, "id", message_id,
>> &buffer, &buffer_size)) {
>> -fprintf (stderr, "Error: failed to quote message id %s\n",
>> - message_id);
>> +fprintf (stderr, "Error quoting message id %s: %s\n",
>> + message_id, strerror (errno));
>>  return 1;
>>  }
>>  fprintf (output, " -- %s\n", buffer);
>> diff --git a/notmuch-restore.c b/notmuch-restore.c
>> index 77a4c27..81d4d98 100644
>> --- a/notmuch-restore.c
>> +++ b/notmuch-restore.c
>> @@ -26,7 +26,8 @@
>>  static regex_t regex;
>>  
>>  /* Non-zero return indicates an error in retrieving the message,
>> - * or in applying the tags.
>> + * or in applying the tags.  Missing messages are reported, but not
>> + * considered errors.
>>   */
>>  static int
>>  tag_message (unused (void *ctx),
>> @@ -40,13 +41,17 @@ tag_message (unused (void *ctx),
>>  int ret = 0;
>>  
>>  status = notmuch_database_find_message (notmuch, message_id, &message);
>> -if (status || message == NULL) {
>> -fprintf (stderr, "Warning: cannot apply tags to %smessage: %s\n",
>> - message ? "" : "missing ", message_id);
>> -if (status)
>> -fprintf (stderr, "%s\n", notmuch_status_to_string (status));
>> +if (status) {
>> +fprintf (stderr, "Error applying tags to message %s: %s\n",
>> + message_id, notmuch_status_to_string (status));
>>  return 1;
>>  }
>> +if (message == NULL) {
>> +fprintf (stderr, "Warning: cannot apply tags to missing message: %s\n",
>> + message_id);
>> +/* We consider this a non-fatal error. */
>> +return 0;
>> +}
>>  
>>  /* In order to detect missing messages, this check/optimization is
>>   * intentionally done *after* first finding the message. */
>> @@ -222,12 +227,17 @@ notmuch_restore_command (unused (void *ctx), int argc, 
>> char *argv[])
>>  if (ret == 0) {
>>  ret = parse_boolean_term (line_ctx, query_string,
>>&prefix, &term);
>> -if (ret) {
>> -fprintf (stderr, "Warning: cannot parse query: %s\n",
>> - query_string);
>> +if (ret && errno == EINVAL) {
>> +fprintf (stderr, "Warning: cannot parse query: %s 
>> (skipping)\n", query_string);
>>  continue;
>> +} else if (ret) {
>> +/* This is more fatal (e.g., out of memory) */
>> +fprintf (stderr, "Error parsing query: %s\

Re: [PATCH v5 0/6] Use Xapian query syntax for batch-tag dump/restore

2013-01-06 Thread Jani Nikula
On Sun, 06 Jan 2013, Austin Clements  wrote:
> This obsoletes
>
>   id:1356936162-2589-1-git-send-email-amdra...@mit.edu
>
> v5 should address all of the comments on v4 except those I
> specifically replied to (via the ML or IRC).  It also adds a new patch
> at the beginning that makes missing message IDs non-fatal in restore,
> like they were in 0.14.  This patch can be pushed separately; it's in
> this series because later tests rely on it.

The series LGMT,
Jani.

>
> The diff from v4 follows.
>
> diff --git a/notmuch-dump.c b/notmuch-dump.c
> index bf01a39..a3244e0 100644
> --- a/notmuch-dump.c
> +++ b/notmuch-dump.c
> @@ -103,6 +103,18 @@ notmuch_dump_command (unused (void *ctx), int argc, char 
> *argv[])
>   message = notmuch_messages_get (messages);
>   message_id = notmuch_message_get_message_id (message);
>  
> + if (output_format == DUMP_FORMAT_BATCH_TAG &&
> + strchr (message_id, '\n')) {
> + /* This will produce a line break in the output, which
> +  * would be difficult to handle in tools.  However, it's
> +  * also impossible to produce an email containing a line
> +  * break in a message ID because of unfolding, so we can
> +  * safely disallow it. */
> + fprintf (stderr, "Warning: skipping message id containing line 
> break: \"%s\"\n", message_id);
> + notmuch_message_destroy (message);
> + continue;
> + }
> +
>   if (output_format == DUMP_FORMAT_SUP) {
>   fprintf (output, "%s (", message_id);
>   }
> @@ -133,19 +145,10 @@ notmuch_dump_command (unused (void *ctx), int argc, 
> char *argv[])
>   if (output_format == DUMP_FORMAT_SUP) {
>   fputs (")\n", output);
>   } else {
> - if (strchr (message_id, '\n')) {
> - /* This will produce a line break in the output, which
> -  * would be difficult to handle in tools.  However,
> -  * it's also impossible to produce an email containing
> -  * a line break in a message ID because of unfolding,
> -  * so we can safely disallow it. */
> - fprintf (stderr, "Error: cannot dump message id containing line 
> break: %s\n", message_id);
> - return 1;
> - }
>   if (make_boolean_term (notmuch, "id", message_id,
>  &buffer, &buffer_size)) {
> - fprintf (stderr, "Error: failed to quote message id %s\n",
> -  message_id);
> + fprintf (stderr, "Error quoting message id %s: %s\n",
> +  message_id, strerror (errno));
>   return 1;
>   }
>   fprintf (output, " -- %s\n", buffer);
> diff --git a/notmuch-restore.c b/notmuch-restore.c
> index 77a4c27..81d4d98 100644
> --- a/notmuch-restore.c
> +++ b/notmuch-restore.c
> @@ -26,7 +26,8 @@
>  static regex_t regex;
>  
>  /* Non-zero return indicates an error in retrieving the message,
> - * or in applying the tags.
> + * or in applying the tags.  Missing messages are reported, but not
> + * considered errors.
>   */
>  static int
>  tag_message (unused (void *ctx),
> @@ -40,13 +41,17 @@ tag_message (unused (void *ctx),
>  int ret = 0;
>  
>  status = notmuch_database_find_message (notmuch, message_id, &message);
> -if (status || message == NULL) {
> - fprintf (stderr, "Warning: cannot apply tags to %smessage: %s\n",
> -  message ? "" : "missing ", message_id);
> - if (status)
> - fprintf (stderr, "%s\n", notmuch_status_to_string (status));
> +if (status) {
> + fprintf (stderr, "Error applying tags to message %s: %s\n",
> +  message_id, notmuch_status_to_string (status));
>   return 1;
>  }
> +if (message == NULL) {
> + fprintf (stderr, "Warning: cannot apply tags to missing message: %s\n",
> +  message_id);
> + /* We consider this a non-fatal error. */
> + return 0;
> +}
>  
>  /* In order to detect missing messages, this check/optimization is
>   * intentionally done *after* first finding the message. */
> @@ -222,12 +227,17 @@ notmuch_restore_command (unused (void *ctx), int argc, 
> char *argv[])
>   if (ret == 0) {
>   ret = parse_boolean_term (line_ctx, query_string,
> &prefix, &term);
> - if (ret) {
> - fprintf (stderr, "Warning: cannot parse query: %s\n",
> -  query_string);
> + if (ret && errno == EINVAL) {
> + fprintf (stderr, "Warning: cannot parse query: %s 
> (skipping)\n", query_string);
>   continue;
> + } else if (ret) {
> + /* This is more fatal (e.g., out of memory) */
> + fprintf (stderr, "Error parsing query: %s\n",
> +  strerror (errno));
> + ret = 1;
> + break;
> 

Re: [PATCH v3] emacs: Use the minibuffer for CLI error reporting

2013-01-06 Thread Austin Clements
On Thu, 03 Jan 2013, Mark Walters  wrote:
> On Thu, 03 Jan 2013, Austin Clements  wrote:
>> We recently switched to popping up a buffer to report CLI errors, but
>> this was too intrusive, especially for transient errors and especially
>> since we made fewer things ignore errors.  This patch changes this to
>> display a basic error message in the minibuffer (using Emacs' usual
>> error handling path) and, if there are additional details, to log
>> these to a separate error buffer and reference the error buffer from
>> the minibuffer message.  This is more in line with how Emacs typically
>> handles errors, but makes the details available to the user without
>> flooding them with the details.
>>
>> Given this split, we pare down the basic message and make it more
>> user-friendly, and also make the verbose message even more detailed
>> (and more debugging-oriented).
>> ---
>>
>> This version fixes two hard-coded paths in the tests.
>
>
> This version looks good to me but I have one query we may like to
> consider. 
>
> At the moment notmuch-call-notmuch-process returns the stderr mixed with
> stdout and we might like to separate that out (particularly as the error
> message lists stderr and stdout separately and in this case it all gets
> called stdout).

This sounds like a good idea to me, though I'd rather do it as a
separate patch.  Your patch looks good to me (modulo commit message,
obviously).  Care to roll an official patch?

As I mentioned elsewhere, there are several direct calls to notmuch that
don't go through any of this error handling (for example,
`notmuch-call-notmuch-process' is only used in 'notmuch-tag').  It would
be great if Someone (TM) cleaned this up.

> I attach a patch that does this: I am not really familiar with this so I
> just took Austin's code from notmuch-call-notmuch-json.
>
> Austin: obviously feel free to fold this into your patch if you think
> appropriate.
>
> Best wishes
>
> Mark
>
> From b73395c8efb57111bd4de281797004747de6c2ed Mon Sep 17 00:00:00 2001
> From: Mark Walters 
> Date: Thu, 3 Jan 2013 22:25:02 +
> Subject: [PATCH] tweak notmuch-call-notmuch-process
>
> ---
>  emacs/notmuch.el |   10 +++---
>  1 files changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/emacs/notmuch.el b/emacs/notmuch.el
> index c98a4fe..4f7ee2c 100644
> --- a/emacs/notmuch.el
> +++ b/emacs/notmuch.el
> @@ -540,9 +540,13 @@ If notmuch exits with a non-zero status, output from the 
> process
>  will appear in a buffer named \"*Notmuch errors*\" and an error
>  will be signaled."
>(with-temp-buffer
> -(let ((status (apply #'call-process notmuch-command nil t nil args)))
> -  (notmuch-check-exit-status status (cons notmuch-command args)
> -  (buffer-string)
> +(let ((err-file (make-temp-file "nmerr")))
> +  (unwind-protect
> +   (let ((status (apply #'call-process
> +notmuch-command nil (list t err-file) nil args)))
> + (notmuch-check-exit-status status (cons notmuch-command args)
> +(buffer-string) err-file))
> + (delete-file err-file)
>  
>  (defun notmuch-search-set-tags (tags &optional pos)
>(let ((new-result (plist-put (notmuch-search-get-result pos) :tags tags)))
> -- 
> 1.7.9.1
___
notmuch mailing list
notmuch@notmuchmail.org
http://notmuchmail.org/mailman/listinfo/notmuch


[PATCH v5 1/6] restore: Make missing messages non-fatal (again)

2013-01-06 Thread Austin Clements
Previously, restore would abort if a message ID in the dump was
missing.  Furthermore, it would only report this as a warning.  This
patch makes it distinguish abort-worthy lookup failures like
out-of-memory from non-fatal failure to find a message ID.  The former
is reported as an error and causes restore to abort, while the latter
is reported as a warning and does not cause an abort.

This restores 0.14's non-fatal handling of missing message IDs in
restore (though 0.14 also considered serious errors non-fatal; we
retain the new and better handling of serious errors).
---
 notmuch-restore.c |   17 +++--
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/notmuch-restore.c b/notmuch-restore.c
index 9ed9b51..96834c0 100644
--- a/notmuch-restore.c
+++ b/notmuch-restore.c
@@ -26,7 +26,8 @@
 static regex_t regex;
 
 /* Non-zero return indicates an error in retrieving the message,
- * or in applying the tags.
+ * or in applying the tags.  Missing messages are reported, but not
+ * considered errors.
  */
 static int
 tag_message (unused (void *ctx),
@@ -40,13 +41,17 @@ tag_message (unused (void *ctx),
 int ret = 0;
 
 status = notmuch_database_find_message (notmuch, message_id, &message);
-if (status || message == NULL) {
-   fprintf (stderr, "Warning: cannot apply tags to %smessage: %s\n",
-message ? "" : "missing ", message_id);
-   if (status)
-   fprintf (stderr, "%s\n", notmuch_status_to_string (status));
+if (status) {
+   fprintf (stderr, "Error applying tags to message %s: %s\n",
+message_id, notmuch_status_to_string (status));
return 1;
 }
+if (message == NULL) {
+   fprintf (stderr, "Warning: cannot apply tags to missing message: %s\n",
+message_id);
+   /* We consider this a non-fatal error. */
+   return 0;
+}
 
 /* In order to detect missing messages, this check/optimization is
  * intentionally done *after* first finding the message. */
-- 
1.7.10.4

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


[PATCH v5 6/6] man: Update notmuch-dump(1) and notmuch-restore(1)

2013-01-06 Thread Austin Clements
Describe the new batch-tag format.  For notmuch-restore, rather than
half-heartedly duplicating the description, we now cite notmuch-dump.
---
 man/man1/notmuch-dump.1|   11 +++
 man/man1/notmuch-restore.1 |6 ++
 2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/man/man1/notmuch-dump.1 b/man/man1/notmuch-dump.1
index 770b00f..7bd6def 100644
--- a/man/man1/notmuch-dump.1
+++ b/man/man1/notmuch-dump.1
@@ -64,13 +64,16 @@ and tags containing whitespace or non-\fBascii\fR(7) 
characters.
 Each line has the form
 
 .RS 4
-.RI "+<" "encoded-tag" "> " "" "+<" "encoded-tag" "> ... -- " "" " id:<" 
encoded-message-id >
+.RI "+<" "encoded-tag" "> " "" "+<" "encoded-tag" "> ... -- " "" " id:<" 
quoted-message-id >
 
-where encoded means that every byte not matching the regex
+Tags are hex-encoded by replacing every byte not matching the regex
 .B [A-Za-z0-9@=.,_+-]
-is replace by
+with
 .B %nn
-where nn is the two digit hex encoding.
+where nn is the two digit hex encoding.  The message ID is a valid Xapian
+query, quoted using Xapian boolean term quoting rules: if the ID contains
+whitespace or a close paren or starts with a double quote, it must be
+enclosed in double quotes and double quotes inside the ID must be doubled.
 The astute reader will notice this is a special case of the batch input
 format for \fBnotmuch-tag\fR(1); note that the single message-id query is
 mandatory for \fBnotmuch-restore\fR(1).
diff --git a/man/man1/notmuch-restore.1 b/man/man1/notmuch-restore.1
index 6bba628..78fef52 100644
--- a/man/man1/notmuch-restore.1
+++ b/man/man1/notmuch-restore.1
@@ -57,10 +57,8 @@ sup calls them).
 The
 .B batch-tag
 dump format is intended to more robust against malformed message-ids
-and tags containing whitespace or non-\fBascii\fR(7) characters.  This
-format hex-escapes all characters those outside of a small character
-set, intended to be suitable for e.g. pathnames in most UNIX-like
-systems.
+and tags containing whitespace or non-\fBascii\fR(7) characters.  See
+\fBnotmuch-dump\fR(1) for details on this format.
 
 .B "notmuch restore"
 updates the maildir flags according to tag changes if the
-- 
1.7.10.4

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


[PATCH v5 2/6] util: Factor out boolean term quoting routine

2013-01-06 Thread Austin Clements
From: Austin Clements 

This is now a generic boolean term quoting function.  It performs
minimal quoting to produce user-friendly queries.

This could live in tag-util as well, but it is really nothing specific
to tags (although the conventions are specific to Xapian).

The API is changed from "caller-allocates" to "readline-like".  The
scan for max tag length is pushed down into the quoting routine.
Furthermore, this now combines the term prefix with the quoted term;
arguably this is just as easy to do in the caller, but this will
nicely parallel the boolean term parsing function to be introduced
shortly.

This is an amalgamation of code written by David Bremner and myself.
---
 notmuch-tag.c  |   48 ++---
 util/string-util.c |   75 
 util/string-util.h |   15 +++
 3 files changed, 104 insertions(+), 34 deletions(-)

diff --git a/notmuch-tag.c b/notmuch-tag.c
index 88d559b..fc9d43a 100644
--- a/notmuch-tag.c
+++ b/notmuch-tag.c
@@ -19,6 +19,7 @@
  */
 
 #include "notmuch-client.h"
+#include "string-util.h"
 
 static volatile sig_atomic_t interrupted;
 
@@ -35,25 +36,6 @@ handle_sigint (unused (int sig))
 interrupted = 1;
 }
 
-static char *
-_escape_tag (char *buf, const char *tag)
-{
-const char *in = tag;
-char *out = buf;
-
-/* Boolean terms surrounded by double quotes can contain any
- * character.  Double quotes are quoted by doubling them. */
-*out++ = '"';
-while (*in) {
-   if (*in == '"')
-   *out++ = '"';
-   *out++ = *in++;
-}
-*out++ = '"';
-*out = 0;
-return buf;
-}
-
 typedef struct {
 const char *tag;
 notmuch_bool_t remove;
@@ -71,25 +53,16 @@ _optimize_tag_query (void *ctx, const char 
*orig_query_string,
  * parenthesize and the exclusion part of the query must not use
  * the '-' operator (though the NOT operator is fine). */
 
-char *escaped, *query_string;
+char *escaped = NULL;
+size_t escaped_len = 0;
+char *query_string;
 const char *join = "";
-int i;
-unsigned int max_tag_len = 0;
+size_t i;
 
 /* Don't optimize if there are no tag changes. */
 if (tag_ops[0].tag == NULL)
return talloc_strdup (ctx, orig_query_string);
 
-/* Allocate a buffer for escaping tags.  This is large enough to
- * hold a fully escaped tag with every character doubled plus
- * enclosing quotes and a NUL. */
-for (i = 0; tag_ops[i].tag; i++)
-   if (strlen (tag_ops[i].tag) > max_tag_len)
-   max_tag_len = strlen (tag_ops[i].tag);
-escaped = talloc_array (ctx, char, max_tag_len * 2 + 3);
-if (! escaped)
-   return NULL;
-
 /* Build the new query string */
 if (strcmp (orig_query_string, "*") == 0)
query_string = talloc_strdup (ctx, "(");
@@ -97,10 +70,17 @@ _optimize_tag_query (void *ctx, const char 
*orig_query_string,
query_string = talloc_asprintf (ctx, "( %s ) and (", orig_query_string);
 
 for (i = 0; tag_ops[i].tag && query_string; i++) {
+   /* XXX in case of OOM, query_string will be deallocated when
+* ctx is, which might be at shutdown */
+   if (make_boolean_term (ctx,
+  "tag", tag_ops[i].tag,
+  &escaped, &escaped_len))
+   return NULL;
+
query_string = talloc_asprintf_append_buffer (
-   query_string, "%s%stag:%s", join,
+   query_string, "%s%s%s", join,
tag_ops[i].remove ? "" : "not ",
-   _escape_tag (escaped, tag_ops[i].tag));
+   escaped);
join = " or ";
 }
 
diff --git a/util/string-util.c b/util/string-util.c
index 44f8cd3..7a71049 100644
--- a/util/string-util.c
+++ b/util/string-util.c
@@ -20,6 +20,9 @@
 
 
 #include "string-util.h"
+#include "talloc.h"
+
+#include 
 
 char *
 strtok_len (char *s, const char *delim, size_t *len)
@@ -32,3 +35,75 @@ strtok_len (char *s, const char *delim, size_t *len)
 
 return *len ? s : NULL;
 }
+
+static int
+is_unquoted_terminator (unsigned char c)
+{
+return c == 0 || c <= ' ' || c == ')';
+}
+
+int
+make_boolean_term (void *ctx, const char *prefix, const char *term,
+  char **buf, size_t *len)
+{
+const char *in;
+char *out;
+size_t needed = 3;
+int need_quoting = 0;
+
+/* Do we need quoting?  To be paranoid, we quote anything
+ * containing a quote, even though it only matters at the
+ * beginning, and anything containing non-ASCII text. */
+for (in = term; *in && !need_quoting; in++)
+   if (is_unquoted_terminator (*in) || *in == '"'
+   || (unsigned char)*in > 127)
+   need_quoting = 1;
+
+if (need_quoting)
+   for (in = term; *in; in++)
+   needed += (*in == '"') ? 2 : 1;
+else
+   needed = strlen (term) + 1;
+
+/* Reserve space for the prefix */
+if (prefix)
+   needed += strlen (prefix) + 1;
+
+if ((*buf == N

[PATCH v5 5/6] dump/restore: Use Xapian queries for batch-tag format

2013-01-06 Thread Austin Clements
This switches the new batch-tag format away from using a home-grown
hex-encoding scheme for message IDs in the dump to simply using Xapian
queries with Xapian quoting syntax.

This has a variety of advantages beyond presenting a cleaner and more
consistent interface.  Foremost is that it will dramatically simplify
the quoting for batch tagging, which shares the same input format.
While the hex-encoding is no better or worse for the simple ID queries
used by dump/restore, it becomes onerous for general-purpose queries
used in batch tagging.  It also better handles strange cases like
"id:foo and bar", since this is no longer syntactically valid.
---
 notmuch-dump.c|   11 ++-
 notmuch-restore.c |   27 +++
 tag-util.c|6 --
 test/dump-restore |   28 ++--
 4 files changed, 43 insertions(+), 29 deletions(-)

diff --git a/notmuch-dump.c b/notmuch-dump.c
index 5bbda36..a3244e0 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -20,6 +20,7 @@
 
 #include "notmuch-client.h"
 #include "dump-restore-private.h"
+#include "string-util.h"
 
 int
 notmuch_dump_command (unused (void *ctx), int argc, char *argv[])
@@ -144,13 +145,13 @@ notmuch_dump_command (unused (void *ctx), int argc, char 
*argv[])
if (output_format == DUMP_FORMAT_SUP) {
fputs (")\n", output);
} else {
-   if (hex_encode (notmuch, message_id,
-   &buffer, &buffer_size) != HEX_SUCCESS) {
-   fprintf (stderr, "Error: failed to hex-encode msg-id %s\n",
-message_id);
+   if (make_boolean_term (notmuch, "id", message_id,
+  &buffer, &buffer_size)) {
+   fprintf (stderr, "Error quoting message id %s: %s\n",
+message_id, strerror (errno));
return 1;
}
-   fprintf (output, " -- id:%s\n", buffer);
+   fprintf (output, " -- %s\n", buffer);
}
 
notmuch_message_destroy (message);
diff --git a/notmuch-restore.c b/notmuch-restore.c
index 96834c0..81d4d98 100644
--- a/notmuch-restore.c
+++ b/notmuch-restore.c
@@ -212,7 +212,7 @@ notmuch_restore_command (unused (void *ctx), int argc, char 
*argv[])
INTERNAL_ERROR ("compile time constant regex failed.");
 
 do {
-   char *query_string;
+   char *query_string, *prefix, *term;
 
if (line_ctx != NULL)
talloc_free (line_ctx);
@@ -225,19 +225,22 @@ notmuch_restore_command (unused (void *ctx), int argc, 
char *argv[])
  &query_string, tag_ops);
 
if (ret == 0) {
-   if (strncmp ("id:", query_string, 3) != 0) {
-   fprintf (stderr, "Warning: unsupported query: %s\n", 
query_string);
+   ret = parse_boolean_term (line_ctx, query_string,
+ &prefix, &term);
+   if (ret && errno == EINVAL) {
+   fprintf (stderr, "Warning: cannot parse query: %s 
(skipping)\n", query_string);
+   continue;
+   } else if (ret) {
+   /* This is more fatal (e.g., out of memory) */
+   fprintf (stderr, "Error parsing query: %s\n",
+strerror (errno));
+   ret = 1;
+   break;
+   } else if (strcmp ("id", prefix) != 0) {
+   fprintf (stderr, "Warning: not an id query: %s 
(skipping)\n", query_string);
continue;
}
-   /* delete id: from front of string; tag_message
-* expects a raw message-id.
-*
-* XXX: Note that query string id:foo and bar will be
-* interpreted as a message id "foo and bar". This
-* should eventually be fixed to give a better error
-* message.
-*/
-   query_string = query_string + 3;
+   query_string = term;
}
}
 
diff --git a/tag-util.c b/tag-util.c
index 705b7ba..e4e5dda 100644
--- a/tag-util.c
+++ b/tag-util.c
@@ -124,12 +124,6 @@ parse_tag_line (void *ctx, char *line,
 }
 
 /* tok now points to the query string */
-if (hex_decode_inplace (tok) != HEX_SUCCESS) {
-   ret = line_error (TAG_PARSE_INVALID, line_for_error,
- "hex decoding of query %s failed", tok);
-   goto DONE;
-}
-
 *query_string = tok;
 
   DONE:
diff --git a/test/dump-restore b/test/dump-restore
index 6a989b6..f076c12 100755
--- a/test/dump-restore
+++ b/test/dump-restore
@@ -195,23 +195,39 @@ a
 
 # the previous line was blank; also no yelling please
 +%zz -- id:whatever
-+e +f id:%yy
++e +f id:"
++e +f tag:abc
 # the next non-comment line should report an an empty tag error for
 # batch tagging, but not for restore
 + +e -- id:20091117232137.ga7...@griffis

[PATCH v5 4/6] dump: Disallow \n in message IDs

2013-01-06 Thread Austin Clements
When we switch to using regular Xapian queries in the dump format, \n
will cause problems, so we disallow it.  Specially, while Xapian can
quote and parse queries containing \n without difficultly, quoted
queries containing \n still span multiple lines, which breaks the
line-orientedness of the dump format.  Strictly speaking, we could
still round-trip these, but it would significantly complicate restore
as well as scripts that deal with tag dumps.  This complexity would
come at absolutely no benefit: because of the RFC 2822 unfolding
rules, no amount of standards negligence can produce a message with a
message ID containing a line break (not even Outlook can do it!).

Hence, we simply disallow it.
---
 notmuch-dump.c   |   12 
 test/random-corpus.c |4 +++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/notmuch-dump.c b/notmuch-dump.c
index d2dad40..5bbda36 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -102,6 +102,18 @@ notmuch_dump_command (unused (void *ctx), int argc, char 
*argv[])
message = notmuch_messages_get (messages);
message_id = notmuch_message_get_message_id (message);
 
+   if (output_format == DUMP_FORMAT_BATCH_TAG &&
+   strchr (message_id, '\n')) {
+   /* This will produce a line break in the output, which
+* would be difficult to handle in tools.  However, it's
+* also impossible to produce an email containing a line
+* break in a message ID because of unfolding, so we can
+* safely disallow it. */
+   fprintf (stderr, "Warning: skipping message id containing line 
break: \"%s\"\n", message_id);
+   notmuch_message_destroy (message);
+   continue;
+   }
+
if (output_format == DUMP_FORMAT_SUP) {
fprintf (output, "%s (", message_id);
}
diff --git a/test/random-corpus.c b/test/random-corpus.c
index f354d4b..8b7748e 100644
--- a/test/random-corpus.c
+++ b/test/random-corpus.c
@@ -96,7 +96,9 @@ random_utf8_string (void *ctx, size_t char_count)
buf = talloc_realloc (ctx, buf, gchar, buf_size);
}
 
-   randomchar = random_unichar ();
+   do {
+   randomchar = random_unichar ();
+   } while (randomchar == '\n');
 
written = g_unichar_to_utf8 (randomchar, buf + offset);
 
-- 
1.7.10.4

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


[PATCH v5 0/6] Use Xapian query syntax for batch-tag dump/restore

2013-01-06 Thread Austin Clements
This obsoletes

  id:1356936162-2589-1-git-send-email-amdra...@mit.edu

v5 should address all of the comments on v4 except those I
specifically replied to (via the ML or IRC).  It also adds a new patch
at the beginning that makes missing message IDs non-fatal in restore,
like they were in 0.14.  This patch can be pushed separately; it's in
this series because later tests rely on it.

The diff from v4 follows.

diff --git a/notmuch-dump.c b/notmuch-dump.c
index bf01a39..a3244e0 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -103,6 +103,18 @@ notmuch_dump_command (unused (void *ctx), int argc, char 
*argv[])
message = notmuch_messages_get (messages);
message_id = notmuch_message_get_message_id (message);
 
+   if (output_format == DUMP_FORMAT_BATCH_TAG &&
+   strchr (message_id, '\n')) {
+   /* This will produce a line break in the output, which
+* would be difficult to handle in tools.  However, it's
+* also impossible to produce an email containing a line
+* break in a message ID because of unfolding, so we can
+* safely disallow it. */
+   fprintf (stderr, "Warning: skipping message id containing line 
break: \"%s\"\n", message_id);
+   notmuch_message_destroy (message);
+   continue;
+   }
+
if (output_format == DUMP_FORMAT_SUP) {
fprintf (output, "%s (", message_id);
}
@@ -133,19 +145,10 @@ notmuch_dump_command (unused (void *ctx), int argc, char 
*argv[])
if (output_format == DUMP_FORMAT_SUP) {
fputs (")\n", output);
} else {
-   if (strchr (message_id, '\n')) {
-   /* This will produce a line break in the output, which
-* would be difficult to handle in tools.  However,
-* it's also impossible to produce an email containing
-* a line break in a message ID because of unfolding,
-* so we can safely disallow it. */
-   fprintf (stderr, "Error: cannot dump message id containing line 
break: %s\n", message_id);
-   return 1;
-   }
if (make_boolean_term (notmuch, "id", message_id,
   &buffer, &buffer_size)) {
-   fprintf (stderr, "Error: failed to quote message id %s\n",
-message_id);
+   fprintf (stderr, "Error quoting message id %s: %s\n",
+message_id, strerror (errno));
return 1;
}
fprintf (output, " -- %s\n", buffer);
diff --git a/notmuch-restore.c b/notmuch-restore.c
index 77a4c27..81d4d98 100644
--- a/notmuch-restore.c
+++ b/notmuch-restore.c
@@ -26,7 +26,8 @@
 static regex_t regex;
 
 /* Non-zero return indicates an error in retrieving the message,
- * or in applying the tags.
+ * or in applying the tags.  Missing messages are reported, but not
+ * considered errors.
  */
 static int
 tag_message (unused (void *ctx),
@@ -40,13 +41,17 @@ tag_message (unused (void *ctx),
 int ret = 0;
 
 status = notmuch_database_find_message (notmuch, message_id, &message);
-if (status || message == NULL) {
-   fprintf (stderr, "Warning: cannot apply tags to %smessage: %s\n",
-message ? "" : "missing ", message_id);
-   if (status)
-   fprintf (stderr, "%s\n", notmuch_status_to_string (status));
+if (status) {
+   fprintf (stderr, "Error applying tags to message %s: %s\n",
+message_id, notmuch_status_to_string (status));
return 1;
 }
+if (message == NULL) {
+   fprintf (stderr, "Warning: cannot apply tags to missing message: %s\n",
+message_id);
+   /* We consider this a non-fatal error. */
+   return 0;
+}
 
 /* In order to detect missing messages, this check/optimization is
  * intentionally done *after* first finding the message. */
@@ -222,12 +227,17 @@ notmuch_restore_command (unused (void *ctx), int argc, 
char *argv[])
if (ret == 0) {
ret = parse_boolean_term (line_ctx, query_string,
  &prefix, &term);
-   if (ret) {
-   fprintf (stderr, "Warning: cannot parse query: %s\n",
-query_string);
+   if (ret && errno == EINVAL) {
+   fprintf (stderr, "Warning: cannot parse query: %s 
(skipping)\n", query_string);
continue;
+   } else if (ret) {
+   /* This is more fatal (e.g., out of memory) */
+   fprintf (stderr, "Error parsing query: %s\n",
+strerror (errno));
+   ret = 1;
+   break;
} else if (strcmp ("id", prefix) != 0) {
-   fprintf (stderr, "Warning: not an id query: %s\n", 
query_string);
+   fprintf (stderr, "Warn

[PATCH v5 3/6] util: Function to parse boolean term queries

2013-01-06 Thread Austin Clements
This parses the subset of Xapian's boolean term quoting rules that are
used by make_boolean_term.  This is provided as a generic string
utility, but will be used shortly in notmuch restore to parse and
optimize for ID queries.
---
 util/string-util.c |   82 
 util/string-util.h |   16 ++
 2 files changed, 98 insertions(+)

diff --git a/util/string-util.c b/util/string-util.c
index 7a71049..aba9aa8 100644
--- a/util/string-util.c
+++ b/util/string-util.c
@@ -22,6 +22,7 @@
 #include "string-util.h"
 #include "talloc.h"
 
+#include 
 #include 
 
 char *
@@ -107,3 +108,84 @@ make_boolean_term (void *ctx, const char *prefix, const 
char *term,
 
 return 0;
 }
+
+static const char*
+skip_space (const char *str)
+{
+while (*str && isspace ((unsigned char) *str))
+   ++str;
+return str;
+}
+
+int
+parse_boolean_term (void *ctx, const char *str,
+   char **prefix_out, char **term_out)
+{
+int err = EINVAL;
+*prefix_out = *term_out = NULL;
+
+/* Parse prefix */
+str = skip_space (str);
+const char *pos = strchr (str, ':');
+if (! pos)
+   goto FAIL;
+*prefix_out = talloc_strndup (ctx, str, pos - str);
+if (! *prefix_out) {
+   err = ENOMEM;
+   goto FAIL;
+}
+++pos;
+
+/* Implement de-quoting compatible with make_boolean_term. */
+if (*pos == '"') {
+   char *out = talloc_array (ctx, char, strlen (pos));
+   int closed = 0;
+   if (! out) {
+   err = ENOMEM;
+   goto FAIL;
+   }
+   *term_out = out;
+   /* Skip the opening quote, find the closing quote, and
+* un-double doubled internal quotes. */
+   for (++pos; *pos; ) {
+   if (*pos == '"') {
+   ++pos;
+   if (*pos != '"') {
+   /* Found the closing quote. */
+   closed = 1;
+   pos = skip_space (pos);
+   break;
+   }
+   }
+   *out++ = *pos++;
+   }
+   /* Did the term terminate without a closing quote or is there
+* trailing text after the closing quote? */
+   if (!closed || *pos)
+   goto FAIL;
+   *out = '\0';
+} else {
+   const char *start = pos;
+   /* Check for text after the boolean term. */
+   while (! is_unquoted_terminator (*pos))
+   ++pos;
+   if (*skip_space (pos)) {
+   err = EINVAL;
+   goto FAIL;
+   }
+   /* No trailing text; dup the string so the caller can free
+* it. */
+   *term_out = talloc_strndup (ctx, start, pos - start);
+   if (! *term_out) {
+   err = ENOMEM;
+   goto FAIL;
+   }
+}
+return 0;
+
+ FAIL:
+talloc_free (*prefix_out);
+talloc_free (*term_out);
+errno = err;
+return -1;
+}
diff --git a/util/string-util.h b/util/string-util.h
index 719c276..0194607 100644
--- a/util/string-util.h
+++ b/util/string-util.h
@@ -34,4 +34,20 @@ char *strtok_len (char *s, const char *delim, size_t *len);
 int make_boolean_term (void *talloc_ctx, const char *prefix, const char *term,
   char **buf, size_t *len);
 
+/* Parse a boolean term query consisting of a prefix, a colon, and a
+ * term that may be quoted as described for make_boolean_term.  If the
+ * term is not quoted, then it ends at the first whitespace or close
+ * parenthesis.  str may containing leading or trailing whitespace,
+ * but anything else is considered a parse error.  This is compatible
+ * with anything produced by make_boolean_term, and supports a subset
+ * of the quoting styles supported by Xapian (and hence notmuch).
+ * *prefix_out and *term_out will be talloc'd with context ctx.
+ *
+ * Return: 0 on success, -1 on error.  errno will be set to EINVAL if
+ * there is a parse error or ENOMEM if there is an allocation failure.
+ */
+int
+parse_boolean_term (void *ctx, const char *str,
+   char **prefix_out, char **term_out);
+
 #endif
-- 
1.7.10.4

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


[Patch v2] notmuch-restore: handle empty input file, leading blank lines and comments.

2013-01-06 Thread da...@tethera.net
From: David Bremner 

This patch corrects several undesirable behaviours:

1) Empty files were not detected, leading to buffer read overrun.

2) An initial blank line cause restore to silently abort

3) Initial comment line caused format detection to fail
---
 notmuch-restore.c |   18 +-
 test/dump-restore |3 ---
 2 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/notmuch-restore.c b/notmuch-restore.c
index d43546d..f436989 100644
--- a/notmuch-restore.c
+++ b/notmuch-restore.c
@@ -181,11 +181,6 @@ notmuch_restore_command (unused (void *ctx), int argc, 
char *argv[])
 argv[opt_index]);
return 1;
 }
-char *p;
-
-line_len = getline (&line, &line_size, input);
-if (line_len == 0)
-   return 0;

 tag_ops = tag_op_list_create (ctx);
 if (tag_ops == NULL) {
@@ -193,6 +188,19 @@ notmuch_restore_command (unused (void *ctx), int argc, 
char *argv[])
return 1;
 }

+do {
+   line_len = getline (&line, &line_size, input);
+
+   /* empty input file not considered an error */
+   if (line_len < 0)
+   return 0;
+
+} while ((line_len == 0) ||
+(line[0] == '#') ||
+/* the cast is safe because we checked about for line_len < 0 */
+(strspn (line, " \t\n") == (unsigned)line_len));
+
+char *p;
 for (p = line; (input_format == DUMP_FORMAT_AUTO) && *p; p++) {
if (*p == '(')
input_format = DUMP_FORMAT_SUP;
diff --git a/test/dump-restore b/test/dump-restore
index c2ddb92..ae30cd1 100755
--- a/test/dump-restore
+++ b/test/dump-restore
@@ -146,13 +146,11 @@ cat < comments-and-blanks
 EOF

 test_begin_subtest 'restoring empty file is not an error'
-test_subtest_known_broken
 notmuch restore < /dev/null 2>OUTPUT.$test_count
 cp /dev/null EXPECTED
 test_expect_equal_file EXPECTED OUTPUT.$test_count

 test_begin_subtest 'file of comments and blank lines is not an error'
-test_subtest_known_broken
 notmuch restore --input=comments-and-blanks
 ret_val=$?
 test_expect_equal "$ret_val" "0"
@@ -172,7 +170,6 @@ echo "yun1vjwegii.fsf at aiko.keithp.com (another_tag)" \
 >> leading-comments-blanks-sup

 test_begin_subtest 'detect format=sup with leading comments and blanks'
-test_subtest_known_broken
 notmuch restore --input=leading-comments-blanks-sup
 notmuch search --output=tags id:yun1vjwegii.fsf at aiko.keithp.com > 
OUTPUT.$test_count
 echo "another_tag" > EXPECTED
-- 
1.7.10.4



Re: [PATCH] emacs: show: make id links respect window

2013-01-06 Thread Mark Walters

On Sun, 06 Jan 2013, Tomi Ollila  wrote:
> On Sun, Jan 06 2013, Mark Walters  wrote:
>
>> On Sat, 05 Jan 2013, Tomi Ollila  wrote:
>>> On Wed, Dec 26 2012, David Bremner  wrote:
>>>
 Mark Walters  writes:

>> I can imagine that people would want/like the "open in other window" 
>> effect of the current code, even if the reason is a bug.
>
> That's definitely possible. I generally expect a mouse click to select
> the window I click and this feels counter intuitive. I think that some
> people might like an option "open this link in a new window" but I would
> guess that would like that whether they clicked or pressed RET on the
> button.

 I don't care much either way myself, but before we change notmuch-show
 behaviour (effectively) to accomodate notmuch-pick, I'd like a bit more
 feedback from other people.
>>>
>>> I tested the old behaviour -- split frame to 2 windows, one containing
>>> *scratch* and one *notmuch-hello*. Then I searched for 'obsoletes',
>>> chose last message (id:1356936162-2589-1-git-send-email-amdra...@mit.edu)
>>> moved point to *scratch* buffer and clicked the id: link -- and indeed,
>>> the *scratch* window was replaced.
>>>
>>> I didn't look or test Mark's patch as he stated:
>>> "The lisp is not pretty but seems to work." ;)
>>> ... well, not entirely because of that but I trust it opens the message
>>> in window where the clicked link were and keeps point where it used to
>>> be before clicking (in case point was in different window).
>>
>> It doesn't quite do this: point moves to the window that was clicked. It
>> is just as easy to do as Tomi says (patch below but I should resend if
>> people like it so the commit message gets picked up).
>
> It seems to be the default (and iniuitively expected) behaviour that
> point moves to the window that was clicked... so that's ok :)
>
>> Incidentally, I would be interested to know what people expect the
>> following to do: go to notmuch hello and then search. Now display the
>> results in two windows simultaneously (either split the frame into two
>> windows (c-x 2) or use 2 frames (C-x 5 2)) and then press q.
>>
>> What actually happens is that q runs kill buffer so it disappears in
>> both windows: one of which will fall back to notmuch-hello and one of
>> which will fall back to some other window (eg scratch)
>
> I think that is just expected behaviour, there are e.g.. c-x b and c-x 0
> which should be familiar to every emacs users...

My instinct would be that the window I press q in should go to the
previous buffer in that window but the other window should stay where it
was. But if others are happy as it that is obviously fine.


>> Best wishes
>>
>> Mark
>
> Tomi
>
> PS: did you change anything in the patch below ?

Yes: the new version uses (with-selected-window window ...) compared
with (select-window window) (...)

So the old version switches point to the clicked window, the new version
does not. I am happy either way: the click does normally move point, but
other buttons such as invisibility buttons do not move point when
mouse-clicked so

Best wishes


Mark


>>
>>  emacs/notmuch-show.el |5 +
>>  1 files changed, 5 insertions(+), 0 deletions(-)
>>
>> diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el
>> index 5751d98..00b9b56 100644
>> --- a/emacs/notmuch-show.el
>> +++ b/emacs/notmuch-show.el
>> @@ -1077,6 +1077,11 @@ buttons for a corresponding notmuch search."
>>  (make-text-button (first link) (second link)
>>'action `(lambda (arg)
>>   (notmuch-show ,(third link)))
>> +  'mouse-action `(lambda (arg)
>> +   (let* ((event last-input-event)
>> +  (window (car (cadr event
>> + (with-selected-window window
>> +   (notmuch-show ,(third link)
>>'follow-link t
>>'help-echo "Mouse-1, RET: search for this message"
>>'face goto-address-mail-face)
>> -- 
>> 1.7.9.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


Re: [PATCH] emacs: show: make id links respect window

2013-01-06 Thread Tomi Ollila
On Sun, Jan 06 2013, Mark Walters  wrote:

> On Sat, 05 Jan 2013, Tomi Ollila  wrote:
>> On Wed, Dec 26 2012, David Bremner  wrote:
>>
>>> Mark Walters  writes:
>>>
> I can imagine that people would want/like the "open in other window" 
> effect of the current code, even if the reason is a bug.

 That's definitely possible. I generally expect a mouse click to select
 the window I click and this feels counter intuitive. I think that some
 people might like an option "open this link in a new window" but I would
 guess that would like that whether they clicked or pressed RET on the
 button.
>>>
>>> I don't care much either way myself, but before we change notmuch-show
>>> behaviour (effectively) to accomodate notmuch-pick, I'd like a bit more
>>> feedback from other people.
>>
>> I tested the old behaviour -- split frame to 2 windows, one containing
>> *scratch* and one *notmuch-hello*. Then I searched for 'obsoletes',
>> chose last message (id:1356936162-2589-1-git-send-email-amdra...@mit.edu)
>> moved point to *scratch* buffer and clicked the id: link -- and indeed,
>> the *scratch* window was replaced.
>>
>> I didn't look or test Mark's patch as he stated:
>> "The lisp is not pretty but seems to work." ;)
>> ... well, not entirely because of that but I trust it opens the message
>> in window where the clicked link were and keeps point where it used to
>> be before clicking (in case point was in different window).
>
> It doesn't quite do this: point moves to the window that was clicked. It
> is just as easy to do as Tomi says (patch below but I should resend if
> people like it so the commit message gets picked up).

It seems to be the default (and iniuitively expected) behaviour that
point moves to the window that was clicked... so that's ok :)

> Incidentally, I would be interested to know what people expect the
> following to do: go to notmuch hello and then search. Now display the
> results in two windows simultaneously (either split the frame into two
> windows (c-x 2) or use 2 frames (C-x 5 2)) and then press q.
>
> What actually happens is that q runs kill buffer so it disappears in
> both windows: one of which will fall back to notmuch-hello and one of
> which will fall back to some other window (eg scratch)

I think that is just expected behaviour, there are e.g.. c-x b and c-x 0
which should be familiar to every emacs users...

> Best wishes
>
> Mark

Tomi

PS: did you change anything in the patch below ?

>
>  emacs/notmuch-show.el |5 +
>  1 files changed, 5 insertions(+), 0 deletions(-)
>
> diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el
> index 5751d98..00b9b56 100644
> --- a/emacs/notmuch-show.el
> +++ b/emacs/notmuch-show.el
> @@ -1077,6 +1077,11 @@ buttons for a corresponding notmuch search."
>   (make-text-button (first link) (second link)
> 'action `(lambda (arg)
>(notmuch-show ,(third link)))
> +   'mouse-action `(lambda (arg)
> +(let* ((event last-input-event)
> +   (window (car (cadr event
> +  (with-selected-window window
> +(notmuch-show ,(third link)
> 'follow-link t
> 'help-echo "Mouse-1, RET: search for this message"
> 'face goto-address-mail-face)
> -- 
> 1.7.9.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


Re: gui client

2013-01-06 Thread Rainer M Krug
-BEGIN PGP SIGNED MESSAGE-
Hash: SHA1

On 23/12/12 17:08, Jameson Graef Rollins wrote:
> On Fri, Dec 21 2012, Sergei Shilovsky 
> wrote:
>> Is there a GUI notmuch client? Or is somebody working on it?
>> 
>> I'm thinking of writing one myself, so I want to know current situation.
> 
> Hi, Sergei.  There are some curses front-ends [0], but I haven't heard of any 
> GUI ones.  Good
> luck!

I think it would be a good idea to have a GUI for searching emails and, 
although I am using emacs,
would use it quite regularly.

Cheers,

Rainer


> 
> jamie.
> 
> [0] http://notmuchmail.org/frontends/
> 
> 
> 

-BEGIN PGP SIGNATURE-
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iEYEARECAAYFAlDpnaMACgkQoYgNqgF2egpbIACfUYOOj6H5zbSWFu8ilHW1t0o4
J4QAoIhgDQT7HDTRjhzOXsyfivWUA2NW
=g94w
-END PGP SIGNATURE-

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


Re: [PATCH] emacs: show: make id links respect window

2013-01-06 Thread Mark Walters

On Sat, 05 Jan 2013, Tomi Ollila  wrote:
> On Wed, Dec 26 2012, David Bremner  wrote:
>
>> Mark Walters  writes:
>>
 I can imagine that people would want/like the "open in other window" 
 effect of the current code, even if the reason is a bug.
>>>
>>> That's definitely possible. I generally expect a mouse click to select
>>> the window I click and this feels counter intuitive. I think that some
>>> people might like an option "open this link in a new window" but I would
>>> guess that would like that whether they clicked or pressed RET on the
>>> button.
>>
>> I don't care much either way myself, but before we change notmuch-show
>> behaviour (effectively) to accomodate notmuch-pick, I'd like a bit more
>> feedback from other people.
>
> I tested the old behaviour -- split frame to 2 windows, one containing
> *scratch* and one *notmuch-hello*. Then I searched for 'obsoletes',
> chose last message (id:1356936162-2589-1-git-send-email-amdra...@mit.edu)
> moved point to *scratch* buffer and clicked the id: link -- and indeed,
> the *scratch* window was replaced.
>
> I didn't look or test Mark's patch as he stated:
> "The lisp is not pretty but seems to work." ;)
> ... well, not entirely because of that but I trust it opens the message
> in window where the clicked link were and keeps point where it used to
> be before clicking (in case point was in different window).

It doesn't quite do this: point moves to the window that was clicked. It
is just as easy to do as Tomi says (patch below but I should resend if
people like it so the commit message gets picked up).

Incidentally, I would be interested to know what people expect the
following to do: go to notmuch hello and then search. Now display the
results in two windows simultaneously (either split the frame into two
windows (c-x 2) or use 2 frames (C-x 5 2)) and then press q.

What actually happens is that q runs kill buffer so it disappears in
both windows: one of which will fall back to notmuch-hello and one of
which will fall back to some other window (eg scratch)

Best wishes

Mark

 emacs/notmuch-show.el |5 +
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el
index 5751d98..00b9b56 100644
--- a/emacs/notmuch-show.el
+++ b/emacs/notmuch-show.el
@@ -1077,6 +1077,11 @@ buttons for a corresponding notmuch search."
(make-text-button (first link) (second link)
  'action `(lambda (arg)
 (notmuch-show ,(third link)))
+ 'mouse-action `(lambda (arg)
+  (let* ((event last-input-event)
+ (window (car (cadr event
+(with-selected-window window
+  (notmuch-show ,(third link)
  'follow-link t
  'help-echo "Mouse-1, RET: search for this message"
  'face goto-address-mail-face)
-- 
1.7.9.1

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


Re: [Patch v2] notmuch-restore: handle empty input file, leading blank lines and comments.

2013-01-06 Thread Tomi Ollila
On Sun, Jan 06 2013, da...@tethera.net wrote:

> From: David Bremner 
>
> This patch corrects several undesirable behaviours:
>
> 1) Empty files were not detected, leading to buffer read overrun.
>
> 2) An initial blank line cause restore to silently abort
>
> 3) Initial comment line caused format detection to fail
> ---

LGTM.

Tomi

>  notmuch-restore.c |   18 +-
>  test/dump-restore |3 ---
>  2 files changed, 13 insertions(+), 8 deletions(-)
>
> diff --git a/notmuch-restore.c b/notmuch-restore.c
> index d43546d..f436989 100644
> --- a/notmuch-restore.c
> +++ b/notmuch-restore.c
> @@ -181,11 +181,6 @@ notmuch_restore_command (unused (void *ctx), int argc, 
> char *argv[])
>argv[opt_index]);
>   return 1;
>  }
> -char *p;
> -
> -line_len = getline (&line, &line_size, input);
> -if (line_len == 0)
> - return 0;
>  
>  tag_ops = tag_op_list_create (ctx);
>  if (tag_ops == NULL) {
> @@ -193,6 +188,19 @@ notmuch_restore_command (unused (void *ctx), int argc, 
> char *argv[])
>   return 1;
>  }
>  
> +do {
> + line_len = getline (&line, &line_size, input);
> +
> + /* empty input file not considered an error */
> + if (line_len < 0)
> + return 0;
> +
> +} while ((line_len == 0) ||
> +  (line[0] == '#') ||
> +  /* the cast is safe because we checked about for line_len < 0 */
> +  (strspn (line, " \t\n") == (unsigned)line_len));
> +
> +char *p;
>  for (p = line; (input_format == DUMP_FORMAT_AUTO) && *p; p++) {
>   if (*p == '(')
>   input_format = DUMP_FORMAT_SUP;
> diff --git a/test/dump-restore b/test/dump-restore
> index c2ddb92..ae30cd1 100755
> --- a/test/dump-restore
> +++ b/test/dump-restore
> @@ -146,13 +146,11 @@ cat < comments-and-blanks
>  EOF
>  
>  test_begin_subtest 'restoring empty file is not an error'
> -test_subtest_known_broken
>  notmuch restore < /dev/null 2>OUTPUT.$test_count
>  cp /dev/null EXPECTED
>  test_expect_equal_file EXPECTED OUTPUT.$test_count
>  
>  test_begin_subtest 'file of comments and blank lines is not an error'
> -test_subtest_known_broken
>  notmuch restore --input=comments-and-blanks
>  ret_val=$?
>  test_expect_equal "$ret_val" "0"
> @@ -172,7 +170,6 @@ echo "yun1vjwegii@aiko.keithp.com (another_tag)" \
>  >> leading-comments-blanks-sup
>  
>  test_begin_subtest 'detect format=sup with leading comments and blanks'
> -test_subtest_known_broken
>  notmuch restore --input=leading-comments-blanks-sup
>  notmuch search --output=tags id:yun1vjwegii@aiko.keithp.com > 
> OUTPUT.$test_count
>  echo "another_tag" > EXPECTED
> -- 
> 1.7.10.4
>
> ___
> 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 v2] notmuch-restore: handle empty input file, leading blank lines and comments.

2013-01-06 Thread david
From: David Bremner 

This patch corrects several undesirable behaviours:

1) Empty files were not detected, leading to buffer read overrun.

2) An initial blank line cause restore to silently abort

3) Initial comment line caused format detection to fail
---
 notmuch-restore.c |   18 +-
 test/dump-restore |3 ---
 2 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/notmuch-restore.c b/notmuch-restore.c
index d43546d..f436989 100644
--- a/notmuch-restore.c
+++ b/notmuch-restore.c
@@ -181,11 +181,6 @@ notmuch_restore_command (unused (void *ctx), int argc, 
char *argv[])
 argv[opt_index]);
return 1;
 }
-char *p;
-
-line_len = getline (&line, &line_size, input);
-if (line_len == 0)
-   return 0;
 
 tag_ops = tag_op_list_create (ctx);
 if (tag_ops == NULL) {
@@ -193,6 +188,19 @@ notmuch_restore_command (unused (void *ctx), int argc, 
char *argv[])
return 1;
 }
 
+do {
+   line_len = getline (&line, &line_size, input);
+
+   /* empty input file not considered an error */
+   if (line_len < 0)
+   return 0;
+
+} while ((line_len == 0) ||
+(line[0] == '#') ||
+/* the cast is safe because we checked about for line_len < 0 */
+(strspn (line, " \t\n") == (unsigned)line_len));
+
+char *p;
 for (p = line; (input_format == DUMP_FORMAT_AUTO) && *p; p++) {
if (*p == '(')
input_format = DUMP_FORMAT_SUP;
diff --git a/test/dump-restore b/test/dump-restore
index c2ddb92..ae30cd1 100755
--- a/test/dump-restore
+++ b/test/dump-restore
@@ -146,13 +146,11 @@ cat < comments-and-blanks
 EOF
 
 test_begin_subtest 'restoring empty file is not an error'
-test_subtest_known_broken
 notmuch restore < /dev/null 2>OUTPUT.$test_count
 cp /dev/null EXPECTED
 test_expect_equal_file EXPECTED OUTPUT.$test_count
 
 test_begin_subtest 'file of comments and blank lines is not an error'
-test_subtest_known_broken
 notmuch restore --input=comments-and-blanks
 ret_val=$?
 test_expect_equal "$ret_val" "0"
@@ -172,7 +170,6 @@ echo "yun1vjwegii@aiko.keithp.com (another_tag)" \
 >> leading-comments-blanks-sup
 
 test_begin_subtest 'detect format=sup with leading comments and blanks'
-test_subtest_known_broken
 notmuch restore --input=leading-comments-blanks-sup
 notmuch search --output=tags id:yun1vjwegii@aiko.keithp.com > 
OUTPUT.$test_count
 echo "another_tag" > EXPECTED
-- 
1.7.10.4

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


[PATCH 2/2] notmuch-restore: handle empty input file, leading blank lines and comments.

2013-01-06 Thread Tomi Ollila
On Wed, Dec 26 2012, david at tethera.net wrote:

> From: David Bremner 
>
> This patch corrects several undesirable behaviours:
>
> 1) Empty files were not detected, leading to buffer read overrun.
>
> 2) An initial blank line cause restore to silently abort
>
> 3) Initial comment line caused format detection to fail
> ---
>  notmuch-restore.c |   17 -
>  test/dump-restore |3 ---
>  2 files changed, 12 insertions(+), 8 deletions(-)
>
> diff --git a/notmuch-restore.c b/notmuch-restore.c
> index 9ed9b51..c93f1ac 100644
> --- a/notmuch-restore.c
> +++ b/notmuch-restore.c
> @@ -180,11 +180,6 @@ notmuch_restore_command (unused (void *ctx), int argc, 
> char *argv[])
>argv[opt_index]);
>   return 1;
>  }
> -char *p;
> -
> -line_len = getline (&line, &line_size, input);
> -if (line_len == 0)
> - return 0;
>  
>  tag_ops = tag_op_list_create (ctx);
>  if (tag_ops == NULL) {
> @@ -192,6 +187,18 @@ notmuch_restore_command (unused (void *ctx), int argc, 
> char *argv[])
>   return 1;
>  }
>  
> +do {
> + line_len = getline (&line, &line_size, input);
> +
> + /* empty input file not considered an error */
> + if (line_len < 0)
> + return 0;
> +
> +} while ((line_len == 0) ||
> +  (line[0] == '#') ||
> +  (strspn (line, " \t\n") == strlen (line)));

The strspn -- strlen doesn't take embedded \0:s in input line
into consideration (and strlen() scans the full line again),

 (strspn (line, " \t\n") == line_len));

might better

(if not, then  line[strspn(line, " \t\n")] == '\0'  would drop length scan)

Otherwise LGTM.

Tomi

> +
> +char *p;
>  for (p = line; (input_format == DUMP_FORMAT_AUTO) && *p; p++) {
>   if (*p == '(')
>   input_format = DUMP_FORMAT_SUP;
> diff --git a/test/dump-restore b/test/dump-restore
> index c2ddb92..ae30cd1 100755
> --- a/test/dump-restore
> +++ b/test/dump-restore
> @@ -146,13 +146,11 @@ cat < comments-and-blanks
>  EOF
>  
>  test_begin_subtest 'restoring empty file is not an error'
> -test_subtest_known_broken
>  notmuch restore < /dev/null 2>OUTPUT.$test_count
>  cp /dev/null EXPECTED
>  test_expect_equal_file EXPECTED OUTPUT.$test_count
>  
>  test_begin_subtest 'file of comments and blank lines is not an error'
> -test_subtest_known_broken
>  notmuch restore --input=comments-and-blanks
>  ret_val=$?
>  test_expect_equal "$ret_val" "0"
> @@ -172,7 +170,6 @@ echo "yun1vjwegii.fsf at aiko.keithp.com (another_tag)" \
>  >> leading-comments-blanks-sup
>  
>  test_begin_subtest 'detect format=sup with leading comments and blanks'
> -test_subtest_known_broken
>  notmuch restore --input=leading-comments-blanks-sup
>  notmuch search --output=tags id:yun1vjwegii.fsf at aiko.keithp.com > 
> OUTPUT.$test_count
>  echo "another_tag" > EXPECTED
> -- 
> 1.7.10.4
>
> ___
> notmuch mailing list
> notmuch at notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch