+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 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;
+
+ 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;
+ }
+ }
+
+ talloc_free (path);
+
+ return ret;
+}
+
+/* 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_index_file (notmuch, filename, indexing_cli_choices.opts, &message);
+ switch (status) {
+ /* Success. */
+ case NOTMUCH_STATUS_SUCCESS:
+ state->added_messages++;
+ notmuch_message_freeze (message);
+ 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) {
+ 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_FILE_ERROR:
+ /* Someone renamed/removed the file between scandir and now. */
+ state->vanished_files++;
+ fprintf (stderr, "Unexpected error with file %s\n", filename);
+ (void) print_status_database ("add_file", notmuch, status);
+ break;
+ /* Fatal issues. Don't process anymore. */
+ case NOTMUCH_STATUS_READ_ONLY_DATABASE:
+ case NOTMUCH_STATUS_XAPIAN_EXCEPTION:
+ case NOTMUCH_STATUS_OUT_OF_MEMORY:
+ (void) print_status_database ("add_file", notmuch, 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;
+}
+