};
typedef struct {
+ const char *db_path;
+ const char *mail_root;
+
+ notmuch_indexopts_t *indexopts;
int output_is_a_tty;
enum verbosity verbosity;
- notmuch_bool_t debug;
- const char **new_tags;
- size_t new_tags_length;
- const char **new_ignore;
- size_t new_ignore_length;
+ bool debug;
+ bool full_scan;
+ notmuch_config_values_t *new_tags;
+ const char **ignore_verbatim;
+ size_t ignore_verbatim_length;
+ regex_t *ignore_regex;
+ size_t ignore_regex_length;
int total_files;
int processed_files;
static void
handle_sigint (unused (int sig))
{
- static char msg[] = "Stopping... \n";
+ static const char msg[] = "Stopping... \n";
/* This write is "opportunistic", so it's okay to ignore the
* result. It is not required for correctness, and if it does
* fail or produce a short write, we want to get out of the signal
* handler as quickly as possible, not retry it. */
- IGNORE_RESULT (write (2, msg, sizeof(msg)-1));
+ IGNORE_RESULT (write (2, msg, sizeof (msg) - 1));
interrupted = 1;
}
elapsed_overall = notmuch_time_elapsed (tv_start, tv_now);
rate_overall = processed / elapsed_overall;
- printf ("%s %d ", action, processed);
+ printf ("%s %u ", action, processed);
if (total) {
- printf ("of %d %s", total, object);
+ printf ("of %u %s", total, object);
if (processed > 0 && elapsed_overall > 0.5) {
double time_remaining = ((total - processed) / rate_overall);
printf (" (");
/* Mapping from d_type to stat mode_t. We omit DT_LNK so that
* we'll fall through to stat and get the real file type. */
static const mode_t modes[] = {
- [DT_BLK] = S_IFBLK,
- [DT_CHR] = S_IFCHR,
- [DT_DIR] = S_IFDIR,
+ [DT_BLK] = S_IFBLK,
+ [DT_CHR] = S_IFCHR,
+ [DT_DIR] = S_IFDIR,
[DT_FIFO] = S_IFIFO,
- [DT_REG] = S_IFREG,
+ [DT_REG] = S_IFREG,
[DT_SOCK] = S_IFSOCK
};
- if (entry->d_type < ARRAY_SIZE(modes) && modes[entry->d_type])
+ if (entry->d_type < ARRAY_SIZE (modes) && modes[entry->d_type])
return modes[entry->d_type];
#endif
abspath = talloc_asprintf (NULL, "%s/%s", path, entry->d_name);
- if (!abspath) {
+ if (! abspath) {
errno = ENOMEM;
return -1;
}
- err = stat(abspath, &statbuf);
+ err = stat (abspath, &statbuf);
saved_errno = errno;
talloc_free (abspath);
if (err < 0) {
if (dirent_type (path, entries[i]) != S_IFDIR)
continue;
- if (strcmp(entries[i]->d_name, "new") == 0 ||
- strcmp(entries[i]->d_name, "cur") == 0 ||
- strcmp(entries[i]->d_name, "tmp") == 0)
- {
+ if (strcmp (entries[i]->d_name, "new") == 0 ||
+ strcmp (entries[i]->d_name, "cur") == 0 ||
+ strcmp (entries[i]->d_name, "tmp") == 0) {
found++;
if (found == 3)
return 1;
return 0;
}
+static bool
+_special_directory (const char *entry)
+{
+ return strcmp (entry, ".") == 0 || strcmp (entry, "..") == 0;
+}
+
+static bool
+_setup_ignore (notmuch_database_t *notmuch, add_files_state_t *state)
+{
+ notmuch_config_values_t *ignore_list;
+ int nregex = 0, nverbatim = 0;
+ const char **verbatim = NULL;
+ regex_t *regex = NULL;
+
+ for (ignore_list = notmuch_config_get_values (notmuch, NOTMUCH_CONFIG_NEW_IGNORE);
+ notmuch_config_values_valid (ignore_list);
+ notmuch_config_values_move_to_next (ignore_list)) {
+ const char *s = notmuch_config_values_get (ignore_list);
+ size_t len = strlen (s);
+
+ if (len == 0) {
+ fprintf (stderr, "Error: Empty string in new.ignore list\n");
+ return false;
+ }
+
+ if (s[0] == '/') {
+ regex_t *preg;
+ char *r;
+ int rerr;
+
+ if (len < 3 || s[len - 1] != '/') {
+ fprintf (stderr, "Error: Malformed pattern '%s' in new.ignore\n",
+ s);
+ return false;
+ }
+
+ r = talloc_strndup (notmuch, s + 1, len - 2);
+ regex = talloc_realloc (notmuch, regex, regex_t, nregex + 1);
+ preg = ®ex[nregex];
+
+ rerr = regcomp (preg, r, REG_EXTENDED | REG_NOSUB);
+ if (rerr) {
+ size_t error_size = regerror (rerr, preg, NULL, 0);
+ char *error = talloc_size (r, error_size);
+
+ regerror (rerr, preg, error, error_size);
+
+ fprintf (stderr, "Error: Invalid regex '%s' in new.ignore: %s\n",
+ r, error);
+ return false;
+ }
+ nregex++;
+
+ talloc_free (r);
+ } else {
+ verbatim = talloc_realloc (notmuch, verbatim, const char *,
+ nverbatim + 1);
+ verbatim[nverbatim++] = s;
+ }
+ }
+
+ state->ignore_regex = regex;
+ state->ignore_regex_length = nregex;
+ state->ignore_verbatim = verbatim;
+ state->ignore_verbatim_length = nverbatim;
+
+ return true;
+}
+
+static char *
+_get_relative_path (const char *mail_root, const char *dirpath, const char *entry)
+{
+ size_t mail_root_len = strlen (mail_root);
+
+ /* paranoia? */
+ if (strncmp (dirpath, mail_root, mail_root_len) != 0) {
+ fprintf (stderr, "Warning: '%s' is not a subdirectory of '%s'\n",
+ dirpath, mail_root);
+ return NULL;
+ }
+
+ dirpath += mail_root_len;
+ while (*dirpath == '/')
+ dirpath++;
+
+ if (*dirpath)
+ return talloc_asprintf (NULL, "%s/%s", dirpath, entry);
+ else
+ return talloc_strdup (NULL, entry);
+}
+
/* Test if the file/directory is to be ignored.
*/
-static notmuch_bool_t
-_entry_in_ignore_list (const char *entry, add_files_state_t *state)
+static bool
+_entry_in_ignore_list (add_files_state_t *state, const char *dirpath,
+ const char *entry)
{
+ bool ret = false;
size_t i;
+ char *path;
+
+ for (i = 0; i < state->ignore_verbatim_length; i++) {
+ if (strcmp (entry, state->ignore_verbatim[i]) == 0)
+ return true;
+ }
+
+ if (state->ignore_regex_length == 0)
+ return false;
- for (i = 0; i < state->new_ignore_length; i++)
- if (strcmp (entry, state->new_ignore[i]) == 0)
- return TRUE;
+ path = _get_relative_path (state->mail_root, dirpath, entry);
+ if (! path)
+ return false;
+
+ for (i = 0; i < state->ignore_regex_length; i++) {
+ if (regexec (&state->ignore_regex[i], path, 0, NULL, 0) == 0) {
+ ret = true;
+ break;
+ }
+ }
- return FALSE;
+ talloc_free (path);
+
+ return ret;
}
/* Add a single file to the database. */
add_files_state_t *state)
{
notmuch_message_t *message = NULL;
- const char **tag;
+ 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);
+ status = notmuch_database_index_file (notmuch, filename, state->indexopts, &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);
+
+ for (notmuch_config_values_start (state->new_tags);
+ notmuch_config_values_valid (state->new_tags);
+ notmuch_config_values_move_to_next (state->new_tags)) {
+ notmuch_bool_t is_set;
+
+ tag = notmuch_config_values_get (state->new_tags);
+ /* Currently all errors from has_maildir_flag are fatal */
+ if ((status = notmuch_message_has_maildir_flag_st (message, 'S', &is_set)))
+ goto DONE;
+ if (strcmp ("unread", tag) != 0 || ! is_set) {
+ notmuch_message_add_tag (message, tag);
+ }
+ }
+
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);
+ if (state->synchronize_flags) {
+ status = notmuch_message_maildir_flags_to_tags (message);
+ if (print_status_message ("add_file", message, status))
+ goto DONE;
+ }
break;
case NOTMUCH_STATUS_FILE_NOT_EMAIL:
fprintf (stderr, "Note: Ignoring non-mail file: %s\n", filename);
break;
+ case NOTMUCH_STATUS_PATH_ERROR:
+ fprintf (stderr, "Note: Ignoring non-indexable path: %s\n", filename);
+ (void) print_status_database ("add_file", notmuch, status);
+ break;
case NOTMUCH_STATUS_FILE_ERROR:
/* Someone renamed/removed the file between scandir and now. */
state->vanished_files++;
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));
+ (void) print_status_database ("add_file", notmuch, status);
goto DONE;
default:
INTERNAL_ERROR ("add_message returned unexpected value: %d", status);
notmuch_filenames_t *db_subdirs = NULL;
time_t stat_time;
struct stat st;
- notmuch_bool_t is_maildir;
+ bool is_maildir;
if (stat (path, &st)) {
fprintf (stderr, "Error reading directory %s: %s\n",
* 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) {
+ if (directory && (! state->full_scan) && 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))
+ if (! notmuch_filenames_valid (db_subdirs))
goto DONE;
notmuch_filenames_destroy (db_subdirs);
db_subdirs = NULL;
/* Pass 1: Recurse into all sub-directories. */
is_maildir = _entries_resemble_maildir (path, fs_entries, num_fs_entries);
- for (i = 0; i < num_fs_entries; i++) {
- if (interrupted)
- break;
-
+ for (i = 0; i < num_fs_entries && ! interrupted; i++) {
entry = fs_entries[i];
+ /* Ignore special directories to avoid infinite recursion. */
+ if (_special_directory (entry->d_name))
+ continue;
+
/* Ignore any files/directories the user has configured to
* ignore. We do this before dirent_type both for performance
* and because we don't care if dirent_type fails on entries
* that are explicitly ignored.
*/
- if (_entry_in_ignore_list (entry->d_name, state)) {
+ if (_entry_in_ignore_list (state, path, entry->d_name)) {
if (state->debug)
printf ("(D) add_files, pass 1: explicitly ignoring %s/%s\n",
path, entry->d_name);
continue;
}
- /* Ignore special directories to avoid infinite recursion.
- * Also ignore the .notmuch directory and any "tmp" directory
+ /* Ignore any top level .notmuch directory and any "tmp" directory
* that appears within a maildir.
*/
- if (strcmp (entry->d_name, ".") == 0 ||
- strcmp (entry->d_name, "..") == 0 ||
- (is_maildir && strcmp (entry->d_name, "tmp") == 0) ||
- strcmp (entry->d_name, ".notmuch") == 0)
+ if ((is_maildir && strcmp (entry->d_name, "tmp") == 0) ||
+ (strcmp (entry->d_name, ".notmuch") == 0
+ && (strcmp (path, state->mail_root)) == 0))
continue;
next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);
* being discovered until the clock catches up and the directory
* is modified again).
*/
- if (directory && fs_mtime == db_mtime)
+ if (directory && (! state->full_scan) && fs_mtime == db_mtime)
goto DONE;
/* If the database has never seen this directory before, we can
}
/* Pass 2: Scan for new files, removed files, and removed directories. */
- for (i = 0; i < num_fs_entries; i++)
- {
- if (interrupted)
- break;
+ for (i = 0; i < num_fs_entries && ! interrupted; i++) {
+ entry = fs_entries[i];
- entry = fs_entries[i];
+ /* Ignore special directories early. */
+ if (_special_directory (entry->d_name))
+ continue;
/* Ignore files & directories user has configured to be ignored */
- if (_entry_in_ignore_list (entry->d_name, state)) {
+ if (_entry_in_ignore_list (state, path, entry->d_name)) {
if (state->debug)
printf ("(D) add_files, pass 2: explicitly ignoring %s/%s\n",
path, entry->d_name);
/* Check if we've walked past any names in db_files or
* db_subdirs. If so, these have been deleted. */
while (notmuch_filenames_valid (db_files) &&
- strcmp (notmuch_filenames_get (db_files), entry->d_name) < 0)
- {
+ strcmp (notmuch_filenames_get (db_files), entry->d_name) < 0) {
char *absolute = talloc_asprintf (state->removed_files,
"%s/%s", path,
notmuch_filenames_get (db_files));
}
while (notmuch_filenames_valid (db_subdirs) &&
- strcmp (notmuch_filenames_get (db_subdirs), entry->d_name) <= 0)
- {
+ strcmp (notmuch_filenames_get (db_subdirs), entry->d_name) <= 0) {
const char *filename = notmuch_filenames_get (db_subdirs);
- if (strcmp (filename, entry->d_name) < 0)
- {
+ if (strcmp (filename, entry->d_name) < 0) {
char *absolute = talloc_asprintf (state->removed_directories,
"%s/%s", path, filename);
if (state->debug)
- printf ("(D) add_files, pass 2: queuing passed directory %s for deletion from database\n",
+ printf (
+ "(D) add_files, pass 2: queuing passed directory %s for deletion from database\n",
absolute);
_filename_list_add (state->removed_directories, absolute);
/* Don't add a file that we've added before. */
if (notmuch_filenames_valid (db_files) &&
- strcmp (notmuch_filenames_get (db_files), entry->d_name) == 0)
- {
+ strcmp (notmuch_filenames_get (db_files), entry->d_name) == 0) {
notmuch_filenames_move_to_next (db_files);
continue;
}
if (state->verbosity >= VERBOSITY_VERBOSE) {
if (state->output_is_a_tty)
- printf("\r\033[K");
+ printf ("\r\033[K");
printf ("%i/%i: %s", state->processed_files, state->total_files,
next);
- putchar((state->output_is_a_tty) ? '\r' : '\n');
+ putchar ((state->output_is_a_tty) ? '\r' : '\n');
fflush (stdout);
}
/* Now that we've walked the whole filesystem list, anything left
* over in the database lists has been deleted. */
- while (notmuch_filenames_valid (db_files))
- {
+ while (notmuch_filenames_valid (db_files)) {
char *absolute = talloc_asprintf (state->removed_files,
"%s/%s", path,
notmuch_filenames_get (db_files));
notmuch_filenames_move_to_next (db_files);
}
- while (notmuch_filenames_valid (db_subdirs))
- {
+ while (notmuch_filenames_valid (db_subdirs)) {
char *absolute = talloc_asprintf (state->removed_directories,
"%s/%s", path,
notmuch_filenames_get (db_subdirs));
if (state->debug)
- printf ("(D) add_files, pass 3: queuing leftover directory %s for deletion from database\n",
- absolute);
+ printf (
+ "(D) add_files, pass 3: queuing leftover directory %s for deletion from database\n",
+ absolute);
_filename_list_add (state->removed_directories, absolute);
}
for (i = 0; i < num_fs_entries && ! interrupted; i++) {
- entry = fs_entries[i];
+ entry = fs_entries[i];
/* Ignore special directories to avoid infinite recursion.
* Also ignore the .notmuch directory.
*/
- if (strcmp (entry->d_name, ".") == 0 ||
- strcmp (entry->d_name, "..") == 0 ||
+ if (_special_directory (entry->d_name) ||
strcmp (entry->d_name, ".notmuch") == 0)
continue;
/* Ignore any files/directories the user has configured to be
* ignored
*/
- if (_entry_in_ignore_list (entry->d_name, state)) {
+ if (_entry_in_ignore_list (state, path, entry->d_name)) {
if (state->debug)
printf ("(D) count_files: explicitly ignoring %s/%s\n",
path, entry->d_name);
for (i = 0; i < num_fs_entries; i++)
free (fs_entries[i]);
- free (fs_entries);
+ free (fs_entries);
}
}
{
notmuch_status_t status;
notmuch_message_t *message;
+
status = notmuch_database_begin_atomic (notmuch);
if (status)
return status;
status = notmuch_database_remove_message (notmuch, path);
if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) {
add_files_state->renamed_messages++;
- if (add_files_state->synchronize_flags == TRUE)
+ if (add_files_state->synchronize_flags == true)
notmuch_message_maildir_flags_to_tags (message);
status = NOTMUCH_STATUS_SUCCESS;
} else if (status == NOTMUCH_STATUS_SUCCESS) {
/* Recursively remove all filenames from the database referring to
* 'path' (or to any of its children). */
static notmuch_status_t
-_remove_directory (void *ctx,
- notmuch_database_t *notmuch,
+_remove_directory (notmuch_database_t *notmuch,
const char *path,
add_files_state_t *add_files_state)
{
- notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
+ notmuch_status_t status;
notmuch_directory_t *directory;
notmuch_filenames_t *files, *subdirs;
char *absolute;
status = notmuch_database_get_directory (notmuch, path, &directory);
- if (status || !directory)
+ if (status || ! directory)
return status;
for (files = notmuch_directory_get_child_files (directory);
notmuch_filenames_valid (files);
- notmuch_filenames_move_to_next (files))
- {
- absolute = talloc_asprintf (ctx, "%s/%s", path,
+ notmuch_filenames_move_to_next (files)) {
+ absolute = talloc_asprintf (notmuch, "%s/%s", path,
notmuch_filenames_get (files));
status = remove_filename (notmuch, absolute, add_files_state);
talloc_free (absolute);
for (subdirs = notmuch_directory_get_child_directories (directory);
notmuch_filenames_valid (subdirs);
- notmuch_filenames_move_to_next (subdirs))
- {
- absolute = talloc_asprintf (ctx, "%s/%s", path,
+ notmuch_filenames_move_to_next (subdirs)) {
+ absolute = talloc_asprintf (notmuch, "%s/%s", path,
notmuch_filenames_get (subdirs));
- status = _remove_directory (ctx, notmuch, absolute, add_files_state);
+ status = _remove_directory (notmuch, absolute, add_files_state);
talloc_free (absolute);
if (status)
goto DONE;
printf ("\n");
}
+static int
+_maybe_upgrade (notmuch_database_t *notmuch, add_files_state_t *state)
+{
+ if (notmuch_database_needs_upgrade (notmuch)) {
+ time_t now = time (NULL);
+ struct tm *gm_time = gmtime (&now);
+ int err;
+ notmuch_status_t status;
+ const char *backup_dir = notmuch_config_get (notmuch, NOTMUCH_CONFIG_BACKUP_DIR);
+ const char *backup_name;
+
+ err = mkdir (backup_dir, 0755);
+ if (err && errno != EEXIST) {
+ fprintf (stderr, "Failed to create %s: %s\n", backup_dir, strerror (errno));
+ return EXIT_FAILURE;
+ }
+
+ /* since dump files are written atomically, the amount of
+ * harm from overwriting one within a second seems
+ * relatively small. */
+ backup_name = talloc_asprintf (notmuch, "%s/dump-%04d%02d%02dT%02d%02d%02d.gz",
+ backup_dir,
+ gm_time->tm_year + 1900,
+ gm_time->tm_mon + 1,
+ gm_time->tm_mday,
+ gm_time->tm_hour,
+ gm_time->tm_min,
+ gm_time->tm_sec);
+
+ if (state->verbosity >= VERBOSITY_NORMAL) {
+ printf ("Welcome to a new version of notmuch! Your database will now be upgraded.\n");
+ printf ("This process is safe to interrupt.\n");
+ printf ("Backing up tags to %s...\n", backup_name);
+ }
+
+ if (notmuch_database_dump (notmuch, backup_name, "",
+ DUMP_FORMAT_BATCH_TAG, DUMP_INCLUDE_DEFAULT, true)) {
+ fprintf (stderr, "Backup failed. Aborting upgrade.");
+ return EXIT_FAILURE;
+ }
+
+ gettimeofday (&state->tv_start, NULL);
+ status = notmuch_database_upgrade (
+ notmuch,
+ state->verbosity >= VERBOSITY_NORMAL ? upgrade_print_progress : NULL,
+ state);
+ if (status) {
+ printf ("Upgrade failed: %s\n",
+ notmuch_status_to_string (status));
+ notmuch_database_destroy (notmuch);
+ return EXIT_FAILURE;
+ }
+ if (state->verbosity >= VERBOSITY_NORMAL)
+ printf ("Your notmuch database has now been upgraded.\n");
+ }
+ return EXIT_SUCCESS;
+}
+
int
-notmuch_new_command (notmuch_config_t *config, int argc, char *argv[])
+notmuch_new_command (notmuch_database_t *notmuch, int argc, char *argv[])
{
- notmuch_database_t *notmuch;
add_files_state_t add_files_state = {
.verbosity = VERBOSITY_NORMAL,
- .debug = FALSE,
+ .debug = false,
+ .full_scan = false,
.output_is_a_tty = isatty (fileno (stdout)),
};
struct timeval tv_start;
int ret = 0;
- struct stat st;
- const char *db_path;
- char *dot_notmuch_path;
+ const char *db_path, *mail_root;
struct sigaction action;
_filename_node_t *f;
int opt_index;
unsigned int i;
- notmuch_bool_t timer_is_active = FALSE;
- notmuch_bool_t no_hooks = FALSE;
- notmuch_bool_t quiet = FALSE, verbose = FALSE;
+ bool timer_is_active = false;
+ bool hooks = true;
+ bool quiet = false, verbose = false;
notmuch_status_t status;
notmuch_opt_desc_t options[] = {
- { NOTMUCH_OPT_BOOLEAN, &quiet, "quiet", 'q', 0 },
- { NOTMUCH_OPT_BOOLEAN, &verbose, "verbose", 'v', 0 },
- { NOTMUCH_OPT_BOOLEAN, &add_files_state.debug, "debug", 'd', 0 },
- { NOTMUCH_OPT_BOOLEAN, &no_hooks, "no-hooks", 'n', 0 },
- { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 },
- { 0, 0, 0, 0, 0 }
+ { .opt_bool = &quiet, .name = "quiet" },
+ { .opt_bool = &verbose, .name = "verbose" },
+ { .opt_bool = &add_files_state.debug, .name = "debug" },
+ { .opt_bool = &add_files_state.full_scan, .name = "full-scan" },
+ { .opt_bool = &hooks, .name = "hooks" },
+ { .opt_inherit = notmuch_shared_indexing_options },
+ { .opt_inherit = notmuch_shared_options },
+ { }
};
opt_index = parse_arguments (argc, argv, options, 1);
if (opt_index < 0)
return EXIT_FAILURE;
- notmuch_process_shared_options (argv[0]);
+ notmuch_process_shared_options (notmuch, argv[0]);
/* quiet trumps verbose */
if (quiet)
else if (verbose)
add_files_state.verbosity = VERBOSITY_VERBOSE;
- 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);
- add_files_state.synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config);
- db_path = notmuch_config_get_database_path (config);
+ add_files_state.indexopts = notmuch_database_get_default_indexopts (notmuch);
+
+ add_files_state.new_tags = notmuch_config_get_values (notmuch, NOTMUCH_CONFIG_NEW_TAGS);
+
+ if (print_status_database (
+ "notmuch new",
+ notmuch,
+ notmuch_config_get_bool (notmuch, NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS,
+ &add_files_state.synchronize_flags)))
+ return EXIT_FAILURE;
+
+ db_path = notmuch_config_get (notmuch, NOTMUCH_CONFIG_DATABASE_PATH);
+ add_files_state.db_path = db_path;
+
+ mail_root = notmuch_config_get (notmuch, NOTMUCH_CONFIG_MAIL_ROOT);
+ add_files_state.mail_root = mail_root;
+
+ if (! _setup_ignore (notmuch, &add_files_state))
+ return EXIT_FAILURE;
- for (i = 0; i < add_files_state.new_tags_length; i++) {
- const char *error_msg;
+ for (notmuch_config_values_start (add_files_state.new_tags);
+ notmuch_config_values_valid (add_files_state.new_tags);
+ notmuch_config_values_move_to_next (add_files_state.new_tags)) {
+ const char *tag, *error_msg;
- error_msg = illegal_tag (add_files_state.new_tags[i], FALSE);
+ tag = notmuch_config_values_get (add_files_state.new_tags);
+ error_msg = illegal_tag (tag, false);
if (error_msg) {
- fprintf (stderr, "Error: tag '%s' in new.tags: %s\n",
- add_files_state.new_tags[i], error_msg);
+ fprintf (stderr, "Error: tag '%s' in new.tags: %s\n", tag, error_msg);
return EXIT_FAILURE;
}
}
- if (!no_hooks) {
- ret = notmuch_run_hook (db_path, "pre-new");
- if (ret)
+ if (hooks) {
+ /* Drop write lock to run hook */
+ status = notmuch_database_reopen (notmuch, NOTMUCH_DATABASE_MODE_READ_ONLY);
+ if (print_status_database ("notmuch new", notmuch, status))
return EXIT_FAILURE;
- }
- dot_notmuch_path = talloc_asprintf (config, "%s/%s", db_path, ".notmuch");
+ ret = notmuch_run_hook (notmuch, "pre-new");
+ if (ret)
+ return EXIT_FAILURE;
- if (stat (dot_notmuch_path, &st)) {
- int count;
+ /* acquire write lock again */
+ status = notmuch_database_reopen (notmuch, NOTMUCH_DATABASE_MODE_READ_WRITE);
+ if (print_status_database ("notmuch new", notmuch, status))
+ return EXIT_FAILURE;
+ }
- count = 0;
- count_files (db_path, &count, &add_files_state);
+ if (notmuch_database_get_revision (notmuch, NULL) == 0) {
+ int count = 0;
+ count_files (mail_root, &count, &add_files_state);
if (interrupted)
return EXIT_FAILURE;
if (add_files_state.verbosity >= VERBOSITY_NORMAL)
printf ("Found %d total files (that's not much mail).\n", count);
- if (notmuch_database_create (db_path, ¬much))
- return EXIT_FAILURE;
+
add_files_state.total_files = count;
} else {
- char *status_string = NULL;
- if (notmuch_database_open_verbose (db_path, NOTMUCH_DATABASE_MODE_READ_WRITE,
- ¬much, &status_string)) {
- if (status_string) {
- fputs (status_string, stderr);
- free (status_string);
- }
+ if (_maybe_upgrade (notmuch, &add_files_state))
return EXIT_FAILURE;
- }
-
- notmuch_exit_if_unmatched_db_uuid (notmuch);
-
- if (notmuch_database_needs_upgrade (notmuch)) {
- time_t now = time (NULL);
- struct tm *gm_time = gmtime (&now);
-
- /* since dump files are written atomically, the amount of
- * harm from overwriting one within a second seems
- * relatively small. */
-
- const char *backup_name =
- talloc_asprintf (notmuch, "%s/dump-%04d%02d%02dT%02d%02d%02d.gz",
- dot_notmuch_path,
- gm_time->tm_year + 1900,
- gm_time->tm_mon + 1,
- gm_time->tm_mday,
- gm_time->tm_hour,
- gm_time->tm_min,
- gm_time->tm_sec);
-
- if (add_files_state.verbosity >= VERBOSITY_NORMAL) {
- printf ("Welcome to a new version of notmuch! Your database will now be upgraded.\n");
- printf ("This process is safe to interrupt.\n");
- printf ("Backing up tags to %s...\n", backup_name);
- }
-
- if (notmuch_database_dump (notmuch, backup_name, "",
- DUMP_FORMAT_BATCH_TAG, DUMP_INCLUDE_DEFAULT, TRUE)) {
- fprintf (stderr, "Backup failed. Aborting upgrade.");
- return EXIT_FAILURE;
- }
-
- gettimeofday (&add_files_state.tv_start, NULL);
- status = notmuch_database_upgrade (
- notmuch,
- add_files_state.verbosity >= VERBOSITY_NORMAL ? upgrade_print_progress : NULL,
- &add_files_state);
- if (status) {
- printf ("Upgrade failed: %s\n",
- notmuch_status_to_string (status));
- notmuch_database_destroy (notmuch);
- return EXIT_FAILURE;
- }
- if (add_files_state.verbosity >= VERBOSITY_NORMAL)
- printf ("Your notmuch database has now been upgraded.\n");
- }
add_files_state.total_files = 0;
}
if (notmuch == NULL)
return EXIT_FAILURE;
+ status = notmuch_process_shared_indexing_options (add_files_state.indexopts);
+ if (status != NOTMUCH_STATUS_SUCCESS) {
+ fprintf (stderr, "Error: Failed to process index options. (%s)\n",
+ notmuch_status_to_string (status));
+ return EXIT_FAILURE;
+ }
+
/* Set up our handler for SIGINT. We do this after having
* potentially done a database upgrade we this interrupt handler
* won't support. */
action.sa_flags = SA_RESTART;
sigaction (SIGINT, &action, NULL);
- talloc_free (dot_notmuch_path);
- dot_notmuch_path = NULL;
-
gettimeofday (&add_files_state.tv_start, NULL);
- 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);
+ add_files_state.removed_files = _filename_list_create (notmuch);
+ add_files_state.removed_directories = _filename_list_create (notmuch);
+ add_files_state.directory_mtimes = _filename_list_create (notmuch);
if (add_files_state.verbosity == VERBOSITY_NORMAL &&
add_files_state.output_is_a_tty && ! debugger_is_active ()) {
setup_progress_printing_timer ();
- timer_is_active = TRUE;
+ timer_is_active = true;
}
- ret = add_files (notmuch, db_path, &add_files_state);
+ ret = add_files (notmuch, mail_root, &add_files_state);
if (ret)
goto DONE;
gettimeofday (&tv_start, NULL);
- for (f = add_files_state.removed_files->head; f && !interrupted; f = f->next) {
+ for (f = add_files_state.removed_files->head; f && ! interrupted; f = f->next) {
ret = remove_filename (notmuch, f->filename, &add_files_state);
if (ret)
goto DONE;
if (do_print_progress) {
do_print_progress = 0;
generic_print_progress ("Cleaned up", "messages",
- tv_start, add_files_state.removed_messages + add_files_state.renamed_messages,
- add_files_state.removed_files->count);
+ tv_start, add_files_state.removed_messages +
+ add_files_state.renamed_messages,
+ add_files_state.removed_files->count);
}
}
gettimeofday (&tv_start, NULL);
- for (f = add_files_state.removed_directories->head, i = 0; f && !interrupted; f = f->next, i++) {
- ret = _remove_directory (config, notmuch, f->filename, &add_files_state);
+ for (f = add_files_state.removed_directories->head, i = 0; f && ! interrupted; f = f->next, i++) {
+ ret = _remove_directory (notmuch, f->filename, &add_files_state);
if (ret)
goto DONE;
if (do_print_progress) {
do_print_progress = 0;
generic_print_progress ("Cleaned up", "directories",
- tv_start, i,
- add_files_state.removed_directories->count);
+ tv_start, i,
+ add_files_state.removed_directories->count);
}
}
- for (f = add_files_state.directory_mtimes->head; f && !interrupted; f = f->next) {
+ for (f = add_files_state.directory_mtimes->head; f && ! interrupted; f = f->next) {
notmuch_directory_t *directory;
status = notmuch_database_get_directory (notmuch, f->filename, &directory);
if (status == NOTMUCH_STATUS_SUCCESS && directory) {
fprintf (stderr, "Note: A fatal error was encountered: %s\n",
notmuch_status_to_string (ret));
- notmuch_database_destroy (notmuch);
+ notmuch_database_close (notmuch);
+
+ if (hooks && ! ret && ! interrupted)
+ ret = notmuch_run_hook (notmuch, "post-new");
- if (!no_hooks && !ret && !interrupted)
- ret = notmuch_run_hook (db_path, "post-new");
+ notmuch_database_destroy (notmuch);
if (ret || interrupted)
return EXIT_FAILURE;