[Patch v2 13/17] notmuch-restore: add support for input format 'batch-tag'
authordavid <david@tethera.net>
Sat, 24 Nov 2012 21:20:13 +0000 (17:20 +2000)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:50:52 +0000 (09:50 -0800)
0a/09f1b9b362a04af42ba8ed697f72b3d8b48ad7 [new file with mode: 0644]

diff --git a/0a/09f1b9b362a04af42ba8ed697f72b3d8b48ad7 b/0a/09f1b9b362a04af42ba8ed697f72b3d8b48ad7
new file mode 100644 (file)
index 0000000..0f2aba4
--- /dev/null
@@ -0,0 +1,350 @@
+Return-Path: <bremner@tethera.net>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+       by olra.theworths.org (Postfix) with ESMTP id 73164431FD5\r
+       for <notmuch@notmuchmail.org>; Sat, 24 Nov 2012 13:20:50 -0800 (PST)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]\r
+       autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+       with ESMTP id ugtun4eivJZn for <notmuch@notmuchmail.org>;\r
+       Sat, 24 Nov 2012 13:20:47 -0800 (PST)\r
+Received: from tesseract.cs.unb.ca (tesseract.cs.unb.ca [131.202.240.238])\r
+       (using TLSv1 with cipher AES256-SHA (256/256 bits))\r
+       (No client certificate requested)\r
+       by olra.theworths.org (Postfix) with ESMTPS id 43FF5431E62\r
+       for <notmuch@notmuchmail.org>; Sat, 24 Nov 2012 13:20:34 -0800 (PST)\r
+Received: from fctnnbsc30w-156034089108.dhcp-dynamic.fibreop.nb.bellaliant.net\r
+       ([156.34.89.108] helo=zancas.localnet)\r
+       by tesseract.cs.unb.ca with esmtpsa\r
+       (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:16) (Exim 4.72)\r
+       (envelope-from <bremner@tethera.net>)\r
+       id 1TcN9N-0006ST-3y; Sat, 24 Nov 2012 17:20:33 -0400\r
+Received: from bremner by zancas.localnet with local (Exim 4.80)\r
+       (envelope-from <bremner@tethera.net>)\r
+       id 1TcN9H-0008DN-Lr; Sat, 24 Nov 2012 17:20:27 -0400\r
+From: david@tethera.net\r
+To: notmuch@notmuchmail.org\r
+Subject: [Patch v2 13/17] notmuch-restore: add support for input format\r
+       'batch-tag'\r
+Date: Sat, 24 Nov 2012 17:20:13 -0400\r
+Message-Id: <1353792017-31459-14-git-send-email-david@tethera.net>\r
+X-Mailer: git-send-email 1.7.10.4\r
+In-Reply-To: <1353792017-31459-1-git-send-email-david@tethera.net>\r
+References: <1353792017-31459-1-git-send-email-david@tethera.net>\r
+X-Spam_bar: -\r
+Cc: David Bremner <bremner@debian.org>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+       <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Sat, 24 Nov 2012 21:20:50 -0000\r
+\r
+From: David Bremner <bremner@debian.org>\r
+\r
+This is the same as the batch input for notmuch tag, except by default\r
+it removes all tags before modifying a given message id and only "id:"\r
+is supported.\r
+---\r
+ notmuch-restore.c |  199 +++++++++++++++++++++++++++++++++--------------------\r
+ 1 file changed, 125 insertions(+), 74 deletions(-)\r
+\r
+diff --git a/notmuch-restore.c b/notmuch-restore.c\r
+index f03dcac..22fcd2d 100644\r
+--- a/notmuch-restore.c\r
++++ b/notmuch-restore.c\r
+@@ -19,18 +19,22 @@\r
+  */\r
+ \r
+ #include "notmuch-client.h"\r
++#include "dump-restore-private.h"\r
++#include "tag-util.h"\r
++#include "string-util.h"\r
++\r
++static volatile sig_atomic_t interrupted;\r
++static regex_t regex;\r
+ \r
+ static int\r
+-tag_message (notmuch_database_t *notmuch, const char *message_id,\r
+-           char *file_tags, notmuch_bool_t remove_all,\r
+-           notmuch_bool_t synchronize_flags)\r
++tag_message (unused (void *ctx),\r
++           notmuch_database_t *notmuch,\r
++           const char *message_id,\r
++           tag_op_list_t *tag_ops,\r
++           tag_op_flag_t flags)\r
+ {\r
+     notmuch_status_t status;\r
+-    notmuch_tags_t *db_tags;\r
+-    char *db_tags_str;\r
+     notmuch_message_t *message = NULL;\r
+-    const char *tag;\r
+-    char *next;\r
+     int ret = 0;\r
+ \r
+     status = notmuch_database_find_message (notmuch, message_id, &message);\r
+@@ -44,55 +48,63 @@ tag_message (notmuch_database_t *notmuch, const char *message_id,\r
+ \r
+     /* In order to detect missing messages, this check/optimization is\r
+      * intentionally done *after* first finding the message. */\r
+-    if (! remove_all && (file_tags == NULL || *file_tags == '\0'))\r
+-      goto DONE;\r
+-\r
+-    db_tags_str = NULL;\r
+-    for (db_tags = notmuch_message_get_tags (message);\r
+-       notmuch_tags_valid (db_tags);\r
+-       notmuch_tags_move_to_next (db_tags)) {\r
+-      tag = notmuch_tags_get (db_tags);\r
+-\r
+-      if (db_tags_str)\r
+-          db_tags_str = talloc_asprintf_append (db_tags_str, " %s", tag);\r
+-      else\r
+-          db_tags_str = talloc_strdup (message, tag);\r
+-    }\r
++    if ( (flags & TAG_FLAG_REMOVE_ALL) || (tag_op_list_size (tag_ops)))\r
++      tag_op_list_apply (message, tag_ops, flags);\r
+ \r
+-    if (((file_tags == NULL || *file_tags == '\0') &&\r
+-       (db_tags_str == NULL || *db_tags_str == '\0')) ||\r
+-      (file_tags && db_tags_str && strcmp (file_tags, db_tags_str) == 0))\r
+-      goto DONE;\r
++    if (message)\r
++      notmuch_message_destroy (message);\r
+ \r
+-    notmuch_message_freeze (message);\r
++    return ret;\r
++}\r
+ \r
+-    if (remove_all)\r
+-      notmuch_message_remove_all_tags (message);\r
++static int\r
++parse_sup_line (void *ctx, char *line,\r
++              char **query_str, tag_op_list_t *tag_ops)\r
++{\r
+ \r
+-    next = file_tags;\r
+-    while (next) {\r
+-      tag = strsep (&next, " ");\r
+-      if (*tag == '\0')\r
+-          continue;\r
+-      status = notmuch_message_add_tag (message, tag);\r
+-      if (status) {\r
+-          fprintf (stderr, "Error applying tag %s to message %s:\n",\r
+-                   tag, message_id);\r
+-          fprintf (stderr, "%s\n", notmuch_status_to_string (status));\r
+-          ret = 1;\r
+-      }\r
++    regmatch_t match[3];\r
++    char *file_tags;\r
++    int rerr;\r
++\r
++    tag_op_list_reset (tag_ops);\r
++\r
++    chomp_newline (line);\r
++\r
++    /* Silently ignore blank lines */\r
++    if (line[0] == '\0') {\r
++      return 1;\r
++    }\r
++\r
++    rerr = xregexec (&regex, line, 3, match, 0);\r
++    if (rerr == REG_NOMATCH) {\r
++      fprintf (stderr, "Warning: Ignoring invalid input line: %s\n",\r
++               line);\r
++      return 1;\r
+     }\r
+ \r
+-    notmuch_message_thaw (message);\r
++    *query_str = talloc_strndup (ctx, line + match[1].rm_so,\r
++                               match[1].rm_eo - match[1].rm_so);\r
++    file_tags = talloc_strndup (ctx, line + match[2].rm_so,\r
++                              match[2].rm_eo - match[2].rm_so);\r
+ \r
+-    if (synchronize_flags)\r
+-      notmuch_message_tags_to_maildir_flags (message);\r
++    char *tok = file_tags;\r
++    size_t tok_len = 0;\r
+ \r
+-  DONE:\r
+-    if (message)\r
+-      notmuch_message_destroy (message);\r
++    tag_op_list_reset (tag_ops);\r
++\r
++    while ((tok = strtok_len (tok + tok_len, " ", &tok_len)) != NULL) {\r
++\r
++      if (*(tok + tok_len) != '\0') {\r
++          *(tok + tok_len) = '\0';\r
++          tok_len++;\r
++      }\r
++\r
++      if (tag_op_list_append (ctx, tag_ops, tok, FALSE))\r
++          return -1;\r
++    }\r
++\r
++    return 0;\r
+ \r
+-    return ret;\r
+ }\r
+ \r
+ int\r
+@@ -100,16 +112,19 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])\r
+ {\r
+     notmuch_config_t *config;\r
+     notmuch_database_t *notmuch;\r
+-    notmuch_bool_t synchronize_flags;\r
+     notmuch_bool_t accumulate = FALSE;\r
++    tag_op_flag_t flags = 0;\r
++    tag_op_list_t *tag_ops;\r
++\r
+     char *input_file_name = NULL;\r
+     FILE *input = stdin;\r
+     char *line = NULL;\r
+     size_t line_size;\r
+     ssize_t line_len;\r
+-    regex_t regex;\r
+-    int rerr;\r
++\r
++    int ret = 0;\r
+     int opt_index;\r
++    int input_format = DUMP_FORMAT_AUTO;\r
+ \r
+     config = notmuch_config_open (ctx, NULL, NULL);\r
+     if (config == NULL)\r
+@@ -119,9 +134,15 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])\r
+                              NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch))\r
+       return 1;\r
+ \r
+-    synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config);\r
++    if (notmuch_config_get_maildir_synchronize_flags (config))\r
++      flags |= TAG_FLAG_MAILDIR_SYNC;\r
+ \r
+     notmuch_opt_desc_t options[] = {\r
++      { NOTMUCH_OPT_KEYWORD, &input_format, "format", 'f',\r
++        (notmuch_keyword_t []){ { "auto", DUMP_FORMAT_AUTO },\r
++                                { "batch-tag", DUMP_FORMAT_BATCH_TAG },\r
++                                { "sup", DUMP_FORMAT_SUP },\r
++                                { 0, 0 } } },\r
+       { NOTMUCH_OPT_STRING, &input_file_name, "input", 'i', 0 },\r
+       { NOTMUCH_OPT_BOOLEAN,  &accumulate, "accumulate", 'a', 0 },\r
+       { 0, 0, 0, 0, 0 }\r
+@@ -134,6 +155,9 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])\r
+       return 1;\r
+     }\r
+ \r
++    if (! accumulate)\r
++      flags |= TAG_FLAG_REMOVE_ALL;\r
++\r
+     if (input_file_name) {\r
+       input = fopen (input_file_name, "r");\r
+       if (input == NULL) {\r
+@@ -154,35 +178,61 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])\r
+      * non-space characters for the message-id, then one or more\r
+      * spaces, then a list of space-separated tags as a sequence of\r
+      * characters within literal '(' and ')'. */\r
+-    if ( xregcomp (&regex,\r
+-                 "^([^ ]+) \\(([^)]*)\\)$",\r
+-                 REG_EXTENDED) )\r
+-      INTERNAL_ERROR ("compile time constant regex failed.");\r
++    char *p;\r
+ \r
+-    while ((line_len = getline (&line, &line_size, input)) != -1) {\r
+-      regmatch_t match[3];\r
+-      char *message_id, *file_tags;\r
++    line_len = getline (&line, &line_size, input);\r
++    if (line_len == 0)\r
++      return 0;\r
+ \r
+-      chomp_newline (line);\r
++    for (p = line; *p; p++) {\r
++      if (*p == '(')\r
++          input_format = DUMP_FORMAT_SUP;\r
++    }\r
+ \r
+-      rerr = xregexec (&regex, line, 3, match, 0);\r
+-      if (rerr == REG_NOMATCH) {\r
+-          fprintf (stderr, "Warning: Ignoring invalid input line: %s\n",\r
+-                   line);\r
+-          continue;\r
++    if (input_format == DUMP_FORMAT_AUTO)\r
++      input_format = DUMP_FORMAT_BATCH_TAG;\r
++\r
++    if (input_format == DUMP_FORMAT_SUP)\r
++      if ( xregcomp (&regex,\r
++                     "^([^ ]+) \\(([^)]*)\\)$",\r
++                     REG_EXTENDED) )\r
++          INTERNAL_ERROR ("compile time constant regex failed.");\r
++\r
++    tag_ops = tag_op_list_create (ctx);\r
++    if (tag_ops == NULL) {\r
++      fprintf (stderr, "Out of memory.\n");\r
++      return 1;\r
++    }\r
++\r
++    do {\r
++      char *query_string;\r
++\r
++      if (input_format == DUMP_FORMAT_SUP) {\r
++          ret =  parse_sup_line (ctx, line, &query_string, tag_ops);\r
++      } else {\r
++          ret =  parse_tag_line (ctx, line, TAG_FLAG_BE_GENEROUS,\r
++                                 &query_string, tag_ops);\r
++\r
++          if (ret == 0) {\r
++              if ( strncmp ("id:", query_string, 3) != 0) {\r
++                  fprintf (stderr, "Unsupported query: %s\n", query_string);\r
++                  continue;\r
++              }\r
++              /* delete id: from front of string; tag_message expects a\r
++               * raw message-id */\r
++              query_string = query_string + 3;\r
++          }\r
+       }\r
+ \r
+-      message_id = xstrndup (line + match[1].rm_so,\r
+-                             match[1].rm_eo - match[1].rm_so);\r
+-      file_tags = xstrndup (line + match[2].rm_so,\r
+-                            match[2].rm_eo - match[2].rm_so);\r
++      if (ret > 0)\r
++          continue;\r
+ \r
+-      tag_message (notmuch, message_id, file_tags, ! accumulate,\r
+-                   synchronize_flags);\r
++      if (ret < 0 || tag_message (ctx, notmuch, query_string,\r
++                                  tag_ops, flags))\r
++          break;\r
++\r
++    }  while ((line_len = getline (&line, &line_size, input)) != -1);\r
+ \r
+-      free (message_id);\r
+-      free (file_tags);\r
+-    }\r
+ \r
+     regfree (&regex);\r
+ \r
+@@ -190,8 +240,9 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])\r
+       free (line);\r
+ \r
+     notmuch_database_destroy (notmuch);\r
++\r
+     if (input != stdin)\r
+       fclose (input);\r
+ \r
+-    return 0;\r
++    return ret;\r
+ }\r
+-- \r
+1.7.10.4\r
+\r