* V6 batch tagging patches.
@ 2012-12-09 23:33 david
2012-12-09 23:33 ` [Patch v6 1/6] tag-util: factor out rules for illegal tags, use in parse_tag_line david
` (6 more replies)
0 siblings, 7 replies; 17+ messages in thread
From: david @ 2012-12-09 23:33 UTC (permalink / raw)
To: notmuch
This obsoletes the remaining patches in
id:1353792017-31459-1-git-send-email-david@tethera.net
This isn't really v6 of these particular patches, but oh well.
^ permalink raw reply [flat|nested] 17+ messages in thread
* [Patch v6 1/6] tag-util: factor out rules for illegal tags, use in parse_tag_line
2012-12-09 23:33 V6 batch tagging patches david
@ 2012-12-09 23:33 ` david
2012-12-10 20:55 ` Jani Nikula
2012-12-13 22:07 ` Mark Walters
2012-12-09 23:33 ` [Patch v6 2/6] notmuch-tag.c: convert to use tag-utils david
` (5 subsequent siblings)
6 siblings, 2 replies; 17+ messages in thread
From: david @ 2012-12-09 23:33 UTC (permalink / raw)
To: notmuch; +Cc: David Bremner
From: David Bremner <bremner@debian.org>
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 | 35 ++++++++++++++++++++++++++++++-----
1 file changed, 30 insertions(+), 5 deletions(-)
diff --git a/tag-util.c b/tag-util.c
index eab482f..c071ea8 100644
--- a/tag-util.c
+++ b/tag-util.c
@@ -31,6 +31,29 @@ 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 "adding empty tag";
+
+ /* This disallows adding the non-removable tag "-" and
+ * enables notmuch tag to take long options more easily.
+ */
+
+ if (*tag == '-' && !remove)
+ return "adding tag starting with -";
+
+ return NULL;
+}
+
tag_parse_status_t
parse_tag_line (void *ctx, char *line,
tag_op_flag_t flags,
@@ -95,11 +118,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
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [Patch v6 2/6] notmuch-tag.c: convert to use tag-utils
2012-12-09 23:33 V6 batch tagging patches david
2012-12-09 23:33 ` [Patch v6 1/6] tag-util: factor out rules for illegal tags, use in parse_tag_line david
@ 2012-12-09 23:33 ` david
2012-12-10 21:18 ` Jani Nikula
2012-12-10 21:51 ` Jani Nikula
2012-12-09 23:33 ` [Patch v6 3/6] cli: add support for batch tagging operations to "notmuch tag" david
` (4 subsequent siblings)
6 siblings, 2 replies; 17+ messages in thread
From: david @ 2012-12-09 23:33 UTC (permalink / raw)
To: notmuch; +Cc: David Bremner
From: David Bremner <bremner@debian.org>
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 | 104 +++++++++++++--------------------------------------------
tag-util.c | 48 ++++++++++++++++++++++++++
tag-util.h | 16 +++++++++
3 files changed, 87 insertions(+), 81 deletions(-)
diff --git a/notmuch-tag.c b/notmuch-tag.c
index 88d559b..b5cd578 100644
--- a/notmuch-tag.c
+++ b/notmuch-tag.c
@@ -19,6 +19,7 @@
*/
#include "notmuch-client.h"
+#include "tag-util.h"
static volatile sig_atomic_t interrupted;
@@ -54,14 +55,9 @@ _escape_tag (char *buf, const char *tag)
return buf;
}
-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,
@@ -73,19 +69,20 @@ _optimize_tag_query (void *ctx, const char *orig_query_string,
char *escaped, *query_string;
const char *join = "";
- int i;
+ size_t i;
unsigned int max_tag_len = 0;
/* 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);
/* 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);
+ for (i = 0; i < tag_op_list_size (list); i++)
+ if (strlen (tag_op_list_tag (list, i)) > max_tag_len)
+ max_tag_len = strlen (tag_op_list_tag (list, i));
+
escaped = talloc_array (ctx, char, max_tag_len * 2 + 3);
if (! escaped)
return NULL;
@@ -96,11 +93,11 @@ _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++) {
query_string = talloc_asprintf_append_buffer (
query_string, "%s%stag:%s", join,
- tag_ops[i].remove ? "" : "not ",
- _escape_tag (escaped, tag_ops[i].tag));
+ tag_op_list_isremove (list, i) ? "" : "not ",
+ _escape_tag (escaped, tag_op_list_tag (list, i)));
join = " or ";
}
@@ -116,12 +113,11 @@ _optimize_tag_query (void *ctx, const char *orig_query_string,
* element. */
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. */
@@ -144,21 +140,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);
}
@@ -170,15 +152,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));
@@ -187,54 +167,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. */
- tag_ops = talloc_array (ctx, tag_operation_t, argc + 1);
+ tag_ops = tag_op_list_create (ctx);
if (tag_ops == NULL) {
fprintf (stderr, "Out of memory.\n");
return 1;
}
- for (i = 0; i < argc; i++) {
- if (strcmp (argv[i], "--") == 0) {
- i++;
- break;
- }
- if (argv[i][0] == '+' || argv[i][0] == '-') {
- if (argv[i][0] == '+' && argv[i][1] == '\0') {
- fprintf (stderr, "Error: tag names cannot be empty.\n");
- return 1;
- }
- if (argv[i][0] == '+' && argv[i][1] == '-') {
- /* This disallows adding the non-removable tag "-" and
- * enables notmuch tag to take long options in the
- * future. */
- fprintf (stderr, "Error: tag names must not start with '-'.\n");
- return 1;
- }
- tag_ops[tag_ops_count].tag = argv[i] + 1;
- tag_ops[tag_ops_count].remove = (argv[i][0] == '-');
- tag_ops_count++;
- } else {
- break;
- }
- }
-
- tag_ops[tag_ops_count].tag = NULL;
-
- if (tag_ops_count == 0) {
- fprintf (stderr, "Error: 'notmuch tag' requires at least one tag to add or remove.\n");
- return 1;
- }
-
- query_string = query_string_from_args (ctx, argc - i, &argv[i]);
-
- if (*query_string == '\0') {
- fprintf (stderr, "Error: notmuch tag requires at least one search term.\n");
+ if (parse_tag_command_line (ctx, argc - 1, argv + 1,
+ 0, &query_string, tag_ops))
return 1;
- }
config = notmuch_config_open (ctx, NULL, NULL);
if (config == NULL)
@@ -244,9 +185,10 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])
NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much))
return 1;
- synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config);
+ if (notmuch_config_get_maildir_synchronize_flags (config))
+ tag_flags |= TAG_FLAG_MAILDIR_SYNC;
- ret = tag_query (ctx, notmuch, query_string, tag_ops, synchronize_flags);
+ ret = tag_query (ctx, notmuch, query_string, tag_ops, tag_flags);
notmuch_database_destroy (notmuch);
diff --git a/tag-util.c b/tag-util.c
index c071ea8..6e3c069 100644
--- a/tag-util.c
+++ b/tag-util.c
@@ -162,6 +162,54 @@ parse_tag_line (void *ctx, char *line,
return ret;
}
+int
+parse_tag_command_line (void *ctx, int argc, char **argv,
+ unused(tag_op_flag_t flags),
+ char **query_str, tag_op_list_t *tag_ops){
+
+ int i;
+
+ tag_op_list_reset(tag_ops);
+
+ for (i = 0; i < argc; i++) {
+ if (strcmp (argv[i], "--") == 0) {
+ i++;
+ break;
+ }
+
+ if (argv[i][0] == '+' || argv[i][0] == '-') {
+ notmuch_bool_t is_remove = argv[i][0] == '-';
+ const char *msg;
+
+ msg = illegal_tag(argv[i]+1, is_remove);
+ if (msg) {
+ fprintf (stderr, "Error: %s", msg);
+ return 1;
+ }
+
+ tag_op_list_append (ctx, tag_ops,
+ argv[i] + 1, (argv[i][0] == '-'));
+ } else {
+ break;
+ }
+ }
+
+ if (tag_op_list_size (tag_ops) == 0) {
+ fprintf (stderr, "Error: 'notmuch tag' requires at least one tag to add or remove.\n");
+ return 1;
+ }
+
+ *query_str = query_string_from_args (ctx, argc - i, &argv[i]);
+
+ if (**query_str == '\0') {
+ fprintf (stderr, "Error: notmuch tag requires at least one search term.\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+
static inline void
message_error (notmuch_message_t *message,
notmuch_status_t status,
diff --git a/tag-util.h b/tag-util.h
index 99b0fa0..4956725 100644
--- a/tag-util.h
+++ b/tag-util.h
@@ -72,6 +72,22 @@ parse_tag_line (void *ctx, char *line,
tag_op_flag_t flags,
char **query_str, tag_op_list_t *ops);
+
+
+/* Parse a command line of the following format:
+ *
+ * +<tag>|-<tag> [...] [--] <search-terms>
+ *
+ * Output Parameters:
+ * ops contains a list of tag operations
+ * query_str the search terms.
+ */
+
+tag_parse_status_t
+parse_tag_command_line (void *ctx, int argc, char **argv,
+ tag_op_flag_t flags,
+ char **query_str, tag_op_list_t *ops);
+
/*
* Create an empty list of tag operations
*
--
1.7.10.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [Patch v6 3/6] cli: add support for batch tagging operations to "notmuch tag"
2012-12-09 23:33 V6 batch tagging patches david
2012-12-09 23:33 ` [Patch v6 1/6] tag-util: factor out rules for illegal tags, use in parse_tag_line david
2012-12-09 23:33 ` [Patch v6 2/6] notmuch-tag.c: convert to use tag-utils david
@ 2012-12-09 23:33 ` david
2012-12-10 21:29 ` Jani Nikula
2012-12-09 23:33 ` [Patch v6 4/6] test: add test for notmuch tag --batch option david
` (3 subsequent siblings)
6 siblings, 1 reply; 17+ messages in thread
From: david @ 2012-12-09 23:33 UTC (permalink / raw)
To: notmuch
From: Jani Nikula <jani@nikula.org>
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:
+<tag>|-<tag> [...] [--] <search-terms>
Each line is interpreted similarly to "notmuch tag" command line
arguments. The delimiter is one or more spaces ' '. Any characters in
<tag> and <search-terms> MAY be hex encoded with %NN where NN is the
hexadecimal value of the character. Any ' ' and '%' characters in
<tag> and <search-terms> MUST be hex encoded (using %20 and %25,
respectively). Any characters that are not part of <tag> or
<search-terms> MUST NOT be hex encoded.
Leading and trailing space ' ' is ignored. Empty lines and lines
beginning with '#' are ignored.
Signed-off-by: Jani Nikula <jani@nikula.org>
Hacked-like-crazy-by: David Bremner <david@tethera.net>
---
notmuch-tag.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 80 insertions(+), 8 deletions(-)
diff --git a/notmuch-tag.c b/notmuch-tag.c
index b5cd578..2665037 100644
--- a/notmuch-tag.c
+++ b/notmuch-tag.c
@@ -149,6 +149,43 @@ 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 || tag_query (ctx, notmuch, query_string,
+ tag_ops, flags))
+ break;
+ }
+
+ if (line)
+ free (line);
+
+ return ret;
+}
+
int
notmuch_tag_command (void *ctx, int argc, char *argv[])
{
@@ -158,6 +195,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 */
@@ -167,15 +208,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,
- 0, &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,
+ 0, &query_string, tag_ops))
+ return 1;
+ }
config = notmuch_config_open (ctx, NULL, NULL);
if (config == NULL)
@@ -188,9 +257,12 @@ 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;
+ return ret || interrupted;
}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [Patch v6 4/6] test: add test for notmuch tag --batch option
2012-12-09 23:33 V6 batch tagging patches david
` (2 preceding siblings ...)
2012-12-09 23:33 ` [Patch v6 3/6] cli: add support for batch tagging operations to "notmuch tag" david
@ 2012-12-09 23:33 ` david
2012-12-10 21:31 ` Jani Nikula
2012-12-09 23:33 ` [Patch v6 5/6] notmuch-tag.1: tidy synopsis formatting david
` (2 subsequent siblings)
6 siblings, 1 reply; 17+ messages in thread
From: david @ 2012-12-09 23:33 UTC (permalink / raw)
To: notmuch
From: Jani Nikula <jani@nikula.org>
Basic test of functionality, along with all combinations of options.
Modified extensively by David Bremner <david@tethera.net>
The choice of @ as a tag is intended to be non-alphanumeric, but still
not too much trouble in the shell and in the old sup dump/restore format.
---
test/tagging | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/test/tagging b/test/tagging
index 980ff92..0f3d797 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 <<EOF
+# %20 is a space in tag
+-:"%20 -tag1 +tag5 +tag6 -- One
++tag1 -tag1 -tag4 +tag4 -- Two
+-tag6 One
++tag5 Two
+EOF
+output=$(notmuch search \* | notmuch_search_sanitize)
+test_expect_equal "$output" "\
+thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; One (inbox tag5 unread)
+thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag4 tag5 unread)"
+
+# generate a common input file for the next several tests.
+cat > batch.in <<EOF
+# %40 is an @ in tag
++%40 -tag5 +tag6 -- One
++tag1 -tag1 -tag4 +tag4 -- Two
+-tag5 +tag6 Two
+EOF
+
+cat > batch.expected <<EOF
+thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; One (@ inbox tag6 unread)
+thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag4 tag6 unread)
+EOF
+
+test_begin_subtest "--input"
+notmuch dump --format=batch-tag > backup.tags
+notmuch tag --input=batch.in
+notmuch search \* | notmuch_search_sanitize > OUTPUT.$test_count
+notmuch restore --format=batch-tag < backup.tags
+test_expect_equal_file batch.expected OUTPUT.$test_count
+
+test_begin_subtest "--batch --input"
+notmuch dump --format=batch-tag > backup.tags
+notmuch tag --batch --input=batch.in
+notmuch search \* | notmuch_search_sanitize > OUTPUT.$test_count
+notmuch restore --format=batch-tag < backup.tags
+test_expect_equal_file batch.expected OUTPUT.$test_count
+
+test_begin_subtest "--batch, blank lines and comments"
+notmuch dump | sort > EXPECTED.$test_count
+notmuch tag --batch <<EOF
+# this line is a comment; the next has only white space
+
+
+# the previous line is empty
+EOF
+notmuch dump | sort > OUTPUT.$test_count
+test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
+
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
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [Patch v6 5/6] notmuch-tag.1: tidy synopsis formatting
2012-12-09 23:33 V6 batch tagging patches david
` (3 preceding siblings ...)
2012-12-09 23:33 ` [Patch v6 4/6] test: add test for notmuch tag --batch option david
@ 2012-12-09 23:33 ` david
2012-12-10 21:32 ` Jani Nikula
2012-12-09 23:33 ` [Patch v6 6/6] man: document notmuch tag --batch, --input options david
2012-12-10 0:59 ` V6 batch tagging patches David Bremner
6 siblings, 1 reply; 17+ messages in thread
From: david @ 2012-12-09 23:33 UTC (permalink / raw)
To: notmuch; +Cc: David Bremner
From: David Bremner <bremner@debian.org>
Consistently use [...]; one less space.
---
man/man1/notmuch-tag.1 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/man/man1/notmuch-tag.1 b/man/man1/notmuch-tag.1
index 0f86582..d23700d 100644
--- a/man/man1/notmuch-tag.1
+++ b/man/man1/notmuch-tag.1
@@ -4,7 +4,7 @@ notmuch-tag \- add/remove tags for all messages matching the search terms
.SH SYNOPSIS
.B notmuch tag
-.RI "+<" tag> "|\-<" tag "> [...] [\-\-] <" search-term ">..."
+.RI "+<" tag ">|\-<" tag "> [...] [\-\-] <" search-term "> [...]"
.SH DESCRIPTION
--
1.7.10.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [Patch v6 6/6] man: document notmuch tag --batch, --input options
2012-12-09 23:33 V6 batch tagging patches david
` (4 preceding siblings ...)
2012-12-09 23:33 ` [Patch v6 5/6] notmuch-tag.1: tidy synopsis formatting david
@ 2012-12-09 23:33 ` david
2012-12-10 21:34 ` Jani Nikula
2012-12-10 0:59 ` V6 batch tagging patches David Bremner
6 siblings, 1 reply; 17+ messages in thread
From: david @ 2012-12-09 23:33 UTC (permalink / raw)
To: notmuch
From: Jani Nikula <jani@nikula.org>
---
man/man1/notmuch-tag.1 | 50 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/man/man1/notmuch-tag.1 b/man/man1/notmuch-tag.1
index d23700d..c4e60d3 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.
@@ -29,6 +34,51 @@ 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=" <filename>
+
+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 "> [...] [\-\-] <" search-term "> [...]"
+
+Each line is interpreted similarly to
+.B notmuch tag
+command line arguments. The delimiter is one or more spaces ' '. Any
+characters in <tag> and <search-term>
+.B may
+be hex encoded with %NN where NN is the hexadecimal value of the
+character. Any ' ' and '%' characters in <tag> and <search-terms>
+.B must
+be hex encoded (using %20 and %25, respectively). Any characters that
+are not part of <tag> or <search-terms>
+.B must not
+be hex encoded.
+
+Leading and trailing space ' ' is ignored. Empty lines and lines
+beginning with '#' are ignored.
+
.SH SEE ALSO
\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1),
--
1.7.10.4
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: V6 batch tagging patches.
2012-12-09 23:33 V6 batch tagging patches david
` (5 preceding siblings ...)
2012-12-09 23:33 ` [Patch v6 6/6] man: document notmuch tag --batch, --input options david
@ 2012-12-10 0:59 ` David Bremner
6 siblings, 0 replies; 17+ messages in thread
From: David Bremner @ 2012-12-10 0:59 UTC (permalink / raw)
To: notmuch
david@tethera.net writes:
> This obsoletes the remaining patches in
>
> id:1353792017-31459-1-git-send-email-david@tethera.net
>
> This isn't really v6 of these particular patches, but oh well.
I forgot to mention that I will probably add some more tests; several of
the dump/restore batch-tag format tests can be re-used.
Here is a log of changes for the last set of reviews.
commit 120b1aef754cbe969e0421d5557f0308381f73d2
Author: David Bremner <bremner@debian.org>
Date: Sun Dec 9 15:57:10 2012 -0400
fixup for id:87txs5qqeb.fsf@nikula.org
diff --git a/notmuch-tag.c b/notmuch-tag.c
index dbd98a0..b8732f6 100644
--- a/notmuch-tag.c
+++ b/notmuch-tag.c
@@ -169,7 +169,7 @@ tag_file (void *ctx, notmuch_database_t *notmuch, tag_op_flag_t flags,
while ((line_len = getline (&line, &line_size, input)) != -1 &&
! interrupted) {
- ret = parse_tag_line (ctx, line, TAG_FLAG_NONE,
+ ret = parse_tag_line (ctx, line, TAG_FLAG_NONE,
&query_string, tag_ops);
if (ret > 0)
@@ -194,7 +194,7 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])
notmuch_config_t *config;
notmuch_database_t *notmuch;
struct sigaction action;
- tag_op_flag_t synchronize_flags = TAG_FLAG_NONE;
+ tag_op_flag_t tag_flags = TAG_FLAG_NONE;
notmuch_bool_t batch = FALSE;
FILE *input = stdin;
char *input_file_name = NULL;
@@ -255,12 +255,12 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])
return 1;
if (notmuch_config_get_maildir_synchronize_flags (config))
- synchronize_flags = TAG_FLAG_MAILDIR_SYNC;
+ tag_flags |= TAG_FLAG_MAILDIR_SYNC;
if (batch)
- ret = tag_file (ctx, notmuch, synchronize_flags, input);
+ ret = tag_file (ctx, notmuch, tag_flags, input);
else
- ret = tag_query (ctx, notmuch, query_string, tag_ops, synchronize_flags);
+ ret = tag_query (ctx, notmuch, query_string, tag_ops, tag_flags);
notmuch_database_destroy (notmuch);
commit bf8d4de2b174cff389d5f5fe4bdd10df2323fe77
Author: David Bremner <bremner@debian.org>
Date: Sun Dec 9 16:19:32 2012 -0400
changes for id:87pq2tqpvq.fsf@nikula.org
diff --git a/test/tagging b/test/tagging
index 7155e47..0f3d797 100755
--- a/test/tagging
+++ b/test/tagging
@@ -59,8 +59,9 @@ test_expect_equal "$output" "\
thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; One (inbox tag5 unread)
thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag4 tag5 unread)"
+# generate a common input file for the next several tests.
cat > batch.in <<EOF
-# %20 is a space in tag
+# %40 is an @ in tag
+%40 -tag5 +tag6 -- One
+tag1 -tag1 -tag4 +tag4 -- Two
-tag5 +tag6 Two
@@ -72,18 +73,18 @@ thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag4 tag6 unread)
EOF
test_begin_subtest "--input"
-notmuch dump > backup.tags
+notmuch dump --format=batch-tag > backup.tags
notmuch tag --input=batch.in
-notmuch search \* | notmuch_search_sanitize > OUTPUT
-notmuch restore < backup.tags
-test_expect_equal_file OUTPUT batch.expected
+notmuch search \* | notmuch_search_sanitize > OUTPUT.$test_count
+notmuch restore --format=batch-tag < backup.tags
+test_expect_equal_file batch.expected OUTPUT.$test_count
test_begin_subtest "--batch --input"
-notmuch dump > backup.tags
+notmuch dump --format=batch-tag > backup.tags
notmuch tag --batch --input=batch.in
-notmuch search \* | notmuch_search_sanitize > OUTPUT
-notmuch restore < backup.tags
-test_expect_equal_file OUTPUT batch.expected
+notmuch search \* | notmuch_search_sanitize > OUTPUT.$test_count
+notmuch restore --format=batch-tag < backup.tags
+test_expect_equal_file batch.expected OUTPUT.$test_count
test_begin_subtest "--batch, blank lines and comments"
notmuch dump | sort > EXPECTED.$test_count
commit 4fa12e718b0eaf82f26b9b8aea48f860fc5e1f72
Author: David Bremner <bremner@debian.org>
Date: Sun Dec 9 16:24:13 2012 -0400
changes for id:87sj7xi9j5.fsf@qmul.ac.uk and id:871uf8egi8.fsf@nikula.org
diff --git a/man/man1/notmuch-tag.1 b/man/man1/notmuch-tag.1
index 751db7b..3c15393 100644
--- a/man/man1/notmuch-tag.1
+++ b/man/man1/notmuch-tag.1
@@ -61,12 +61,12 @@ Read input from given file, instead of from stdin. Implies
The input must consist of lines of the format:
-.RI "T +<" tag ">|\-<" tag "> [...] [\-\-] <" search-terms ">"
+.RI "+<" tag ">|\-<" tag "> [...] [\-\-] <" search-term "> [...]"
Each line is interpreted similarly to
.B notmuch tag
command line arguments. The delimiter is one or more spaces ' '. Any
-characters in <tag> and <search-terms>
+characters in <tag> and <search-term>
.B may
be hex encoded with %NN where NN is the hexadecimal value of the
character. Any ' ' and '%' characters in <tag> and <search-terms>
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [Patch v6 1/6] tag-util: factor out rules for illegal tags, use in parse_tag_line
2012-12-09 23:33 ` [Patch v6 1/6] tag-util: factor out rules for illegal tags, use in parse_tag_line david
@ 2012-12-10 20:55 ` Jani Nikula
2012-12-13 22:07 ` Mark Walters
1 sibling, 0 replies; 17+ messages in thread
From: Jani Nikula @ 2012-12-10 20:55 UTC (permalink / raw)
To: david, notmuch; +Cc: David Bremner
On Mon, 10 Dec 2012, david@tethera.net wrote:
> From: David Bremner <bremner@debian.org>
>
> This will allow us to be consistent between batch tagging and command
> line tagging as far as what is an illegal tag.
LGTM, with some nitpick and observations below.
Jani.
> ---
> tag-util.c | 35 ++++++++++++++++++++++++++++++-----
> 1 file changed, 30 insertions(+), 5 deletions(-)
>
> diff --git a/tag-util.c b/tag-util.c
> index eab482f..c071ea8 100644
> --- a/tag-util.c
> +++ b/tag-util.c
> @@ -31,6 +31,29 @@ 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) {
Add \n before opening brace.
> +
> + if (*tag == '\0' && !remove)
> + return "adding empty tag";
> +
> + /* This disallows adding the non-removable tag "-" and
> + * enables notmuch tag to take long options more easily.
> + */
Maybe make that: "This disallows adding tags starting with "-",
including the non-removable tag "-", and enables ...", or similar?
> +
> + if (*tag == '-' && !remove)
> + return "adding tag starting with -";
> +
> + return NULL;
> +}
> +
> tag_parse_status_t
> parse_tag_line (void *ctx, char *line,
> tag_op_flag_t flags,
> @@ -95,11 +118,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;
> + }
> }
I guess I failed to notice during the dump/restore review that this
function was (and still is until later in the series) always called with
TAG_FLAG_BE_GENEROUS. I guess that's what we want with restore;
otherwise we should warn about such tags during dump.
>
> /* Decode tag. */
> --
> 1.7.10.4
>
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Patch v6 2/6] notmuch-tag.c: convert to use tag-utils
2012-12-09 23:33 ` [Patch v6 2/6] notmuch-tag.c: convert to use tag-utils david
@ 2012-12-10 21:18 ` Jani Nikula
2012-12-10 21:51 ` Jani Nikula
1 sibling, 0 replies; 17+ messages in thread
From: Jani Nikula @ 2012-12-10 21:18 UTC (permalink / raw)
To: david, notmuch; +Cc: David Bremner
On Mon, 10 Dec 2012, david@tethera.net wrote:
> From: David Bremner <bremner@debian.org>
>
> 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.
Functionally LGTM, nitpicks below.
BR,
Jani.
> ---
> notmuch-tag.c | 104 +++++++++++++--------------------------------------------
> tag-util.c | 48 ++++++++++++++++++++++++++
> tag-util.h | 16 +++++++++
> 3 files changed, 87 insertions(+), 81 deletions(-)
>
> diff --git a/notmuch-tag.c b/notmuch-tag.c
> index 88d559b..b5cd578 100644
> --- a/notmuch-tag.c
> +++ b/notmuch-tag.c
> @@ -19,6 +19,7 @@
> */
>
> #include "notmuch-client.h"
> +#include "tag-util.h"
>
> static volatile sig_atomic_t interrupted;
>
> @@ -54,14 +55,9 @@ _escape_tag (char *buf, const char *tag)
> return buf;
> }
>
> -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,
> @@ -73,19 +69,20 @@ _optimize_tag_query (void *ctx, const char *orig_query_string,
>
> char *escaped, *query_string;
> const char *join = "";
> - int i;
> + size_t i;
> unsigned int max_tag_len = 0;
>
> /* 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);
>
> /* 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);
> + for (i = 0; i < tag_op_list_size (list); i++)
> + if (strlen (tag_op_list_tag (list, i)) > max_tag_len)
> + max_tag_len = strlen (tag_op_list_tag (list, i));
> +
> escaped = talloc_array (ctx, char, max_tag_len * 2 + 3);
> if (! escaped)
> return NULL;
> @@ -96,11 +93,11 @@ _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++) {
> query_string = talloc_asprintf_append_buffer (
> query_string, "%s%stag:%s", join,
> - tag_ops[i].remove ? "" : "not ",
> - _escape_tag (escaped, tag_ops[i].tag));
> + tag_op_list_isremove (list, i) ? "" : "not ",
> + _escape_tag (escaped, tag_op_list_tag (list, i)));
> join = " or ";
> }
>
> @@ -116,12 +113,11 @@ _optimize_tag_query (void *ctx, const char *orig_query_string,
> * element. */
> 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. */
> @@ -144,21 +140,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);
> }
>
> @@ -170,15 +152,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));
> @@ -187,54 +167,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. */
> - tag_ops = talloc_array (ctx, tag_operation_t, argc + 1);
> + tag_ops = tag_op_list_create (ctx);
> if (tag_ops == NULL) {
> fprintf (stderr, "Out of memory.\n");
> return 1;
> }
>
> - for (i = 0; i < argc; i++) {
> - if (strcmp (argv[i], "--") == 0) {
> - i++;
> - break;
> - }
> - if (argv[i][0] == '+' || argv[i][0] == '-') {
> - if (argv[i][0] == '+' && argv[i][1] == '\0') {
> - fprintf (stderr, "Error: tag names cannot be empty.\n");
> - return 1;
> - }
> - if (argv[i][0] == '+' && argv[i][1] == '-') {
> - /* This disallows adding the non-removable tag "-" and
> - * enables notmuch tag to take long options in the
> - * future. */
> - fprintf (stderr, "Error: tag names must not start with '-'.\n");
> - return 1;
> - }
> - tag_ops[tag_ops_count].tag = argv[i] + 1;
> - tag_ops[tag_ops_count].remove = (argv[i][0] == '-');
> - tag_ops_count++;
> - } else {
> - break;
> - }
> - }
> -
> - tag_ops[tag_ops_count].tag = NULL;
> -
> - if (tag_ops_count == 0) {
> - fprintf (stderr, "Error: 'notmuch tag' requires at least one tag to add or remove.\n");
> - return 1;
> - }
> -
> - query_string = query_string_from_args (ctx, argc - i, &argv[i]);
> -
> - if (*query_string == '\0') {
> - fprintf (stderr, "Error: notmuch tag requires at least one search term.\n");
> + if (parse_tag_command_line (ctx, argc - 1, argv + 1,
> + 0, &query_string, tag_ops))
> return 1;
> - }
>
> config = notmuch_config_open (ctx, NULL, NULL);
> if (config == NULL)
> @@ -244,9 +185,10 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])
> NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much))
> return 1;
>
> - synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config);
> + if (notmuch_config_get_maildir_synchronize_flags (config))
> + tag_flags |= TAG_FLAG_MAILDIR_SYNC;
>
> - ret = tag_query (ctx, notmuch, query_string, tag_ops, synchronize_flags);
> + ret = tag_query (ctx, notmuch, query_string, tag_ops, tag_flags);
>
> notmuch_database_destroy (notmuch);
>
> diff --git a/tag-util.c b/tag-util.c
> index c071ea8..6e3c069 100644
> --- a/tag-util.c
> +++ b/tag-util.c
> @@ -162,6 +162,54 @@ parse_tag_line (void *ctx, char *line,
> return ret;
> }
>
> +int
> +parse_tag_command_line (void *ctx, int argc, char **argv,
> + unused(tag_op_flag_t flags),
Flags aren't used in the whole series; do you have something in mind for
the future?
> + char **query_str, tag_op_list_t *tag_ops){
Add \n before {.
> +
> + int i;
> +
> + tag_op_list_reset(tag_ops);
Space before (.
> +
> + for (i = 0; i < argc; i++) {
> + if (strcmp (argv[i], "--") == 0) {
> + i++;
> + break;
> + }
> +
> + if (argv[i][0] == '+' || argv[i][0] == '-') {
Could also have
if (argv[i][0] != '+' && argv[i][0] != '-')
break;
here and take the stuff below out of the if block. Would get rid of the
lone break in the else block. But that's just bikeshedding...
> + notmuch_bool_t is_remove = argv[i][0] == '-';
> + const char *msg;
> +
> + msg = illegal_tag(argv[i]+1, is_remove);
Spaces around +.
> + if (msg) {
> + fprintf (stderr, "Error: %s", msg);
> + return 1;
> + }
> +
> + tag_op_list_append (ctx, tag_ops,
> + argv[i] + 1, (argv[i][0] == '-'));
You have is_remove you can use as the last parameter.
> + } else {
> + break;
> + }
> + }
> +
> + if (tag_op_list_size (tag_ops) == 0) {
> + fprintf (stderr, "Error: 'notmuch tag' requires at least one tag to add or remove.\n");
> + return 1;
> + }
> +
> + *query_str = query_string_from_args (ctx, argc - i, &argv[i]);
> +
Pedantically you should check if (*query_string == NULL) here (though
this check didn't exist previously either).
> + if (**query_str == '\0') {
> + fprintf (stderr, "Error: notmuch tag requires at least one search term.\n");
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> +
> static inline void
> message_error (notmuch_message_t *message,
> notmuch_status_t status,
> diff --git a/tag-util.h b/tag-util.h
> index 99b0fa0..4956725 100644
> --- a/tag-util.h
> +++ b/tag-util.h
> @@ -72,6 +72,22 @@ parse_tag_line (void *ctx, char *line,
> tag_op_flag_t flags,
> char **query_str, tag_op_list_t *ops);
>
> +
> +
> +/* Parse a command line of the following format:
> + *
> + * +<tag>|-<tag> [...] [--] <search-terms>
> + *
> + * Output Parameters:
> + * ops contains a list of tag operations
> + * query_str the search terms.
> + */
> +
> +tag_parse_status_t
Either make this an int, or change the type in .c to tag_parse_status_t
too and use the enums in the code.
> +parse_tag_command_line (void *ctx, int argc, char **argv,
> + tag_op_flag_t flags,
> + char **query_str, tag_op_list_t *ops);
> +
> /*
> * Create an empty list of tag operations
> *
> --
> 1.7.10.4
>
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Patch v6 3/6] cli: add support for batch tagging operations to "notmuch tag"
2012-12-09 23:33 ` [Patch v6 3/6] cli: add support for batch tagging operations to "notmuch tag" david
@ 2012-12-10 21:29 ` Jani Nikula
0 siblings, 0 replies; 17+ messages in thread
From: Jani Nikula @ 2012-12-10 21:29 UTC (permalink / raw)
To: david, notmuch
On Mon, 10 Dec 2012, david@tethera.net wrote:
> From: Jani Nikula <jani@nikula.org>
>
> 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:
>
> +<tag>|-<tag> [...] [--] <search-terms>
Maybe clarify <search-term> [...] here too.
Mostly LGTM, small things below.
BR,
Jani.
> Each line is interpreted similarly to "notmuch tag" command line
> arguments. The delimiter is one or more spaces ' '. Any characters in
> <tag> and <search-terms> MAY be hex encoded with %NN where NN is the
> hexadecimal value of the character. Any ' ' and '%' characters in
> <tag> and <search-terms> MUST be hex encoded (using %20 and %25,
> respectively). Any characters that are not part of <tag> or
> <search-terms> MUST NOT be hex encoded.
>
> Leading and trailing space ' ' is ignored. Empty lines and lines
> beginning with '#' are ignored.
>
> Signed-off-by: Jani Nikula <jani@nikula.org>
>
> Hacked-like-crazy-by: David Bremner <david@tethera.net>
> ---
> notmuch-tag.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++------
> 1 file changed, 80 insertions(+), 8 deletions(-)
>
> diff --git a/notmuch-tag.c b/notmuch-tag.c
> index b5cd578..2665037 100644
> --- a/notmuch-tag.c
> +++ b/notmuch-tag.c
> @@ -149,6 +149,43 @@ 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 || tag_query (ctx, notmuch, query_string,
> + tag_ops, flags))
If tag_query fails the function returns 0.
> + break;
> + }
> +
> + if (line)
> + free (line);
> +
> + return ret;
> +}
> +
> int
> notmuch_tag_command (void *ctx, int argc, char *argv[])
> {
> @@ -158,6 +195,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 */
> @@ -167,15 +208,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,
> - 0, &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,
> + 0, &query_string, tag_ops))
> + return 1;
> + }
>
> config = notmuch_config_open (ctx, NULL, NULL);
> if (config == NULL)
> @@ -188,9 +257,12 @@ 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);
fclose if not stdin?
> + else
> + ret = tag_query (ctx, notmuch, query_string, tag_ops, tag_flags);
>
> notmuch_database_destroy (notmuch);
>
> - return ret;
> + return ret || interrupted;
> }
> --
> 1.7.10.4
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Patch v6 4/6] test: add test for notmuch tag --batch option
2012-12-09 23:33 ` [Patch v6 4/6] test: add test for notmuch tag --batch option david
@ 2012-12-10 21:31 ` Jani Nikula
0 siblings, 0 replies; 17+ messages in thread
From: Jani Nikula @ 2012-12-10 21:31 UTC (permalink / raw)
To: david, notmuch
On Mon, 10 Dec 2012, david@tethera.net wrote:
> From: Jani Nikula <jani@nikula.org>
>
> Basic test of functionality, along with all combinations of options.
>
> Modified extensively by David Bremner <david@tethera.net>
>
> The choice of @ as a tag is intended to be non-alphanumeric, but still
> not too much trouble in the shell and in the old sup dump/restore format.
LGTM.
> ---
> test/tagging | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 51 insertions(+)
>
> diff --git a/test/tagging b/test/tagging
> index 980ff92..0f3d797 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 <<EOF
> +# %20 is a space in tag
> +-:"%20 -tag1 +tag5 +tag6 -- One
> ++tag1 -tag1 -tag4 +tag4 -- Two
> +-tag6 One
> ++tag5 Two
> +EOF
> +output=$(notmuch search \* | notmuch_search_sanitize)
> +test_expect_equal "$output" "\
> +thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; One (inbox tag5 unread)
> +thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag4 tag5 unread)"
> +
> +# generate a common input file for the next several tests.
> +cat > batch.in <<EOF
> +# %40 is an @ in tag
> ++%40 -tag5 +tag6 -- One
> ++tag1 -tag1 -tag4 +tag4 -- Two
> +-tag5 +tag6 Two
> +EOF
> +
> +cat > batch.expected <<EOF
> +thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; One (@ inbox tag6 unread)
> +thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag4 tag6 unread)
> +EOF
> +
> +test_begin_subtest "--input"
> +notmuch dump --format=batch-tag > backup.tags
> +notmuch tag --input=batch.in
> +notmuch search \* | notmuch_search_sanitize > OUTPUT.$test_count
> +notmuch restore --format=batch-tag < backup.tags
> +test_expect_equal_file batch.expected OUTPUT.$test_count
> +
> +test_begin_subtest "--batch --input"
> +notmuch dump --format=batch-tag > backup.tags
> +notmuch tag --batch --input=batch.in
> +notmuch search \* | notmuch_search_sanitize > OUTPUT.$test_count
> +notmuch restore --format=batch-tag < backup.tags
> +test_expect_equal_file batch.expected OUTPUT.$test_count
> +
> +test_begin_subtest "--batch, blank lines and comments"
> +notmuch dump | sort > EXPECTED.$test_count
> +notmuch tag --batch <<EOF
> +# this line is a comment; the next has only white space
> +
> +
> +# the previous line is empty
> +EOF
> +notmuch dump | sort > OUTPUT.$test_count
> +test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
> +
> 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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Patch v6 5/6] notmuch-tag.1: tidy synopsis formatting
2012-12-09 23:33 ` [Patch v6 5/6] notmuch-tag.1: tidy synopsis formatting david
@ 2012-12-10 21:32 ` Jani Nikula
0 siblings, 0 replies; 17+ messages in thread
From: Jani Nikula @ 2012-12-10 21:32 UTC (permalink / raw)
To: david, notmuch; +Cc: David Bremner
On Mon, 10 Dec 2012, david@tethera.net wrote:
> From: David Bremner <bremner@debian.org>
>
> Consistently use [...]; one less space.
LGTM.
> ---
> man/man1/notmuch-tag.1 | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/man/man1/notmuch-tag.1 b/man/man1/notmuch-tag.1
> index 0f86582..d23700d 100644
> --- a/man/man1/notmuch-tag.1
> +++ b/man/man1/notmuch-tag.1
> @@ -4,7 +4,7 @@ notmuch-tag \- add/remove tags for all messages matching the search terms
>
> .SH SYNOPSIS
> .B notmuch tag
> -.RI "+<" tag> "|\-<" tag "> [...] [\-\-] <" search-term ">..."
> +.RI "+<" tag ">|\-<" tag "> [...] [\-\-] <" search-term "> [...]"
>
> .SH DESCRIPTION
>
> --
> 1.7.10.4
>
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Patch v6 6/6] man: document notmuch tag --batch, --input options
2012-12-09 23:33 ` [Patch v6 6/6] man: document notmuch tag --batch, --input options david
@ 2012-12-10 21:34 ` Jani Nikula
0 siblings, 0 replies; 17+ messages in thread
From: Jani Nikula @ 2012-12-10 21:34 UTC (permalink / raw)
To: david, notmuch
LGTM.
On Mon, 10 Dec 2012, david@tethera.net wrote:
> From: Jani Nikula <jani@nikula.org>
>
> ---
> man/man1/notmuch-tag.1 | 50 ++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 50 insertions(+)
>
> diff --git a/man/man1/notmuch-tag.1 b/man/man1/notmuch-tag.1
> index d23700d..c4e60d3 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.
> @@ -29,6 +34,51 @@ 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=" <filename>
> +
> +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 "> [...] [\-\-] <" search-term "> [...]"
> +
> +Each line is interpreted similarly to
> +.B notmuch tag
> +command line arguments. The delimiter is one or more spaces ' '. Any
> +characters in <tag> and <search-term>
> +.B may
> +be hex encoded with %NN where NN is the hexadecimal value of the
> +character. Any ' ' and '%' characters in <tag> and <search-terms>
> +.B must
> +be hex encoded (using %20 and %25, respectively). Any characters that
> +are not part of <tag> or <search-terms>
> +.B must not
> +be hex encoded.
> +
> +Leading and trailing space ' ' is ignored. Empty lines and lines
> +beginning with '#' are ignored.
> +
> .SH SEE ALSO
>
> \fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1),
> --
> 1.7.10.4
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Patch v6 2/6] notmuch-tag.c: convert to use tag-utils
2012-12-09 23:33 ` [Patch v6 2/6] notmuch-tag.c: convert to use tag-utils david
2012-12-10 21:18 ` Jani Nikula
@ 2012-12-10 21:51 ` Jani Nikula
1 sibling, 0 replies; 17+ messages in thread
From: Jani Nikula @ 2012-12-10 21:51 UTC (permalink / raw)
To: david, notmuch; +Cc: David Bremner
On Mon, 10 Dec 2012, david@tethera.net wrote:
> From: David Bremner <bremner@debian.org>
>
> 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 | 104 +++++++++++++--------------------------------------------
> tag-util.c | 48 ++++++++++++++++++++++++++
> tag-util.h | 16 +++++++++
> 3 files changed, 87 insertions(+), 81 deletions(-)
>
> diff --git a/notmuch-tag.c b/notmuch-tag.c
> index 88d559b..b5cd578 100644
> --- a/notmuch-tag.c
> +++ b/notmuch-tag.c
> @@ -19,6 +19,7 @@
> */
>
> #include "notmuch-client.h"
> +#include "tag-util.h"
>
> static volatile sig_atomic_t interrupted;
>
> @@ -54,14 +55,9 @@ _escape_tag (char *buf, const char *tag)
> return buf;
> }
>
> -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,
> @@ -73,19 +69,20 @@ _optimize_tag_query (void *ctx, const char *orig_query_string,
>
> char *escaped, *query_string;
> const char *join = "";
> - int i;
> + size_t i;
> unsigned int max_tag_len = 0;
>
> /* 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);
>
> /* 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);
> + for (i = 0; i < tag_op_list_size (list); i++)
> + if (strlen (tag_op_list_tag (list, i)) > max_tag_len)
> + max_tag_len = strlen (tag_op_list_tag (list, i));
> +
> escaped = talloc_array (ctx, char, max_tag_len * 2 + 3);
> if (! escaped)
> return NULL;
> @@ -96,11 +93,11 @@ _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++) {
> query_string = talloc_asprintf_append_buffer (
> query_string, "%s%stag:%s", join,
> - tag_ops[i].remove ? "" : "not ",
> - _escape_tag (escaped, tag_ops[i].tag));
> + tag_op_list_isremove (list, i) ? "" : "not ",
> + _escape_tag (escaped, tag_op_list_tag (list, i)));
> join = " or ";
> }
>
> @@ -116,12 +113,11 @@ _optimize_tag_query (void *ctx, const char *orig_query_string,
> * element. */
The comment "array of tagging operations terminated with an empty
element" needs revision too.
BR,
Jani.
> 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. */
> @@ -144,21 +140,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);
> }
>
> @@ -170,15 +152,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));
> @@ -187,54 +167,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. */
> - tag_ops = talloc_array (ctx, tag_operation_t, argc + 1);
> + tag_ops = tag_op_list_create (ctx);
> if (tag_ops == NULL) {
> fprintf (stderr, "Out of memory.\n");
> return 1;
> }
>
> - for (i = 0; i < argc; i++) {
> - if (strcmp (argv[i], "--") == 0) {
> - i++;
> - break;
> - }
> - if (argv[i][0] == '+' || argv[i][0] == '-') {
> - if (argv[i][0] == '+' && argv[i][1] == '\0') {
> - fprintf (stderr, "Error: tag names cannot be empty.\n");
> - return 1;
> - }
> - if (argv[i][0] == '+' && argv[i][1] == '-') {
> - /* This disallows adding the non-removable tag "-" and
> - * enables notmuch tag to take long options in the
> - * future. */
> - fprintf (stderr, "Error: tag names must not start with '-'.\n");
> - return 1;
> - }
> - tag_ops[tag_ops_count].tag = argv[i] + 1;
> - tag_ops[tag_ops_count].remove = (argv[i][0] == '-');
> - tag_ops_count++;
> - } else {
> - break;
> - }
> - }
> -
> - tag_ops[tag_ops_count].tag = NULL;
> -
> - if (tag_ops_count == 0) {
> - fprintf (stderr, "Error: 'notmuch tag' requires at least one tag to add or remove.\n");
> - return 1;
> - }
> -
> - query_string = query_string_from_args (ctx, argc - i, &argv[i]);
> -
> - if (*query_string == '\0') {
> - fprintf (stderr, "Error: notmuch tag requires at least one search term.\n");
> + if (parse_tag_command_line (ctx, argc - 1, argv + 1,
> + 0, &query_string, tag_ops))
> return 1;
> - }
>
> config = notmuch_config_open (ctx, NULL, NULL);
> if (config == NULL)
> @@ -244,9 +185,10 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])
> NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much))
> return 1;
>
> - synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config);
> + if (notmuch_config_get_maildir_synchronize_flags (config))
> + tag_flags |= TAG_FLAG_MAILDIR_SYNC;
>
> - ret = tag_query (ctx, notmuch, query_string, tag_ops, synchronize_flags);
> + ret = tag_query (ctx, notmuch, query_string, tag_ops, tag_flags);
>
> notmuch_database_destroy (notmuch);
>
> diff --git a/tag-util.c b/tag-util.c
> index c071ea8..6e3c069 100644
> --- a/tag-util.c
> +++ b/tag-util.c
> @@ -162,6 +162,54 @@ parse_tag_line (void *ctx, char *line,
> return ret;
> }
>
> +int
> +parse_tag_command_line (void *ctx, int argc, char **argv,
> + unused(tag_op_flag_t flags),
> + char **query_str, tag_op_list_t *tag_ops){
> +
> + int i;
> +
> + tag_op_list_reset(tag_ops);
> +
> + for (i = 0; i < argc; i++) {
> + if (strcmp (argv[i], "--") == 0) {
> + i++;
> + break;
> + }
> +
> + if (argv[i][0] == '+' || argv[i][0] == '-') {
> + notmuch_bool_t is_remove = argv[i][0] == '-';
> + const char *msg;
> +
> + msg = illegal_tag(argv[i]+1, is_remove);
> + if (msg) {
> + fprintf (stderr, "Error: %s", msg);
> + return 1;
> + }
> +
> + tag_op_list_append (ctx, tag_ops,
> + argv[i] + 1, (argv[i][0] == '-'));
> + } else {
> + break;
> + }
> + }
> +
> + if (tag_op_list_size (tag_ops) == 0) {
> + fprintf (stderr, "Error: 'notmuch tag' requires at least one tag to add or remove.\n");
> + return 1;
> + }
> +
> + *query_str = query_string_from_args (ctx, argc - i, &argv[i]);
> +
> + if (**query_str == '\0') {
> + fprintf (stderr, "Error: notmuch tag requires at least one search term.\n");
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> +
> static inline void
> message_error (notmuch_message_t *message,
> notmuch_status_t status,
> diff --git a/tag-util.h b/tag-util.h
> index 99b0fa0..4956725 100644
> --- a/tag-util.h
> +++ b/tag-util.h
> @@ -72,6 +72,22 @@ parse_tag_line (void *ctx, char *line,
> tag_op_flag_t flags,
> char **query_str, tag_op_list_t *ops);
>
> +
> +
> +/* Parse a command line of the following format:
> + *
> + * +<tag>|-<tag> [...] [--] <search-terms>
> + *
> + * Output Parameters:
> + * ops contains a list of tag operations
> + * query_str the search terms.
> + */
> +
> +tag_parse_status_t
> +parse_tag_command_line (void *ctx, int argc, char **argv,
> + tag_op_flag_t flags,
> + char **query_str, tag_op_list_t *ops);
> +
> /*
> * Create an empty list of tag operations
> *
> --
> 1.7.10.4
>
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Patch v6 1/6] tag-util: factor out rules for illegal tags, use in parse_tag_line
2012-12-09 23:33 ` [Patch v6 1/6] tag-util: factor out rules for illegal tags, use in parse_tag_line david
2012-12-10 20:55 ` Jani Nikula
@ 2012-12-13 22:07 ` Mark Walters
2012-12-15 12:39 ` David Bremner
1 sibling, 1 reply; 17+ messages in thread
From: Mark Walters @ 2012-12-13 22:07 UTC (permalink / raw)
To: david, notmuch; +Cc: David Bremner
I don't know if this matters but it seems that one can still add a "-"
tag by doing something like
echo "+%2d -- search" | notmuch tag --batch
This might be the right thing to do but I thought I would mention it.
Best wishes
Mark
On Sun, 09 Dec 2012, david@tethera.net wrote:
> From: David Bremner <bremner@debian.org>
>
> 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 | 35 ++++++++++++++++++++++++++++++-----
> 1 file changed, 30 insertions(+), 5 deletions(-)
>
> diff --git a/tag-util.c b/tag-util.c
> index eab482f..c071ea8 100644
> --- a/tag-util.c
> +++ b/tag-util.c
> @@ -31,6 +31,29 @@ 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 "adding empty tag";
> +
> + /* This disallows adding the non-removable tag "-" and
> + * enables notmuch tag to take long options more easily.
> + */
> +
> + if (*tag == '-' && !remove)
> + return "adding tag starting with -";
> +
> + return NULL;
> +}
> +
> tag_parse_status_t
> parse_tag_line (void *ctx, char *line,
> tag_op_flag_t flags,
> @@ -95,11 +118,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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Patch v6 1/6] tag-util: factor out rules for illegal tags, use in parse_tag_line
2012-12-13 22:07 ` Mark Walters
@ 2012-12-15 12:39 ` David Bremner
0 siblings, 0 replies; 17+ messages in thread
From: David Bremner @ 2012-12-15 12:39 UTC (permalink / raw)
To: Mark Walters, notmuch
Mark Walters <markwalters1009@gmail.com> writes:
> I don't know if this matters but it seems that one can still add a "-"
> tag by doing something like
>
> echo "+%2d -- search" | notmuch tag --batch
>
> This might be the right thing to do but I thought I would mention it.
I _think_ it's a feature, so I'm tempted to document it and move on
unless somebody convinces me it's a bad idea.
d
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2012-12-15 12:39 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-12-09 23:33 V6 batch tagging patches david
2012-12-09 23:33 ` [Patch v6 1/6] tag-util: factor out rules for illegal tags, use in parse_tag_line david
2012-12-10 20:55 ` Jani Nikula
2012-12-13 22:07 ` Mark Walters
2012-12-15 12:39 ` David Bremner
2012-12-09 23:33 ` [Patch v6 2/6] notmuch-tag.c: convert to use tag-utils david
2012-12-10 21:18 ` Jani Nikula
2012-12-10 21:51 ` Jani Nikula
2012-12-09 23:33 ` [Patch v6 3/6] cli: add support for batch tagging operations to "notmuch tag" david
2012-12-10 21:29 ` Jani Nikula
2012-12-09 23:33 ` [Patch v6 4/6] test: add test for notmuch tag --batch option david
2012-12-10 21:31 ` Jani Nikula
2012-12-09 23:33 ` [Patch v6 5/6] notmuch-tag.1: tidy synopsis formatting david
2012-12-10 21:32 ` Jani Nikula
2012-12-09 23:33 ` [Patch v6 6/6] man: document notmuch tag --batch, --input options david
2012-12-10 21:34 ` Jani Nikula
2012-12-10 0:59 ` V6 batch tagging patches David Bremner
Code repositories for project(s) associated with this public inbox
https://yhetil.org/notmuch.git/
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).