--- /dev/null
+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