+ notmuch->index_as_text = NULL;
+ notmuch->index_as_text_length = 0;
+
+ notmuch->params = NOTMUCH_PARAM_NONE;
+ if (database_path)
+ notmuch->params |= NOTMUCH_PARAM_DATABASE;
+ if (config_path)
+ notmuch->params |= NOTMUCH_PARAM_CONFIG;
+ if (profile)
+ notmuch->params |= NOTMUCH_PARAM_PROFILE;
+
+ return notmuch;
+}
+
+static notmuch_status_t
+_trial_open (const char *xapian_path, char **message_ptr)
+{
+ try {
+ Xapian::Database db (xapian_path);
+ } catch (const Xapian::DatabaseOpeningError &error) {
+ IGNORE_RESULT (asprintf (message_ptr,
+ "Cannot open Xapian database at %s: %s\n",
+ xapian_path,
+ error.get_msg ().c_str ()));
+ return NOTMUCH_STATUS_PATH_ERROR;
+ } catch (const Xapian::Error &error) {
+ IGNORE_RESULT (asprintf (message_ptr,
+ "A Xapian exception occurred opening database: %s\n",
+ error.get_msg ().c_str ()));
+ return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
+ }
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
+notmuch_status_t
+_notmuch_choose_xapian_path (void *ctx, const char *database_path,
+ const char **xapian_path, char **message_ptr)
+{
+ notmuch_status_t status;
+ const char *trial_path, *notmuch_path;
+
+ status = _db_dir_exists (database_path, message_ptr);
+ if (status)
+ goto DONE;
+
+ trial_path = talloc_asprintf (ctx, "%s/xapian", database_path);
+ status = _trial_open (trial_path, message_ptr);
+ if (status != NOTMUCH_STATUS_PATH_ERROR)
+ goto DONE;
+
+ if (*message_ptr)
+ free (*message_ptr);
+
+ notmuch_path = talloc_asprintf (ctx, "%s/.notmuch", database_path);
+ status = _db_dir_exists (notmuch_path, message_ptr);
+ if (status)
+ goto DONE;
+
+ trial_path = talloc_asprintf (ctx, "%s/xapian", notmuch_path);
+ status = _trial_open (trial_path, message_ptr);
+
+ DONE:
+ if (status == NOTMUCH_STATUS_SUCCESS)
+ *xapian_path = trial_path;
+ return status;
+}
+
+static void
+_set_database_path (notmuch_database_t *notmuch,
+ const char *database_path)
+{
+ char *path = talloc_strdup (notmuch, database_path);
+
+ strip_trailing (path, '/');
+
+ _notmuch_config_cache (notmuch, NOTMUCH_CONFIG_DATABASE_PATH, path);
+}
+
+static void
+_load_database_state (notmuch_database_t *notmuch)
+{
+ std::string last_thread_id;
+ std::string last_mod;
+
+ notmuch->last_doc_id = notmuch->xapian_db->get_lastdocid ();
+ last_thread_id = notmuch->xapian_db->get_metadata ("last_thread_id");
+ if (last_thread_id.empty ()) {
+ notmuch->last_thread_id = 0;
+ } else {
+ const char *str;
+ char *end;
+
+ str = last_thread_id.c_str ();
+ notmuch->last_thread_id = strtoull (str, &end, 16);
+ if (*end != '\0')
+ INTERNAL_ERROR ("Malformed database last_thread_id: %s", str);
+ }
+
+ /* Get current highest revision number. */
+ last_mod = notmuch->xapian_db->get_value_upper_bound (
+ NOTMUCH_VALUE_LAST_MOD);
+ if (last_mod.empty ())
+ notmuch->revision = 0;
+ else
+ notmuch->revision = Xapian::sortable_unserialise (last_mod);
+ notmuch->uuid = talloc_strdup (
+ notmuch, notmuch->xapian_db->get_uuid ().c_str ());
+}
+
+/* XXX This should really be done lazily, but the error reporting path in the indexing code
+ * would need to be redone to report any errors.
+ */
+notmuch_status_t
+_ensure_index_as_text (notmuch_database_t *notmuch, char **message)
+{
+ int nregex = 0;
+ regex_t *regexv = NULL;
+
+ if (notmuch->index_as_text)
+ return NOTMUCH_STATUS_SUCCESS;
+
+ for (notmuch_config_values_t *list = notmuch_config_get_values (notmuch,
+ NOTMUCH_CONFIG_INDEX_AS_TEXT);
+ notmuch_config_values_valid (list);
+ notmuch_config_values_move_to_next (list)) {
+ regex_t *new_regex;
+ int rerr;
+ const char *str = notmuch_config_values_get (list);
+ size_t len = strlen (str);
+
+ /* str must be non-empty, because n_c_get_values skips empty
+ * strings */
+ assert (len > 0);
+
+ regexv = talloc_realloc (notmuch, regexv, regex_t, nregex + 1);
+ new_regex = ®exv[nregex];
+
+ rerr = regcomp (new_regex, str, REG_EXTENDED | REG_NOSUB);
+ if (rerr) {
+ size_t error_size = regerror (rerr, new_regex, NULL, 0);
+ char *error = (char *) talloc_size (str, error_size);
+
+ regerror (rerr, new_regex, error, error_size);
+ IGNORE_RESULT (asprintf (message, "Error in index.as_text: %s: %s\n", error, str));
+
+ return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
+ }
+ nregex++;
+ }
+
+ notmuch->index_as_text = regexv;
+ notmuch->index_as_text_length = nregex;
+
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
+static notmuch_status_t
+_finish_open (notmuch_database_t *notmuch,
+ const char *profile,
+ notmuch_database_mode_t mode,
+ GKeyFile *key_file,
+ char **message_ptr)
+{
+ notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
+ char *incompat_features;
+ char *message = NULL;
+ const char *autocommit_str;
+ char *autocommit_end;
+ unsigned int version;
+ const char *database_path = notmuch_database_get_path (notmuch);
+