+#include "hex-escape.h"
+#include "tag-util.h"
+#include "string-util.h"
+#include "zlib-extra.h"
+
+static regex_t regex;
+
+/* Non-zero return indicates an error in retrieving the message,
+ * or in applying the tags. Missing messages are reported, but not
+ * considered errors.
+ */
+static int
+tag_message (unused (void *ctx),
+ notmuch_database_t *notmuch,
+ const char *message_id,
+ tag_op_list_t *tag_ops,
+ tag_op_flag_t flags)
+{
+ notmuch_status_t status;
+ notmuch_message_t *message = NULL;
+ int ret = 0;
+
+ status = notmuch_database_find_message (notmuch, message_id, &message);
+ if (status) {
+ fprintf (stderr, "Error applying tags to message %s: %s\n",
+ message_id, notmuch_status_to_string (status));
+ return 1;
+ }
+ if (message == NULL) {
+ fprintf (stderr, "Warning: cannot apply tags to missing message: %s\n",
+ message_id);
+ /* We consider this a non-fatal error. */
+ return 0;
+ }
+
+ /* In order to detect missing messages, this check/optimization is
+ * intentionally done *after* first finding the message. */
+ if ((flags & TAG_FLAG_REMOVE_ALL) || tag_op_list_size (tag_ops))
+ ret = tag_op_list_apply (message, tag_ops, flags);
+
+ notmuch_message_destroy (message);
+
+ return ret;
+}
+
+/* Sup dump output is one line per message. We match a sequence of
+ * non-space characters for the message-id, then one or more
+ * spaces, then a list of space-separated tags as a sequence of
+ * characters within literal '(' and ')'. */
+
+static int
+parse_sup_line (void *ctx, char *line,
+ char **query_str, tag_op_list_t *tag_ops)
+{
+
+ regmatch_t match[3];
+ char *file_tags;
+ int rerr;
+
+ tag_op_list_reset (tag_ops);
+
+ chomp_newline (line);
+
+ /* Silently ignore blank lines */
+ if (line[0] == '\0') {
+ return 1;
+ }
+
+ rerr = xregexec (®ex, line, 3, match, 0);
+ if (rerr == REG_NOMATCH) {
+ fprintf (stderr, "Warning: Ignoring invalid sup format line: %s\n",
+ line);
+ return 1;
+ }
+
+ *query_str = talloc_strndup_debug (ctx, line + match[1].rm_so,
+ match[1].rm_eo - match[1].rm_so);
+
+ file_tags = talloc_strndup_debug (ctx, line + match[2].rm_so,
+ match[2].rm_eo - match[2].rm_so);
+
+ char *tok = file_tags;
+ size_t tok_len = 0;
+
+ tag_op_list_reset (tag_ops);
+
+ while ((tok = strtok_len (tok + tok_len, " ", &tok_len)) != NULL) {
+
+ if (*(tok + tok_len) != '\0') {
+ *(tok + tok_len) = '\0';
+ tok_len++;
+ }
+
+ if (tag_op_list_append (tag_ops, tok, FALSE))
+ return -1;
+ }
+
+ return 0;
+
+}