Re: [Patch v2 10/17] cli: add support for batch tagging operations to notmuch tag

2012-12-01 Thread Jani Nikula

Hi David, overall looks good. The tag-util abstractions are paying off
nicely here.

First two high level bikesheds: 1) This could be split in two patches,
first one adding the tag-util usage to normal operation and second one
adding the batch tagging, and 2) I think it would be neater if the batch
tagging was in a separate function instead of inline in the top level
function. I'm not insisting though; we should probably just go with
this, and the latter part can be refactored later if needed.

Some minor nits inline.

BR,
Jani.

On Sat, 24 Nov 2012, da...@tethera.net wrote:
 From: Jani Nikula j...@nikula.org

 Add support for batch tagging operations through stdin to notmuch
 tag. This can be enabled with the new --stdin command line option to

--batch

 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 j...@nikula.org

 Hacked-like-crazy-by: David Bremner da...@tethera.net
 ---
  notmuch-tag.c |  194 
 +++--
  1 file changed, 118 insertions(+), 76 deletions(-)

 diff --git a/notmuch-tag.c b/notmuch-tag.c
 index 88d559b..8a8af0b 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)
 - 

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

2012-11-24 Thread da...@tethera.net
From: Jani Nikula 

Add support for batch tagging operations through stdin to "notmuch
tag". This can be enabled with the new --stdin 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
 and  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). Any characters that are not part of  or
 MUST NOT be hex encoded.

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 |  194 +++--
 1 file changed, 118 insertions(+), 76 deletions(-)

diff --git a/notmuch-tag.c b/notmuch-tag.c
index 88d559b..8a8af0b 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);
notmuch_message_destroy (message);
 }

@@ -170,15 +152,17 @@ 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;
 

[Patch v2 10/17] cli: add support for batch tagging operations to notmuch tag

2012-11-24 Thread david
From: Jani Nikula j...@nikula.org

Add support for batch tagging operations through stdin to notmuch
tag. This can be enabled with the new --stdin 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 j...@nikula.org

Hacked-like-crazy-by: David Bremner da...@tethera.net
---
 notmuch-tag.c |  194 +++--
 1 file changed, 118 insertions(+), 76 deletions(-)

diff --git a/notmuch-tag.c b/notmuch-tag.c
index 88d559b..8a8af0b 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);
notmuch_message_destroy (message);
 }
 
@@ -170,15 +152,17 @@ 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