[notmuch] [PATCH v2 2/4] Conversion to mailstore abstraction
authorMichal Sojka <sojkam1@fel.cvut.cz>
Fri, 26 Mar 2010 21:42:55 +0000 (22:42 +0100)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:36:26 +0000 (09:36 -0800)
53/21c24776bfa6488ca758edf2609caaedd21239 [new file with mode: 0644]

diff --git a/53/21c24776bfa6488ca758edf2609caaedd21239 b/53/21c24776bfa6488ca758edf2609caaedd21239
new file mode 100644 (file)
index 0000000..6432295
--- /dev/null
@@ -0,0 +1,1470 @@
+Return-Path: <sojkam1@fel.cvut.cz>\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 24FC541733B\r
+       for <notmuch@notmuchmail.org>; Fri, 26 Mar 2010 14:43:29 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -1.9\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-1.9 tagged_above=-999 required=5\r
+       tests=[BAYES_00=-1.9] autolearn=ham\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 vOQ1eGD+IG3l for <notmuch@notmuchmail.org>;\r
+       Fri, 26 Mar 2010 14:43:17 -0700 (PDT)\r
+Received: from max.feld.cvut.cz (max.feld.cvut.cz [147.32.192.36])\r
+       by olra.theworths.org (Postfix) with ESMTP id B10594196F5\r
+       for <notmuch@notmuchmail.org>; Fri, 26 Mar 2010 14:43:13 -0700 (PDT)\r
+Received: from localhost (unknown [192.168.200.4])\r
+       by max.feld.cvut.cz (Postfix) with ESMTP id B99F019F3604;\r
+       Fri, 26 Mar 2010 22:43:12 +0100 (CET)\r
+X-Virus-Scanned: IMAP AMAVIS\r
+Received: from max.feld.cvut.cz ([192.168.200.1])\r
+       by localhost (styx.feld.cvut.cz [192.168.200.4]) (amavisd-new,\r
+       port 10044)\r
+       with ESMTP id ZlspbgAK3NpO; Fri, 26 Mar 2010 22:43:10 +0100 (CET)\r
+Received: from imap.feld.cvut.cz (imap.feld.cvut.cz [147.32.192.34])\r
+       by max.feld.cvut.cz (Postfix) with ESMTP id 0844519F3403;\r
+       Fri, 26 Mar 2010 22:43:09 +0100 (CET)\r
+Received: from steelpick.2x.cz (r5da224.net.upc.cz [86.49.116.224])\r
+       (Authenticated sender: sojkam1)\r
+       by imap.feld.cvut.cz (Postfix) with ESMTPSA id ACB73FA003;\r
+       Fri, 26 Mar 2010 22:43:09 +0100 (CET)\r
+Received: from wsh by steelpick.2x.cz with local (Exim 4.71)\r
+       (envelope-from <sojkam1@fel.cvut.cz>)\r
+       id 1NvHJ7-0006i9-4i; Fri, 26 Mar 2010 22:43:09 +0100\r
+From: Michal Sojka <sojkam1@fel.cvut.cz>\r
+To: notmuch@notmuchmail.org\r
+Date: Fri, 26 Mar 2010 22:42:55 +0100\r
+Message-Id: <1269639777-25765-2-git-send-email-sojkam1@fel.cvut.cz>\r
+X-Mailer: git-send-email 1.7.0.2\r
+In-Reply-To: <87tys292w7.fsf@steelpick.2x.cz>\r
+References: <87tys292w7.fsf@steelpick.2x.cz>\r
+Subject: [notmuch] [PATCH v2 2/4] Conversion to mailstore abstraction\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, 26 Mar 2010 21:43:29 -0000\r
+\r
+The code for detection of new files in the mailstore and their\r
+addition to the database is moved from notmuch-new.c to\r
+lib/mailstore-files.c, where it is called by the abstract mailstore\r
+interface.\r
+\r
+The code was only changed to allow the progress reporting functions to\r
+be implemented outside of notmuch library, as can be seen by\r
+git diff HEAD^^ -C\r
+\r
+Signed-off-by: Michal Sojka <sojkam1@fel.cvut.cz>\r
+---\r
+ lib/mailstore-files.c |  581 ++++++++++++++++++++++++++++++++++++++++++++\r
+ notmuch-new.c         |  640 ++++---------------------------------------------\r
+ 2 files changed, 634 insertions(+), 587 deletions(-)\r
+\r
+diff --git a/lib/mailstore-files.c b/lib/mailstore-files.c\r
+index 92d7f5d..e4086ba 100644\r
+--- a/lib/mailstore-files.c\r
++++ b/lib/mailstore-files.c\r
+@@ -20,9 +20,587 @@\r
+  *        Michal Sojka <sojkam1@fel.cvut.cz>\r
+  */\r
\r
++#define _GNU_SOURCE           /* For asprintf() */\r
+ #include "notmuch.h"\r
+ #include "mailstore-private.h"\r
++#include <dirent.h>\r
\r
++typedef struct _filename_node {\r
++    char *filename;\r
++    struct _filename_node *next;\r
++} _filename_node_t;\r
++\r
++typedef struct _filename_list {\r
++    _filename_node_t *head;\r
++    _filename_node_t **tail;\r
++} _filename_list_t;\r
++\r
++typedef struct _indexing_context_priv {\r
++    _filename_list_t *removed_files;\r
++    _filename_list_t *removed_directories;\r
++} _indexing_context_priv_t;\r
++\r
++static _filename_list_t *\r
++_filename_list_create (const void *ctx)\r
++{\r
++    _filename_list_t *list;\r
++\r
++    list = talloc (ctx, _filename_list_t);\r
++    if (list == NULL)\r
++      return NULL;\r
++\r
++    list->head = NULL;\r
++    list->tail = &list->head;\r
++\r
++    return list;\r
++}\r
++\r
++static void\r
++_filename_list_add (_filename_list_t *list,\r
++                  const char *filename)\r
++{\r
++    _filename_node_t *node = talloc (list, _filename_node_t);\r
++\r
++    node->filename = talloc_strdup (list, filename);\r
++    node->next = NULL;\r
++\r
++    *(list->tail) = node;\r
++    list->tail = &node->next;\r
++}\r
++\r
++static void\r
++tag_inbox_and_unread (notmuch_message_t *message)\r
++{\r
++    notmuch_message_add_tag (message, "inbox");\r
++    notmuch_message_add_tag (message, "unread");\r
++}\r
++\r
++static int\r
++dirent_sort_inode (const struct dirent **a, const struct dirent **b)\r
++{\r
++    return ((*a)->d_ino < (*b)->d_ino) ? -1 : 1;\r
++}\r
++\r
++static int\r
++dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b)\r
++{\r
++    return strcmp ((*a)->d_name, (*b)->d_name);\r
++}\r
++\r
++/* Test if the directory looks like a Maildir directory.\r
++ *\r
++ * Search through the array of directory entries to see if we can find all\r
++ * three subdirectories typical for Maildir, that is "new", "cur", and "tmp".\r
++ *\r
++ * Return 1 if the directory looks like a Maildir and 0 otherwise.\r
++ */\r
++static int\r
++_entries_resemble_maildir (struct dirent **entries, int count)\r
++{\r
++    int i, found = 0;\r
++\r
++    for (i = 0; i < count; i++) {\r
++      if (entries[i]->d_type != DT_DIR && entries[i]->d_type != DT_UNKNOWN)\r
++          continue;\r
++\r
++      if (strcmp(entries[i]->d_name, "new") == 0 ||\r
++          strcmp(entries[i]->d_name, "cur") == 0 ||\r
++          strcmp(entries[i]->d_name, "tmp") == 0)\r
++      {\r
++          found++;\r
++          if (found == 3)\r
++              return 1;\r
++      }\r
++    }\r
++\r
++    return 0;\r
++}\r
++\r
++\r
++/* Examine 'path' recursively as follows:\r
++ *\r
++ *   o Ask the filesystem for the mtime of 'path' (fs_mtime)\r
++ *   o Ask the database for its timestamp of 'path' (db_mtime)\r
++ *\r
++ *   o Ask the filesystem for files and directories within 'path'\r
++ *     (via scandir and stored in fs_entries)\r
++ *   o Ask the database for files and directories within 'path'\r
++ *     (db_files and db_subdirs)\r
++ *\r
++ *   o Pass 1: For each directory in fs_entries, recursively call into\r
++ *     this same function.\r
++ *\r
++ *   o Pass 2: If 'fs_mtime' > 'db_mtime', then walk fs_entries\r
++ *     simultaneously with db_files and db_subdirs. Look for one of\r
++ *     three interesting cases:\r
++ *\r
++ *       1. Regular file in fs_entries and not in db_files\r
++ *            This is a new file to add_message into the database.\r
++ *\r
++ *         2. Filename in db_files not in fs_entries.\r
++ *            This is a file that has been removed from the mail store.\r
++ *\r
++ *         3. Directory in db_subdirs not in fs_entries\r
++ *            This is a directory that has been removed from the mail store.\r
++ *\r
++ *     Note that the addition of a directory is not interesting here,\r
++ *     since that will have been taken care of in pass 1. Also, we\r
++ *     don't immediately act on file/directory removal since we must\r
++ *     ensure that in the case of a rename that the new filename is\r
++ *     added before the old filename is removed, (so that no\r
++ *     information is lost from the database).\r
++ *\r
++ *   o Tell the database to update its time of 'path' to 'fs_mtime'\r
++ */\r
++static notmuch_status_t\r
++add_files_recursive (notmuch_mailstore_t *mailstore,\r
++                   const char *path,\r
++                   notmuch_indexing_context_t *state)\r
++{\r
++    DIR *dir = NULL;\r
++    struct dirent *entry = NULL;\r
++    char *next = NULL;\r
++    time_t fs_mtime, db_mtime;\r
++    notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS;\r
++    notmuch_message_t *message = NULL;\r
++    struct dirent **fs_entries = NULL;\r
++    int i, num_fs_entries;\r
++    notmuch_directory_t *directory;\r
++    notmuch_filenames_t *db_files = NULL;\r
++    notmuch_filenames_t *db_subdirs = NULL;\r
++    struct stat st;\r
++    notmuch_bool_t is_maildir, new_directory;\r
++    _indexing_context_priv_t *priv = state->priv;\r
++    notmuch_database_t *notmuch = mailstore->notmuch;\r
++\r
++    if (stat (path, &st)) {\r
++      fprintf (stderr, "Error reading directory %s: %s\n",\r
++               path, strerror (errno));\r
++      return NOTMUCH_STATUS_FILE_ERROR;\r
++    }\r
++\r
++    /* This is not an error since we may have recursed based on a\r
++     * symlink to a regular file, not a directory, and we don't know\r
++     * that until this stat. */\r
++    if (! S_ISDIR (st.st_mode))\r
++      return NOTMUCH_STATUS_SUCCESS;\r
++\r
++    fs_mtime = st.st_mtime;\r
++\r
++    directory = notmuch_database_get_directory (notmuch, path);\r
++    db_mtime = notmuch_directory_get_mtime (directory);\r
++\r
++    if (db_mtime == 0) {\r
++      new_directory = TRUE;\r
++      db_files = NULL;\r
++      db_subdirs = NULL;\r
++    } else {\r
++      new_directory = FALSE;\r
++      db_files = notmuch_directory_get_child_files (directory);\r
++      db_subdirs = notmuch_directory_get_child_directories (directory);\r
++    }\r
++\r
++    /* If the database knows about this directory, then we sort based\r
++     * on strcmp to match the database sorting. Otherwise, we can do\r
++     * inode-based sorting for faster filesystem operation. */\r
++    num_fs_entries = scandir (path, &fs_entries, 0,\r
++                            new_directory ?\r
++                            dirent_sort_inode : dirent_sort_strcmp_name);\r
++\r
++    if (num_fs_entries == -1) {\r
++      fprintf (stderr, "Error opening directory %s: %s\n",\r
++               path, strerror (errno));\r
++      ret = NOTMUCH_STATUS_FILE_ERROR;\r
++      goto DONE;\r
++    }\r
++\r
++    /* Pass 1: Recurse into all sub-directories. */\r
++    is_maildir = _entries_resemble_maildir (fs_entries, num_fs_entries);\r
++\r
++    for (i = 0; i < num_fs_entries; i++) {\r
++      if (state->interrupted)\r
++          break;\r
++\r
++      entry = fs_entries[i];\r
++\r
++      /* We only want to descend into directories.\r
++       * But symlinks can be to directories too, of course.\r
++       *\r
++       * And if the filesystem doesn't tell us the file type in the\r
++       * scandir results, then it might be a directory (and if not,\r
++       * then we'll stat and return immediately in the next level of\r
++       * recursion). */\r
++      if (entry->d_type != DT_DIR &&\r
++          entry->d_type != DT_LNK &&\r
++          entry->d_type != DT_UNKNOWN)\r
++      {\r
++          continue;\r
++      }\r
++\r
++      /* Ignore special directories to avoid infinite recursion.\r
++       * Also ignore the .notmuch directory and any "tmp" directory\r
++       * that appears within a maildir.\r
++       */\r
++      /* XXX: Eventually we'll want more sophistication to let the\r
++       * user specify files to be ignored. */\r
++      if (strcmp (entry->d_name, ".") == 0 ||\r
++          strcmp (entry->d_name, "..") == 0 ||\r
++          (is_maildir && strcmp (entry->d_name, "tmp") == 0) ||\r
++          strcmp (entry->d_name, ".notmuch") ==0)\r
++      {\r
++          continue;\r
++      }\r
++\r
++      next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);\r
++      status = add_files_recursive (mailstore, next, state);\r
++      if (status && ret == NOTMUCH_STATUS_SUCCESS)\r
++          ret = status;\r
++      talloc_free (next);\r
++      next = NULL;\r
++    }\r
++\r
++    /* If this directory hasn't been modified since the last\r
++     * "notmuch new", then we can skip the second pass entirely. */\r
++    if (fs_mtime <= db_mtime)\r
++      goto DONE;\r
++\r
++    /* Pass 2: Scan for new files, removed files, and removed directories. */\r
++    for (i = 0; i < num_fs_entries; i++)\r
++    {\r
++      if (state->interrupted)\r
++          break;\r
++\r
++        entry = fs_entries[i];\r
++\r
++      /* Check if we've walked past any names in db_files or\r
++       * db_subdirs. If so, these have been deleted. */\r
++      while (notmuch_filenames_valid (db_files) &&\r
++             strcmp (notmuch_filenames_get (db_files), entry->d_name) < 0)\r
++      {\r
++          char *absolute = talloc_asprintf (priv->removed_files,\r
++                                            "%s/%s", path,\r
++                                            notmuch_filenames_get (db_files));\r
++\r
++          _filename_list_add (priv->removed_files, absolute);\r
++\r
++          notmuch_filenames_move_to_next (db_files);\r
++      }\r
++\r
++      while (notmuch_filenames_valid (db_subdirs) &&\r
++             strcmp (notmuch_filenames_get (db_subdirs), entry->d_name) <= 0)\r
++      {\r
++          const char *filename = notmuch_filenames_get (db_subdirs);\r
++\r
++          if (strcmp (filename, entry->d_name) < 0)\r
++          {\r
++              char *absolute = talloc_asprintf (priv->removed_directories,\r
++                                                "%s/%s", path, filename);\r
++\r
++              _filename_list_add (priv->removed_directories, absolute);\r
++          }\r
++\r
++          notmuch_filenames_move_to_next (db_subdirs);\r
++      }\r
++\r
++      /* If we're looking at a symlink, we only want to add it if it\r
++       * links to a regular file, (and not to a directory, say).\r
++       *\r
++       * Similarly, if the file is of unknown type (due to filesytem\r
++       * limitations), then we also need to look closer.\r
++       *\r
++       * In either case, a stat does the trick.\r
++       */\r
++      if (entry->d_type == DT_LNK || entry->d_type == DT_UNKNOWN) {\r
++          int err;\r
++\r
++          next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);\r
++          err = stat (next, &st);\r
++          talloc_free (next);\r
++          next = NULL;\r
++\r
++          /* Don't emit an error for a link pointing nowhere, since\r
++           * the directory-traversal pass will have already done\r
++           * that. */\r
++          if (err)\r
++              continue;\r
++\r
++          if (! S_ISREG (st.st_mode))\r
++              continue;\r
++      } else if (entry->d_type != DT_REG) {\r
++          continue;\r
++      }\r
++\r
++      /* Don't add a file that we've added before. */\r
++      if (notmuch_filenames_valid (db_files) &&\r
++          strcmp (notmuch_filenames_get (db_files), entry->d_name) == 0)\r
++      {\r
++          notmuch_filenames_move_to_next (db_files);\r
++          continue;\r
++      }\r
++\r
++      /* We're now looking at a regular file that doesn't yet exist\r
++       * in the database, so add it. */\r
++      next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);\r
++\r
++      state->processed_files++;\r
++\r
++      if (state->verbose && state->print_verbose_cb)\r
++          state->print_verbose_cb(state, next);\r
++\r
++      status = notmuch_database_add_message (notmuch, next, &message);\r
++      switch (status) {\r
++      /* success */\r
++      case NOTMUCH_STATUS_SUCCESS:\r
++          state->added_messages++;\r
++          tag_inbox_and_unread (message);\r
++          break;\r
++      /* Non-fatal issues (go on to next file) */\r
++      case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID:\r
++          /* Stay silent on this one. */\r
++          break;\r
++      case NOTMUCH_STATUS_FILE_NOT_EMAIL:\r
++          fprintf (stderr, "Note: Ignoring non-mail file: %s\n",\r
++                   next);\r
++          break;\r
++      /* Fatal issues. Don't process anymore. */\r
++      case NOTMUCH_STATUS_READ_ONLY_DATABASE:\r
++      case NOTMUCH_STATUS_XAPIAN_EXCEPTION:\r
++      case NOTMUCH_STATUS_OUT_OF_MEMORY:\r
++          fprintf (stderr, "Error: %s. Halting processing.\n",\r
++                   notmuch_status_to_string (status));\r
++          ret = status;\r
++          goto DONE;\r
++      default:\r
++      case NOTMUCH_STATUS_FILE_ERROR:\r
++      case NOTMUCH_STATUS_NULL_POINTER:\r
++      case NOTMUCH_STATUS_TAG_TOO_LONG:\r
++      case NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW:\r
++      case NOTMUCH_STATUS_LAST_STATUS:\r
++          INTERNAL_ERROR ("add_message returned unexpected value: %d",  status);\r
++          goto DONE;\r
++      }\r
++\r
++      if (message) {\r
++          notmuch_message_destroy (message);\r
++          message = NULL;\r
++      }\r
++\r
++      if (state->print_progress &&\r
++          state->print_progress_cb) {\r
++          state->print_progress = 0;\r
++          state->print_progress_cb (state);\r
++      }\r
++\r
++      talloc_free (next);\r
++      next = NULL;\r
++    }\r
++\r
++    if (state->interrupted)\r
++      goto DONE;\r
++\r
++    /* Now that we've walked the whole filesystem list, anything left\r
++     * over in the database lists has been deleted. */\r
++    while (notmuch_filenames_valid (db_files))\r
++    {\r
++      char *absolute = talloc_asprintf (priv->removed_files,\r
++                                        "%s/%s", path,\r
++                                        notmuch_filenames_get (db_files));\r
++\r
++      _filename_list_add (priv->removed_files, absolute);\r
++\r
++      notmuch_filenames_move_to_next (db_files);\r
++    }\r
++\r
++    while (notmuch_filenames_valid (db_subdirs))\r
++    {\r
++      char *absolute = talloc_asprintf (priv->removed_directories,\r
++                                        "%s/%s", path,\r
++                                        notmuch_filenames_get (db_subdirs));\r
++\r
++      _filename_list_add (priv->removed_directories, absolute);\r
++\r
++      notmuch_filenames_move_to_next (db_subdirs);\r
++    }\r
++\r
++    if (! state->interrupted) {\r
++      status = notmuch_directory_set_mtime (directory, fs_mtime);\r
++      if (status && ret == NOTMUCH_STATUS_SUCCESS)\r
++          ret = status;\r
++    }\r
++\r
++DONE:\r
++    if (next)\r
++      talloc_free (next);\r
++    if (entry)\r
++      free (entry);\r
++    if (dir)\r
++      closedir (dir);\r
++    if (fs_entries)\r
++      free (fs_entries);\r
++    if (db_subdirs)\r
++      notmuch_filenames_destroy (db_subdirs);\r
++    if (db_files)\r
++      notmuch_filenames_destroy (db_files);\r
++    if (directory)\r
++      notmuch_directory_destroy (directory);\r
++\r
++    return ret;\r
++}\r
++\r
++/* XXX: This should be merged with the add_files function since it\r
++ * shares a lot of logic with it. */\r
++/* Recursively count all regular files in path and all sub-directories\r
++ * of path.  The result is added to *count (which should be\r
++ * initialized to zero by the top-level caller before calling\r
++ * count_files). */\r
++static void\r
++count_files (notmuch_mailstore_t *mailstore,\r
++           const char *path, int *count,\r
++           volatile sig_atomic_t *interrupted)\r
++{\r
++    struct dirent *entry = NULL;\r
++    char *next;\r
++    struct stat st;\r
++    struct dirent **fs_entries = NULL;\r
++    int num_fs_entries = scandir (path, &fs_entries, 0, dirent_sort_inode);\r
++    int i = 0;\r
++\r
++    (void)mailstore;\r
++    if (num_fs_entries == -1) {\r
++      fprintf (stderr, "Warning: failed to open directory %s: %s\n",\r
++               path, strerror (errno));\r
++      goto DONE;\r
++    }\r
++\r
++    while (!*interrupted) {\r
++        if (i == num_fs_entries)\r
++          break;\r
++\r
++        entry = fs_entries[i++];\r
++\r
++      /* Ignore special directories to avoid infinite recursion.\r
++       * Also ignore the .notmuch directory.\r
++       */\r
++      /* XXX: Eventually we'll want more sophistication to let the\r
++       * user specify files to be ignored. */\r
++      if (strcmp (entry->d_name, ".") == 0 ||\r
++          strcmp (entry->d_name, "..") == 0 ||\r
++          strcmp (entry->d_name, ".notmuch") == 0)\r
++      {\r
++          continue;\r
++      }\r
++\r
++      if (asprintf (&next, "%s/%s", path, entry->d_name) == -1) {\r
++          next = NULL;\r
++          fprintf (stderr, "Error descending from %s to %s: Out of memory\n",\r
++                   path, entry->d_name);\r
++          continue;\r
++      }\r
++\r
++      stat (next, &st);\r
++\r
++      if (S_ISREG (st.st_mode)) {\r
++          *count = *count + 1;\r
++          if (*count % 1000 == 0) {\r
++              printf ("Found %d files so far.\r", *count);\r
++              fflush (stdout);\r
++          }\r
++      } else if (S_ISDIR (st.st_mode)) {\r
++          count_files (mailstore, next, count, interrupted);\r
++      }\r
++\r
++      free (next);\r
++    }\r
++\r
++DONE:\r
++    if (entry)\r
++      free (entry);\r
++    if (fs_entries)\r
++        free (fs_entries);\r
++}\r
++\r
++/* Recursively remove all filenames from the database referring to\r
++ * 'path' (or to any of its children). */\r
++static void\r
++_remove_directory (void *ctx,\r
++                 notmuch_database_t *notmuch,\r
++                 const char *path,\r
++                 int *renamed_files,\r
++                 int *removed_files)\r
++{\r
++    notmuch_directory_t *directory;\r
++    notmuch_filenames_t *files, *subdirs;\r
++    notmuch_status_t status;\r
++    char *absolute;\r
++\r
++    directory = notmuch_database_get_directory (notmuch, path);\r
++\r
++    for (files = notmuch_directory_get_child_files (directory);\r
++       notmuch_filenames_valid (files);\r
++       notmuch_filenames_move_to_next (files))\r
++    {\r
++      absolute = talloc_asprintf (ctx, "%s/%s", path,\r
++                                  notmuch_filenames_get (files));\r
++      status = notmuch_database_remove_message (notmuch, absolute);\r
++      if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID)\r
++          *renamed_files = *renamed_files + 1;\r
++      else\r
++          *removed_files = *removed_files + 1;\r
++      talloc_free (absolute);\r
++    }\r
++\r
++    for (subdirs = notmuch_directory_get_child_directories (directory);\r
++       notmuch_filenames_valid (subdirs);\r
++       notmuch_filenames_move_to_next (subdirs))\r
++    {\r
++      absolute = talloc_asprintf (ctx, "%s/%s", path,\r
++                                  notmuch_filenames_get (subdirs));\r
++      _remove_directory (ctx, notmuch, absolute, renamed_files, removed_files);\r
++      talloc_free (absolute);\r
++    }\r
++\r
++    notmuch_directory_destroy (directory);\r
++}\r
++\r
++static notmuch_private_status_t\r
++index_new(notmuch_mailstore_t *mailstore, const char* path,\r
++        notmuch_indexing_context_t *indexing_ctx)\r
++{\r
++    _indexing_context_priv_t *priv;\r
++    _filename_node_t *f;\r
++    notmuch_status_t status, ret;\r
++    notmuch_database_t *notmuch = mailstore->notmuch;\r
++\r
++    priv = talloc(NULL, _indexing_context_priv_t);\r
++    indexing_ctx->priv = priv;\r
++    if (priv == NULL)\r
++      return NOTMUCH_STATUS_OUT_OF_MEMORY;\r
++\r
++    priv->removed_files = _filename_list_create (priv);\r
++    priv->removed_directories = _filename_list_create (priv);\r
++\r
++    ret = add_files_recursive(mailstore, path, indexing_ctx);\r
++\r
++    indexing_ctx->removed_files = 0;\r
++    indexing_ctx->renamed_files = 0;\r
++    for (f = priv->removed_files->head; f; f = f->next) {\r
++      status = notmuch_database_remove_message (notmuch, f->filename);\r
++      if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID)\r
++          indexing_ctx->renamed_files++;\r
++      else\r
++          indexing_ctx->removed_files++;\r
++    }\r
++\r
++    for (f = priv->removed_directories->head; f; f = f->next) {\r
++      _remove_directory (priv, notmuch, f->filename,\r
++                         &indexing_ctx->renamed_files,\r
++                         &indexing_ctx->removed_files);\r
++    }\r
++\r
++    talloc_free(priv);\r
++\r
++    return ret;\r
++}\r
\r
+ static FILE *\r
+ open_file(notmuch_mailstore_t *mailstore, const char *filename)\r
+@@ -39,5 +617,8 @@ open_file(notmuch_mailstore_t *mailstore, const char *filename)\r
+ /* Original notmuch mail store */\r
+ struct _notmuch_mailstore mailstore_files = {\r
+     .type = "files",\r
++    .count_files = count_files,\r
++    .index_new = index_new,\r
++    .sync_tags = NULL,\r
+     .open_file = open_file,\r
+ };\r
+diff --git a/notmuch-new.c b/notmuch-new.c\r
+index 2d0ba6c..feb6246 100644\r
+--- a/notmuch-new.c\r
++++ b/notmuch-new.c\r
+@@ -22,35 +22,12 @@\r
\r
+ #include <unistd.h>\r
\r
+-typedef struct _filename_node {\r
+-    char *filename;\r
+-    struct _filename_node *next;\r
+-} _filename_node_t;\r
+-\r
+-typedef struct _filename_list {\r
+-    _filename_node_t *head;\r
+-    _filename_node_t **tail;\r
+-} _filename_list_t;\r
+-\r
+-typedef struct {\r
+-    int output_is_a_tty;\r
+-    int verbose;\r
+-\r
+-    int total_files;\r
+-    int processed_files;\r
+-    int added_messages;\r
+-    struct timeval tv_start;\r
+-\r
+-    _filename_list_t *removed_files;\r
+-    _filename_list_t *removed_directories;\r
+-} add_files_state_t;\r
+-\r
+-static volatile sig_atomic_t do_add_files_print_progress = 0;\r
++notmuch_indexing_context_t *indexing_ctx;\r
\r
+ static void\r
+ handle_sigalrm (unused (int signal))\r
+ {\r
+-    do_add_files_print_progress = 1;\r
++    indexing_ctx->print_progress = 1;\r
+ }\r
\r
+ static volatile sig_atomic_t interrupted;\r
+@@ -62,53 +39,24 @@ handle_sigint (unused (int sig))\r
+     static char msg[] = "Stopping...         \n";\r
\r
+     ignored = write(2, msg, sizeof(msg)-1);\r
+-    interrupted = 1;\r
++    indexing_ctx->interrupted = 1;\r
+ }\r
\r
+-static _filename_list_t *\r
+-_filename_list_create (const void *ctx)\r
+-{\r
+-    _filename_list_t *list;\r
+-\r
+-    list = talloc (ctx, _filename_list_t);\r
+-    if (list == NULL)\r
+-      return NULL;\r
+-\r
+-    list->head = NULL;\r
+-    list->tail = &list->head;\r
+-\r
+-    return list;\r
+-}\r
+-\r
+-static void\r
+-_filename_list_add (_filename_list_t *list,\r
+-                  const char *filename)\r
+-{\r
+-    _filename_node_t *node = talloc (list, _filename_node_t);\r
+-\r
+-    node->filename = talloc_strdup (list, filename);\r
+-    node->next = NULL;\r
+-\r
+-    *(list->tail) = node;\r
+-    list->tail = &node->next;\r
+-}\r
+-\r
+-static void\r
+-tag_inbox_and_unread (notmuch_message_t *message)\r
+-{\r
+-    notmuch_message_add_tag (message, "inbox");\r
+-    notmuch_message_add_tag (message, "unread");\r
+-}\r
++struct priv_ctx {\r
++      struct timeval tv_start;\r
++      int output_is_a_tty;\r
++};\r
\r
+ static void\r
+-add_files_print_progress (add_files_state_t *state)\r
++add_files_print_progress (notmuch_indexing_context_t *state)\r
+ {\r
++    struct priv_ctx *state2 = state->priv;\r
+     struct timeval tv_now;\r
+     double elapsed_overall, rate_overall;\r
\r
+     gettimeofday (&tv_now, NULL);\r
\r
+-    elapsed_overall = notmuch_time_elapsed (state->tv_start, tv_now);\r
++    elapsed_overall = notmuch_time_elapsed (state2->tv_start, tv_now);\r
+     rate_overall = (state->processed_files) / elapsed_overall;\r
\r
+     printf ("Processed %d", state->processed_files);\r
+@@ -128,396 +76,42 @@ add_files_print_progress (add_files_state_t *state)\r
+     fflush (stdout);\r
+ }\r
\r
+-static int\r
+-dirent_sort_inode (const struct dirent **a, const struct dirent **b)\r
+-{\r
+-    return ((*a)->d_ino < (*b)->d_ino) ? -1 : 1;\r
+-}\r
+-\r
+-static int\r
+-dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b)\r
+-{\r
+-    return strcmp ((*a)->d_name, (*b)->d_name);\r
+-}\r
+-\r
+-/* Test if the directory looks like a Maildir directory.\r
+- *\r
+- * Search through the array of directory entries to see if we can find all\r
+- * three subdirectories typical for Maildir, that is "new", "cur", and "tmp".\r
+- *\r
+- * Return 1 if the directory looks like a Maildir and 0 otherwise.\r
+- */\r
+-static int\r
+-_entries_resemble_maildir (struct dirent **entries, int count)\r
+-{\r
+-    int i, found = 0;\r
+-\r
+-    for (i = 0; i < count; i++) {\r
+-      if (entries[i]->d_type != DT_DIR && entries[i]->d_type != DT_UNKNOWN)\r
+-          continue;\r
+-\r
+-      if (strcmp(entries[i]->d_name, "new") == 0 ||\r
+-          strcmp(entries[i]->d_name, "cur") == 0 ||\r
+-          strcmp(entries[i]->d_name, "tmp") == 0)\r
+-      {\r
+-          found++;\r
+-          if (found == 3)\r
+-              return 1;\r
+-      }\r
+-    }\r
+-\r
+-    return 0;\r
+-}\r
+-\r
+-/* Examine 'path' recursively as follows:\r
+- *\r
+- *   o Ask the filesystem for the mtime of 'path' (fs_mtime)\r
+- *   o Ask the database for its timestamp of 'path' (db_mtime)\r
+- *\r
+- *   o Ask the filesystem for files and directories within 'path'\r
+- *     (via scandir and stored in fs_entries)\r
+- *   o Ask the database for files and directories within 'path'\r
+- *     (db_files and db_subdirs)\r
+- *\r
+- *   o Pass 1: For each directory in fs_entries, recursively call into\r
+- *     this same function.\r
+- *\r
+- *   o Pass 2: If 'fs_mtime' > 'db_mtime', then walk fs_entries\r
+- *     simultaneously with db_files and db_subdirs. Look for one of\r
+- *     three interesting cases:\r
+- *\r
+- *       1. Regular file in fs_entries and not in db_files\r
+- *            This is a new file to add_message into the database.\r
+- *\r
+- *         2. Filename in db_files not in fs_entries.\r
+- *            This is a file that has been removed from the mail store.\r
+- *\r
+- *         3. Directory in db_subdirs not in fs_entries\r
+- *            This is a directory that has been removed from the mail store.\r
+- *\r
+- *     Note that the addition of a directory is not interesting here,\r
+- *     since that will have been taken care of in pass 1. Also, we\r
+- *     don't immediately act on file/directory removal since we must\r
+- *     ensure that in the case of a rename that the new filename is\r
+- *     added before the old filename is removed, (so that no\r
+- *     information is lost from the database).\r
+- *\r
+- *   o Tell the database to update its time of 'path' to 'fs_mtime'\r
+- */\r
+-static notmuch_status_t\r
+-add_files_recursive (notmuch_database_t *notmuch,\r
+-                   const char *path,\r
+-                   add_files_state_t *state)\r
++static void\r
++add_files_print_verbose (notmuch_indexing_context_t *state, const char *filename)\r
+ {\r
+-    DIR *dir = NULL;\r
+-    struct dirent *entry = NULL;\r
+-    char *next = NULL;\r
+-    time_t fs_mtime, db_mtime;\r
+-    notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS;\r
+-    notmuch_message_t *message = NULL;\r
+-    struct dirent **fs_entries = NULL;\r
+-    int i, num_fs_entries;\r
+-    notmuch_directory_t *directory;\r
+-    notmuch_filenames_t *db_files = NULL;\r
+-    notmuch_filenames_t *db_subdirs = NULL;\r
+-    struct stat st;\r
+-    notmuch_bool_t is_maildir, new_directory;\r
+-\r
+-    if (stat (path, &st)) {\r
+-      fprintf (stderr, "Error reading directory %s: %s\n",\r
+-               path, strerror (errno));\r
+-      return NOTMUCH_STATUS_FILE_ERROR;\r
+-    }\r
+-\r
+-    /* This is not an error since we may have recursed based on a\r
+-     * symlink to a regular file, not a directory, and we don't know\r
+-     * that until this stat. */\r
+-    if (! S_ISDIR (st.st_mode))\r
+-      return NOTMUCH_STATUS_SUCCESS;\r
+-\r
+-    fs_mtime = st.st_mtime;\r
+-\r
+-    directory = notmuch_database_get_directory (notmuch, path);\r
+-    db_mtime = notmuch_directory_get_mtime (directory);\r
+-\r
+-    if (db_mtime == 0) {\r
+-      new_directory = TRUE;\r
+-      db_files = NULL;\r
+-      db_subdirs = NULL;\r
+-    } else {\r
+-      new_directory = FALSE;\r
+-      db_files = notmuch_directory_get_child_files (directory);\r
+-      db_subdirs = notmuch_directory_get_child_directories (directory);\r
+-    }\r
+-\r
+-    /* If the database knows about this directory, then we sort based\r
+-     * on strcmp to match the database sorting. Otherwise, we can do\r
+-     * inode-based sorting for faster filesystem operation. */\r
+-    num_fs_entries = scandir (path, &fs_entries, 0,\r
+-                            new_directory ?\r
+-                            dirent_sort_inode : dirent_sort_strcmp_name);\r
+-\r
+-    if (num_fs_entries == -1) {\r
+-      fprintf (stderr, "Error opening directory %s: %s\n",\r
+-               path, strerror (errno));\r
+-      ret = NOTMUCH_STATUS_FILE_ERROR;\r
+-      goto DONE;\r
+-    }\r
+-\r
+-    /* Pass 1: Recurse into all sub-directories. */\r
+-    is_maildir = _entries_resemble_maildir (fs_entries, num_fs_entries);\r
+-\r
+-    for (i = 0; i < num_fs_entries; i++) {\r
+-      if (interrupted)\r
+-          break;\r
+-\r
+-      entry = fs_entries[i];\r
+-\r
+-      /* We only want to descend into directories.\r
+-       * But symlinks can be to directories too, of course.\r
+-       *\r
+-       * And if the filesystem doesn't tell us the file type in the\r
+-       * scandir results, then it might be a directory (and if not,\r
+-       * then we'll stat and return immediately in the next level of\r
+-       * recursion). */\r
+-      if (entry->d_type != DT_DIR &&\r
+-          entry->d_type != DT_LNK &&\r
+-          entry->d_type != DT_UNKNOWN)\r
+-      {\r
+-          continue;\r
+-      }\r
+-\r
+-      /* Ignore special directories to avoid infinite recursion.\r
+-       * Also ignore the .notmuch directory and any "tmp" directory\r
+-       * that appears within a maildir.\r
+-       */\r
+-      /* XXX: Eventually we'll want more sophistication to let the\r
+-       * user specify files to be ignored. */\r
+-      if (strcmp (entry->d_name, ".") == 0 ||\r
+-          strcmp (entry->d_name, "..") == 0 ||\r
+-          (is_maildir && strcmp (entry->d_name, "tmp") == 0) ||\r
+-          strcmp (entry->d_name, ".notmuch") ==0)\r
+-      {\r
+-          continue;\r
+-      }\r
+-\r
+-      next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);\r
+-      status = add_files_recursive (notmuch, next, state);\r
+-      if (status && ret == NOTMUCH_STATUS_SUCCESS)\r
+-          ret = status;\r
+-      talloc_free (next);\r
+-      next = NULL;\r
+-    }\r
+-\r
+-    /* If this directory hasn't been modified since the last\r
+-     * "notmuch new", then we can skip the second pass entirely. */\r
+-    if (fs_mtime <= db_mtime)\r
+-      goto DONE;\r
+-\r
+-    /* Pass 2: Scan for new files, removed files, and removed directories. */\r
+-    for (i = 0; i < num_fs_entries; i++)\r
+-    {\r
+-      if (interrupted)\r
+-          break;\r
++    struct priv_ctx *state2 = state->priv;\r
++    if (state2->output_is_a_tty)\r
++      printf("\r\033[K");\r
\r
+-        entry = fs_entries[i];\r
++    printf ("%i/%i: %s",\r
++          state->processed_files,\r
++          state->total_files,\r
++          filename);\r
\r
+-      /* Check if we've walked past any names in db_files or\r
+-       * db_subdirs. If so, these have been deleted. */\r
+-      while (notmuch_filenames_valid (db_files) &&\r
+-             strcmp (notmuch_filenames_get (db_files), entry->d_name) < 0)\r
+-      {\r
+-          char *absolute = talloc_asprintf (state->removed_files,\r
+-                                            "%s/%s", path,\r
+-                                            notmuch_filenames_get (db_files));\r
+-\r
+-          _filename_list_add (state->removed_files, absolute);\r
+-\r
+-          notmuch_filenames_move_to_next (db_files);\r
+-      }\r
+-\r
+-      while (notmuch_filenames_valid (db_subdirs) &&\r
+-             strcmp (notmuch_filenames_get (db_subdirs), entry->d_name) <= 0)\r
+-      {\r
+-          const char *filename = notmuch_filenames_get (db_subdirs);\r
+-\r
+-          if (strcmp (filename, entry->d_name) < 0)\r
+-          {\r
+-              char *absolute = talloc_asprintf (state->removed_directories,\r
+-                                                "%s/%s", path, filename);\r
+-\r
+-              _filename_list_add (state->removed_directories, absolute);\r
+-          }\r
+-\r
+-          notmuch_filenames_move_to_next (db_subdirs);\r
+-      }\r
+-\r
+-      /* If we're looking at a symlink, we only want to add it if it\r
+-       * links to a regular file, (and not to a directory, say).\r
+-       *\r
+-       * Similarly, if the file is of unknown type (due to filesytem\r
+-       * limitations), then we also need to look closer.\r
+-       *\r
+-       * In either case, a stat does the trick.\r
+-       */\r
+-      if (entry->d_type == DT_LNK || entry->d_type == DT_UNKNOWN) {\r
+-          int err;\r
+-\r
+-          next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);\r
+-          err = stat (next, &st);\r
+-          talloc_free (next);\r
+-          next = NULL;\r
+-\r
+-          /* Don't emit an error for a link pointing nowhere, since\r
+-           * the directory-traversal pass will have already done\r
+-           * that. */\r
+-          if (err)\r
+-              continue;\r
+-\r
+-          if (! S_ISREG (st.st_mode))\r
+-              continue;\r
+-      } else if (entry->d_type != DT_REG) {\r
+-          continue;\r
+-      }\r
+-\r
+-      /* Don't add a file that we've added before. */\r
+-      if (notmuch_filenames_valid (db_files) &&\r
+-          strcmp (notmuch_filenames_get (db_files), entry->d_name) == 0)\r
+-      {\r
+-          notmuch_filenames_move_to_next (db_files);\r
+-          continue;\r
+-      }\r
+-\r
+-      /* We're now looking at a regular file that doesn't yet exist\r
+-       * in the database, so add it. */\r
+-      next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);\r
+-\r
+-      state->processed_files++;\r
+-\r
+-      if (state->verbose) {\r
+-          if (state->output_is_a_tty)\r
+-              printf("\r\033[K");\r
+-\r
+-          printf ("%i/%i: %s",\r
+-                  state->processed_files,\r
+-                  state->total_files,\r
+-                  next);\r
+-\r
+-          putchar((state->output_is_a_tty) ? '\r' : '\n');\r
+-          fflush (stdout);\r
+-      }\r
+-\r
+-      status = notmuch_database_add_message (notmuch, next, &message);\r
+-      switch (status) {\r
+-      /* success */\r
+-      case NOTMUCH_STATUS_SUCCESS:\r
+-          state->added_messages++;\r
+-          tag_inbox_and_unread (message);\r
+-          break;\r
+-      /* Non-fatal issues (go on to next file) */\r
+-      case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID:\r
+-          /* Stay silent on this one. */\r
+-          break;\r
+-      case NOTMUCH_STATUS_FILE_NOT_EMAIL:\r
+-          fprintf (stderr, "Note: Ignoring non-mail file: %s\n",\r
+-                   next);\r
+-          break;\r
+-      /* Fatal issues. Don't process anymore. */\r
+-      case NOTMUCH_STATUS_READ_ONLY_DATABASE:\r
+-      case NOTMUCH_STATUS_XAPIAN_EXCEPTION:\r
+-      case NOTMUCH_STATUS_OUT_OF_MEMORY:\r
+-          fprintf (stderr, "Error: %s. Halting processing.\n",\r
+-                   notmuch_status_to_string (status));\r
+-          ret = status;\r
+-          goto DONE;\r
+-      default:\r
+-      case NOTMUCH_STATUS_FILE_ERROR:\r
+-      case NOTMUCH_STATUS_NULL_POINTER:\r
+-      case NOTMUCH_STATUS_TAG_TOO_LONG:\r
+-      case NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW:\r
+-      case NOTMUCH_STATUS_LAST_STATUS:\r
+-          INTERNAL_ERROR ("add_message returned unexpected value: %d",  status);\r
+-          goto DONE;\r
+-      }\r
+-\r
+-      if (message) {\r
+-          notmuch_message_destroy (message);\r
+-          message = NULL;\r
+-      }\r
+-\r
+-      if (do_add_files_print_progress) {\r
+-          do_add_files_print_progress = 0;\r
+-          add_files_print_progress (state);\r
+-      }\r
+-\r
+-      talloc_free (next);\r
+-      next = NULL;\r
+-    }\r
+-\r
+-    /* Now that we've walked the whole filesystem list, anything left\r
+-     * over in the database lists has been deleted. */\r
+-    while (notmuch_filenames_valid (db_files))\r
+-    {\r
+-      char *absolute = talloc_asprintf (state->removed_files,\r
+-                                        "%s/%s", path,\r
+-                                        notmuch_filenames_get (db_files));\r
+-\r
+-      _filename_list_add (state->removed_files, absolute);\r
+-\r
+-      notmuch_filenames_move_to_next (db_files);\r
+-    }\r
+-\r
+-    while (notmuch_filenames_valid (db_subdirs))\r
+-    {\r
+-      char *absolute = talloc_asprintf (state->removed_directories,\r
+-                                        "%s/%s", path,\r
+-                                        notmuch_filenames_get (db_subdirs));\r
+-\r
+-      _filename_list_add (state->removed_directories, absolute);\r
+-\r
+-      notmuch_filenames_move_to_next (db_subdirs);\r
+-    }\r
+-\r
+-    if (! interrupted) {\r
+-      status = notmuch_directory_set_mtime (directory, fs_mtime);\r
+-      if (status && ret == NOTMUCH_STATUS_SUCCESS)\r
+-          ret = status;\r
+-    }\r
+-\r
+-  DONE:\r
+-    if (next)\r
+-      talloc_free (next);\r
+-    if (entry)\r
+-      free (entry);\r
+-    if (dir)\r
+-      closedir (dir);\r
+-    if (fs_entries)\r
+-      free (fs_entries);\r
+-    if (db_subdirs)\r
+-      notmuch_filenames_destroy (db_subdirs);\r
+-    if (db_files)\r
+-      notmuch_filenames_destroy (db_files);\r
+-    if (directory)\r
+-      notmuch_directory_destroy (directory);\r
+-\r
+-    return ret;\r
++    putchar((state2->output_is_a_tty) ? '\r' : '\n');\r
++    fflush (stdout);\r
+ }\r
\r
+ /* This is the top-level entry point for add_files. It does a couple\r
+  * of error checks, sets up the progress-printing timer and then calls\r
+  * into the recursive function. */\r
+ static notmuch_status_t\r
+-add_files (notmuch_database_t *notmuch,\r
++add_files (notmuch_mailstore_t *mailstore,\r
+          const char *path,\r
+-         add_files_state_t *state)\r
++         notmuch_indexing_context_t *state) /* FIXME: rename */\r
+ {\r
++    struct priv_ctx *state2 = state->priv;\r
+     notmuch_status_t status;\r
+     struct sigaction action;\r
+     struct itimerval timerval;\r
+     notmuch_bool_t timer_is_active = FALSE;\r
+     struct stat st;\r
\r
+-    if (state->output_is_a_tty && ! debugger_is_active () && ! state->verbose) {\r
++    state->print_progress = 0;\r
++    state->print_progress_cb = add_files_print_progress;\r
++    state->print_verbose_cb = add_files_print_verbose;\r
++\r
++    if (state2->output_is_a_tty && ! debugger_is_active () && ! state->verbose) {\r
+       /* Setup our handler for SIGALRM */\r
+       memset (&action, 0, sizeof (struct sigaction));\r
+       action.sa_handler = handle_sigalrm;\r
+@@ -546,7 +140,7 @@ add_files (notmuch_database_t *notmuch,\r
+       return NOTMUCH_STATUS_FILE_ERROR;\r
+     }\r
\r
+-    status = add_files_recursive (notmuch, path, state);\r
++    status = notmuch_mailstore_index_new (mailstore, path, state);\r
\r
+     if (timer_is_active) {\r
+       /* Now stop the timer. */\r
+@@ -564,80 +158,12 @@ add_files (notmuch_database_t *notmuch,\r
+     return status;\r
+ }\r
\r
+-/* XXX: This should be merged with the add_files function since it\r
+- * shares a lot of logic with it. */\r
+-/* Recursively count all regular files in path and all sub-directories\r
+- * of path.  The result is added to *count (which should be\r
+- * initialized to zero by the top-level caller before calling\r
+- * count_files). */\r
+-static void\r
+-count_files (const char *path, int *count)\r
+-{\r
+-    struct dirent *entry = NULL;\r
+-    char *next;\r
+-    struct stat st;\r
+-    struct dirent **fs_entries = NULL;\r
+-    int num_fs_entries = scandir (path, &fs_entries, 0, dirent_sort_inode);\r
+-    int i = 0;\r
+-\r
+-    if (num_fs_entries == -1) {\r
+-      fprintf (stderr, "Warning: failed to open directory %s: %s\n",\r
+-               path, strerror (errno));\r
+-      goto DONE;\r
+-    }\r
+-\r
+-    while (!interrupted) {\r
+-        if (i == num_fs_entries)\r
+-          break;\r
+-\r
+-        entry = fs_entries[i++];\r
+-\r
+-      /* Ignore special directories to avoid infinite recursion.\r
+-       * Also ignore the .notmuch directory.\r
+-       */\r
+-      /* XXX: Eventually we'll want more sophistication to let the\r
+-       * user specify files to be ignored. */\r
+-      if (strcmp (entry->d_name, ".") == 0 ||\r
+-          strcmp (entry->d_name, "..") == 0 ||\r
+-          strcmp (entry->d_name, ".notmuch") == 0)\r
+-      {\r
+-          continue;\r
+-      }\r
+-\r
+-      if (asprintf (&next, "%s/%s", path, entry->d_name) == -1) {\r
+-          next = NULL;\r
+-          fprintf (stderr, "Error descending from %s to %s: Out of memory\n",\r
+-                   path, entry->d_name);\r
+-          continue;\r
+-      }\r
+-\r
+-      stat (next, &st);\r
+-\r
+-      if (S_ISREG (st.st_mode)) {\r
+-          *count = *count + 1;\r
+-          if (*count % 1000 == 0) {\r
+-              printf ("Found %d files so far.\r", *count);\r
+-              fflush (stdout);\r
+-          }\r
+-      } else if (S_ISDIR (st.st_mode)) {\r
+-          count_files (next, count);\r
+-      }\r
+-\r
+-      free (next);\r
+-    }\r
+-\r
+-  DONE:\r
+-    if (entry)\r
+-      free (entry);\r
+-    if (fs_entries)\r
+-        free (fs_entries);\r
+-}\r
+-\r
+ static void\r
+ upgrade_print_progress (void *closure,\r
+                       double progress)\r
+ {\r
+-    add_files_state_t *state = closure;\r
++    notmuch_indexing_context_t *state = closure;\r
++    struct priv_ctx *state2 = state->priv;\r
\r
+     printf ("Upgrading database: %.2f%% complete", progress * 100.0);\r
\r
+@@ -647,7 +173,7 @@ upgrade_print_progress (void *closure,\r
\r
+       gettimeofday (&tv_now, NULL);\r
\r
+-      elapsed = notmuch_time_elapsed (state->tv_start, tv_now);\r
++      elapsed = notmuch_time_elapsed (state2->tv_start, tv_now);\r
+       time_remaining = (elapsed / progress) * (1.0 - progress);\r
+       printf (" (");\r
+       notmuch_time_print_formatted_seconds (time_remaining);\r
+@@ -659,55 +185,13 @@ upgrade_print_progress (void *closure,\r
+     fflush (stdout);\r
+ }\r
\r
+-/* Recursively remove all filenames from the database referring to\r
+- * 'path' (or to any of its children). */\r
+-static void\r
+-_remove_directory (void *ctx,\r
+-                 notmuch_database_t *notmuch,\r
+-                 const char *path,\r
+-                 int *renamed_files,\r
+-                 int *removed_files)\r
+-{\r
+-    notmuch_directory_t *directory;\r
+-    notmuch_filenames_t *files, *subdirs;\r
+-    notmuch_status_t status;\r
+-    char *absolute;\r
+-\r
+-    directory = notmuch_database_get_directory (notmuch, path);\r
+-\r
+-    for (files = notmuch_directory_get_child_files (directory);\r
+-       notmuch_filenames_valid (files);\r
+-       notmuch_filenames_move_to_next (files))\r
+-    {\r
+-      absolute = talloc_asprintf (ctx, "%s/%s", path,\r
+-                                  notmuch_filenames_get (files));\r
+-      status = notmuch_database_remove_message (notmuch, absolute);\r
+-      if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID)\r
+-          *renamed_files = *renamed_files + 1;\r
+-      else\r
+-          *removed_files = *removed_files + 1;\r
+-      talloc_free (absolute);\r
+-    }\r
+-\r
+-    for (subdirs = notmuch_directory_get_child_directories (directory);\r
+-       notmuch_filenames_valid (subdirs);\r
+-       notmuch_filenames_move_to_next (subdirs))\r
+-    {\r
+-      absolute = talloc_asprintf (ctx, "%s/%s", path,\r
+-                                  notmuch_filenames_get (subdirs));\r
+-      _remove_directory (ctx, notmuch, absolute, renamed_files, removed_files);\r
+-      talloc_free (absolute);\r
+-    }\r
+-\r
+-    notmuch_directory_destroy (directory);\r
+-}\r
+-\r
+ int\r
+ notmuch_new_command (void *ctx, int argc, char *argv[])\r
+ {\r
+     notmuch_config_t *config;\r
+     notmuch_database_t *notmuch;\r
+-    add_files_state_t add_files_state;\r
++    struct priv_ctx state2;\r
++    notmuch_indexing_context_t add_files_state; /* FIXME: Rename */\r
+     double elapsed;\r
+     struct timeval tv_now;\r
+     int ret = 0;\r
+@@ -715,13 +199,14 @@ notmuch_new_command (void *ctx, int argc, char *argv[])\r
+     const char *db_path;\r
+     char *dot_notmuch_path;\r
+     struct sigaction action;\r
+-    _filename_node_t *f;\r
+-    int renamed_files, removed_files;\r
+-    notmuch_status_t status;\r
+     int i;\r
++    notmuch_mailstore_t *mailstore;\r
++\r
++    indexing_ctx = &add_files_state;\r
\r
+     add_files_state.verbose = 0;\r
+-    add_files_state.output_is_a_tty = isatty (fileno (stdout));\r
++    add_files_state.priv = &state2;\r
++    state2.output_is_a_tty = isatty (fileno (stdout));\r
\r
+     for (i = 0; i < argc && argv[i][0] == '-'; i++) {\r
+       if (STRNCMP_LITERAL (argv[i], "--verbose") == 0) {\r
+@@ -737,6 +222,7 @@ notmuch_new_command (void *ctx, int argc, char *argv[])\r
+       return 1;\r
\r
+     db_path = notmuch_config_get_database_path (config);\r
++    mailstore = notmuch_config_get_mailstore (config);\r
\r
+     dot_notmuch_path = talloc_asprintf (ctx, "%s/%s", db_path, ".notmuch");\r
\r
+@@ -744,7 +230,7 @@ notmuch_new_command (void *ctx, int argc, char *argv[])\r
+       int count;\r
\r
+       count = 0;\r
+-      count_files (db_path, &count);\r
++      notmuch_mailstore_count_files (mailstore, db_path, &count, &interrupted);\r
+       if (interrupted)\r
+           return 1;\r
\r
+@@ -761,7 +247,7 @@ notmuch_new_command (void *ctx, int argc, char *argv[])\r
\r
+       if (notmuch_database_needs_upgrade (notmuch)) {\r
+           printf ("Welcome to a new version of notmuch! Your database will now be upgraded.\n");\r
+-          gettimeofday (&add_files_state.tv_start, NULL);\r
++          gettimeofday (&state2.tv_start, NULL);\r
+           notmuch_database_upgrade (notmuch, upgrade_print_progress,\r
+                                     &add_files_state);\r
+           printf ("Your notmuch database has now been upgraded to database format version %u.\n",\r
+@@ -777,6 +263,7 @@ notmuch_new_command (void *ctx, int argc, char *argv[])\r
+     /* Setup our handler for SIGINT. We do this after having\r
+      * potentially done a database upgrade we this interrupt handler\r
+      * won't support. */\r
++    add_files_state.interrupted = 0;\r
+     memset (&action, 0, sizeof (struct sigaction));\r
+     action.sa_handler = handle_sigint;\r
+     sigemptyset (&action.sa_mask);\r
+@@ -788,33 +275,12 @@ notmuch_new_command (void *ctx, int argc, char *argv[])\r
\r
+     add_files_state.processed_files = 0;\r
+     add_files_state.added_messages = 0;\r
+-    gettimeofday (&add_files_state.tv_start, NULL);\r
+-\r
+-    add_files_state.removed_files = _filename_list_create (ctx);\r
+-    add_files_state.removed_directories = _filename_list_create (ctx);\r
+-\r
+-    ret = add_files (notmuch, db_path, &add_files_state);\r
+-\r
+-    removed_files = 0;\r
+-    renamed_files = 0;\r
+-    for (f = add_files_state.removed_files->head; f; f = f->next) {\r
+-      status = notmuch_database_remove_message (notmuch, f->filename);\r
+-      if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID)\r
+-          renamed_files++;\r
+-      else\r
+-          removed_files++;\r
+-    }\r
+-\r
+-    for (f = add_files_state.removed_directories->head; f; f = f->next) {\r
+-      _remove_directory (ctx, notmuch, f->filename,\r
+-                         &renamed_files, &removed_files);\r
+-    }\r
++    gettimeofday (&state2.tv_start, NULL);\r
\r
+-    talloc_free (add_files_state.removed_files);\r
+-    talloc_free (add_files_state.removed_directories);\r
++    ret = add_files (mailstore, db_path, &add_files_state);\r
\r
+     gettimeofday (&tv_now, NULL);\r
+-    elapsed = notmuch_time_elapsed (add_files_state.tv_start,\r
++    elapsed = notmuch_time_elapsed (state2.tv_start,\r
+                                   tv_now);\r
\r
+     if (add_files_state.processed_files) {\r
+@@ -839,16 +305,16 @@ notmuch_new_command (void *ctx, int argc, char *argv[])\r
+       printf ("No new mail.");\r
+     }\r
\r
+-    if (removed_files) {\r
++    if (add_files_state.removed_files) {\r
+       printf (" Removed %d %s.",\r
+-              removed_files,\r
+-              removed_files == 1 ? "message" : "messages");\r
++              add_files_state.removed_files,\r
++              add_files_state.removed_files == 1 ? "message" : "messages");\r
+     }\r
\r
+-    if (renamed_files) {\r
++    if (add_files_state.renamed_files) {\r
+       printf (" Detected %d file %s.",\r
+-              renamed_files,\r
+-              renamed_files == 1 ? "rename" : "renames");\r
++              add_files_state.renamed_files,\r
++              add_files_state.renamed_files == 1 ? "rename" : "renames");\r
+     }\r
\r
+     printf ("\n");\r
+-- \r
+1.7.0.2\r
+\r