[Patch v2 9/9] test/tagging: add test for naked punctuation in tags; compare with quoting spaces.
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
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
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
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
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
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"
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
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
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.
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
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
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
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.
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
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
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.
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
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
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
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
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
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
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.
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"
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
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
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
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.
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
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
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.
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
-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
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
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
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)
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
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
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
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
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)
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
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
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
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
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)
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)
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
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
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
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
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
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.
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
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
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
-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
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.
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.
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.
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