X-Git-Url: https://git.notmuchmail.org/git?a=blobdiff_plain;f=notmuch-new.c;h=1dd8fc95c97ca4ad40b632a08e30eac80e4efbf8;hb=caf5514a3692d6ad6be257a24a5b5a44164bb90a;hp=491541801f5fc5bab63180474c09398a01bf7361;hpb=e76f6517de020783d828be59f461f1d4f465c4b4;p=notmuch diff --git a/notmuch-new.c b/notmuch-new.c index 49154180..1dd8fc95 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -240,6 +240,60 @@ _entry_in_ignore_list (const char *entry, add_files_state_t *state) 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) @@ -291,7 +345,6 @@ add_files (notmuch_database_t *notmuch, 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; @@ -300,7 +353,6 @@ add_files (notmuch_database_t *notmuch, 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", @@ -323,6 +375,35 @@ add_files (notmuch_database_t *notmuch, } 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. */ @@ -498,63 +579,12 @@ add_files (notmuch_database_t *notmuch, 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, @@ -840,9 +870,8 @@ _remove_directory (void *ctx, } int -notmuch_new_command (void *ctx, int argc, char *argv[]) +notmuch_new_command (notmuch_config_t *config, int argc, char *argv[]) { - notmuch_config_t *config; notmuch_database_t *notmuch; add_files_state_t add_files_state; double elapsed; @@ -870,14 +899,8 @@ notmuch_new_command (void *ctx, int argc, char *argv[]) }; opt_index = parse_arguments (argc, argv, options, 1); - if (opt_index < 0) { - /* diagnostics already printed */ - return 1; - } - - config = notmuch_config_open (ctx, NULL, FALSE); - if (config == NULL) - 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); @@ -887,10 +910,10 @@ notmuch_new_command (void *ctx, int argc, char *argv[]) if (!no_hooks) { ret = notmuch_run_hook (db_path, "pre-new"); if (ret) - return ret; + return EXIT_FAILURE; } - dot_notmuch_path = talloc_asprintf (ctx, "%s/%s", db_path, ".notmuch"); + dot_notmuch_path = talloc_asprintf (config, "%s/%s", db_path, ".notmuch"); if (stat (dot_notmuch_path, &st)) { int count; @@ -898,16 +921,16 @@ notmuch_new_command (void *ctx, int argc, char *argv[]) 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"); @@ -922,7 +945,7 @@ notmuch_new_command (void *ctx, int argc, char *argv[]) } 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 @@ -941,9 +964,9 @@ notmuch_new_command (void *ctx, int argc, char *argv[]) add_files_state.removed_messages = add_files_state.renamed_messages = 0; gettimeofday (&add_files_state.tv_start, NULL); - add_files_state.removed_files = _filename_list_create (ctx); - add_files_state.removed_directories = _filename_list_create (ctx); - add_files_state.directory_mtimes = _filename_list_create (ctx); + add_files_state.removed_files = _filename_list_create (config); + add_files_state.removed_directories = _filename_list_create (config); + add_files_state.directory_mtimes = _filename_list_create (config); if (! debugger_is_active () && add_files_state.output_is_a_tty && ! add_files_state.verbose) { @@ -970,7 +993,7 @@ notmuch_new_command (void *ctx, int argc, char *argv[]) gettimeofday (&tv_start, NULL); for (f = add_files_state.removed_directories->head, i = 0; f && !interrupted; f = f->next, i++) { - ret = _remove_directory (ctx, notmuch, f->filename, &add_files_state); + ret = _remove_directory (config, notmuch, f->filename, &add_files_state); if (ret) goto DONE; if (do_print_progress) { @@ -1048,5 +1071,5 @@ notmuch_new_command (void *ctx, int argc, char *argv[]) if (!no_hooks && !ret && !interrupted) ret = notmuch_run_hook (db_path, "post-new"); - return ret || interrupted; + return ret || interrupted ? EXIT_FAILURE : EXIT_SUCCESS; }