return FALSE;
}
+/* Add a single file to the database. */
+static notmuch_status_t
+add_file (notmuch_database_t *notmuch, const char *filename,
+ add_files_state_t *state)
+{
+ notmuch_message_t *message = NULL;
+ const char **tag;
+ notmuch_status_t status;
+
+ status = notmuch_database_begin_atomic (notmuch);
+ if (status)
+ goto DONE;
+
+ status = notmuch_database_add_message (notmuch, filename, &message);
+ switch (status) {
+ /* Success. */
+ case NOTMUCH_STATUS_SUCCESS:
+ state->added_messages++;
+ notmuch_message_freeze (message);
+ for (tag = state->new_tags; *tag != NULL; tag++)
+ notmuch_message_add_tag (message, *tag);
+ if (state->synchronize_flags)
+ notmuch_message_maildir_flags_to_tags (message);
+ notmuch_message_thaw (message);
+ break;
+ /* Non-fatal issues (go on to next file). */
+ case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID:
+ if (state->synchronize_flags)
+ notmuch_message_maildir_flags_to_tags (message);
+ break;
+ case NOTMUCH_STATUS_FILE_NOT_EMAIL:
+ fprintf (stderr, "Note: Ignoring non-mail file: %s\n", filename);
+ break;
+ /* Fatal issues. Don't process anymore. */
+ case NOTMUCH_STATUS_READ_ONLY_DATABASE:
+ case NOTMUCH_STATUS_XAPIAN_EXCEPTION:
+ case NOTMUCH_STATUS_OUT_OF_MEMORY:
+ fprintf (stderr, "Error: %s. Halting processing.\n",
+ notmuch_status_to_string (status));
+ goto DONE;
+ default:
+ INTERNAL_ERROR ("add_message returned unexpected value: %d", status);
+ goto DONE;
+ }
+
+ status = notmuch_database_end_atomic (notmuch);
+
+ DONE:
+ if (message)
+ notmuch_message_destroy (message);
+
+ return status;
+}
+
/* Examine 'path' recursively as follows:
*
* o Ask the filesystem for the mtime of 'path' (fs_mtime)
char *next = NULL;
time_t fs_mtime, db_mtime;
notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS;
- notmuch_message_t *message = NULL;
struct dirent **fs_entries = NULL;
int i, num_fs_entries = 0, entry_type;
notmuch_directory_t *directory;
time_t stat_time;
struct stat st;
notmuch_bool_t is_maildir;
- const char **tag;
if (stat (path, &st)) {
fprintf (stderr, "Error reading directory %s: %s\n",
}
db_mtime = directory ? notmuch_directory_get_mtime (directory) : 0;
+ /* If the directory is unchanged from our last scan and has no
+ * sub-directories, then return without scanning it at all. In
+ * some situations, skipping the scan can substantially reduce the
+ * cost of notmuch new, especially since the huge numbers of files
+ * in Maildirs make scans expensive, but all files live in leaf
+ * directories.
+ *
+ * To check for sub-directories, we borrow a trick from find,
+ * kpathsea, and many other UNIX tools: since a directory's link
+ * count is the number of sub-directories (specifically, their
+ * '..' entries) plus 2 (the link from the parent and the link for
+ * '.'). This check is safe even on weird file systems, since
+ * file systems that can't compute this will return 0 or 1. This
+ * is safe even on *really* weird file systems like HFS+ that
+ * mistakenly return the total number of directory entries, since
+ * that only inflates the count beyond 2.
+ */
+ if (directory && fs_mtime == db_mtime && st.st_nlink == 2) {
+ /* There's one catch: pass 1 below considers symlinks to
+ * directories to be directories, but these don't increase the
+ * file system link count. So, only bail early if the
+ * database agrees that there are no sub-directories. */
+ db_subdirs = notmuch_directory_get_child_directories (directory);
+ if (!notmuch_filenames_valid (db_subdirs))
+ goto DONE;
+ notmuch_filenames_destroy (db_subdirs);
+ db_subdirs = NULL;
+ }
+
/* If the database knows about this directory, then we sort based
* on strcmp to match the database sorting. Otherwise, we can do
* inode-based sorting for faster filesystem operation. */
fflush (stdout);
}
- status = notmuch_database_begin_atomic (notmuch);
- if (status) {
- ret = status;
- goto DONE;
- }
-
- status = notmuch_database_add_message (notmuch, next, &message);
- switch (status) {
- /* success */
- case NOTMUCH_STATUS_SUCCESS:
- state->added_messages++;
- notmuch_message_freeze (message);
- for (tag=state->new_tags; *tag != NULL; tag++)
- notmuch_message_add_tag (message, *tag);
- if (state->synchronize_flags == TRUE)
- notmuch_message_maildir_flags_to_tags (message);
- notmuch_message_thaw (message);
- break;
- /* Non-fatal issues (go on to next file) */
- case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID:
- if (state->synchronize_flags == TRUE)
- notmuch_message_maildir_flags_to_tags (message);
- break;
- case NOTMUCH_STATUS_FILE_NOT_EMAIL:
- fprintf (stderr, "Note: Ignoring non-mail file: %s\n",
- next);
- break;
- /* Fatal issues. Don't process anymore. */
- case NOTMUCH_STATUS_READ_ONLY_DATABASE:
- case NOTMUCH_STATUS_XAPIAN_EXCEPTION:
- case NOTMUCH_STATUS_OUT_OF_MEMORY:
- fprintf (stderr, "Error: %s. Halting processing.\n",
- notmuch_status_to_string (status));
- ret = status;
- goto DONE;
- default:
- case NOTMUCH_STATUS_FILE_ERROR:
- case NOTMUCH_STATUS_NULL_POINTER:
- case NOTMUCH_STATUS_TAG_TOO_LONG:
- case NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW:
- case NOTMUCH_STATUS_UNBALANCED_ATOMIC:
- case NOTMUCH_STATUS_LAST_STATUS:
- INTERNAL_ERROR ("add_message returned unexpected value: %d", status);
- goto DONE;
- }
-
- status = notmuch_database_end_atomic (notmuch);
+ status = add_file (notmuch, next, state);
if (status) {
ret = status;
goto DONE;
}
- if (message) {
- notmuch_message_destroy (message);
- message = NULL;
- }
-
if (do_print_progress) {
do_print_progress = 0;
generic_print_progress ("Processed", "files", state->tv_start,
{
struct dirent *entry = NULL;
char *next;
- struct stat st;
struct dirent **fs_entries = NULL;
int num_fs_entries = scandir (path, &fs_entries, 0, dirent_sort_inode);
- int i = 0;
+ int entry_type, i;
if (num_fs_entries == -1) {
fprintf (stderr, "Warning: failed to open directory %s: %s\n",
goto DONE;
}
- while (!interrupted) {
- if (i == num_fs_entries)
- break;
-
- entry = fs_entries[i++];
+ for (i = 0; i < num_fs_entries && ! interrupted; i++) {
+ entry = fs_entries[i];
/* Ignore special directories to avoid infinite recursion.
* Also ignore the .notmuch directory and files/directories
strcmp (entry->d_name, ".notmuch") == 0 ||
_entry_in_ignore_list (entry->d_name, state))
{
- if (_entry_in_ignore_list (entry->d_name, state) && state->debug)
+ if (state->debug && _entry_in_ignore_list (entry->d_name, state))
printf ("(D) count_files: explicitly ignoring %s/%s\n",
path,
entry->d_name);
continue;
}
- stat (next, &st);
-
- if (S_ISREG (st.st_mode)) {
+ entry_type = dirent_type (path, entry);
+ if (entry_type == S_IFREG) {
*count = *count + 1;
if (*count % 1000 == 0) {
printf ("Found %d files so far.\r", *count);
fflush (stdout);
}
- } else if (S_ISDIR (st.st_mode)) {
+ } else if (entry_type == S_IFDIR) {
count_files (next, count, state);
}
};
opt_index = parse_arguments (argc, argv, options, 1);
- if (opt_index < 0) {
- /* diagnostics already printed */
- return 1;
- }
+ if (opt_index < 0)
+ return EXIT_FAILURE;
add_files_state.new_tags = notmuch_config_get_new_tags (config, &add_files_state.new_tags_length);
add_files_state.new_ignore = notmuch_config_get_new_ignore (config, &add_files_state.new_ignore_length);
if (!no_hooks) {
ret = notmuch_run_hook (db_path, "pre-new");
if (ret)
- return ret;
+ return EXIT_FAILURE;
}
dot_notmuch_path = talloc_asprintf (config, "%s/%s", db_path, ".notmuch");
count = 0;
count_files (db_path, &count, &add_files_state);
if (interrupted)
- return 1;
+ return EXIT_FAILURE;
printf ("Found %d total files (that's not much mail).\n", count);
if (notmuch_database_create (db_path, ¬much))
- return 1;
+ return EXIT_FAILURE;
add_files_state.total_files = count;
} else {
if (notmuch_database_open (db_path, NOTMUCH_DATABASE_MODE_READ_WRITE,
¬much))
- return 1;
+ return EXIT_FAILURE;
if (notmuch_database_needs_upgrade (notmuch)) {
printf ("Welcome to a new version of notmuch! Your database will now be upgraded.\n");
}
if (notmuch == NULL)
- return 1;
+ return EXIT_FAILURE;
/* Setup our handler for SIGINT. We do this after having
* potentially done a database upgrade we this interrupt handler
if (!no_hooks && !ret && !interrupted)
ret = notmuch_run_hook (db_path, "post-new");
- return ret || interrupted;
+ return ret || interrupted ? EXIT_FAILURE : EXIT_SUCCESS;
}