[PATCH 2/4] Conversion to mailstore abstraction
authorMichal Sojka <sojkam1@fel.cvut.cz>
Thu, 8 Apr 2010 14:42:44 +0000 (16:42 +0200)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:36:32 +0000 (09:36 -0800)
cb/df8c76b2c6a34125cd2a3798081cced38d575d [new file with mode: 0644]

diff --git a/cb/df8c76b2c6a34125cd2a3798081cced38d575d b/cb/df8c76b2c6a34125cd2a3798081cced38d575d
new file mode 100644 (file)
index 0000000..cb36517
--- /dev/null
@@ -0,0 +1,1549 @@
+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 D128D414BBD\r
+       for <notmuch@notmuchmail.org>; Thu,  8 Apr 2010 07:43:21 -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 IkQxQXRFH36F for <notmuch@notmuchmail.org>;\r
+       Thu,  8 Apr 2010 07:43:12 -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 2DFD04196F2\r
+       for <notmuch@notmuchmail.org>; Thu,  8 Apr 2010 07:43:08 -0700 (PDT)\r
+Received: from localhost (unknown [192.168.200.4])\r
+       by max.feld.cvut.cz (Postfix) with ESMTP id 9222219F3411;\r
+       Thu,  8 Apr 2010 16:43:07 +0200 (CEST)\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 0oznKvEf7Niy; Thu,  8 Apr 2010 16:43:04 +0200 (CEST)\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 CB34119F33F6;\r
+       Thu,  8 Apr 2010 16:43:04 +0200 (CEST)\r
+Received: from steelpick.2x.cz (k335-30.felk.cvut.cz [147.32.86.30])\r
+       (Authenticated sender: sojkam1)\r
+       by imap.feld.cvut.cz (Postfix) with ESMTPSA id C696215C062;\r
+       Thu,  8 Apr 2010 16:43:04 +0200 (CEST)\r
+Received: from wsh by steelpick.2x.cz with local (Exim 4.71)\r
+       (envelope-from <sojkam1@fel.cvut.cz>)\r
+       id 1Nzswi-0007cF-Im; Thu, 08 Apr 2010 16:43:04 +0200\r
+From: Michal Sojka <sojkam1@fel.cvut.cz>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH 2/4] Conversion to mailstore abstraction\r
+Date: Thu,  8 Apr 2010 16:42:44 +0200\r
+Message-Id: <1270737766-29237-3-git-send-email-sojkam1@fel.cvut.cz>\r
+X-Mailer: git-send-email 1.7.0.2\r
+In-Reply-To: <1270737766-29237-1-git-send-email-sojkam1@fel.cvut.cz>\r
+References: <1270737766-29237-1-git-send-email-sojkam1@fel.cvut.cz>\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: Thu, 08 Apr 2010 14:43:22 -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/Makefile.local                     |    1 +\r
+ notmuch-new.c => lib/mailstore-files.c |  406 ++++----------------\r
+ lib/mailstore.c                        |    5 -\r
+ notmuch-new.c                          |  660 +++-----------------------------\r
+ 4 files changed, 141 insertions(+), 931 deletions(-)\r
+ copy notmuch-new.c => lib/mailstore-files.c (61%)\r
+\r
+diff --git a/lib/Makefile.local b/lib/Makefile.local\r
+index 6243af1..863df4c 100644\r
+--- a/lib/Makefile.local\r
++++ b/lib/Makefile.local\r
+@@ -32,6 +32,7 @@ extra_cflags += -I$(dir) -fPIC\r
+ libnotmuch_c_srcs =           \\r
+       $(dir)/libsha1.c        \\r
+       $(dir)/mailstore.c      \\r
++      $(dir)/mailstore-files.c\\r
+       $(dir)/message-file.c   \\r
+       $(dir)/messages.c       \\r
+       $(dir)/sha1.c           \\r
+diff --git a/notmuch-new.c b/lib/mailstore-files.c\r
+similarity index 61%\r
+copy from notmuch-new.c\r
+copy to lib/mailstore-files.c\r
+index 2d0ba6c..8b5e1d5 100644\r
+--- a/notmuch-new.c\r
++++ b/lib/mailstore-files.c\r
+@@ -1,4 +1,5 @@\r
+-/* notmuch - Not much of an email program, (just index and search)\r
++/* mailstore-files.c - Original notmuch mail store - a collection of\r
++ * plain-text email messages (one message per file).\r
+  *\r
+  * Copyright © 2009 Carl Worth\r
+  *\r
+@@ -15,12 +16,14 @@\r
+  * You should have received a copy of the GNU General Public License\r
+  * along with this program.  If not, see http://www.gnu.org/licenses/ .\r
+  *\r
+- * Author: Carl Worth <cworth@cworth.org>\r
++ * Authors: Carl Worth <cworth@cworth.org>\r
++ *        Michal Sojka <sojkam1@fel.cvut.cz>\r
+  */\r
\r
+-#include "notmuch-client.h"\r
+-\r
+-#include <unistd.h>\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
+@@ -32,38 +35,10 @@ typedef struct _filename_list {\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
++typedef struct _indexing_context_priv {\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
+-\r
+-static void\r
+-handle_sigalrm (unused (int signal))\r
+-{\r
+-    do_add_files_print_progress = 1;\r
+-}\r
+-\r
+-static volatile sig_atomic_t interrupted;\r
+-\r
+-static void\r
+-handle_sigint (unused (int sig))\r
+-{\r
+-    ssize_t ignored;\r
+-    static char msg[] = "Stopping...         \n";\r
+-\r
+-    ignored = write(2, msg, sizeof(msg)-1);\r
+-    interrupted = 1;\r
+-}\r
++} _indexing_context_priv_t;\r
\r
+ static _filename_list_t *\r
+ _filename_list_create (const void *ctx)\r
+@@ -100,34 +75,6 @@ tag_inbox_and_unread (notmuch_message_t *message)\r
+     notmuch_message_add_tag (message, "unread");\r
+ }\r
\r
+-static void\r
+-add_files_print_progress (add_files_state_t *state)\r
+-{\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
+-    rate_overall = (state->processed_files) / elapsed_overall;\r
+-\r
+-    printf ("Processed %d", state->processed_files);\r
+-\r
+-    if (state->total_files) {\r
+-      double time_remaining;\r
+-\r
+-      time_remaining = ((state->total_files - state->processed_files) /\r
+-                        rate_overall);\r
+-      printf (" of %d files (", state->total_files);\r
+-      notmuch_time_print_formatted_seconds (time_remaining);\r
+-      printf (" remaining).      \r");\r
+-    } else {\r
+-      printf (" files (%d files/sec.)    \r", (int) rate_overall);\r
+-    }\r
+-\r
+-    fflush (stdout);\r
+-}\r
+-\r
+ static int\r
+ dirent_sort_inode (const struct dirent **a, const struct dirent **b)\r
+ {\r
+@@ -169,6 +116,7 @@ _entries_resemble_maildir (struct dirent **entries, int count)\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
+@@ -205,9 +153,9 @@ _entries_resemble_maildir (struct dirent **entries, int count)\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
++add_files_recursive (notmuch_mailstore_t *mailstore,\r
+                    const char *path,\r
+-                   add_files_state_t *state)\r
++                   notmuch_indexing_context_t *state)\r
+ {\r
+     DIR *dir = NULL;\r
+     struct dirent *entry = NULL;\r
+@@ -222,6 +170,8 @@ add_files_recursive (notmuch_database_t *notmuch,\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
+@@ -268,7 +218,7 @@ add_files_recursive (notmuch_database_t *notmuch,\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
++      if (state->interrupted)\r
+           break;\r
\r
+       entry = fs_entries[i];\r
+@@ -302,7 +252,7 @@ add_files_recursive (notmuch_database_t *notmuch,\r
+       }\r
\r
+       next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);\r
+-      status = add_files_recursive (notmuch, next, state);\r
++      status = add_files_recursive (mailstore, next, state);\r
+       if (status && ret == NOTMUCH_STATUS_SUCCESS)\r
+           ret = status;\r
+       talloc_free (next);\r
+@@ -317,7 +267,7 @@ add_files_recursive (notmuch_database_t *notmuch,\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
++      if (state->interrupted)\r
+           break;\r
\r
+         entry = fs_entries[i];\r
+@@ -327,11 +277,11 @@ add_files_recursive (notmuch_database_t *notmuch,\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
++          char *absolute = talloc_asprintf (priv->removed_files,\r
+                                             "%s/%s", path,\r
+                                             notmuch_filenames_get (db_files));\r
\r
+-          _filename_list_add (state->removed_files, absolute);\r
++          _filename_list_add (priv->removed_files, absolute);\r
\r
+           notmuch_filenames_move_to_next (db_files);\r
+       }\r
+@@ -343,10 +293,10 @@ add_files_recursive (notmuch_database_t *notmuch,\r
\r
+           if (strcmp (filename, entry->d_name) < 0)\r
+           {\r
+-              char *absolute = talloc_asprintf (state->removed_directories,\r
++              char *absolute = talloc_asprintf (priv->removed_directories,\r
+                                                 "%s/%s", path, filename);\r
\r
+-              _filename_list_add (state->removed_directories, absolute);\r
++              _filename_list_add (priv->removed_directories, absolute);\r
+           }\r
\r
+           notmuch_filenames_move_to_next (db_subdirs);\r
+@@ -394,18 +344,8 @@ add_files_recursive (notmuch_database_t *notmuch,\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
++      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
+@@ -445,46 +385,50 @@ add_files_recursive (notmuch_database_t *notmuch,\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
++      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 (state->removed_files,\r
++      char *absolute = talloc_asprintf (priv->removed_files,\r
+                                         "%s/%s", path,\r
+                                         notmuch_filenames_get (db_files));\r
\r
+-      _filename_list_add (state->removed_files, absolute);\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 (state->removed_directories,\r
++      char *absolute = talloc_asprintf (priv->removed_directories,\r
+                                         "%s/%s", path,\r
+                                         notmuch_filenames_get (db_subdirs));\r
\r
+-      _filename_list_add (state->removed_directories, absolute);\r
++      _filename_list_add (priv->removed_directories, absolute);\r
\r
+       notmuch_filenames_move_to_next (db_subdirs);\r
+     }\r
\r
+-    if (! interrupted) {\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
++DONE:\r
+     if (next)\r
+       talloc_free (next);\r
+     if (entry)\r
+@@ -503,67 +447,6 @@ add_files_recursive (notmuch_database_t *notmuch,\r
+     return ret;\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
+-         const char *path,\r
+-         add_files_state_t *state)\r
+-{\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
+-      /* Setup our handler for SIGALRM */\r
+-      memset (&action, 0, sizeof (struct sigaction));\r
+-      action.sa_handler = handle_sigalrm;\r
+-      sigemptyset (&action.sa_mask);\r
+-      action.sa_flags = SA_RESTART;\r
+-      sigaction (SIGALRM, &action, NULL);\r
+-\r
+-      /* Then start a timer to send SIGALRM once per second. */\r
+-      timerval.it_interval.tv_sec = 1;\r
+-      timerval.it_interval.tv_usec = 0;\r
+-      timerval.it_value.tv_sec = 1;\r
+-      timerval.it_value.tv_usec = 0;\r
+-      setitimer (ITIMER_REAL, &timerval, NULL);\r
+-\r
+-      timer_is_active = TRUE;\r
+-    }\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
+-    if (! S_ISDIR (st.st_mode)) {\r
+-      fprintf (stderr, "Error: %s is not a directory.\n", path);\r
+-      return NOTMUCH_STATUS_FILE_ERROR;\r
+-    }\r
+-\r
+-    status = add_files_recursive (notmuch, path, state);\r
+-\r
+-    if (timer_is_active) {\r
+-      /* Now stop the timer. */\r
+-      timerval.it_interval.tv_sec = 0;\r
+-      timerval.it_interval.tv_usec = 0;\r
+-      timerval.it_value.tv_sec = 0;\r
+-      timerval.it_value.tv_usec = 0;\r
+-      setitimer (ITIMER_REAL, &timerval, NULL);\r
+-\r
+-      /* And disable the signal handler. */\r
+-      action.sa_handler = SIG_IGN;\r
+-      sigaction (SIGALRM, &action, NULL);\r
+-    }\r
+-\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
+@@ -571,7 +454,9 @@ add_files (notmuch_database_t *notmuch,\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
++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
+@@ -580,13 +465,14 @@ count_files (const char *path, int *count)\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
++    while (!*interrupted) {\r
+         if (i == num_fs_entries)\r
+           break;\r
\r
+@@ -620,45 +506,19 @@ count_files (const char *path, int *count)\r
+               fflush (stdout);\r
+           }\r
+       } else if (S_ISDIR (st.st_mode)) {\r
+-          count_files (next, count);\r
++          count_files (mailstore, next, count, interrupted);\r
+       }\r
\r
+       free (next);\r
+     }\r
\r
+-  DONE:\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
+-\r
+-    printf ("Upgrading database: %.2f%% complete", progress * 100.0);\r
+-\r
+-    if (progress > 0) {\r
+-      struct timeval tv_now;\r
+-      double elapsed, time_remaining;\r
+-\r
+-      gettimeofday (&tv_now, NULL);\r
+-\r
+-      elapsed = notmuch_time_elapsed (state->tv_start, tv_now);\r
+-      time_remaining = (elapsed / progress) * (1.0 - progress);\r
+-      printf (" (");\r
+-      notmuch_time_print_formatted_seconds (time_remaining);\r
+-      printf (" remaining)");\r
+-    }\r
+-\r
+-    printf (".      \r");\r
+-\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
+@@ -702,163 +562,51 @@ _remove_directory (void *ctx,\r
+     notmuch_directory_destroy (directory);\r
+ }\r
\r
+-int\r
+-notmuch_new_command (void *ctx, int argc, char *argv[])\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
+-    notmuch_config_t *config;\r
+-    notmuch_database_t *notmuch;\r
+-    add_files_state_t add_files_state;\r
+-    double elapsed;\r
+-    struct timeval tv_now;\r
+-    int ret = 0;\r
+-    struct stat st;\r
+-    const char *db_path;\r
+-    char *dot_notmuch_path;\r
+-    struct sigaction action;\r
++    _indexing_context_priv_t *priv;\r
+     _filename_node_t *f;\r
+-    int renamed_files, removed_files;\r
+-    notmuch_status_t status;\r
+-    int i;\r
+-\r
+-    add_files_state.verbose = 0;\r
+-    add_files_state.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
+-          add_files_state.verbose = 1;\r
+-      } else {\r
+-          fprintf (stderr, "Unrecognized option: %s\n", argv[i]);\r
+-          return 1;\r
+-      }\r
+-    }\r
+-\r
+-    config = notmuch_config_open (ctx, NULL, NULL);\r
+-    if (config == NULL)\r
+-      return 1;\r
++    notmuch_status_t status, ret;\r
++    notmuch_database_t *notmuch = mailstore->notmuch;\r
\r
+-    db_path = notmuch_config_get_database_path (config);\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
+-    dot_notmuch_path = talloc_asprintf (ctx, "%s/%s", db_path, ".notmuch");\r
++    priv->removed_files = _filename_list_create (priv);\r
++    priv->removed_directories = _filename_list_create (priv);\r
\r
+-    if (stat (dot_notmuch_path, &st)) {\r
+-      int count;\r
++    ret = add_files_recursive(mailstore, path, indexing_ctx);\r
\r
+-      count = 0;\r
+-      count_files (db_path, &count);\r
+-      if (interrupted)\r
+-          return 1;\r
+-\r
+-      printf ("Found %d total files (that's not much mail).\n", count);\r
+-      notmuch = notmuch_database_create (db_path,\r
+-                                         notmuch_config_get_mailstore (config));\r
+-      add_files_state.total_files = count;\r
+-    } else {\r
+-      notmuch = notmuch_database_open (db_path,\r
+-                                       NOTMUCH_DATABASE_MODE_READ_WRITE,\r
+-                                       notmuch_config_get_mailstore (config));\r
+-      if (notmuch == NULL)\r
+-          return 1;\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
+-          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
+-                  notmuch_database_get_version (notmuch));\r
+-      }\r
+-\r
+-      add_files_state.total_files = 0;\r
+-    }\r
+-\r
+-    if (notmuch == NULL)\r
+-      return 1;\r
+-\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
+-    memset (&action, 0, sizeof (struct sigaction));\r
+-    action.sa_handler = handle_sigint;\r
+-    sigemptyset (&action.sa_mask);\r
+-    action.sa_flags = SA_RESTART;\r
+-    sigaction (SIGINT, &action, NULL);\r
+-\r
+-    talloc_free (dot_notmuch_path);\r
+-    dot_notmuch_path = NULL;\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
++    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
+-          renamed_files++;\r
++          indexing_ctx->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
+-\r
+-    talloc_free (add_files_state.removed_files);\r
+-    talloc_free (add_files_state.removed_directories);\r
+-\r
+-    gettimeofday (&tv_now, NULL);\r
+-    elapsed = notmuch_time_elapsed (add_files_state.tv_start,\r
+-                                  tv_now);\r
+-\r
+-    if (add_files_state.processed_files) {\r
+-      printf ("Processed %d %s in ", add_files_state.processed_files,\r
+-              add_files_state.processed_files == 1 ?\r
+-              "file" : "total files");\r
+-      notmuch_time_print_formatted_seconds (elapsed);\r
+-      if (elapsed > 1) {\r
+-          printf (" (%d files/sec.).                 \n",\r
+-                  (int) (add_files_state.processed_files / elapsed));\r
+-      } else {\r
+-          printf (".                    \n");\r
+-      }\r
+-    }\r
+-\r
+-    if (add_files_state.added_messages) {\r
+-      printf ("Added %d new %s to the database.",\r
+-              add_files_state.added_messages,\r
+-              add_files_state.added_messages == 1 ?\r
+-              "message" : "messages");\r
+-    } else {\r
+-      printf ("No new mail.");\r
+-    }\r
+-\r
+-    if (removed_files) {\r
+-      printf (" Removed %d %s.",\r
+-              removed_files,\r
+-              removed_files == 1 ? "message" : "messages");\r
+-    }\r
+-\r
+-    if (renamed_files) {\r
+-      printf (" Detected %d file %s.",\r
+-              renamed_files,\r
+-              renamed_files == 1 ? "rename" : "renames");\r
++          indexing_ctx->removed_files++;\r
+     }\r
\r
+-    printf ("\n");\r
+-\r
+-    if (ret) {\r
+-      printf ("\nNote: At least one error was encountered: %s\n",\r
+-              notmuch_status_to_string (ret));\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
+-    notmuch_database_close (notmuch);\r
++    talloc_free(priv);\r
\r
+-    return ret || interrupted;\r
++    return ret;\r
+ }\r
++\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,                /* We cannot store tags in this mailstore. */\r
++    .open_file = NULL,                /* Currently not implemented */\r
++};\r
+diff --git a/lib/mailstore.c b/lib/mailstore.c\r
+index eb27952..709db72 100644\r
+--- a/lib/mailstore.c\r
++++ b/lib/mailstore.c\r
+@@ -23,11 +23,6 @@\r
\r
+ #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))\r
\r
+-/* Original notmuch mail store */\r
+-struct _notmuch_mailstore mailstore_files = {\r
+-    .type = "files",\r
+-};\r
+-\r
+ static notmuch_mailstore_t *available[] = {\r
+     &mailstore_files,\r
+ };\r
+diff --git a/notmuch-new.c b/notmuch-new.c\r
+index 2d0ba6c..6c527e9 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,63 +39,35 @@ handle_sigint (unused (int sig))\r
+     static char msg[] = "Stopping...         \n";\r
\r
+     ignored = write(2, msg, sizeof(msg)-1);\r
+-    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
++    indexing_ctx->interrupted = 1;\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 print_ctx {\r
++    int total_files;\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 print_ctx *print_ctx = state->print_ctx;\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 (print_ctx->tv_start, tv_now);\r
+     rate_overall = (state->processed_files) / elapsed_overall;\r
\r
+     printf ("Processed %d", state->processed_files);\r
\r
+-    if (state->total_files) {\r
++    if (print_ctx->total_files) {\r
+       double time_remaining;\r
\r
+-      time_remaining = ((state->total_files - state->processed_files) /\r
++      time_remaining = ((print_ctx->total_files - state->processed_files) /\r
+                         rate_overall);\r
+-      printf (" of %d files (", state->total_files);\r
++      printf (" of %d files (", print_ctx->total_files);\r
+       notmuch_time_print_formatted_seconds (time_remaining);\r
+       printf (" remaining).      \r");\r
+     } else {\r
+@@ -128,396 +77,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
++    struct print_ctx *print_ctx = state->print_ctx;\r
++    if (print_ctx->output_is_a_tty)\r
++      printf("\r\033[K");\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
++    printf ("%i/%i: %s",\r
++          state->processed_files,\r
++          print_ctx->total_files,\r
++          filename);\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
+-\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 (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((print_ctx->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 print_ctx *print_ctx = state->print_ctx;\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 (print_ctx->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 +141,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 +159,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 print_ctx *print_ctx = state->print_ctx;\r
\r
+     printf ("Upgrading database: %.2f%% complete", progress * 100.0);\r
\r
+@@ -647,7 +174,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 (print_ctx->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 +186,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 print_ctx print_ctx;\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 +200,15 @@ 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
++    memset (&add_files_state, 0, sizeof(add_files_state));\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.print_ctx = &print_ctx;\r
++    print_ctx.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 +224,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,31 +232,29 @@ 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
+       printf ("Found %d total files (that's not much mail).\n", count);\r
+-      notmuch = notmuch_database_create (db_path,\r
+-                                         notmuch_config_get_mailstore (config));\r
+-      add_files_state.total_files = count;\r
++      notmuch = notmuch_database_create (db_path, mailstore);\r
++      print_ctx.total_files = count;\r
+     } else {\r
+-      notmuch = notmuch_database_open (db_path,\r
+-                                       NOTMUCH_DATABASE_MODE_READ_WRITE,\r
+-                                       notmuch_config_get_mailstore (config));\r
++      notmuch = notmuch_database_open (db_path, NOTMUCH_DATABASE_MODE_READ_WRITE,\r
++                                       mailstore);\r
+       if (notmuch == NULL)\r
+           return 1;\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 (&print_ctx.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
+                   notmuch_database_get_version (notmuch));\r
+       }\r
\r
+-      add_files_state.total_files = 0;\r
++      print_ctx.total_files = 0;\r
+     }\r
\r
+     if (notmuch == NULL)\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 (&print_ctx.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 (print_ctx.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