[Patch v3b 5/9] notmuch-restore: add support for input format 'batch-tag'
authordavid <david@tethera.net>
Fri, 7 Dec 2012 01:26:43 +0000 (21:26 +2000)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:51:39 +0000 (09:51 -0800)
80/f284eb5cb5aa76192253faf430ec18cc2c49a1 [new file with mode: 0644]

diff --git a/80/f284eb5cb5aa76192253faf430ec18cc2c49a1 b/80/f284eb5cb5aa76192253faf430ec18cc2c49a1
new file mode 100644 (file)
index 0000000..2ec2d4e
--- /dev/null
@@ -0,0 +1,372 @@
+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 E9AAC431FB6\r
+       for <notmuch@notmuchmail.org>; Thu,  6 Dec 2012 17:27:32 -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 oL3JWVz4b02E for <notmuch@notmuchmail.org>;\r
+       Thu,  6 Dec 2012 17:27:30 -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 DF471429E31\r
+       for <notmuch@notmuchmail.org>; Thu,  6 Dec 2012 17:27:13 -0800 (PST)\r
+Received: from fctnnbsc30w-142167090129.dhcp-dynamic.fibreop.nb.bellaliant.net\r
+       ([142.167.90.129] 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 1Tgmie-0003Nu-RN; Thu, 06 Dec 2012 21:27:13 -0400\r
+Received: from bremner by zancas.localnet with local (Exim 4.80)\r
+       (envelope-from <bremner@tethera.net>)\r
+       id 1TgmiZ-0004kf-AR; Thu, 06 Dec 2012 21:27:07 -0400\r
+From: david@tethera.net\r
+To: notmuch@notmuchmail.org\r
+Subject: [Patch v3b 5/9] notmuch-restore: add support for input format\r
+       'batch-tag'\r
+Date: Thu,  6 Dec 2012 21:26:43 -0400\r
+Message-Id: <1354843607-17980-6-git-send-email-david@tethera.net>\r
+X-Mailer: git-send-email 1.7.10.4\r
+In-Reply-To: <1354843607-17980-1-git-send-email-david@tethera.net>\r
+References: <1354843607-17980-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: Fri, 07 Dec 2012 01:27:33 -0000\r
+\r
+From: David Bremner <bremner@debian.org>\r
+\r
+This can be enabled with the new --format=batch-tag command line\r
+option to "notmuch restore". The input must consist of lines of the\r
+format:\r
+\r
+    +<tag>|-<tag> [...] [--] id:<msg-id>\r
+\r
+Each line is interpreted similarly to "notmuch tag" command line\r
+arguments. The delimiter is one or more spaces ' '. Any characters in\r
+<tag> and <search-terms> MAY be hex encoded with %NN where NN is the\r
+hexadecimal value of the character. Any ' ' and '%' characters in\r
+<tag> and <msg-id> MUST be hex encoded (using %20 and %25,\r
+respectively). Any characters that are not part of <tag> or\r
+<search-terms> MUST NOT be hex encoded.\r
+\r
+Leading and trailing space ' ' is ignored. Empty lines and lines\r
+beginning with '#' are ignored.\r
+\r
+Commit message mainly stolen from Jani's batch tagging commit, to\r
+follow.\r
+---\r
+ notmuch-restore.c |  206 ++++++++++++++++++++++++++++++++++-------------------\r
+ 1 file changed, 131 insertions(+), 75 deletions(-)\r
+\r
+diff --git a/notmuch-restore.c b/notmuch-restore.c\r
+index f03dcac..ceec2d3 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,62 @@ 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
++    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 sup format 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 +111,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 +133,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 +154,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,44 +177,77 @@ 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
++    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
+-      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
++    for (p = line; (input_format == DUMP_FORMAT_AUTO) && *p; p++) {\r
++      if (*p == '(')\r
++          input_format = DUMP_FORMAT_SUP;\r
++    }\r
++\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
++    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\r
++               * expects a raw message-id.\r
++               *\r
++               * XXX: Note that query string id:foo and bar will be\r
++               * interpreted as a message id "foo and bar". This\r
++               * should eventually be fixed to give a better error\r
++               * message.\r
++               */\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
+-      free (message_id);\r
+-      free (file_tags);\r
+-    }\r
++    }  while ((line_len = getline (&line, &line_size, input)) != -1);\r
\r
+-    regfree (&regex);\r
++    if (input_format == DUMP_FORMAT_SUP)\r
++      regfree (&regex);\r
\r
+     if (line)\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