The main difference since Tomi's review is that I made several more tests for configuration path searching in T035-read-config.sh, and moved some of the existing ones there. The other diffs are as follows. The update of the private status values is partially cleanup, it includes the codes added by this series, but also others missed. I have to confess that I have not really been using the private status values in new code. diff --git a/lib/config.cc b/lib/config.cc index 4b115a07..948751bc 100644 --- a/lib/config.cc +++ b/lib/config.cc @@ -330,7 +330,7 @@ _notmuch_config_load_from_file (notmuch_database_t *notmuch, GKeyFile *file) { notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; - gchar **groups,**keys, *val; + gchar **groups, **keys, *val; if (notmuch->config == NULL) notmuch->config = _notmuch_string_map_create (notmuch); diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 5e0b42e3..2fbf7ab9 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -129,10 +129,22 @@ typedef enum _notmuch_private_status { NOTMUCH_PRIVATE_STATUS_OUT_OF_MEMORY = NOTMUCH_STATUS_OUT_OF_MEMORY, NOTMUCH_PRIVATE_STATUS_READ_ONLY_DATABASE = NOTMUCH_STATUS_READ_ONLY_DATABASE, NOTMUCH_PRIVATE_STATUS_XAPIAN_EXCEPTION = NOTMUCH_STATUS_XAPIAN_EXCEPTION, + NOTMUCH_PRIVATE_STATUS_FILE_ERROR = NOTMUCH_STATUS_FILE_ERROR, NOTMUCH_PRIVATE_STATUS_FILE_NOT_EMAIL = NOTMUCH_STATUS_FILE_NOT_EMAIL, NOTMUCH_PRIVATE_STATUS_NULL_POINTER = NOTMUCH_STATUS_NULL_POINTER, NOTMUCH_PRIVATE_STATUS_TAG_TOO_LONG = NOTMUCH_STATUS_TAG_TOO_LONG, NOTMUCH_PRIVATE_STATUS_UNBALANCED_FREEZE_THAW = NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW, + NOTMUCH_PRIVATE_STATUS_UNBALANCED_ATOMIC = NOTMUCH_STATUS_UNBALANCED_ATOMIC, + NOTMUCH_PRIVATE_STATUS_UNSUPPORTED_OPERATION = NOTMUCH_STATUS_UNSUPPORTED_OPERATION, + NOTMUCH_PRIVATE_STATUS_UPGRADE_REQUIRED = NOTMUCH_STATUS_UPGRADE_REQUIRED, + NOTMUCH_PRIVATE_STATUS_PATH_ERROR = NOTMUCH_STATUS_PATH_ERROR, + NOTMUCH_PRIVATE_STATUS_IGNORED = NOTMUCH_STATUS_IGNORED, + NOTMUCH_PRIVATE_STATUS_ILLEGAL_ARGUMENT = NOTMUCH_STATUS_ILLEGAL_ARGUMENT, + NOTMUCH_PRIVATE_STATUS_MALFORMED_CRYPTO_PROTOCOL = NOTMUCH_STATUS_MALFORMED_CRYPTO_PROTOCOL, + NOTMUCH_PRIVATE_STATUS_FAILED_CRYPTO_CONTEXT_CREATION = NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION, + NOTMUCH_PRIVATE_STATUS_UNKNOWN_CRYPTO_PROTOCOL = NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL, + NOTMUCH_PRIVATE_STATUS_NO_CONFIG = NOTMUCH_STATUS_NO_CONFIG, + NOTMUCH_PRIVATE_STATUS_DATABASE_EXISTS = NOTMUCH_STATUS_DATABASE_EXISTS, /* Then add our own private values. */ NOTMUCH_PRIVATE_STATUS_TERM_TOO_LONG = NOTMUCH_STATUS_LAST_STATUS, diff --git a/lib/notmuch.h b/lib/notmuch.h index 3c3fd487..5a5d99c0 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -446,13 +446,12 @@ notmuch_database_open_with_config (const char *database_path, * Create a new notmuch database located at 'database_path', using * configuration in 'config_path'. * - * @param[in,out] is_new if NULL consider an existing database as an - * error. Otherwise report whether actually created. - * - * For description of other arguments, see notmuch_database_open_with_config + * For description of arguments, @see notmuch_database_open_with_config * * @retval NOTMUCH_STATUS_SUCCESS: Successfully created the database. * + * @retval NOTMUCH_STATUS_DATABASE_EXISTS: Database already exists, not created + * * @retval NOTMUCH_STATUS_OUT_OF_MEMORY: Out of memory. * * @retval NOTMUCH_STATUS_FILE_ERROR: An error occurred trying to open the diff --git a/lib/open.cc b/lib/open.cc index eb7c8a01..b4637ec5 100644 --- a/lib/open.cc +++ b/lib/open.cc @@ -44,9 +44,10 @@ _xdg_dir (void *ctx, const char *profile_name) { const char *xdg_root = getenv (xdg_root_variable); - const char *home = getenv ("HOME"); if (! xdg_root) { + const char *home = getenv ("HOME"); + if (! home) return NULL; xdg_root = talloc_asprintf (ctx, @@ -152,11 +153,11 @@ DONE: } static notmuch_status_t -_pre_open(const char *config_path, - const char *profile, - GKeyFile **key_file, - const char **database_path, - char **message) +_choose_database_path (const char *config_path, + const char *profile, + GKeyFile **key_file, + const char **database_path, + char **message) { notmuch_status_t status; @@ -200,7 +201,7 @@ notmuch_database_open_with_config (const char *database_path, GKeyFile *key_file = NULL; static int initialized = 0; - if ((status = _pre_open (config_path, profile, &key_file, &database_path, &message))) + if ((status = _choose_database_path (config_path, profile, &key_file, &database_path, &message))) goto DONE; if (! (notmuch_path = talloc_asprintf (local, "%s/%s", database_path, ".notmuch"))) { @@ -423,7 +424,7 @@ notmuch_database_create_with_config (const char *database_path, struct stat st; int err; - if ((status = _pre_open (config_path, profile, &key_file, &database_path, &message))) + if ((status = _choose_database_path (config_path, profile, &key_file, &database_path, &message))) goto DONE; err = stat (database_path, &st); diff --git a/lib/string-map.c b/lib/string-map.c index 1f3215fb..71eac634 100644 --- a/lib/string-map.c +++ b/lib/string-map.c @@ -142,8 +142,11 @@ bsearch_first (notmuch_string_pair_t *array, size_t len, const char *key, bool e return NULL; } + void -_notmuch_string_map_set (notmuch_string_map_t *map, const char *key, const char *val) +_notmuch_string_map_set (notmuch_string_map_t *map, + const char *key, + const char *val) { notmuch_string_pair_t *pair; diff --git a/notmuch-new.c b/notmuch-new.c index 2fc34e2c..21e66af1 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -1105,7 +1105,6 @@ notmuch_new_command (unused(notmuch_config_t *config), notmuch_database_t *notmu struct timeval tv_start; int ret = 0; const char *db_path; - char *dot_notmuch_path; struct sigaction action; _filename_node_t *f; int opt_index; @@ -1172,8 +1171,6 @@ notmuch_new_command (unused(notmuch_config_t *config), notmuch_database_t *notmu return EXIT_FAILURE; } - dot_notmuch_path = talloc_asprintf (notmuch, "%s/%s", db_path, ".notmuch"); - notmuch_exit_if_unmatched_db_uuid (notmuch); if (notmuch_database_get_revision (notmuch, NULL) == 0) { @@ -1212,9 +1209,6 @@ notmuch_new_command (unused(notmuch_config_t *config), notmuch_database_t *notmu 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 (notmuch); diff --git a/test/T400-hooks.sh b/test/T400-hooks.sh index b9894853..a3dd4c63 100755 --- a/test/T400-hooks.sh +++ b/test/T400-hooks.sh @@ -47,7 +47,7 @@ for config in traditional profile explicit XDG; do mkdir -p $HOOK_DIR notmuch config set database.hook_dir $HOOK_DIR ;; - *) + XDG) HOOK_DIR=${HOME}/.config/notmuch/default/hooks ;; esac diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index c78ed204..e7e6e08a 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -17,7 +17,7 @@ int main (int argc, char** argv) notmuch_status_t stat; char *msg = NULL; - for (int i=1; i<argc; i++) + for (int i = 1; i < argc; i++) if (strcmp (argv[i], "%NULL%") == 0) argv[i] = NULL; stat = notmuch_database_open_with_config (argv[1],
This will be used (and tested) by the configuration caching code to be added in the next commit. --- lib/notmuch-private.h | 5 +++++ lib/string-map.c | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 57ec7f72..51016b0b 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -638,6 +638,11 @@ _notmuch_string_map_append (notmuch_string_map_t *map, const char *key, const char *value); +void +_notmuch_string_map_set (notmuch_string_map_t *map, + const char *key, + const char *value); + const char * _notmuch_string_map_get (notmuch_string_map_t *map, const char *key); diff --git a/lib/string-map.c b/lib/string-map.c index a88404c7..71eac634 100644 --- a/lib/string-map.c +++ b/lib/string-map.c @@ -143,6 +143,24 @@ bsearch_first (notmuch_string_pair_t *array, size_t len, const char *key, bool e } +void +_notmuch_string_map_set (notmuch_string_map_t *map, + const char *key, + const char *val) +{ + notmuch_string_pair_t *pair; + + /* this means that calling string_map_set invalidates iterators */ + _notmuch_string_map_sort (map); + pair = bsearch_first (map->pairs, map->length, key, true); + if (! pair) + _notmuch_string_map_append (map, key, val); + else { + talloc_free (pair->value); + pair->value = talloc_strdup (map->pairs, val); + } +} + const char * _notmuch_string_map_get (notmuch_string_map_t *map, const char *key) { -- 2.30.0
The main goal is to allow configuration information to be temporarily overridden by a separate config file. That will require further changes not in this commit. The performance impact is unclear, and will depend on the balance between number of queries and number of distinct metadata items read on the first call to n_d_get_config. --- lib/config.cc | 58 ++++++++++++++++++++++++++++++++++----- lib/database-private.h | 3 ++ lib/notmuch-private.h | 3 ++ lib/open.cc | 7 ++++- lib/prefix.cc | 18 ++++++------ test/T562-lib-database.sh | 7 ++--- 6 files changed, 75 insertions(+), 21 deletions(-) diff --git a/lib/config.cc b/lib/config.cc index 0b760dbc..c079d752 100644 --- a/lib/config.cc +++ b/lib/config.cc @@ -50,6 +50,11 @@ notmuch_database_set_config (notmuch_database_t *notmuch, if (status) return status; + if (! notmuch->config) { + if ((status = _notmuch_config_load_from_database (notmuch))) + return status; + } + try { notmuch->writable_xapian_db->set_metadata (CONFIG_PREFIX + key, value); } catch (const Xapian::Error &error) { @@ -58,7 +63,13 @@ notmuch_database_set_config (notmuch_database_t *notmuch, _notmuch_database_log (notmuch, "Error: A Xapian exception occurred setting metadata: %s\n", error.get_msg ().c_str ()); } - return status; + + if (status) + return status; + + _notmuch_string_map_set (notmuch->config, key, value); + + return NOTMUCH_STATUS_SUCCESS; } static notmuch_status_t @@ -84,17 +95,25 @@ notmuch_database_get_config (notmuch_database_t *notmuch, const char *key, char **value) { - std::string strval; + const char* stored_val; notmuch_status_t status; + if (! notmuch->config) { + if ((status = _notmuch_config_load_from_database (notmuch))) + return status; + } + if (! value) return NOTMUCH_STATUS_NULL_POINTER; - status = _metadata_value (notmuch, key, strval); - if (status) - return status; - - *value = strdup (strval.c_str ()); + stored_val = _notmuch_string_map_get (notmuch->config, key); + if (! stored_val) { + /* XXX in principle this API should be fixed so empty string + * is distinguished from not found */ + *value = strdup(""); + } else { + *value = strdup (stored_val); + } return NOTMUCH_STATUS_SUCCESS; } @@ -201,3 +220,28 @@ notmuch_config_list_destroy (notmuch_config_list_t *list) { talloc_free (list); } + +notmuch_status_t +_notmuch_config_load_from_database (notmuch_database_t *notmuch) +{ + notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; + notmuch_config_list_t *list; + + if (notmuch->config == NULL) + notmuch->config = _notmuch_string_map_create (notmuch); + + if (unlikely(notmuch->config == NULL)) + return NOTMUCH_STATUS_OUT_OF_MEMORY; + + status = notmuch_database_get_config_list (notmuch, "", &list); + if (status) + return status; + + for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) { + _notmuch_string_map_append (notmuch->config, + notmuch_config_list_key (list), + notmuch_config_list_value (list)); + } + + return status; +} diff --git a/lib/database-private.h b/lib/database-private.h index c9bc712b..d83cf0d0 100644 --- a/lib/database-private.h +++ b/lib/database-private.h @@ -228,6 +228,9 @@ struct _notmuch_database { * here, but at least they are small */ notmuch_string_map_t *user_prefix; notmuch_string_map_t *user_header; + + /* Cached and possibly overridden configuration */ + notmuch_string_map_t *config; }; /* Prior to database version 3, features were implied by the database diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 51016b0b..969caac0 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -704,6 +704,9 @@ struct _notmuch_indexopts { #define EMPTY_STRING(s) ((s)[0] == '\0') +/* config.cc */ +notmuch_status_t +_notmuch_config_load_from_database (notmuch_database_t * db); NOTMUCH_END_DECLS #ifdef __cplusplus diff --git a/lib/open.cc b/lib/open.cc index 1b17e63a..0001794a 100644 --- a/lib/open.cc +++ b/lib/open.cc @@ -90,7 +90,7 @@ notmuch_database_open_verbose (const char *path, notmuch->exception_reported = false; notmuch->status_string = NULL; notmuch->path = talloc_strdup (notmuch, path); - + notmuch->config = NULL; strip_trailing (notmuch->path, '/'); notmuch->writable_xapian_db = NULL; @@ -180,6 +180,11 @@ notmuch_database_open_verbose (const char *path, notmuch->query_parser->add_rangeprocessor (notmuch->date_range_processor); notmuch->query_parser->add_rangeprocessor (notmuch->last_mod_range_processor); + /* Configuration information is needed to set up query parser */ + status = _notmuch_config_load_from_database (notmuch); + if (status) + goto DONE; + status = _notmuch_database_setup_standard_query_fields (notmuch); if (status) goto DONE; diff --git a/lib/prefix.cc b/lib/prefix.cc index dd7b193d..71a76991 100644 --- a/lib/prefix.cc +++ b/lib/prefix.cc @@ -166,8 +166,7 @@ _notmuch_database_setup_standard_query_fields (notmuch_database_t *notmuch) notmuch_status_t _notmuch_database_setup_user_query_fields (notmuch_database_t *notmuch) { - notmuch_config_list_t *list; - notmuch_status_t status; + notmuch_string_map_iterator_t *list; notmuch->user_prefix = _notmuch_string_map_create (notmuch); if (notmuch->user_prefix == NULL) @@ -177,15 +176,16 @@ _notmuch_database_setup_user_query_fields (notmuch_database_t *notmuch) if (notmuch->user_header == NULL) return NOTMUCH_STATUS_OUT_OF_MEMORY; - status = notmuch_database_get_config_list (notmuch, CONFIG_HEADER_PREFIX, &list); - if (status) - return status; + list = _notmuch_string_map_iterator_create (notmuch->config, CONFIG_HEADER_PREFIX, FALSE); + if (! list) + INTERNAL_ERROR ("unable to read headers from configuration"); - for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) { + for (; _notmuch_string_map_iterator_valid (list); + _notmuch_string_map_iterator_move_to_next (list)) { prefix_t query_field; - const char *key = notmuch_config_list_key (list) + const char *key = _notmuch_string_map_iterator_key (list) + sizeof (CONFIG_HEADER_PREFIX) - 1; _notmuch_string_map_append (notmuch->user_prefix, @@ -194,7 +194,7 @@ _notmuch_database_setup_user_query_fields (notmuch_database_t *notmuch) _notmuch_string_map_append (notmuch->user_header, key, - notmuch_config_list_value (list)); + _notmuch_string_map_iterator_value (list)); query_field.name = talloc_strdup (notmuch, key); query_field.prefix = _user_prefix (notmuch, key); @@ -204,7 +204,7 @@ _notmuch_database_setup_user_query_fields (notmuch_database_t *notmuch) _setup_query_field_default (&query_field, notmuch); } - notmuch_config_list_destroy (list); + _notmuch_string_map_iterator_destroy (list); return NOTMUCH_STATUS_SUCCESS; } diff --git a/test/T562-lib-database.sh b/test/T562-lib-database.sh index dd4f2566..887851dd 100755 --- a/test/T562-lib-database.sh +++ b/test/T562-lib-database.sh @@ -340,7 +340,7 @@ test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "get config from closed database" cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} { - const char *result; + char *result; EXPECT0(notmuch_database_close (db)); stat = notmuch_database_get_config (db, "foo", &result); printf("%d\n", stat == NOTMUCH_STATUS_XAPIAN_EXCEPTION); @@ -348,9 +348,8 @@ cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} EOF cat <<EOF > EXPECTED == stdout == -1 +0 == stderr == -Error: A Xapian exception occurred getting metadata: Database has been closed EOF test_expect_equal_file EXPECTED OUTPUT @@ -376,7 +375,7 @@ cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} notmuch_indexopts_t *result; EXPECT0(notmuch_database_close (db)); result = notmuch_database_get_default_indexopts (db); - printf("%d\n", result == NULL); + printf("%d\n", result != NULL); } EOF cat <<EOF > EXPECTED -- 2.30.0
Initially document the intended API and copy the code from notmuch_database_open_verbose. Most of the documented functionality is not there yet. --- lib/config.cc | 34 +++++++++ lib/notmuch-private.h | 3 + lib/notmuch.h | 140 ++++++++++++++++++++++++++++++-------- lib/open.cc | 42 ++++++++++-- test/T562-lib-database.sh | 4 +- test/T590-libconfig.sh | 39 +++++++++-- 6 files changed, 221 insertions(+), 41 deletions(-) diff --git a/lib/config.cc b/lib/config.cc index c079d752..8bce7ba8 100644 --- a/lib/config.cc +++ b/lib/config.cc @@ -245,3 +245,37 @@ _notmuch_config_load_from_database (notmuch_database_t *notmuch) return status; } + +notmuch_status_t +_notmuch_config_load_from_file (notmuch_database_t *notmuch, + GKeyFile *file) +{ + notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; + gchar **groups,**keys, *val; + + if (notmuch->config == NULL) + notmuch->config = _notmuch_string_map_create (notmuch); + + if (unlikely(notmuch->config == NULL)) { + status = NOTMUCH_STATUS_OUT_OF_MEMORY; + goto DONE; + } + + for (groups = g_key_file_get_groups (file, NULL); *groups; groups++) { + for (keys = g_key_file_get_keys (file, *groups, NULL, NULL); *keys; keys++) { + char *absolute_key = talloc_asprintf(notmuch, "%s.%s", *groups, *keys); + val = g_key_file_get_value (file, *groups, *keys, NULL); + if (! val) { + status = NOTMUCH_STATUS_FILE_ERROR; + goto DONE; + } + _notmuch_string_map_set (notmuch->config, absolute_key, val); + talloc_free (absolute_key); + if (status) + goto DONE; + } + } + + DONE: + return status; +} diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 969caac0..40b1a855 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -707,6 +707,9 @@ struct _notmuch_indexopts { /* config.cc */ notmuch_status_t _notmuch_config_load_from_database (notmuch_database_t * db); + +notmuch_status_t +_notmuch_config_load_from_file (notmuch_database_t * db, GKeyFile *file); NOTMUCH_END_DECLS #ifdef __cplusplus diff --git a/lib/notmuch.h b/lib/notmuch.h index c66e78b1..9346640a 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -301,52 +301,138 @@ typedef enum { } notmuch_database_mode_t; /** - * Open an existing notmuch database located at 'path'. + * Deprecated alias for notmuch_database_open_with_config with + * config_path=error_message=NULL + * @deprecated Deprecated as of libnotmuch 5.4 (notmuch 0.32) + */ +/* NOTMUCH_DEPRECATED(5, 4) */ +notmuch_status_t +notmuch_database_open (const char *path, + notmuch_database_mode_t mode, + notmuch_database_t **database); +/** + * Deprecated alias for notmuch_database_open_with_config with + * config_path=NULL + * + * @deprecated Deprecated as of libnotmuch 5.4 (notmuch 0.32) + * + */ +/* NOTMUCH_DEPRECATED(5, 4) */ +notmuch_status_t +notmuch_database_open_verbose (const char *path, + notmuch_database_mode_t mode, + notmuch_database_t **database, + char **error_message); + +/** + * Open an existing notmuch database located at 'database_path', using + * configuration in 'config_path'. + * + * @param[in] database_path + * @parblock + * Path to existing database. + * + * A notmuch database is a Xapian database containing appropriate + * metadata. * * The database should have been created at some time in the past, * (not necessarily by this process), by calling - * notmuch_database_create with 'path'. By default the database should be - * opened for reading only. In order to write to the database you need to - * pass the NOTMUCH_DATABASE_MODE_READ_WRITE mode. + * notmuch_database_create. + * + * If 'database_path' is NULL, use the location specified + * + * - in the environment variable NOTMUCH_DATABASE, if non-empty + * + * - in a configuration file, located as described under 'config_path' + * + * - by $XDG_DATA_HOME/notmuch/$PROFILE where XDG_DATA_HOME defaults + * to "$HOME/.local/share" and PROFILE as as discussed in + * 'profile' + * + * If 'database_path' is non-NULL, but does not appear to be a Xapian + * database, check for a directory '.notmuch/xapian' below + * 'database_path' (this is the behavior of + * notmuch_database_open_verbose pre-0.32). + * + * @endparblock + * @param[in] mode + * @parblock + * Mode to open database. Use one of #NOTMUCH_DATABASE_MODE_READ_WRITE + * or #NOTMUCH_DATABASE_MODE_READ_ONLY + * + * @endparblock + * @param[in] config_path + * @parblock + * Path to config file. + * + * Config file is key-value, with mandatory sections. See + * <em>notmuch-config(5)</em> for more information. The key-value pair + * overrides the corresponding configuration data stored in the + * database (see <em>notmuch_database_get_config</em>) * - * An existing notmuch database can be identified by the presence of a - * directory named ".notmuch" below 'path'. + * If <em>config_path</em> is NULL use the path specified + * + * - in environment variable <em>NOTMUCH_CONFIG</em>, if non-empty + * + * - by <em>XDG_CONFIG_HOME</em>/notmuch/ where + * XDG_CONFIG_HOME defaults to "$HOME/.config". + * + * - by $HOME/.notmuch-config + * + * If <em>config_path</em> is "" (empty string) then do not + * open any configuration file. + * @endparblock + * @param[in] profile: + * @parblock + * Name of profile (configuration/database variant). + * + * If non-NULL, append to the directory / file path determined for + * <em>config_path</em> and <em>database_path</em>. + * + * If NULL then use + * - environment variable NOTMUCH_PROFILE if defined, + * - otherwise "default" for directories and "" (empty string) for paths. + * + * @endparblock + * @param[out] database + * @parblock + * Pointer to database object. May not be NULL. * * The caller should call notmuch_database_destroy when finished with * this database. * * In case of any failure, this function returns an error status and - * sets *database to NULL (after printing an error message on stderr). + * sets *database to NULL. * - * Return value: + * @endparblock + * @param[out] error_message + * If non-NULL, store error message from opening the database. + * Any such message is allocated by \a malloc(3) and should be freed + * by the caller. * - * NOTMUCH_STATUS_SUCCESS: Successfully opened the database. + * @retval NOTMUCH_STATUS_SUCCESS: Successfully opened the database. * - * NOTMUCH_STATUS_NULL_POINTER: The given 'path' argument is NULL. + * @retval NOTMUCH_STATUS_NULL_POINTER: The given \a database + * argument is NULL. * - * NOTMUCH_STATUS_OUT_OF_MEMORY: Out of memory. + * @retval NOTMUCH_STATUS_OUT_OF_MEMORY: Out of memory. * - * NOTMUCH_STATUS_FILE_ERROR: An error occurred trying to open the - * database file (such as permission denied, or file not found, + * @retval NOTMUCH_STATUS_FILE_ERROR: An error occurred trying to open the + * database or config file (such as permission denied, or file not found, * etc.), or the database version is unknown. * - * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred. - */ -notmuch_status_t -notmuch_database_open (const char *path, - notmuch_database_mode_t mode, - notmuch_database_t **database); -/** - * Like notmuch_database_open, except optionally return an error - * message. This message is allocated by malloc and should be freed by - * the caller. + * @retval NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred. + * + * @since libnotmuch 5.4 (notmuch 0.32) */ notmuch_status_t -notmuch_database_open_verbose (const char *path, - notmuch_database_mode_t mode, - notmuch_database_t **database, - char **error_message); +notmuch_database_open_with_config (const char *database_path, + notmuch_database_mode_t mode, + const char *config_path, + const char *profile, + notmuch_database_t **database, + char **error_message); /** * Retrieve last status string for given database. diff --git a/lib/open.cc b/lib/open.cc index 0001794a..7acaea7b 100644 --- a/lib/open.cc +++ b/lib/open.cc @@ -32,30 +32,57 @@ notmuch_database_open_verbose (const char *path, notmuch_database_mode_t mode, notmuch_database_t **database, char **status_string) +{ + return notmuch_database_open_with_config (path, mode, "", NULL, + database, status_string); +} + +notmuch_status_t +notmuch_database_open_with_config (const char *database_path, + notmuch_database_mode_t mode, + const char *config_path, + unused(const char *profile), + notmuch_database_t **database, + char **status_string) { notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; void *local = talloc_new (NULL); notmuch_database_t *notmuch = NULL; char *notmuch_path, *xapian_path, *incompat_features; + char *configured_database_path = NULL; char *message = NULL; struct stat st; int err; unsigned int version; + GKeyFile *key_file = NULL; static int initialized = 0; - if (path == NULL) { + /* XXX TODO: default locations for NULL case, handle profiles */ + if (config_path != NULL && ! EMPTY_STRING (config_path)) { + key_file = g_key_file_new (); + if (! g_key_file_load_from_file (key_file, config_path, G_KEY_FILE_NONE, NULL)) { + status = NOTMUCH_STATUS_FILE_ERROR; + goto DONE; + } + configured_database_path = g_key_file_get_value (key_file, "database", "path", NULL); + } + + if (database_path == NULL) + database_path = configured_database_path; + + if (database_path == NULL) { message = strdup ("Error: Cannot open a database for a NULL path.\n"); status = NOTMUCH_STATUS_NULL_POINTER; goto DONE; } - if (path[0] != '/') { + if (database_path[0] != '/') { message = strdup ("Error: Database path must be absolute.\n"); status = NOTMUCH_STATUS_PATH_ERROR; goto DONE; } - if (! (notmuch_path = talloc_asprintf (local, "%s/%s", path, ".notmuch"))) { + if (! (notmuch_path = talloc_asprintf (local, "%s/%s", database_path, ".notmuch"))) { message = strdup ("Out of memory\n"); status = NOTMUCH_STATUS_OUT_OF_MEMORY; goto DONE; @@ -89,8 +116,8 @@ notmuch_database_open_verbose (const char *path, notmuch = talloc_zero (NULL, notmuch_database_t); notmuch->exception_reported = false; notmuch->status_string = NULL; - notmuch->path = talloc_strdup (notmuch, path); - notmuch->config = NULL; + notmuch->path = talloc_strdup (notmuch, database_path); + strip_trailing (notmuch->path, '/'); notmuch->writable_xapian_db = NULL; @@ -185,6 +212,11 @@ notmuch_database_open_verbose (const char *path, if (status) goto DONE; + if (key_file) + status = _notmuch_config_load_from_file (notmuch, key_file); + if (status) + goto DONE; + status = _notmuch_database_setup_standard_query_fields (notmuch); if (status) goto DONE; diff --git a/test/T562-lib-database.sh b/test/T562-lib-database.sh index 887851dd..db251fe7 100755 --- a/test/T562-lib-database.sh +++ b/test/T562-lib-database.sh @@ -343,12 +343,12 @@ cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} char *result; EXPECT0(notmuch_database_close (db)); stat = notmuch_database_get_config (db, "foo", &result); - printf("%d\n", stat == NOTMUCH_STATUS_XAPIAN_EXCEPTION); + printf("%d\n", stat == NOTMUCH_STATUS_SUCCESS); } EOF cat <<EOF > EXPECTED == stdout == -0 +1 == stderr == EOF test_expect_equal_file EXPECTED OUTPUT diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index 8c34acf9..5fb1bb87 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -16,7 +16,12 @@ int main (int argc, char** argv) char *val; notmuch_status_t stat; - EXPECT0(notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db)); + EXPECT0(notmuch_database_open_with_config (argv[1], + NOTMUCH_DATABASE_MODE_READ_WRITE, + argv[2], + NULL, + &db, + NULL)); EOF @@ -26,7 +31,7 @@ cat <<EOF > c_tail EOF test_begin_subtest "notmuch_database_{set,get}_config" -cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} { EXPECT0(notmuch_database_set_config (db, "test.key1", "testvalue1")); EXPECT0(notmuch_database_set_config (db, "test.key2", "testvalue2")); @@ -46,7 +51,7 @@ test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "notmuch_database_get_config_list: empty list" -cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} { notmuch_config_list_t *list; EXPECT0(notmuch_database_get_config_list (db, "nonexistent", &list)); @@ -78,7 +83,7 @@ EOF test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "notmuch_database_get_config_list: all pairs" -cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} { notmuch_config_list_t *list; EXPECT0(notmuch_database_set_config (db, "zzzafter", "afterval")); @@ -123,7 +128,7 @@ EOF test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "notmuch_database_get_config_list: one prefix" -cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} { notmuch_config_list_t *list; EXPECT0(notmuch_database_get_config_list (db, "test.key", &list)); @@ -142,7 +147,7 @@ EOF test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "dump config" -cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} { EXPECT0(notmuch_database_set_config (db, "key with spaces", "value, with, spaces!")); } @@ -160,7 +165,7 @@ test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "restore config" notmuch dump --include=config >EXPECTED -cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} { EXPECT0(notmuch_database_set_config (db, "test.key1", "mutatedvalue")); } @@ -169,4 +174,24 @@ notmuch restore --include=config <EXPECTED notmuch dump --include=config >OUTPUT test_expect_equal_file EXPECTED OUTPUT +backup_database +test_begin_subtest "override config from file" +notmuch config set test.key1 overridden +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} +{ + EXPECT0(notmuch_database_get_config (db, "test.key1", &val)); + printf("test.key1 = %s\n", val); + EXPECT0(notmuch_database_get_config (db, "test.key2", &val)); + printf("test.key2 = %s\n", val); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +test.key1 = overridden +test.key2 = testvalue2 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT +restore_database + test_done -- 2.30.0
Fill in the remainder of the documented functionality for n_d_open_with_config with respect to config file location. Similar searching default locations of the database file still needs to be added. --- lib/config.cc | 2 +- lib/open.cc | 96 ++++++++++++++++++++++---- test/T590-libconfig.sh | 153 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 229 insertions(+), 22 deletions(-) diff --git a/lib/config.cc b/lib/config.cc index 8bce7ba8..32e5a9b7 100644 --- a/lib/config.cc +++ b/lib/config.cc @@ -251,7 +251,7 @@ _notmuch_config_load_from_file (notmuch_database_t *notmuch, GKeyFile *file) { notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; - gchar **groups,**keys, *val; + gchar **groups, **keys, *val; if (notmuch->config == NULL) notmuch->config = _notmuch_string_map_create (notmuch); diff --git a/lib/open.cc b/lib/open.cc index 7acaea7b..2d9c4365 100644 --- a/lib/open.cc +++ b/lib/open.cc @@ -37,6 +37,83 @@ notmuch_database_open_verbose (const char *path, database, status_string); } +static const char * +_xdg_dir (void *ctx, + const char *xdg_root_variable, + const char *xdg_prefix, + const char *profile_name) +{ + const char *xdg_root = getenv (xdg_root_variable); + + if (! xdg_root) { + const char *home = getenv ("HOME"); + + if (! home) return NULL; + + xdg_root = talloc_asprintf (ctx, + "%s/%s", + home, + xdg_prefix); + } + + if (! profile_name) + profile_name = getenv ("NOTMUCH_PROFILE"); + + if (! profile_name) + profile_name = "default"; + + return talloc_asprintf (ctx, + "%s/notmuch/%s", + xdg_root, + profile_name); +} + +static notmuch_status_t +_load_key_file (const char *path, + const char *profile, + GKeyFile **key_file) +{ + notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; + void *local = talloc_new (NULL); + + if (path && EMPTY_STRING (path)) + goto DONE; + + if (! path) + path = getenv ("NOTMUCH_CONFIG"); + + if (! path) { + const char *dir = _xdg_dir (local, "XDG_CONFIG_HOME", ".config", profile); + + if (dir) { + path = talloc_asprintf (local, "%s/config", dir); + if (access (path, R_OK) !=0) + path = NULL; + } + } + + if (! path) { + const char *home = getenv ("HOME"); + + path = talloc_asprintf (local, "%s/.notmuch-config", home); + + if (! profile) + profile = getenv ("NOTMUCH_PROFILE"); + + if (profile) + path = talloc_asprintf (local, "%s.%s", path, profile); + } + + *key_file = g_key_file_new (); + if (! g_key_file_load_from_file (*key_file, path, G_KEY_FILE_NONE, NULL)) { + status = NOTMUCH_STATUS_FILE_ERROR; + } + +DONE: + talloc_free (local); + return status; +} + notmuch_status_t notmuch_database_open_with_config (const char *database_path, notmuch_database_mode_t mode, @@ -49,7 +126,6 @@ notmuch_database_open_with_config (const char *database_path, void *local = talloc_new (NULL); notmuch_database_t *notmuch = NULL; char *notmuch_path, *xapian_path, *incompat_features; - char *configured_database_path = NULL; char *message = NULL; struct stat st; int err; @@ -57,18 +133,14 @@ notmuch_database_open_with_config (const char *database_path, GKeyFile *key_file = NULL; static int initialized = 0; - /* XXX TODO: default locations for NULL case, handle profiles */ - if (config_path != NULL && ! EMPTY_STRING (config_path)) { - key_file = g_key_file_new (); - if (! g_key_file_load_from_file (key_file, config_path, G_KEY_FILE_NONE, NULL)) { - status = NOTMUCH_STATUS_FILE_ERROR; - goto DONE; - } - configured_database_path = g_key_file_get_value (key_file, "database", "path", NULL); + status = _load_key_file (config_path, profile, &key_file); + if (status) { + message = strdup ("Error: cannot load config file"); + goto DONE; } - - if (database_path == NULL) - database_path = configured_database_path; + + if (! database_path && key_file) + database_path = g_key_file_get_value (key_file, "database", "path", NULL); if (database_path == NULL) { message = strdup ("Error: Cannot open a database for a NULL path.\n"); diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index 5fb1bb87..6bfbde56 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -15,14 +15,21 @@ int main (int argc, char** argv) notmuch_database_t *db; char *val; notmuch_status_t stat; + char *msg = NULL; - EXPECT0(notmuch_database_open_with_config (argv[1], + for (int i = 1; i < argc; i++) + if (strcmp (argv[i], "%NULL%") == 0) argv[i] = NULL; + + stat = notmuch_database_open_with_config (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, argv[2], - NULL, + argv[3], &db, - NULL)); - + &msg); + if (stat != NOTMUCH_STATUS_SUCCESS) { + fprintf (stderr, "error opening database: %d %s\n", stat, msg ? msg : ""); + exit (1); + } EOF cat <<EOF > c_tail @@ -51,7 +58,7 @@ test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "notmuch_database_get_config_list: empty list" -cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} %NULL% { notmuch_config_list_t *list; EXPECT0(notmuch_database_get_config_list (db, "nonexistent", &list)); @@ -83,7 +90,7 @@ EOF test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "notmuch_database_get_config_list: all pairs" -cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} %NULL% { notmuch_config_list_t *list; EXPECT0(notmuch_database_set_config (db, "zzzafter", "afterval")); @@ -128,7 +135,7 @@ EOF test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "notmuch_database_get_config_list: one prefix" -cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} %NULL% { notmuch_config_list_t *list; EXPECT0(notmuch_database_get_config_list (db, "test.key", &list)); @@ -147,7 +154,7 @@ EOF test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "dump config" -cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} %NULL% { EXPECT0(notmuch_database_set_config (db, "key with spaces", "value, with, spaces!")); } @@ -165,7 +172,7 @@ test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "restore config" notmuch dump --include=config >EXPECTED -cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} %NULL% { EXPECT0(notmuch_database_set_config (db, "test.key1", "mutatedvalue")); } @@ -194,4 +201,132 @@ EOF test_expect_equal_file EXPECTED OUTPUT restore_database +backup_database +test_begin_subtest "override config from \${NOTMUCH_CONFIG}" +notmuch config set test.key1 overridden +# second argument omitted to make argv[2] == NULL +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +{ + EXPECT0(notmuch_database_get_config (db, "test.key1", &val)); + printf("test.key1 = %s\n", val); + EXPECT0(notmuch_database_get_config (db, "test.key2", &val)); + printf("test.key2 = %s\n", val); +} +EOF +notmuch config set test.key1 +cat <<'EOF' >EXPECTED +== stdout == +test.key1 = overridden +test.key2 = testvalue2 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT +restore_database + +backup_database +test_begin_subtest "override config from \${HOME}/.notmuch-config" +ovconfig=${HOME}/.notmuch-config +cp ${NOTMUCH_CONFIG} ${ovconfig} +old_NOTMUCH_CONFIG=${NOTMUCH_CONFIG} +unset NOTMUCH_CONFIG +notmuch --config=${ovconfig} config set test.key1 overridden-home +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} %NULL% %NULL% +{ + EXPECT0(notmuch_database_get_config (db, "test.key1", &val)); + printf("test.key1 = %s\n", val); + EXPECT0(notmuch_database_get_config (db, "test.key2", &val)); + printf("test.key2 = %s\n", val); +} +EOF +rm -f ${ovconfig} +NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG} +cat <<'EOF' >EXPECTED +== stdout == +test.key1 = overridden-home +test.key2 = testvalue2 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT +restore_database + +backup_database +test_begin_subtest "override config from \${XDG_CONFIG_HOME}/notmuch" +ovconfig=${HOME}/.config/notmuch/default/config +mkdir -p $(dirname ${ovconfig}) +cp ${NOTMUCH_CONFIG} ${ovconfig} +old_NOTMUCH_CONFIG=${NOTMUCH_CONFIG} +unset NOTMUCH_CONFIG +notmuch --config=${ovconfig} config set test.key1 overridden-xdg +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} %NULL% %NULL% +{ + EXPECT0(notmuch_database_get_config (db, "test.key1", &val)); + printf("test.key1 = %s\n", val); + EXPECT0(notmuch_database_get_config (db, "test.key2", &val)); + printf("test.key2 = %s\n", val); +} +EOF +rm -f ${ovconfig} +NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG} +cat <<'EOF' >EXPECTED +== stdout == +test.key1 = overridden-xdg +test.key2 = testvalue2 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT +restore_database + +backup_database +test_begin_subtest "override config from \${XDG_CONFIG_HOME}/notmuch with profile" +ovconfig=${HOME}/.config/notmuch/work/config +mkdir -p $(dirname ${ovconfig}) +cp ${NOTMUCH_CONFIG} ${ovconfig} +old_NOTMUCH_CONFIG=${NOTMUCH_CONFIG} +unset NOTMUCH_CONFIG +notmuch --config=${ovconfig} config set test.key1 overridden-xdg-profile +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} %NULL% work +{ + EXPECT0(notmuch_database_get_config (db, "test.key1", &val)); + printf("test.key1 = %s\n", val); + EXPECT0(notmuch_database_get_config (db, "test.key2", &val)); + printf("test.key2 = %s\n", val); +} +EOF +rm -f ${ovconfig} +NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG} +cat <<'EOF' >EXPECTED +== stdout == +test.key1 = overridden-xdg-profile +test.key2 = testvalue2 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT +restore_database + +backup_database +test_begin_subtest "override config from \${HOME}/.notmuch-config.work (via args)" +ovconfig=${HOME}/.notmuch-config.work +cp ${NOTMUCH_CONFIG} ${ovconfig} +old_NOTMUCH_CONFIG=${NOTMUCH_CONFIG} +unset NOTMUCH_CONFIG +notmuch --config=${ovconfig} config set test.key1 overridden-profile +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} %NULL% work +{ + EXPECT0(notmuch_database_get_config (db, "test.key1", &val)); + printf("test.key1 = %s\n", val); + EXPECT0(notmuch_database_get_config (db, "test.key2", &val)); + printf("test.key2 = %s\n", val); +} +EOF +#rm -f ${ovconfig} +NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG} +cat <<'EOF' >EXPECTED +== stdout == +test.key1 = overridden-profile +test.key2 = testvalue2 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT +restore_database + test_done -- 2.30.0
The renaming and extra values will make sense when we start to convert subcommands to the new configuration framework. It will also avoid collisions with a new enum for configuration keys to be introduced in a future commit. --- notmuch-client.h | 10 ++++++---- notmuch-config.c | 6 +++--- notmuch.c | 36 ++++++++++++++++++------------------ 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/notmuch-client.h b/notmuch-client.h index ebd43e8d..f59b3965 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -253,14 +253,16 @@ json_quote_str (const void *ctx, const char *str); /* notmuch-config.c */ typedef enum { - NOTMUCH_CONFIG_OPEN = 1 << 0, - NOTMUCH_CONFIG_CREATE = 1 << 1, -} notmuch_config_mode_t; + NOTMUCH_COMMAND_CONFIG_OPEN = 1 << 0, + NOTMUCH_COMMAND_CONFIG_CREATE = 1 << 1, + NOTMUCH_COMMAND_DATABASE_EARLY = 1 << 2, + NOTMUCH_COMMAND_DATABASE_WRITE = 1 << 3, +} notmuch_command_mode_t; notmuch_config_t * notmuch_config_open (void *ctx, const char *filename, - notmuch_config_mode_t config_mode); + notmuch_command_mode_t config_mode); void notmuch_config_close (notmuch_config_t *config); diff --git a/notmuch-config.c b/notmuch-config.c index 19c2ddb3..cefb8274 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -324,7 +324,7 @@ get_config_from_file (notmuch_config_t *config, bool create_new) notmuch_config_t * notmuch_config_open (void *ctx, const char *filename, - notmuch_config_mode_t config_mode) + notmuch_command_mode_t config_mode) { GError *error = NULL; size_t tmp; @@ -359,8 +359,8 @@ notmuch_config_open (void *ctx, config->key_file = g_key_file_new (); - if (config_mode & NOTMUCH_CONFIG_OPEN) { - bool create_new = (config_mode & NOTMUCH_CONFIG_CREATE) != 0; + if (config_mode & NOTMUCH_COMMAND_CONFIG_OPEN) { + bool create_new = (config_mode & NOTMUCH_COMMAND_CONFIG_CREATE) != 0; if (! get_config_from_file (config, create_new)) { talloc_free (config); diff --git a/notmuch.c b/notmuch.c index 4ef1484f..314bf53e 100644 --- a/notmuch.c +++ b/notmuch.c @@ -33,7 +33,7 @@ typedef int (*command_function_t) (notmuch_config_t *config, int argc, char *arg typedef struct command { const char *name; command_function_t function; - notmuch_config_mode_t config_mode; + notmuch_command_mode_t mode; const char *summary; } command_t; @@ -136,41 +136,41 @@ notmuch_process_shared_indexing_options (notmuch_database_t *notmuch) static command_t commands[] = { - { NULL, notmuch_command, NOTMUCH_CONFIG_OPEN | NOTMUCH_CONFIG_CREATE, + { NULL, notmuch_command, NOTMUCH_COMMAND_CONFIG_OPEN | NOTMUCH_COMMAND_CONFIG_CREATE, "Notmuch main command." }, - { "setup", notmuch_setup_command, NOTMUCH_CONFIG_OPEN | NOTMUCH_CONFIG_CREATE, + { "setup", notmuch_setup_command, NOTMUCH_COMMAND_CONFIG_OPEN | NOTMUCH_COMMAND_CONFIG_CREATE, "Interactively set up notmuch for first use." }, - { "new", notmuch_new_command, NOTMUCH_CONFIG_OPEN, + { "new", notmuch_new_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Find and import new messages to the notmuch database." }, - { "insert", notmuch_insert_command, NOTMUCH_CONFIG_OPEN, + { "insert", notmuch_insert_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Add a new message into the maildir and notmuch database." }, - { "search", notmuch_search_command, NOTMUCH_CONFIG_OPEN, + { "search", notmuch_search_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Search for messages matching the given search terms." }, - { "address", notmuch_address_command, NOTMUCH_CONFIG_OPEN, + { "address", notmuch_address_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Get addresses from messages matching the given search terms." }, - { "show", notmuch_show_command, NOTMUCH_CONFIG_OPEN, + { "show", notmuch_show_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Show all messages matching the search terms." }, - { "count", notmuch_count_command, NOTMUCH_CONFIG_OPEN, + { "count", notmuch_count_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Count messages matching the search terms." }, - { "reply", notmuch_reply_command, NOTMUCH_CONFIG_OPEN, + { "reply", notmuch_reply_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Construct a reply template for a set of messages." }, - { "tag", notmuch_tag_command, NOTMUCH_CONFIG_OPEN, + { "tag", notmuch_tag_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Add/remove tags for all messages matching the search terms." }, - { "dump", notmuch_dump_command, NOTMUCH_CONFIG_OPEN, + { "dump", notmuch_dump_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Create a plain-text dump of the tags for each message." }, - { "restore", notmuch_restore_command, NOTMUCH_CONFIG_OPEN, + { "restore", notmuch_restore_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Restore the tags from the given dump file (see 'dump')." }, - { "compact", notmuch_compact_command, NOTMUCH_CONFIG_OPEN, + { "compact", notmuch_compact_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Compact the notmuch database." }, - { "reindex", notmuch_reindex_command, NOTMUCH_CONFIG_OPEN, + { "reindex", notmuch_reindex_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Re-index all messages matching the search terms." }, - { "config", notmuch_config_command, NOTMUCH_CONFIG_OPEN, + { "config", notmuch_config_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Get or set settings in the notmuch configuration file." }, #if WITH_EMACS { "emacs-mua", NULL, 0, "send mail with notmuch and emacs." }, #endif - { "help", notmuch_help_command, NOTMUCH_CONFIG_CREATE, /* create but don't save config */ + { "help", notmuch_help_command, NOTMUCH_COMMAND_CONFIG_CREATE, /* create but don't save config */ "This message, or more detailed help for the named command." } }; @@ -496,7 +496,7 @@ main (int argc, char *argv[]) goto DONE; } - config = notmuch_config_open (local, config_file_name, command->config_mode); + config = notmuch_config_open (local, config_file_name, command->mode); if (! config) { ret = EXIT_FAILURE; goto DONE; -- 2.30.0
By using an enum we can have better error detection than copy pasting key strings around. The question of what layer this belongs in is a bit tricky. Historically most of the keys are defined by the CLI. On the other hand features like excludes are supported in the library/bindings, and it makes sense to configure them from the library as well. The somewhat long prefix for notmuch_config_t is to avoid collisions with the existing usage in notmuch-client.h. --- lib/config.cc | 34 +++++++++++++++++++++++++ lib/notmuch.h | 56 ++++++++++++++++++++++++++++++++++++++++++ test/T590-libconfig.sh | 38 ++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) diff --git a/lib/config.cc b/lib/config.cc index 32e5a9b7..c07b607b 100644 --- a/lib/config.cc +++ b/lib/config.cc @@ -279,3 +279,37 @@ _notmuch_config_load_from_file (notmuch_database_t *notmuch, DONE: return status; } + +const char * +_notmuch_config_key_to_string (notmuch_config_key_t key) { + switch (key) { + case NOTMUCH_CONFIG_DATABASE_PATH: + return "database.path"; + case NOTMUCH_CONFIG_EXCLUDE_TAGS: + return "search.exclude_tags"; + case NOTMUCH_CONFIG_NEW_TAGS: + return "new.tags"; + case NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS: + return "maildir.synchronize_flags"; + case NOTMUCH_CONFIG_PRIMARY_EMAIL: + return "user.primary_email"; + case NOTMUCH_CONFIG_OTHER_EMAIL: + return "user.other_email"; + case NOTMUCH_CONFIG_USER_NAME: + return "user.name"; + default: + return NULL; + } +} + +const char * +notmuch_config_get (notmuch_database_t *notmuch, notmuch_config_key_t key) { + + return _notmuch_string_map_get (notmuch->config, _notmuch_config_key_to_string (key)); +} + +notmuch_status_t +notmuch_config_set (notmuch_database_t *notmuch, notmuch_config_key_t key, const char *val) { + + return notmuch_database_set_config (notmuch, _notmuch_config_key_to_string (key), val); +} diff --git a/lib/notmuch.h b/lib/notmuch.h index 9346640a..5c35b44b 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -2325,6 +2325,11 @@ notmuch_filenames_destroy (notmuch_filenames_t *filenames); * set config 'key' to 'value' * * @since libnotmuch 4.4 (notmuch 0.23) + * @retval #NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in + * read-only mode so message cannot be modified. + * @retval #NOTMUCH_STATUS_XAPIAN_EXCEPTION: an exception was thrown + * accessing the database. + * @retval #NOTMUCH_STATUS_SUCCESS */ notmuch_status_t notmuch_database_set_config (notmuch_database_t *db, const char *key, const char *value); @@ -2339,6 +2344,7 @@ notmuch_database_set_config (notmuch_database_t *db, const char *key, const char * caller. * * @since libnotmuch 4.4 (notmuch 0.23) + * */ notmuch_status_t notmuch_database_get_config (notmuch_database_t *db, const char *key, char **value); @@ -2400,6 +2406,56 @@ void notmuch_config_list_destroy (notmuch_config_list_t *config_list); +/** + * Configuration keys known to libnotmuch + */ +typedef enum _notmuch_config_key { + NOTMUCH_CONFIG_FIRST, + NOTMUCH_CONFIG_DATABASE_PATH = NOTMUCH_CONFIG_FIRST, + NOTMUCH_CONFIG_EXCLUDE_TAGS, + NOTMUCH_CONFIG_NEW_TAGS, + NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS, + NOTMUCH_CONFIG_PRIMARY_EMAIL, + NOTMUCH_CONFIG_OTHER_EMAIL, + NOTMUCH_CONFIG_USER_NAME, + NOTMUCH_CONFIG_LAST +} notmuch_config_key_t; + +/** + * get a configuration value from an open database. + * + * This value reflects all configuration information given at the time + * the database was opened. + * + * @param[in] notmuch database + * @param[in] key configuration key + * + * @since libnotmuch 5.4 (notmuch 0.32) + * + * @retval NULL if 'key' unknown or if no value is known for + * 'key'. Otherwise returns a string owned by notmuch which should + * not be modified nor freed by the caller. + */ +const char * +notmuch_config_get (notmuch_database_t *notmuch, notmuch_config_key_t key); + +/** + * set a configuration value from in an open database. + * + * This value reflects all configuration information given at the time + * the database was opened. + * + * @param[in,out] notmuch database open read/write + * @param[in] key configuration key + * @param[in] val configuration value + * + * @since libnotmuch 5.4 (notmuch 0.32) + * + * @retval returns any return value for notmuch_database_set_config. + */ +notmuch_status_t +notmuch_config_set (notmuch_database_t *notmuch, notmuch_config_key_t key, const char *val); + /** * get the current default indexing options for a given database. * diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index 6bfbde56..fcd96876 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -201,6 +201,44 @@ EOF test_expect_equal_file EXPECTED OUTPUT restore_database +backup_database +test_begin_subtest "get config by key" +notmuch config set test.key1 overridden +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} +{ + printf("before = %s\n", notmuch_config_get (db, NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS)); + EXPECT0(notmuch_database_set_config (db, "maildir.synchronize_flags", "false")); + printf("after = %s\n", notmuch_config_get (db, NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS)); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +before = true +after = false +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT +restore_database + +backup_database +test_begin_subtest "set config by key" +notmuch config set test.key1 overridden +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} +{ + printf("before = %s\n", notmuch_config_get (db, NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS)); + EXPECT0(notmuch_config_set (db, NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS, "false")); + printf("after = %s\n", notmuch_config_get (db, NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS)); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +before = true +after = false +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT +restore_database + backup_database test_begin_subtest "override config from \${NOTMUCH_CONFIG}" notmuch config set test.key1 overridden -- 2.30.0
This emulates the behaviour of notmuch_config_open defined in the CLI, in that it fills in default values if they are not otherwise defined. --- lib/config.cc | 50 +++++++++++++++++++++++++++++++++++++++++- lib/notmuch-private.h | 3 +++ lib/open.cc | 4 ++++ test/T590-libconfig.sh | 27 +++++++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) diff --git a/lib/config.cc b/lib/config.cc index c07b607b..a6b75913 100644 --- a/lib/config.cc +++ b/lib/config.cc @@ -31,6 +31,8 @@ struct _notmuch_config_list { char *current_val; }; +static const char * _notmuch_config_key_to_string (notmuch_config_key_t key); + static int _notmuch_config_list_destroy (notmuch_config_list_t *list) { @@ -280,7 +282,7 @@ _notmuch_config_load_from_file (notmuch_database_t *notmuch, return status; } -const char * +static const char * _notmuch_config_key_to_string (notmuch_config_key_t key) { switch (key) { case NOTMUCH_CONFIG_DATABASE_PATH: @@ -302,6 +304,52 @@ _notmuch_config_key_to_string (notmuch_config_key_t key) { } } +static const char * +_notmuch_config_default (void *ctx, notmuch_config_key_t key) { + char *path; + + switch (key) { + case NOTMUCH_CONFIG_DATABASE_PATH: + path = getenv ("MAILDIR"); + if (path) + path = talloc_strdup (ctx, path); + else + path = talloc_asprintf (ctx, "%s/mail", + getenv ("HOME")); + return path; + case NOTMUCH_CONFIG_EXCLUDE_TAGS: + return ""; + case NOTMUCH_CONFIG_NEW_TAGS: + return "inbox;unread"; + case NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS: + return "true"; + case NOTMUCH_CONFIG_USER_NAME: + case NOTMUCH_CONFIG_PRIMARY_EMAIL: + case NOTMUCH_CONFIG_OTHER_EMAIL: + return NULL; + default: + case NOTMUCH_CONFIG_LAST: + INTERNAL_ERROR ("illegal key enum %d", key); + } +} + +notmuch_status_t +_notmuch_config_load_defaults (notmuch_database_t *notmuch) { + notmuch_config_key_t key; + for (key = NOTMUCH_CONFIG_FIRST; + key < NOTMUCH_CONFIG_LAST; + key = notmuch_config_key_t(key + 1)) { + const char *val = notmuch_config_get (notmuch, key); + const char *key_string = _notmuch_config_key_to_string (key); + + val = _notmuch_string_map_get (notmuch->config, key_string); + if (! val) { + _notmuch_string_map_set (notmuch->config, key_string, _notmuch_config_default (notmuch, key)); + } + } + return NOTMUCH_STATUS_SUCCESS; +} + const char * notmuch_config_get (notmuch_database_t *notmuch, notmuch_config_key_t key) { diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 40b1a855..961d50cf 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -710,6 +710,9 @@ _notmuch_config_load_from_database (notmuch_database_t * db); notmuch_status_t _notmuch_config_load_from_file (notmuch_database_t * db, GKeyFile *file); + +notmuch_status_t +_notmuch_config_load_defaults (notmuch_database_t * db); NOTMUCH_END_DECLS #ifdef __cplusplus diff --git a/lib/open.cc b/lib/open.cc index 2d9c4365..ad74ccd6 100644 --- a/lib/open.cc +++ b/lib/open.cc @@ -289,6 +289,10 @@ notmuch_database_open_with_config (const char *database_path, if (status) goto DONE; + status = _notmuch_config_load_defaults (notmuch); + if (status) + goto DONE; + status = _notmuch_database_setup_standard_query_fields (notmuch); if (status) goto DONE; diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index fcd96876..0c148ad9 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -239,6 +239,33 @@ EOF test_expect_equal_file EXPECTED OUTPUT restore_database +test_begin_subtest "load default values" +export MAILDIR=${MAIL_DIR} +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} '' %NULL% +{ + notmuch_config_key_t key; + for (key = NOTMUCH_CONFIG_FIRST; + key < NOTMUCH_CONFIG_LAST; + key = (notmuch_config_key_t)(key + 1)) { + const char *val = notmuch_config_get (db, key); + printf("%s\n", val ? val : "NULL" ); + } +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +MAIL_DIR + +inbox;unread +true +NULL +NULL +NULL +== stderr == +EOF +unset MAILDIR +test_expect_equal_file EXPECTED OUTPUT + backup_database test_begin_subtest "override config from \${NOTMUCH_CONFIG}" notmuch config set test.key1 overridden -- 2.30.0
This will allow transitioning individual subcommands to the new configuration framework. Eventually when they are all converted we can remove the notmuch_config_t * argument. For now, live with the parameter shadowing in some some subcommands; it will go away when they are converted. --- notmuch-client.h | 28 ++++++++++++++-------------- notmuch-compact.c | 2 +- notmuch-config.c | 2 +- notmuch-count.c | 2 +- notmuch-dump.c | 2 +- notmuch-insert.c | 2 +- notmuch-new.c | 2 +- notmuch-reindex.c | 2 +- notmuch-reply.c | 2 +- notmuch-restore.c | 2 +- notmuch-search.c | 4 ++-- notmuch-setup.c | 1 + notmuch-show.c | 2 +- notmuch-tag.c | 2 +- notmuch.c | 16 ++++++++++------ 15 files changed, 38 insertions(+), 33 deletions(-) diff --git a/notmuch-client.h b/notmuch-client.h index f59b3965..e8fb0323 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -171,46 +171,46 @@ void notmuch_exit_if_unsupported_format (void); int -notmuch_count_command (notmuch_config_t *config, int argc, char *argv[]); +notmuch_count_command (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); int -notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[]); +notmuch_dump_command (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); int -notmuch_new_command (notmuch_config_t *config, int argc, char *argv[]); +notmuch_new_command (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); int -notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[]); +notmuch_insert_command (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); int -notmuch_reindex_command (notmuch_config_t *config, int argc, char *argv[]); +notmuch_reindex_command (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); int -notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[]); +notmuch_reply_command (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); int -notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[]); +notmuch_restore_command (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); int -notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]); +notmuch_search_command (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); int -notmuch_address_command (notmuch_config_t *config, int argc, char *argv[]); +notmuch_address_command (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); int -notmuch_setup_command (notmuch_config_t *config, int argc, char *argv[]); +notmuch_setup_command (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); int -notmuch_show_command (notmuch_config_t *config, int argc, char *argv[]); +notmuch_show_command (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); int -notmuch_tag_command (notmuch_config_t *config, int argc, char *argv[]); +notmuch_tag_command (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); int -notmuch_config_command (notmuch_config_t *config, int argc, char *argv[]); +notmuch_config_command (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); int -notmuch_compact_command (notmuch_config_t *config, int argc, char *argv[]); +notmuch_compact_command (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); const char * notmuch_time_relative_date (const void *ctx, time_t then); diff --git a/notmuch-compact.c b/notmuch-compact.c index f8996cf4..ab2066e1 100644 --- a/notmuch-compact.c +++ b/notmuch-compact.c @@ -27,7 +27,7 @@ status_update_cb (const char *msg, unused (void *closure)) } int -notmuch_compact_command (notmuch_config_t *config, int argc, char *argv[]) +notmuch_compact_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) { const char *path = notmuch_config_get_database_path (config); const char *backup_path = NULL; diff --git a/notmuch-config.c b/notmuch-config.c index cefb8274..4fa274c7 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -1102,7 +1102,7 @@ notmuch_config_command_list (notmuch_config_t *config) } int -notmuch_config_command (notmuch_config_t *config, int argc, char *argv[]) +notmuch_config_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) { int ret; int opt_index; diff --git a/notmuch-count.c b/notmuch-count.c index d8ad7d6d..f752ef62 100644 --- a/notmuch-count.c +++ b/notmuch-count.c @@ -148,7 +148,7 @@ count_file (notmuch_database_t *notmuch, FILE *input, const char **exclude_tags, } int -notmuch_count_command (notmuch_config_t *config, int argc, char *argv[]) +notmuch_count_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) { notmuch_database_t *notmuch; char *query_str; diff --git a/notmuch-dump.c b/notmuch-dump.c index 887ef7f0..eb629dc9 100644 --- a/notmuch-dump.c +++ b/notmuch-dump.c @@ -361,7 +361,7 @@ notmuch_database_dump (notmuch_database_t *notmuch, } int -notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[]) +notmuch_dump_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) { notmuch_database_t *notmuch; const char *query_str = NULL; diff --git a/notmuch-insert.c b/notmuch-insert.c index 1d3b0150..de160309 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -444,7 +444,7 @@ add_file (notmuch_database_t *notmuch, const char *path, tag_op_list_t *tag_ops, } int -notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[]) +notmuch_insert_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) { notmuch_status_t status, close_status; notmuch_database_t *notmuch; diff --git a/notmuch-new.c b/notmuch-new.c index 4075d395..e0e3de25 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -1043,7 +1043,7 @@ print_results (const add_files_state_t *state) } int -notmuch_new_command (notmuch_config_t *config, int argc, char *argv[]) +notmuch_new_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) { notmuch_database_t *notmuch; add_files_state_t add_files_state = { diff --git a/notmuch-reindex.c b/notmuch-reindex.c index 5a39ade1..9d337c48 100644 --- a/notmuch-reindex.c +++ b/notmuch-reindex.c @@ -83,7 +83,7 @@ reindex_query (notmuch_database_t *notmuch, const char *query_string, } int -notmuch_reindex_command (notmuch_config_t *config, int argc, char *argv[]) +notmuch_reindex_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) { char *query_string = NULL; notmuch_database_t *notmuch; diff --git a/notmuch-reply.c b/notmuch-reply.c index ceb4f39b..a8ffbf75 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -700,7 +700,7 @@ do_reply (notmuch_config_t *config, } int -notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[]) +notmuch_reply_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) { notmuch_database_t *notmuch; notmuch_query_t *query; diff --git a/notmuch-restore.c b/notmuch-restore.c index e2dc3d45..544f4228 100644 --- a/notmuch-restore.c +++ b/notmuch-restore.c @@ -219,7 +219,7 @@ parse_sup_line (void *ctx, char *line, } int -notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[]) +notmuch_restore_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) { notmuch_database_t *notmuch; bool accumulate = false; diff --git a/notmuch-search.c b/notmuch-search.c index 2805d960..34e27058 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -805,7 +805,7 @@ static const notmuch_opt_desc_t common_options[] = { }; int -notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) +notmuch_search_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) { search_context_t *ctx = &search_context; int opt_index, ret; @@ -871,7 +871,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) } int -notmuch_address_command (notmuch_config_t *config, int argc, char *argv[]) +notmuch_address_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) { search_context_t *ctx = &search_context; int opt_index, ret; diff --git a/notmuch-setup.c b/notmuch-setup.c index cd1a52ff..67214470 100644 --- a/notmuch-setup.c +++ b/notmuch-setup.c @@ -122,6 +122,7 @@ parse_tag_list (void *ctx, char *response) int notmuch_setup_command (notmuch_config_t *config, + unused(notmuch_database_t *notmuch), int argc, char *argv[]) { char *response = NULL; diff --git a/notmuch-show.c b/notmuch-show.c index dd836add..04b90cd7 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -1215,7 +1215,7 @@ static const notmuch_show_format_t *formatters[] = { }; int -notmuch_show_command (notmuch_config_t *config, int argc, char *argv[]) +notmuch_show_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) { notmuch_database_t *notmuch; notmuch_query_t *query; diff --git a/notmuch-tag.c b/notmuch-tag.c index 05b1837d..205f2733 100644 --- a/notmuch-tag.c +++ b/notmuch-tag.c @@ -187,7 +187,7 @@ tag_file (void *ctx, notmuch_database_t *notmuch, tag_op_flag_t flags, } int -notmuch_tag_command (notmuch_config_t *config, int argc, char *argv[]) +notmuch_tag_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) { tag_op_list_t *tag_ops = NULL; char *query_string = NULL; diff --git a/notmuch.c b/notmuch.c index 314bf53e..fd4a7945 100644 --- a/notmuch.c +++ b/notmuch.c @@ -27,8 +27,11 @@ * * The return value will be used as notmuch exit status code, * preferably EXIT_SUCCESS or EXIT_FAILURE. + * + * Each subcommand should be passed either a config object, or an open + * database */ -typedef int (*command_function_t) (notmuch_config_t *config, int argc, char *argv[]); +typedef int (*command_function_t) (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); typedef struct command { const char *name; @@ -38,10 +41,10 @@ typedef struct command { } command_t; static int -notmuch_help_command (notmuch_config_t *config, int argc, char *argv[]); +notmuch_help_command (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); static int -notmuch_command (notmuch_config_t *config, int argc, char *argv[]); +notmuch_command (notmuch_config_t *config, notmuch_database_t *notmuch, int argc, char *argv[]); static int _help_for (const char *topic); @@ -335,7 +338,7 @@ _help_for (const char *topic_name) } static int -notmuch_help_command (unused (notmuch_config_t *config), int argc, char *argv[]) +notmuch_help_command (unused (notmuch_config_t *config), unused(notmuch_database_t *notmuch), int argc, char *argv[]) { int opt_index; @@ -360,6 +363,7 @@ notmuch_help_command (unused (notmuch_config_t *config), int argc, char *argv[]) */ static int notmuch_command (notmuch_config_t *config, + unused(notmuch_database_t *notmuch), unused(int argc), unused(char **argv)) { char *db_path; @@ -369,7 +373,7 @@ notmuch_command (notmuch_config_t *config, * notmuch_setup_command which will give a nice welcome message, * and interactively guide the user through the configuration. */ if (notmuch_config_is_new (config)) - return notmuch_setup_command (config, 0, NULL); + return notmuch_setup_command (config, NULL, 0, NULL); /* Notmuch is already configured, but is there a database? */ db_path = talloc_asprintf (config, "%s/%s", @@ -502,7 +506,7 @@ main (int argc, char *argv[]) goto DONE; } - ret = (command->function)(config, argc - opt_index, argv + opt_index); + ret = (command->function)(config, NULL, argc - opt_index, argv + opt_index); DONE: if (config) -- 2.30.0
This will be used to make iterators for configuration values. --- util/string-util.c | 23 +++++++++++++++++++++++ util/string-util.h | 14 ++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/util/string-util.c b/util/string-util.c index de8430b2..27f8a26b 100644 --- a/util/string-util.c +++ b/util/string-util.c @@ -24,6 +24,7 @@ #include <ctype.h> #include <errno.h> +#include <stdbool.h> char * strtok_len (char *s, const char *delim, size_t *len) @@ -37,6 +38,28 @@ strtok_len (char *s, const char *delim, size_t *len) return *len ? s : NULL; } +const char * +strsplit_len (const char *s, char delim, size_t *len) +{ + bool escaping = false; + size_t count = 0; + + /* Skip initial unescaped delimiters */ + while (*s && *s == delim) + s++; + + while (s[count] && (escaping || s[count] != delim)) { + escaping = (s[count] == '\\'); + count++; + } + + if (count==0) + return NULL; + + *len = count; + return s; +} + const char * strtok_len_c (const char *s, const char *delim, size_t *len) { diff --git a/util/string-util.h b/util/string-util.h index fb95a740..80647c5f 100644 --- a/util/string-util.h +++ b/util/string-util.h @@ -26,6 +26,20 @@ char *strtok_len (char *s, const char *delim, size_t *len); /* Const version of strtok_len. */ const char *strtok_len_c (const char *s, const char *delim, size_t *len); +/* Simplified version of strtok_len, with a single delimiter. + * Handles escaping delimiters with \ + * Usage pattern: + * + * const char *tok = input; + * const char *delim = ';'; + * size_t tok_len = 0; + * + * while ((tok = strsplit_len (tok + tok_len, delim, &tok_len)) != NULL) { + * // do stuff with string tok of length tok_len + * } + */ +const char *strsplit_len (const char *s, char delim, size_t *len); + /* Return a talloced string with str sanitized. * * Whitespace characters (tabs and newlines) are replaced with spaces, -- 2.30.0
This is intended to avoid duplicating the string splitting and traversal code for all clients of the config API. --- lib/config.cc | 52 ++++++++++++++++++++++++++++++++++ lib/notmuch.h | 64 ++++++++++++++++++++++++++++++++++++++++++ notmuch.c | 35 +++++++++++++++++++---- test/T590-libconfig.sh | 48 ++++++++++++++++++++++++++++++- 4 files changed, 192 insertions(+), 7 deletions(-) diff --git a/lib/config.cc b/lib/config.cc index a6b75913..0fe9a268 100644 --- a/lib/config.cc +++ b/lib/config.cc @@ -31,6 +31,11 @@ struct _notmuch_config_list { char *current_val; }; +struct _notmuch_config_values { + const char *iterator; + size_t tok_len; +}; + static const char * _notmuch_config_key_to_string (notmuch_config_key_t key); static int @@ -248,6 +253,53 @@ _notmuch_config_load_from_database (notmuch_database_t *notmuch) return status; } +notmuch_config_values_t * +notmuch_config_get_values (notmuch_database_t *notmuch, notmuch_config_key_t key) +{ + notmuch_config_values_t *values; + + const char *str; + const char *key_str = _notmuch_config_key_to_string (key); + + if (! key_str) + return NULL; + + str = _notmuch_string_map_get (notmuch->config, key_str); + if (! str) + return NULL; + + values = talloc (notmuch, notmuch_config_values_t); + if (unlikely(! values)) + return NULL; + + values->iterator = strsplit_len (str, ';', &(values->tok_len)); + return values; +} + +notmuch_bool_t +notmuch_config_values_valid (notmuch_config_values_t *values) { + if (! values) + return false; + + return (values->iterator != NULL); +} + +const char * +notmuch_config_values_get (notmuch_config_values_t *values) { + return talloc_strndup (values, values->iterator, values->tok_len); +} + +void +notmuch_config_values_move_to_next (notmuch_config_values_t *values) { + values->iterator += values->tok_len; + values->iterator = strsplit_len (values->iterator, ';', &(values->tok_len)); +} + +void +notmuch_config_values_destroy (notmuch_config_values_t *values) { + talloc_free (values); +} + notmuch_status_t _notmuch_config_load_from_file (notmuch_database_t *notmuch, GKeyFile *file) diff --git a/lib/notmuch.h b/lib/notmuch.h index 5c35b44b..7aeff567 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -236,6 +236,7 @@ typedef struct _notmuch_tags notmuch_tags_t; typedef struct _notmuch_directory notmuch_directory_t; typedef struct _notmuch_filenames notmuch_filenames_t; typedef struct _notmuch_config_list notmuch_config_list_t; +typedef struct _notmuch_config_values notmuch_config_values_t; typedef struct _notmuch_indexopts notmuch_indexopts_t; #endif /* __DOXYGEN__ */ @@ -2456,6 +2457,69 @@ notmuch_config_get (notmuch_database_t *notmuch, notmuch_config_key_t key); notmuch_status_t notmuch_config_set (notmuch_database_t *notmuch, notmuch_config_key_t key, const char *val); +/** + * Returns an iterator for a ';'-delimited list of configuration values + * + * These values reflect all configuration information given at the + * time the database was opened. + * + * @param[in] notmuch database + * @param[in] key configuration key + * + * @since libnotmuch 5.4 (notmuch 0.32) + * + * @retval NULL in case of error. + */ +notmuch_config_values_t * +notmuch_config_get_values (notmuch_database_t *notmuch, notmuch_config_key_t key); + +/** + * Is the given 'config_values' iterator pointing at a valid element. + * + * @param[in] values iterator + * + * @since libnotmuch 5.4 (notmuch 0.32) + * + * @retval FALSE if passed a NULL pointer, or the iterator is exhausted. + * + */ +notmuch_bool_t +notmuch_config_values_valid (notmuch_config_values_t *values); + +/** + * Get the current value from the 'values' iterator + * + * @param[in] values iterator + * + * @since libnotmuch 5.4 (notmuch 0.32) + * + * @retval a string with the same lifetime as the iterator + */ +const char * +notmuch_config_values_get (notmuch_config_values_t *values); + +/** + * Move the 'values' iterator to the next element + * + * @param[in,out] values iterator + * + * @since libnotmuch 5.4 (notmuch 0.32) + * + */ +void +notmuch_config_values_move_to_next (notmuch_config_values_t *values); + +/** + * Destroy a config values iterator, along with any associated + * resources. + * + * @param[in,out] values iterator + * + * @since libnotmuch 5.4 (notmuch 0.32) + */ +void +notmuch_config_values_destroy (notmuch_config_values_t *values); + /** * get the current default indexing options for a given database. * diff --git a/notmuch.c b/notmuch.c index fd4a7945..e0649048 100644 --- a/notmuch.c +++ b/notmuch.c @@ -457,6 +457,7 @@ main (int argc, char *argv[]) command_t *command; const char *config_file_name = NULL; notmuch_config_t *config = NULL; + notmuch_database_t *notmuch = NULL; int opt_index; int ret; @@ -500,13 +501,35 @@ main (int argc, char *argv[]) goto DONE; } - config = notmuch_config_open (local, config_file_name, command->mode); - if (! config) { - ret = EXIT_FAILURE; - goto DONE; - } + if (command->mode & NOTMUCH_COMMAND_DATABASE_EARLY) { + char *status_string = NULL; + notmuch_database_mode_t mode; + if (command->mode & NOTMUCH_COMMAND_DATABASE_WRITE) + mode = NOTMUCH_DATABASE_MODE_READ_WRITE; + else + mode = NOTMUCH_DATABASE_MODE_READ_ONLY; + + if (notmuch_database_open_with_config (NULL, + mode, + config_file_name, + NULL, + ¬much, + &status_string)) { + if (status_string) { + fputs (status_string, stderr); + free (status_string); + } - ret = (command->function)(config, NULL, argc - opt_index, argv + opt_index); + return EXIT_FAILURE; + } + } else { + config = notmuch_config_open (local, config_file_name, command->mode); + if (! config) { + ret = EXIT_FAILURE; + goto DONE; + } + } + ret = (command->function)(config, notmuch, argc - opt_index, argv + opt_index); DONE: if (config) diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index 0c148ad9..efbee61d 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -201,9 +201,55 @@ EOF test_expect_equal_file EXPECTED OUTPUT restore_database +test_begin_subtest "notmuch_config_get_values" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} %NULL% +{ + notmuch_config_values_t *values; + EXPECT0(notmuch_config_set (db, NOTMUCH_CONFIG_NEW_TAGS, "a;b;c")); + for (values = notmuch_config_get_values (db, NOTMUCH_CONFIG_NEW_TAGS); + notmuch_config_values_valid (values); + notmuch_config_values_move_to_next (values)) + { + puts (notmuch_config_values_get (values)); + } +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +a +b +c +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT +restore_database + +backup_database +test_begin_subtest "notmuch_config_get_values, trailing ;" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} %NULL% +{ + notmuch_config_values_t *values; + EXPECT0(notmuch_config_set (db, NOTMUCH_CONFIG_NEW_TAGS, "a;b;c")); + for (values = notmuch_config_get_values (db, NOTMUCH_CONFIG_NEW_TAGS); + notmuch_config_values_valid (values); + notmuch_config_values_move_to_next (values)) + { + puts (notmuch_config_values_get (values)); + } +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +a +b +c +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT +restore_database + backup_database test_begin_subtest "get config by key" -notmuch config set test.key1 overridden cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} { printf("before = %s\n", notmuch_config_get (db, NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS)); -- 2.30.0
This is relatively cheap, and makes it easier to transform existing code which uses arrays of pointers to store configuration lists. --- lib/config.cc | 43 +++++++++++++++++++++++++++++++++--------- lib/notmuch.h | 12 ++++++++++++ test/T590-libconfig.sh | 32 +++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 9 deletions(-) diff --git a/lib/config.cc b/lib/config.cc index 0fe9a268..b2957f0c 100644 --- a/lib/config.cc +++ b/lib/config.cc @@ -34,6 +34,8 @@ struct _notmuch_config_list { struct _notmuch_config_values { const char *iterator; size_t tok_len; + const char *string; + void *children; /* talloc_context */ }; static const char * _notmuch_config_key_to_string (notmuch_config_key_t key); @@ -256,23 +258,33 @@ _notmuch_config_load_from_database (notmuch_database_t *notmuch) notmuch_config_values_t * notmuch_config_get_values (notmuch_database_t *notmuch, notmuch_config_key_t key) { - notmuch_config_values_t *values; + notmuch_config_values_t *values = NULL; + bool ok = false; - const char *str; const char *key_str = _notmuch_config_key_to_string (key); if (! key_str) - return NULL; - - str = _notmuch_string_map_get (notmuch->config, key_str); - if (! str) - return NULL; + goto DONE; values = talloc (notmuch, notmuch_config_values_t); if (unlikely(! values)) - return NULL; + goto DONE; + + values->children = talloc_new (values); + + values->string = _notmuch_string_map_get (notmuch->config, key_str); + if (! values->string) + goto DONE; + + values->iterator = strsplit_len (values->string, ';', &(values->tok_len)); + ok = true; - values->iterator = strsplit_len (str, ';', &(values->tok_len)); + DONE: + if (!ok) { + if (values) + talloc_free(values); + return NULL; + } return values; } @@ -289,6 +301,19 @@ notmuch_config_values_get (notmuch_config_values_t *values) { return talloc_strndup (values, values->iterator, values->tok_len); } +void +notmuch_config_values_start (notmuch_config_values_t *values) { + if (values == NULL) + return; + if (values->children) { + talloc_free (values->children); + } + + values->children = talloc_new (values); + + values->iterator = strsplit_len (values->string, ';', &(values->tok_len)); +} + void notmuch_config_values_move_to_next (notmuch_config_values_t *values) { values->iterator += values->tok_len; diff --git a/lib/notmuch.h b/lib/notmuch.h index 7aeff567..39f39423 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -2509,6 +2509,18 @@ notmuch_config_values_get (notmuch_config_values_t *values); void notmuch_config_values_move_to_next (notmuch_config_values_t *values); + +/** + * reset the 'values' iterator to the first element + * + * @param[in,out] values iterator. A NULL value is ignored. + * + * @since libnotmuch 5.4 (notmuch 0.32) + * + */ +void +notmuch_config_values_start (notmuch_config_values_t *values); + /** * Destroy a config values iterator, along with any associated * resources. diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index efbee61d..ab439078 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -224,6 +224,38 @@ EOF test_expect_equal_file EXPECTED OUTPUT restore_database +test_begin_subtest "notmuch_config_get_values (restart)" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} %NULL% +{ + notmuch_config_values_t *values; + EXPECT0(notmuch_config_set (db, NOTMUCH_CONFIG_NEW_TAGS, "a;b;c")); + for (values = notmuch_config_get_values (db, NOTMUCH_CONFIG_NEW_TAGS); + notmuch_config_values_valid (values); + notmuch_config_values_move_to_next (values)) + { + puts (notmuch_config_values_get (values)); + } + for (notmuch_config_values_start (values); + notmuch_config_values_valid (values); + notmuch_config_values_move_to_next (values)) + { + puts (notmuch_config_values_get (values)); + } +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +a +b +c +a +b +c +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT +restore_database + backup_database test_begin_subtest "notmuch_config_get_values, trailing ;" cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} %NULL% -- 2.30.0
The main effort is changing from the old argv style config list iterators to the new more opaque ones provided by the library (and backed by the database+file config cache). --- notmuch-count.c | 48 +++++++++++-------------- notmuch.c | 2 +- test/T035-read-config.sh | 75 ++++++++++++++++++++++++++++++++++++++++ test/T140-excludes.sh | 15 ++++++++ 4 files changed, 112 insertions(+), 28 deletions(-) create mode 100755 test/T035-read-config.sh diff --git a/notmuch-count.c b/notmuch-count.c index f752ef62..048b1f44 100644 --- a/notmuch-count.c +++ b/notmuch-count.c @@ -64,10 +64,9 @@ count_files (notmuch_query_t *query) /* return 0 on success, -1 on failure */ static int print_count (notmuch_database_t *notmuch, const char *query_str, - const char **exclude_tags, size_t exclude_tags_length, int output, int print_lastmod) + notmuch_config_values_t *exclude_tags, int output, int print_lastmod) { notmuch_query_t *query; - size_t i; int count; unsigned int ucount; unsigned long revision; @@ -81,13 +80,18 @@ print_count (notmuch_database_t *notmuch, const char *query_str, return -1; } - for (i = 0; i < exclude_tags_length; i++) { - status = notmuch_query_add_tag_exclude (query, exclude_tags[i]); - if (status && status != NOTMUCH_STATUS_IGNORED) { - print_status_query ("notmuch count", query, status); - return -1; + for (notmuch_config_values_start (exclude_tags); + notmuch_config_values_valid (exclude_tags); + notmuch_config_values_move_to_next (exclude_tags)) { + + status = notmuch_query_add_tag_exclude (query, + notmuch_config_values_get (exclude_tags)); + if (status && status != NOTMUCH_STATUS_IGNORED) { + print_status_query ("notmuch count", query, status); + ret = -1; + goto DONE; + } } - } switch (output) { case OUTPUT_MESSAGES: @@ -127,8 +131,8 @@ print_count (notmuch_database_t *notmuch, const char *query_str, } static int -count_file (notmuch_database_t *notmuch, FILE *input, const char **exclude_tags, - size_t exclude_tags_length, int output, int print_lastmod) +count_file (notmuch_database_t *notmuch, FILE *input, notmuch_config_values_t *exclude_tags, + int output, int print_lastmod) { char *line = NULL; ssize_t line_len; @@ -137,8 +141,7 @@ count_file (notmuch_database_t *notmuch, FILE *input, const char **exclude_tags, while (! ret && (line_len = getline (&line, &line_size, input)) != -1) { chomp_newline (line); - ret = print_count (notmuch, line, exclude_tags, exclude_tags_length, - output, print_lastmod); + ret = print_count (notmuch, line, exclude_tags, output, print_lastmod); } if (line) @@ -148,15 +151,13 @@ count_file (notmuch_database_t *notmuch, FILE *input, const char **exclude_tags, } int -notmuch_count_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) +notmuch_count_command (unused(notmuch_config_t *config), notmuch_database_t *notmuch, int argc, char *argv[]) { - notmuch_database_t *notmuch; char *query_str; int opt_index; int output = OUTPUT_MESSAGES; bool exclude = true; - const char **search_exclude_tags = NULL; - size_t search_exclude_tags_length = 0; + notmuch_config_values_t *exclude_tags = NULL; bool batch = false; bool print_lastmod = false; FILE *input = stdin; @@ -200,29 +201,22 @@ notmuch_count_command (notmuch_config_t *config, unused(notmuch_database_t *notm return EXIT_FAILURE; } - if (notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much)) - return EXIT_FAILURE; - notmuch_exit_if_unmatched_db_uuid (notmuch); - query_str = query_string_from_args (config, argc - opt_index, argv + opt_index); + query_str = query_string_from_args (notmuch, argc - opt_index, argv + opt_index); if (query_str == NULL) { fprintf (stderr, "Out of memory.\n"); return EXIT_FAILURE; } if (exclude) { - search_exclude_tags = notmuch_config_get_search_exclude_tags - (config, &search_exclude_tags_length); + exclude_tags = notmuch_config_get_values (notmuch, NOTMUCH_CONFIG_EXCLUDE_TAGS); } if (batch) - ret = count_file (notmuch, input, search_exclude_tags, - search_exclude_tags_length, output, print_lastmod); + ret = count_file (notmuch, input, exclude_tags, output, print_lastmod); else - ret = print_count (notmuch, query_str, search_exclude_tags, - search_exclude_tags_length, output, print_lastmod); + ret = print_count (notmuch, query_str, exclude_tags, output, print_lastmod); notmuch_database_destroy (notmuch); diff --git a/notmuch.c b/notmuch.c index e0649048..40527893 100644 --- a/notmuch.c +++ b/notmuch.c @@ -153,7 +153,7 @@ static command_t commands[] = { "Get addresses from messages matching the given search terms." }, { "show", notmuch_show_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Show all messages matching the search terms." }, - { "count", notmuch_count_command, NOTMUCH_COMMAND_CONFIG_OPEN, + { "count", notmuch_count_command, NOTMUCH_COMMAND_DATABASE_EARLY, "Count messages matching the search terms." }, { "reply", notmuch_reply_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Construct a reply template for a set of messages." }, diff --git a/test/T035-read-config.sh b/test/T035-read-config.sh new file mode 100755 index 00000000..38d6c609 --- /dev/null +++ b/test/T035-read-config.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +test_description='Various options for reading configuration' +. $(dirname "$0")/test-lib.sh || exit 1 + +backup_config () { + local test_name=$(basename $0 .sh) + cp ${NOTMUCH_CONFIG} notmuch-config-backup.${test_name} +} + +xdg_config () { + local dir + local profile=${1:-default} + if [[ $profile != default ]]; then + export NOTMUCH_PROFILE=$profile + fi + backup_config + dir="${HOME}/.config/notmuch/${profile}" + rm -rf $dir + mkdir -p $dir + CONFIG_PATH=$dir/config + mv ${NOTMUCH_CONFIG} ${CONFIG_PATH} + unset NOTMUCH_CONFIG +} + +restore_config () { + local test_name=$(basename $0 .sh) + export NOTMUCH_CONFIG="${TMP_DIRECTORY}/notmuch-config" + unset CONFIG_PATH + unset NOTMUCH_PROFILE + cp notmuch-config-backup.${test_name} ${NOTMUCH_CONFIG} +} + +add_email_corpus + +test_begin_subtest "count with saved query from config file" +backup_config +query_name="test${RANDOM}" +notmuch count query:$query_name > OUTPUT +printf "\n[query]\n${query_name} = tag:inbox\n" >> notmuch-config +notmuch count query:$query_name >> OUTPUT +cat <<EOF > EXPECTED +0 +52 +EOF +restore_config +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "count with saved query from config file (xdg)" +query_name="test${RANDOM}" +xdg_config +notmuch count query:$query_name > OUTPUT +printf "\n[query]\n${query_name} = tag:inbox\n" >> ${CONFIG_PATH} +notmuch count query:$query_name >> OUTPUT +cat <<EOF > EXPECTED +0 +52 +EOF +restore_config +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "count with saved query from config file (xdg+profile)" +query_name="test${RANDOM}" +xdg_config work +notmuch count query:$query_name > OUTPUT +printf "\n[query]\n${query_name} = tag:inbox\n" >> ${CONFIG_PATH} +notmuch count query:$query_name >> OUTPUT +cat <<EOF > EXPECTED +0 +52 +EOF +restore_config +test_expect_equal_file EXPECTED OUTPUT + + +test_done diff --git a/test/T140-excludes.sh b/test/T140-excludes.sh index cef07095..0cacc41d 100755 --- a/test/T140-excludes.sh +++ b/test/T140-excludes.sh @@ -286,6 +286,21 @@ test_begin_subtest "Count, default exclusion: tag in query (threads)" output=$(notmuch count --output=threads tag:test and tag:deleted) test_expect_equal "$output" "3" +test_begin_subtest "Count, default exclusion, batch" +notmuch count --batch --output=messages<<EOF > OUTPUT +tag:test +tag:test and tag:deleted +tag:test +tag:test and tag:deleted +EOF +cat <<EOF >EXPECTED +2 +4 +2 +4 +EOF +test_expect_equal_file EXPECTED OUTPUT + test_begin_subtest "Count, exclude=true: tag in query (messages)" output=$(notmuch count --exclude=true tag:test and tag:deleted) test_expect_equal "$output" "4" -- 2.30.0
This conversion is trivial because the only configuration information accessed by dump is that stored in the database (in order to dump it). We do need to be careful to keep the write lock on the database to ensure dump consistency. --- notmuch-dump.c | 7 +----- notmuch.c | 2 +- test/T035-read-config.sh | 50 +++++++++++++++++++++++++++++++++++++++ test/T240-dump-restore.sh | 3 ++- 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/notmuch-dump.c b/notmuch-dump.c index eb629dc9..d7017929 100644 --- a/notmuch-dump.c +++ b/notmuch-dump.c @@ -361,16 +361,11 @@ notmuch_database_dump (notmuch_database_t *notmuch, } int -notmuch_dump_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) +notmuch_dump_command (unused(notmuch_config_t *config), notmuch_database_t *notmuch , int argc, char *argv[]) { - notmuch_database_t *notmuch; const char *query_str = NULL; int ret; - if (notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much)) - return EXIT_FAILURE; - notmuch_exit_if_unmatched_db_uuid (notmuch); const char *output_file_name = NULL; diff --git a/notmuch.c b/notmuch.c index 40527893..b10cc702 100644 --- a/notmuch.c +++ b/notmuch.c @@ -159,7 +159,7 @@ static command_t commands[] = { "Construct a reply template for a set of messages." }, { "tag", notmuch_tag_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Add/remove tags for all messages matching the search terms." }, - { "dump", notmuch_dump_command, NOTMUCH_COMMAND_CONFIG_OPEN, + { "dump", notmuch_dump_command, NOTMUCH_COMMAND_DATABASE_EARLY | NOTMUCH_COMMAND_DATABASE_WRITE, "Create a plain-text dump of the tags for each message." }, { "restore", notmuch_restore_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Restore the tags from the given dump file (see 'dump')." }, diff --git a/test/T035-read-config.sh b/test/T035-read-config.sh index 38d6c609..9e506dfa 100755 --- a/test/T035-read-config.sh +++ b/test/T035-read-config.sh @@ -71,5 +71,55 @@ EOF restore_config test_expect_equal_file EXPECTED OUTPUT +cat <<EOF > EXPECTED +Before: +#notmuch-dump batch-tag:3 tags + +After: +#notmuch-dump batch-tag:3 tags ++attachment +inbox +signed +unread -- id:20091118005829.GB25380@dottiness.seas.harvard.edu ++attachment +inbox +signed +unread -- id:20091118010116.GC25380@dottiness.seas.harvard.edu ++inbox +signed +unread -- id:20091117190054.GU3165@dottiness.seas.harvard.edu ++inbox +signed +unread -- id:20091117203301.GV3165@dottiness.seas.harvard.edu ++inbox +signed +unread -- id:20091118002059.067214ed@hikari ++inbox +signed +unread -- id:20091118005040.GA25380@dottiness.seas.harvard.edu ++inbox +signed +unread -- id:87iqd9rn3l.fsf@vertex.dottedmag +EOF + +test_begin_subtest "dump with saved query from config file" +backup_config +query_name="test${RANDOM}" +CONFIG_PATH=notmuch-config +printf "Before:\n" > OUTPUT +notmuch dump --include=tags query:$query_name | sort >> OUTPUT +printf "\nAfter:\n" >> OUTPUT +printf "\n[query]\n${query_name} = tag:signed\n" >> ${CONFIG_PATH} +notmuch dump --include=tags query:$query_name | sort >> OUTPUT +restore_config +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "dump with saved query from config file (xdg)" +backup_config +query_name="test${RANDOM}" +xdg_config +printf "Before:\n" > OUTPUT +notmuch dump --include=tags query:$query_name | sort >> OUTPUT +printf "\nAfter:\n" >> OUTPUT +printf "\n[query]\n${query_name} = tag:signed\n" >> ${CONFIG_PATH} +notmuch dump --include=tags query:$query_name | sort >> OUTPUT +restore_config +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "dump with saved query from config file (xdg+profile)" +backup_config +query_name="test${RANDOM}" +xdg_config work +printf "Before:\n" > OUTPUT +notmuch dump --include=tags query:$query_name | sort >> OUTPUT +printf "\nAfter:\n" >> OUTPUT +printf "\n[query]\n${query_name} = tag:signed\n" >> ${CONFIG_PATH} +notmuch dump --include=tags query:$query_name | sort >> OUTPUT +restore_config +test_expect_equal_file EXPECTED OUTPUT test_done diff --git a/test/T240-dump-restore.sh b/test/T240-dump-restore.sh index 0870ff92..da7502c9 100755 --- a/test/T240-dump-restore.sh +++ b/test/T240-dump-restore.sh @@ -322,6 +322,7 @@ EOF test_expect_equal_file EXPECTED OUTPUT +backup_database test_begin_subtest 'roundtripping random message-ids and tags' ${TEST_DIRECTORY}/random-corpus --config-path=${NOTMUCH_CONFIG} \ @@ -338,7 +339,7 @@ test_begin_subtest 'roundtripping random message-ids and tags' sort > OUTPUT.$test_count test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count +restore_database test_done -# Note the database is "poisoned" for sup format at this point. -- 2.30.0
Booleans have no out of band values, so return a status for errors. --- lib/config.cc | 26 ++++++++++++++++++++++++++ lib/notmuch.h | 19 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/lib/config.cc b/lib/config.cc index b2957f0c..d14f5422 100644 --- a/lib/config.cc +++ b/lib/config.cc @@ -359,6 +359,32 @@ _notmuch_config_load_from_file (notmuch_database_t *notmuch, return status; } +notmuch_status_t +notmuch_config_get_bool (notmuch_database_t *notmuch, notmuch_config_key_t key, notmuch_bool_t *val) +{ + const char *key_string, *val_string; + + key_string = _notmuch_config_key_to_string (key); + if (! key_string) { + return NOTMUCH_STATUS_ILLEGAL_ARGUMENT; + } + + val_string = _notmuch_string_map_get (notmuch->config, key_string); + if (! val_string) { + *val = FALSE; + return NOTMUCH_STATUS_SUCCESS; + } + + if (strcase_equal (val_string, "false") || strcase_equal (val_string, "no")) + *val = FALSE; + else if (strcase_equal (val_string, "true") || strcase_equal (val_string, "yes")) + *val = TRUE; + else + return NOTMUCH_STATUS_ILLEGAL_ARGUMENT; + + return NOTMUCH_STATUS_SUCCESS; +} + static const char * _notmuch_config_key_to_string (notmuch_config_key_t key) { switch (key) { diff --git a/lib/notmuch.h b/lib/notmuch.h index 39f39423..4a513b44 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -2532,6 +2532,25 @@ notmuch_config_values_start (notmuch_config_values_t *values); void notmuch_config_values_destroy (notmuch_config_values_t *values); +/** + * get a configuration value from an open database as Boolean + * + * This value reflects all configuration information given at the time + * the database was opened. + * + * @param[in] notmuch database + * @param[in] key configuration key + * @param[out] val configuration value, converted to Boolean + * + * @since libnotmuch 5.4 (notmuch 0.32) + * + * @retval #NOTMUCH_STATUS_ILLEGAL_ARGUMENT if either key is unknown + * or the corresponding value does not convert to Boolean. + */ +notmuch_status_t +notmuch_config_get_bool (notmuch_database_t *notmuch, + notmuch_config_key_t key, + notmuch_bool_t *val); /** * get the current default indexing options for a given database. * -- 2.30.0
Switch one configuration check to new n_c_get_bool function, and switch use of config as talloc context to notmuch. --- notmuch-restore.c | 17 ++++++++++------- notmuch.c | 2 +- test/T035-read-config.sh | 20 ++++++++++++++++++++ 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/notmuch-restore.c b/notmuch-restore.c index 544f4228..ce07f89d 100644 --- a/notmuch-restore.c +++ b/notmuch-restore.c @@ -219,9 +219,8 @@ parse_sup_line (void *ctx, char *line, } int -notmuch_restore_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) +notmuch_restore_command (unused(notmuch_config_t *config), notmuch_database_t *notmuch, int argc, char *argv[]) { - notmuch_database_t *notmuch; bool accumulate = false; tag_op_flag_t flags = 0; tag_op_list_t *tag_ops; @@ -238,12 +237,16 @@ notmuch_restore_command (notmuch_config_t *config, unused(notmuch_database_t *no int include = 0; int input_format = DUMP_FORMAT_AUTO; int errnum; + notmuch_bool_t synchronize_flags; - if (notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much)) + if (print_status_database ( + "notmuch restore", + notmuch, + notmuch_config_get_bool (notmuch, NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS, + &synchronize_flags))) return EXIT_FAILURE; - if (notmuch_config_get_maildir_synchronize_flags (config)) + if (synchronize_flags) flags |= TAG_FLAG_MAILDIR_SYNC; notmuch_opt_desc_t options[] = { @@ -310,7 +313,7 @@ notmuch_restore_command (notmuch_config_t *config, unused(notmuch_database_t *no goto DONE; } - tag_ops = tag_op_list_create (config); + tag_ops = tag_op_list_create (notmuch); if (tag_ops == NULL) { fprintf (stderr, "Out of memory.\n"); ret = EXIT_FAILURE; @@ -377,7 +380,7 @@ notmuch_restore_command (notmuch_config_t *config, unused(notmuch_database_t *no if (line_ctx != NULL) talloc_free (line_ctx); - line_ctx = talloc_new (config); + line_ctx = talloc_new (notmuch); if ((include & DUMP_INCLUDE_PROPERTIES) && line_len >= 2 && line[0] == '#' && line[1] == '=') { ret = process_properties_line (notmuch, line + 2); diff --git a/notmuch.c b/notmuch.c index b10cc702..4258ed43 100644 --- a/notmuch.c +++ b/notmuch.c @@ -161,7 +161,7 @@ static command_t commands[] = { "Add/remove tags for all messages matching the search terms." }, { "dump", notmuch_dump_command, NOTMUCH_COMMAND_DATABASE_EARLY | NOTMUCH_COMMAND_DATABASE_WRITE, "Create a plain-text dump of the tags for each message." }, - { "restore", notmuch_restore_command, NOTMUCH_COMMAND_CONFIG_OPEN, + { "restore", notmuch_restore_command, NOTMUCH_COMMAND_DATABASE_EARLY | NOTMUCH_COMMAND_DATABASE_WRITE, "Restore the tags from the given dump file (see 'dump')." }, { "compact", notmuch_compact_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Compact the notmuch database." }, diff --git a/test/T035-read-config.sh b/test/T035-read-config.sh index 9e506dfa..35fbd600 100755 --- a/test/T035-read-config.sh +++ b/test/T035-read-config.sh @@ -122,4 +122,24 @@ notmuch dump --include=tags query:$query_name | sort >> OUTPUT restore_config test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "restore with xdg config" +backup_config +notmuch dump '*' > EXPECTED +notmuch tag -inbox '*' +xdg_config +notmuch restore --input=EXPECTED +notmuch dump > OUTPUT +restore_config +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "restore with xdg+profile config" +backup_config +notmuch dump '*' > EXPECTED +notmuch tag -inbox '*' +xdg_config work +notmuch restore --input=EXPECTED +notmuch dump > OUTPUT +restore_config +test_expect_equal_file EXPECTED OUTPUT + test_done -- 2.30.0
The new talloc context is needed to run the hook at the very end of the function. That in turn is needed so that this process gives up the write lock on the database. --- notmuch-insert.c | 59 ++++++++++++++++++++++++---------------- notmuch.c | 2 +- test/T035-read-config.sh | 38 ++++++++++++++++++++++++++ test/T070-insert.sh | 8 ++++-- 4 files changed, 79 insertions(+), 28 deletions(-) diff --git a/notmuch-insert.c b/notmuch-insert.c index de160309..e483b949 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -444,14 +444,12 @@ add_file (notmuch_database_t *notmuch, const char *path, tag_op_list_t *tag_ops, } int -notmuch_insert_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) +notmuch_insert_command (unused(notmuch_config_t *config),notmuch_database_t *notmuch, int argc, char *argv[]) { notmuch_status_t status, close_status; - notmuch_database_t *notmuch; struct sigaction action; const char *db_path; - const char **new_tags; - size_t new_tags_length; + notmuch_config_values_t *new_tags = NULL; tag_op_list_t *tag_ops; char *query_string = NULL; const char *folder = ""; @@ -459,11 +457,11 @@ notmuch_insert_command (notmuch_config_t *config, unused(notmuch_database_t *not bool keep = false; bool hooks = true; bool world_readable = false; - bool synchronize_flags; + notmuch_bool_t synchronize_flags; char *maildir; char *newpath; int opt_index; - unsigned int i; + void *local = talloc_new (NULL); notmuch_opt_desc_t options[] = { { .opt_string = &folder, .name = "folder", .allow_empty = true }, @@ -482,30 +480,46 @@ notmuch_insert_command (notmuch_config_t *config, unused(notmuch_database_t *not notmuch_process_shared_options (argv[0]); - db_path = notmuch_config_get_database_path (config); - new_tags = notmuch_config_get_new_tags (config, &new_tags_length); - synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config); - tag_ops = tag_op_list_create (config); + /* XXX TODO replace this use of DATABASE_PATH with something specific to hooks */ + db_path = notmuch_config_get (notmuch, NOTMUCH_CONFIG_DATABASE_PATH); + + if (! db_path) + INTERNAL_ERROR ("Unable to retrieve database path"); + else + db_path = talloc_strdup (local, db_path); + + new_tags = notmuch_config_get_values (notmuch, NOTMUCH_CONFIG_NEW_TAGS); + + if (print_status_database ( + "notmuch insert", + notmuch, + notmuch_config_get_bool (notmuch, NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS, + &synchronize_flags))) + return EXIT_FAILURE; + + tag_ops = tag_op_list_create (local); if (tag_ops == NULL) { fprintf (stderr, "Out of memory.\n"); return EXIT_FAILURE; } - for (i = 0; i < new_tags_length; i++) { + for (; + notmuch_config_values_valid (new_tags); + notmuch_config_values_move_to_next (new_tags)) { const char *error_msg; - - error_msg = illegal_tag (new_tags[i], false); + const char *tag = notmuch_config_values_get (new_tags); + error_msg = illegal_tag (tag, false); if (error_msg) { fprintf (stderr, "Error: tag '%s' in new.tags: %s\n", - new_tags[i], error_msg); + tag, error_msg); return EXIT_FAILURE; } - if (tag_op_list_append (tag_ops, new_tags[i], false)) + if (tag_op_list_append (tag_ops, tag, false)) return EXIT_FAILURE; } - if (parse_tag_command_line (config, argc - opt_index, argv + opt_index, + if (parse_tag_command_line (local, argc - opt_index, argv + opt_index, &query_string, tag_ops)) return EXIT_FAILURE; @@ -519,14 +533,14 @@ notmuch_insert_command (notmuch_config_t *config, unused(notmuch_database_t *not return EXIT_FAILURE; } - maildir = talloc_asprintf (config, "%s/%s", db_path, folder); + maildir = talloc_asprintf (local, "%s/%s", db_path, folder); if (! maildir) { fprintf (stderr, "Out of memory\n"); return EXIT_FAILURE; } strip_trailing (maildir, '/'); - if (create_folder && ! maildir_create_folder (config, maildir, world_readable)) + if (create_folder && ! maildir_create_folder (local, maildir, world_readable)) return EXIT_FAILURE; /* Set up our handler for SIGINT. We do not set SA_RESTART so that copying @@ -538,16 +552,11 @@ notmuch_insert_command (notmuch_config_t *config, unused(notmuch_database_t *not sigaction (SIGINT, &action, NULL); /* Write the message to the Maildir new directory. */ - newpath = maildir_write_new (config, STDIN_FILENO, maildir, world_readable); + newpath = maildir_write_new (local, STDIN_FILENO, maildir, world_readable); if (! newpath) { return EXIT_FAILURE; } - status = notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much); - if (status) - return keep ? NOTMUCH_STATUS_SUCCESS : status_to_exit (status); - notmuch_exit_if_unmatched_db_uuid (notmuch); status = notmuch_process_shared_indexing_options (notmuch); @@ -589,5 +598,7 @@ notmuch_insert_command (notmuch_config_t *config, unused(notmuch_database_t *not notmuch_run_hook (db_path, "post-insert"); } + talloc_free (local); + return status_to_exit (status); } diff --git a/notmuch.c b/notmuch.c index 4258ed43..8e697396 100644 --- a/notmuch.c +++ b/notmuch.c @@ -145,7 +145,7 @@ static command_t commands[] = { "Interactively set up notmuch for first use." }, { "new", notmuch_new_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Find and import new messages to the notmuch database." }, - { "insert", notmuch_insert_command, NOTMUCH_COMMAND_CONFIG_OPEN, + { "insert", notmuch_insert_command, NOTMUCH_COMMAND_DATABASE_EARLY | NOTMUCH_COMMAND_DATABASE_WRITE, "Add a new message into the maildir and notmuch database." }, { "search", notmuch_search_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Search for messages matching the given search terms." }, diff --git a/test/T035-read-config.sh b/test/T035-read-config.sh index 35fbd600..0aa2e553 100755 --- a/test/T035-read-config.sh +++ b/test/T035-read-config.sh @@ -142,4 +142,42 @@ notmuch dump > OUTPUT restore_config test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "Insert message with custom new.tags (xdg)" +backup_config +xdg_config +tag=test${RANDOM} +notmuch --config=${CONFIG_PATH} config set new.tags $tag +generate_message \ + "[subject]=\"insert-subject\"" \ + "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" \ + "[body]=\"insert-message\"" +mkdir -p ${MAIL_DIR}/{cur,new,tmp} +notmuch insert < "$gen_msg_filename" +notmuch dump id:$gen_msg_id > OUTPUT +cat <<EOF > EXPECTED +#notmuch-dump batch-tag:3 config,properties,tags ++$tag -- id:$gen_msg_id +EOF +restore_config +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "Insert message with custom new.tags (xdg+profile)" +backup_config +tag=test${RANDOM} +xdg_config $tag +notmuch --config=${CONFIG_PATH} config set new.tags $tag +generate_message \ + "[subject]=\"insert-subject\"" \ + "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" \ + "[body]=\"insert-message\"" +mkdir -p ${MAIL_DIR}/{cur,new,tmp} +notmuch insert < "$gen_msg_filename" +notmuch dump id:$gen_msg_id > OUTPUT +cat <<EOF > EXPECTED +#notmuch-dump batch-tag:3 config,properties,tags ++$tag -- id:$gen_msg_id +EOF +restore_config +test_expect_equal_file EXPECTED OUTPUT + test_done diff --git a/test/T070-insert.sh b/test/T070-insert.sh index 1c7ca846..7341810c 100755 --- a/test/T070-insert.sh +++ b/test/T070-insert.sh @@ -226,11 +226,13 @@ test_expect_code 1 "notmuch insert --folder=../G --create-folder < $gen_msg_file OLDCONFIG=$(notmuch config get new.tags) -test_begin_subtest "Empty tags in new.tags are forbidden" +test_begin_subtest "Empty tags in new.tags are ignored" notmuch config set new.tags "foo;;bar" gen_insert_msg -output=$(notmuch insert < $gen_msg_filename 2>&1) -test_expect_equal "$output" "Error: tag '' in new.tags: empty tag forbidden" +notmuch insert < $gen_msg_filename +output=$(notmuch show --format=json id:$gen_msg_id) +test_json_nodes <<<"$output" \ + 'new_tags:[0][0][0]["tags"] = ["bar", "foo"]' test_begin_subtest "Tags starting with '-' in new.tags are forbidden" notmuch config set new.tags "-foo;bar" -- 2.30.0
The only non-trivial part is switching the talloc context for query_string_from args from 'config' to 'notmuch'. --- notmuch-reindex.c | 9 ++------- notmuch.c | 2 +- test/T035-read-config.sh | 30 ++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/notmuch-reindex.c b/notmuch-reindex.c index 9d337c48..fa84d4fc 100644 --- a/notmuch-reindex.c +++ b/notmuch-reindex.c @@ -83,10 +83,9 @@ reindex_query (notmuch_database_t *notmuch, const char *query_string, } int -notmuch_reindex_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) +notmuch_reindex_command (unused(notmuch_config_t *config), notmuch_database_t *notmuch, int argc, char *argv[]) { char *query_string = NULL; - notmuch_database_t *notmuch; struct sigaction action; int opt_index; int ret; @@ -111,10 +110,6 @@ notmuch_reindex_command (notmuch_config_t *config, unused(notmuch_database_t *no notmuch_process_shared_options (argv[0]); - if (notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much)) - return EXIT_FAILURE; - notmuch_exit_if_unmatched_db_uuid (notmuch); status = notmuch_process_shared_indexing_options (notmuch); @@ -124,7 +119,7 @@ notmuch_reindex_command (notmuch_config_t *config, unused(notmuch_database_t *no return EXIT_FAILURE; } - query_string = query_string_from_args (config, argc - opt_index, argv + opt_index); + query_string = query_string_from_args (notmuch, argc - opt_index, argv + opt_index); if (query_string == NULL) { fprintf (stderr, "Out of memory\n"); return EXIT_FAILURE; diff --git a/notmuch.c b/notmuch.c index 8e697396..82d68681 100644 --- a/notmuch.c +++ b/notmuch.c @@ -165,7 +165,7 @@ static command_t commands[] = { "Restore the tags from the given dump file (see 'dump')." }, { "compact", notmuch_compact_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Compact the notmuch database." }, - { "reindex", notmuch_reindex_command, NOTMUCH_COMMAND_CONFIG_OPEN, + { "reindex", notmuch_reindex_command, NOTMUCH_COMMAND_DATABASE_EARLY | NOTMUCH_COMMAND_DATABASE_WRITE, "Re-index all messages matching the search terms." }, { "config", notmuch_config_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Get or set settings in the notmuch configuration file." }, diff --git a/test/T035-read-config.sh b/test/T035-read-config.sh index 0aa2e553..1e740eba 100755 --- a/test/T035-read-config.sh +++ b/test/T035-read-config.sh @@ -180,4 +180,34 @@ EOF restore_config test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "reindex with saved query from config file" +backup_config +query_name="test${RANDOM}" +count1=$(notmuch count --lastmod '*' | cut -f3) +printf "\n[query]\n${query_name} = tag:inbox\n" >> notmuch-config +notmuch reindex query:$query_name +count2=$(notmuch count --lastmod '*' | cut -f3) +restore_config +test_expect_success "test '$count2 -gt $count1'" + +test_begin_subtest "reindex with saved query from config file (xdg)" +query_name="test${RANDOM}" +count1=$(notmuch count --lastmod '*' | cut -f3) +xdg_config +printf "\n[query]\n${query_name} = tag:inbox\n" >> ${CONFIG_PATH} +notmuch reindex query:$query_name +count2=$(notmuch count --lastmod '*' | cut -f3) +restore_config +test_expect_success "test '$count2 -gt $count1'" + +test_begin_subtest "reindex with saved query from config file (xdg+profile)" +query_name="test${RANDOM}" +count1=$(notmuch count --lastmod '*' | cut -f3) +xdg_config $query_name +printf "\n[query]\n${query_name} = tag:inbox\n" >> ${CONFIG_PATH} +notmuch reindex query:$query_name +count2=$(notmuch count --lastmod '*' | cut -f3) +restore_config +test_expect_success "test '$count2 -gt $count1'" + test_done -- 2.30.0
This is messier than some of the other conversions because the extensive use of 'config' as a talloc context. --- notmuch-reply.c | 100 +++++++++++++++++++-------------------- notmuch.c | 2 +- test/T035-read-config.sh | 58 +++++++++++++++++++++++ 3 files changed, 108 insertions(+), 52 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index a8ffbf75..700f3397 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -112,25 +112,26 @@ match_address (const char *str, const char *address, address_match_t mode) /* Match given string against user's configured "primary" and "other" * addresses according to mode. */ static const char * -address_match (const char *str, notmuch_config_t *config, address_match_t mode) +address_match (const char *str, notmuch_database_t *notmuch, address_match_t mode) { const char *primary; - const char **other; - size_t i, other_len; + notmuch_config_values_t *other = NULL; if (! str || *str == '\0') return NULL; - primary = notmuch_config_get_user_primary_email (config); + primary = notmuch_config_get (notmuch, NOTMUCH_CONFIG_PRIMARY_EMAIL); if (match_address (str, primary, mode)) return primary; - other = notmuch_config_get_user_other_email (config, &other_len); - for (i = 0; i < other_len; i++) { - if (match_address (str, other[i], mode)) - return other[i]; - } + for (other = notmuch_config_get_values (notmuch, NOTMUCH_CONFIG_OTHER_EMAIL); + notmuch_config_values_valid (other); + notmuch_config_values_move_to_next (other)) { + const char *addr = notmuch_config_values_get (other); + if (match_address (str, addr, mode)) + return addr; + } return NULL; } @@ -138,26 +139,26 @@ address_match (const char *str, notmuch_config_t *config, address_match_t mode) * user's "primary" or "other" addresses. If so, return the matching * address, NULL otherwise. */ static const char * -user_address_in_string (const char *str, notmuch_config_t *config) +user_address_in_string (const char *str, notmuch_database_t *notmuch) { - return address_match (str, config, USER_ADDRESS_IN_STRING); + return address_match (str, notmuch, USER_ADDRESS_IN_STRING); } /* Do any of the addresses configured as one of the user's "primary" * or "other" addresses contain the given string. If so, return the * matching address, NULL otherwise. */ static const char * -string_in_user_address (const char *str, notmuch_config_t *config) +string_in_user_address (const char *str, notmuch_database_t *notmuch) { - return address_match (str, config, STRING_IN_USER_ADDRESS); + return address_match (str, notmuch, STRING_IN_USER_ADDRESS); } /* Is the given address configured as one of the user's "primary" or * "other" addresses. */ static bool -address_is_users (const char *address, notmuch_config_t *config) +address_is_users (const char *address, notmuch_database_t *notmuch) { - return address_match (address, config, STRING_IS_USER_ADDRESS) != NULL; + return address_match (address, notmuch, STRING_IS_USER_ADDRESS) != NULL; } /* Scan addresses in 'list'. @@ -175,7 +176,7 @@ address_is_users (const char *address, notmuch_config_t *config) */ static unsigned int scan_address_list (InternetAddressList *list, - notmuch_config_t *config, + notmuch_database_t *notmuch, GMimeMessage *message, GMimeAddressType type, const char **user_from) @@ -195,7 +196,7 @@ scan_address_list (InternetAddressList *list, group = INTERNET_ADDRESS_GROUP (address); group_list = internet_address_group_get_members (group); - n += scan_address_list (group_list, config, message, type, user_from); + n += scan_address_list (group_list, notmuch, message, type, user_from); } else { InternetAddressMailbox *mailbox; const char *name; @@ -206,7 +207,7 @@ scan_address_list (InternetAddressList *list, name = internet_address_get_name (address); addr = internet_address_mailbox_get_addr (mailbox); - if (address_is_users (addr, config)) { + if (address_is_users (addr, notmuch)) { if (user_from && *user_from == NULL) *user_from = addr; } else if (message) { @@ -324,7 +325,7 @@ get_bcc (GMimeMessage *message) */ static const char * add_recipients_from_message (GMimeMessage *reply, - notmuch_config_t *config, + notmuch_database_t *notmuch, GMimeMessage *message, bool reply_all) { @@ -346,7 +347,7 @@ add_recipients_from_message (GMimeMessage *reply, recipients = reply_to_map[i].get_header (message); - n += scan_address_list (recipients, config, reply, + n += scan_address_list (recipients, notmuch, reply, reply_to_map[i].recipient_type, &from_addr); if (! reply_all && n) { @@ -384,7 +385,7 @@ add_recipients_from_message (GMimeMessage *reply, * Return the address that was found, if any, and NULL otherwise. */ static const char * -guess_from_in_received_for (notmuch_config_t *config, const char *received) +guess_from_in_received_for (notmuch_database_t *notmuch, const char *received) { const char *ptr; @@ -392,7 +393,7 @@ guess_from_in_received_for (notmuch_config_t *config, const char *received) if (! ptr) return NULL; - return user_address_in_string (ptr, config); + return user_address_in_string (ptr, notmuch); } /* @@ -408,7 +409,7 @@ guess_from_in_received_for (notmuch_config_t *config, const char *received) * Return the address that was found, if any, and NULL otherwise. */ static const char * -guess_from_in_received_by (notmuch_config_t *config, const char *received) +guess_from_in_received_by (notmuch_database_t *notmuch, const char *received) { const char *addr; const char *by = received; @@ -446,7 +447,7 @@ guess_from_in_received_by (notmuch_config_t *config, const char *received) */ *(tld - 1) = '.'; - addr = string_in_user_address (domain, config); + addr = string_in_user_address (domain, notmuch); if (addr) { free (mta); return addr; @@ -469,12 +470,13 @@ guess_from_in_received_by (notmuch_config_t *config, const char *received) * Return the address that was found, if any, and NULL otherwise. */ static const char * -guess_from_in_received_headers (notmuch_config_t *config, - notmuch_message_t *message) +guess_from_in_received_headers (notmuch_message_t *message) { const char *received, *addr; char *sanitized; + notmuch_database_t *notmuch = notmuch_message_get_database (message); + received = notmuch_message_get_header (message, "received"); if (! received) return NULL; @@ -483,9 +485,9 @@ guess_from_in_received_headers (notmuch_config_t *config, if (! sanitized) return NULL; - addr = guess_from_in_received_for (config, sanitized); + addr = guess_from_in_received_for (notmuch, sanitized); if (! addr) - addr = guess_from_in_received_by (config, sanitized); + addr = guess_from_in_received_by (notmuch, sanitized); talloc_free (sanitized); @@ -500,7 +502,7 @@ guess_from_in_received_headers (notmuch_config_t *config, * Return the address that was found, if any, and NULL otherwise. */ static const char * -get_from_in_to_headers (notmuch_config_t *config, notmuch_message_t *message) +get_from_in_to_headers (notmuch_message_t *message) { size_t i; const char *tohdr, *addr; @@ -510,11 +512,13 @@ get_from_in_to_headers (notmuch_config_t *config, notmuch_message_t *message) "Delivered-To", }; + notmuch_database_t *notmuch = notmuch_message_get_database (message); + for (i = 0; i < ARRAY_SIZE (to_headers); i++) { tohdr = notmuch_message_get_header (message, to_headers[i]); /* Note: tohdr potentially contains a list of email addresses. */ - addr = user_address_in_string (tohdr, config); + addr = user_address_in_string (tohdr, notmuch); if (addr) return addr; } @@ -524,7 +528,6 @@ get_from_in_to_headers (notmuch_config_t *config, notmuch_message_t *message) static GMimeMessage * create_reply_message (void *ctx, - notmuch_config_t *config, notmuch_message_t *message, GMimeMessage *mime_message, bool reply_all, @@ -532,7 +535,7 @@ create_reply_message (void *ctx, { const char *subject, *from_addr = NULL; const char *in_reply_to, *orig_references, *references; - + notmuch_database_t *notmuch = notmuch_message_get_database (message); /* * Use the below header order for limited headers, "pretty" order * otherwise. @@ -558,7 +561,7 @@ create_reply_message (void *ctx, g_mime_object_set_header (GMIME_OBJECT (reply), "References", references, NULL); - from_addr = add_recipients_from_message (reply, config, + from_addr = add_recipients_from_message (reply, notmuch, mime_message, reply_all); /* The above is all that is needed for limited headers. */ @@ -578,7 +581,7 @@ create_reply_message (void *ctx, * Delivered-To: headers. */ if (from_addr == NULL) - from_addr = get_from_in_to_headers (config, message); + from_addr = get_from_in_to_headers (message); /* * Check for a (for <email@add.res>) clause in Received: headers, @@ -586,14 +589,14 @@ create_reply_message (void *ctx, * of Received: headers */ if (from_addr == NULL) - from_addr = guess_from_in_received_headers (config, message); + from_addr = guess_from_in_received_headers (message); /* Default to user's primary address. */ if (from_addr == NULL) - from_addr = notmuch_config_get_user_primary_email (config); + from_addr = notmuch_config_get (notmuch, NOTMUCH_CONFIG_PRIMARY_EMAIL); from_addr = talloc_asprintf (ctx, "%s <%s>", - notmuch_config_get_user_name (config), + notmuch_config_get (notmuch, NOTMUCH_CONFIG_USER_NAME), from_addr); g_mime_object_set_header (GMIME_OBJECT (reply), "From", from_addr, NULL); @@ -615,7 +618,7 @@ enum { }; static int -do_reply (notmuch_config_t *config, +do_reply (notmuch_database_t *notmuch, notmuch_query_t *query, notmuch_show_params_t *params, int format, @@ -641,9 +644,9 @@ do_reply (notmuch_config_t *config, } if (format == FORMAT_JSON) - sp = sprinter_json_create (config, stdout); + sp = sprinter_json_create (notmuch, stdout); else - sp = sprinter_sexp_create (config, stdout); + sp = sprinter_sexp_create (notmuch, stdout); } status = notmuch_query_search_messages (query, &messages); @@ -655,10 +658,10 @@ do_reply (notmuch_config_t *config, notmuch_messages_move_to_next (messages)) { message = notmuch_messages_get (messages); - if (mime_node_open (config, message, ¶ms->crypto, &node)) + if (mime_node_open (notmuch, message, ¶ms->crypto, &node)) return 1; - reply = create_reply_message (config, config, message, + reply = create_reply_message (notmuch, message, GMIME_MESSAGE (node->part), reply_all, format == FORMAT_HEADERS_ONLY); if (! reply) @@ -675,7 +678,7 @@ do_reply (notmuch_config_t *config, /* Start the original */ sp->map_key (sp, "original"); - format_part_sprinter (config, sp, node, true, false); + format_part_sprinter (notmuch, sp, node, true, false); /* End */ sp->end (sp); @@ -700,9 +703,8 @@ do_reply (notmuch_config_t *config, } int -notmuch_reply_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) +notmuch_reply_command (unused(notmuch_config_t *config), notmuch_database_t *notmuch, int argc, char *argv[]) { - notmuch_database_t *notmuch; notmuch_query_t *query; char *query_string; int opt_index; @@ -743,7 +745,7 @@ notmuch_reply_command (notmuch_config_t *config, unused(notmuch_database_t *notm notmuch_exit_if_unsupported_format (); - query_string = query_string_from_args (config, argc - opt_index, argv + opt_index); + query_string = query_string_from_args (notmuch, argc - opt_index, argv + opt_index); if (query_string == NULL) { fprintf (stderr, "Out of memory\n"); return EXIT_FAILURE; @@ -754,10 +756,6 @@ notmuch_reply_command (notmuch_config_t *config, unused(notmuch_database_t *notm return EXIT_FAILURE; } - if (notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much)) - return EXIT_FAILURE; - notmuch_exit_if_unmatched_db_uuid (notmuch); query = notmuch_query_create (notmuch, query_string); @@ -766,7 +764,7 @@ notmuch_reply_command (notmuch_config_t *config, unused(notmuch_database_t *notm return EXIT_FAILURE; } - if (do_reply (config, query, ¶ms, format, reply_all) != 0) + if (do_reply (notmuch, query, ¶ms, format, reply_all) != 0) return EXIT_FAILURE; _notmuch_crypto_cleanup (¶ms.crypto); diff --git a/notmuch.c b/notmuch.c index 82d68681..95cd4ae5 100644 --- a/notmuch.c +++ b/notmuch.c @@ -155,7 +155,7 @@ static command_t commands[] = { "Show all messages matching the search terms." }, { "count", notmuch_count_command, NOTMUCH_COMMAND_DATABASE_EARLY, "Count messages matching the search terms." }, - { "reply", notmuch_reply_command, NOTMUCH_COMMAND_CONFIG_OPEN, + { "reply", notmuch_reply_command, NOTMUCH_COMMAND_DATABASE_EARLY, "Construct a reply template for a set of messages." }, { "tag", notmuch_tag_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Add/remove tags for all messages matching the search terms." }, diff --git a/test/T035-read-config.sh b/test/T035-read-config.sh index 1e740eba..3da9cbe6 100755 --- a/test/T035-read-config.sh +++ b/test/T035-read-config.sh @@ -210,4 +210,62 @@ count2=$(notmuch count --lastmod '*' | cut -f3) restore_config test_expect_success "test '$count2 -gt $count1'" + + +add_message '[from]="Sender <sender@example.com>"' \ + [to]=test_suite@notmuchmail.org \ + '[cc]="Other Parties <cc@example.com>"' \ + [subject]=notmuch-reply-test \ + '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \ + '[body]="reply with CC"' + +cat <<EOF > EXPECTED +Before: +After: +From: Notmuch Test Suite <test_suite@notmuchmail.org> +Subject: Re: notmuch-reply-test +To: Sender <sender@example.com> +Cc: Other Parties <cc@example.com> +In-Reply-To: <${gen_msg_id}> +References: <${gen_msg_id}> + +On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote: +> reply with CC +EOF + +test_begin_subtest "reply with saved query from config file" +backup_config +query_name="test${RANDOM}" +printf "Before:\n" > OUTPUT +notmuch reply query:$query_name 2>&1 >> OUTPUT +printf "\n[query]\n${query_name} = id:${gen_msg_id}\n" >> notmuch-config +printf "After:\n" >> OUTPUT +notmuch reply query:$query_name >> OUTPUT +restore_config +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "reply with saved query from config file (xdg)" +backup_config +query_name="test${RANDOM}" +xdg_config +printf "Before:\n" > OUTPUT +notmuch reply query:$query_name 2>&1 >> OUTPUT +printf "\n[query]\n${query_name} = id:${gen_msg_id}\n" >> ${CONFIG_PATH} +printf "After:\n" >> OUTPUT +notmuch reply query:$query_name >> OUTPUT +restore_config +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "reply with saved query from config file (xdg+profile)" +backup_config +query_name="test${RANDOM}" +xdg_config $query_name +printf "Before:\n" > OUTPUT +notmuch reply query:$query_name 2>&1 >> OUTPUT +printf "\n[query]\n${query_name} = id:${gen_msg_id}\n" >> ${CONFIG_PATH} +printf "After:\n" >> OUTPUT +notmuch reply query:$query_name >> OUTPUT +restore_config +test_expect_equal_file EXPECTED OUTPUT + test_done -- 2.30.0
Since we are already passing a search context around as a kind of parameter block, add a new talloc context to that to replace relying on 'config'. Convert notmuch-search and notmuch-address at the same time, because they share some code. Add a test to make sure we don't break passing configuration as a command line argument. --- notmuch-search.c | 54 +++++++++------------ notmuch.c | 4 +- test/T035-read-config.sh | 100 +++++++++++++++++++++++++++++++++++++++ test/T750-user-header.sh | 18 +++++++ 4 files changed, 142 insertions(+), 34 deletions(-) diff --git a/notmuch-search.c b/notmuch-search.c index 34e27058..aba22799 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -52,6 +52,7 @@ typedef enum { typedef struct { notmuch_database_t *notmuch; + void *talloc_ctx; int format_sel; sprinter_t *format; int exclude; @@ -677,28 +678,29 @@ do_search_tags (const search_context_t *ctx) } static int -_notmuch_search_prepare (search_context_t *ctx, notmuch_config_t *config, int argc, char *argv[]) +_notmuch_search_prepare (search_context_t *ctx, int argc, char *argv[]) { char *query_str; - unsigned int i; - char *status_string = NULL; + + if (! ctx->talloc_ctx) + ctx->talloc_ctx = talloc_new (NULL); switch (ctx->format_sel) { case NOTMUCH_FORMAT_TEXT: - ctx->format = sprinter_text_create (config, stdout); + ctx->format = sprinter_text_create (ctx->talloc_ctx, stdout); break; case NOTMUCH_FORMAT_TEXT0: if (ctx->output == OUTPUT_SUMMARY) { fprintf (stderr, "Error: --format=text0 is not compatible with --output=summary.\n"); return EXIT_FAILURE; } - ctx->format = sprinter_text0_create (config, stdout); + ctx->format = sprinter_text0_create (ctx->talloc_ctx, stdout); break; case NOTMUCH_FORMAT_JSON: - ctx->format = sprinter_json_create (config, stdout); + ctx->format = sprinter_json_create (ctx->talloc_ctx, stdout); break; case NOTMUCH_FORMAT_SEXP: - ctx->format = sprinter_sexp_create (config, stdout); + ctx->format = sprinter_sexp_create (ctx->talloc_ctx, stdout); break; default: /* this should never happen */ @@ -707,18 +709,6 @@ _notmuch_search_prepare (search_context_t *ctx, notmuch_config_t *config, int ar notmuch_exit_if_unsupported_format (); - if (notmuch_database_open_verbose ( - notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY, &ctx->notmuch, &status_string)) { - - if (status_string) { - fputs (status_string, stderr); - free (status_string); - } - - return EXIT_FAILURE; - } - notmuch_exit_if_unmatched_db_uuid (ctx->notmuch); query_str = query_string_from_args (ctx->notmuch, argc, argv); @@ -748,21 +738,20 @@ _notmuch_search_prepare (search_context_t *ctx, notmuch_config_t *config, int ar } if (ctx->exclude != NOTMUCH_EXCLUDE_FALSE) { - const char **search_exclude_tags; - size_t search_exclude_tags_length; + notmuch_config_values_t *exclude_tags; notmuch_status_t status; - search_exclude_tags = notmuch_config_get_search_exclude_tags ( - config, &search_exclude_tags_length); + for (exclude_tags = notmuch_config_get_values (ctx->notmuch, NOTMUCH_CONFIG_EXCLUDE_TAGS); + notmuch_config_values_valid (exclude_tags); + notmuch_config_values_move_to_next (exclude_tags)) { - for (i = 0; i < search_exclude_tags_length; i++) { - status = notmuch_query_add_tag_exclude (ctx->query, search_exclude_tags[i]); + status = notmuch_query_add_tag_exclude (ctx->query, + notmuch_config_values_get (exclude_tags)); if (status && status != NOTMUCH_STATUS_IGNORED) { print_status_query ("notmuch search", ctx->query, status); return EXIT_FAILURE; } } - notmuch_query_set_omit_excluded (ctx->query, ctx->exclude); } @@ -805,7 +794,7 @@ static const notmuch_opt_desc_t common_options[] = { }; int -notmuch_search_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) +notmuch_search_command (unused(notmuch_config_t *config), notmuch_database_t *notmuch, int argc, char *argv[]) { search_context_t *ctx = &search_context; int opt_index, ret; @@ -832,6 +821,7 @@ notmuch_search_command (notmuch_config_t *config, unused(notmuch_database_t *not { } }; + ctx->notmuch = notmuch; ctx->output = OUTPUT_SUMMARY; opt_index = parse_arguments (argc, argv, options, 1); if (opt_index < 0) @@ -845,8 +835,7 @@ notmuch_search_command (notmuch_config_t *config, unused(notmuch_database_t *not return EXIT_FAILURE; } - if (_notmuch_search_prepare (ctx, config, - argc - opt_index, argv + opt_index)) + if (_notmuch_search_prepare (ctx, argc - opt_index, argv + opt_index)) return EXIT_FAILURE; switch (ctx->output) { @@ -871,7 +860,7 @@ notmuch_search_command (notmuch_config_t *config, unused(notmuch_database_t *not } int -notmuch_address_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) +notmuch_address_command (unused(notmuch_config_t *config), notmuch_database_t *notmuch, int argc, char *argv[]) { search_context_t *ctx = &search_context; int opt_index, ret; @@ -897,6 +886,8 @@ notmuch_address_command (notmuch_config_t *config, unused(notmuch_database_t *no { } }; + ctx->notmuch = notmuch; + opt_index = parse_arguments (argc, argv, options, 1); if (opt_index < 0) return EXIT_FAILURE; @@ -911,8 +902,7 @@ notmuch_address_command (notmuch_config_t *config, unused(notmuch_database_t *no return EXIT_FAILURE; } - if (_notmuch_search_prepare (ctx, config, - argc - opt_index, argv + opt_index)) + if (_notmuch_search_prepare (ctx, argc - opt_index, argv + opt_index)) return EXIT_FAILURE; ctx->addresses = g_hash_table_new_full (strcase_hash, strcase_equal, diff --git a/notmuch.c b/notmuch.c index 95cd4ae5..16504fc5 100644 --- a/notmuch.c +++ b/notmuch.c @@ -147,9 +147,9 @@ static command_t commands[] = { "Find and import new messages to the notmuch database." }, { "insert", notmuch_insert_command, NOTMUCH_COMMAND_DATABASE_EARLY | NOTMUCH_COMMAND_DATABASE_WRITE, "Add a new message into the maildir and notmuch database." }, - { "search", notmuch_search_command, NOTMUCH_COMMAND_CONFIG_OPEN, + { "search", notmuch_search_command, NOTMUCH_COMMAND_DATABASE_EARLY, "Search for messages matching the given search terms." }, - { "address", notmuch_address_command, NOTMUCH_COMMAND_CONFIG_OPEN, + { "address", notmuch_address_command, NOTMUCH_COMMAND_DATABASE_EARLY, "Get addresses from messages matching the given search terms." }, { "show", notmuch_show_command, NOTMUCH_COMMAND_CONFIG_OPEN, "Show all messages matching the search terms." }, diff --git a/test/T035-read-config.sh b/test/T035-read-config.sh index 3da9cbe6..6cc4da8d 100755 --- a/test/T035-read-config.sh +++ b/test/T035-read-config.sh @@ -268,4 +268,104 @@ notmuch reply query:$query_name >> OUTPUT restore_config test_expect_equal_file EXPECTED OUTPUT +backup_database +test_begin_subtest "search with alternate config" +notmuch tag -- +foobar17 '*' +cp notmuch-config alt-config +notmuch --config=alt-config config set search.exclude_tags foobar17 +output=$(notmuch --config=alt-config count '*') +test_expect_equal "$output" "0" +restore_database + +cat <<EOF > EXPECTED +Before: +After: +thread:XXX 2009-11-18 [1/2] Carl Worth| Alex Botero-Lowry; [notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop (attachment inbox unread) +thread:XXX 2009-11-18 [1/2] Carl Worth| Ingmar Vanhassel; [notmuch] [PATCH] Typsos (inbox unread) +thread:XXX 2009-11-18 [1/3] Carl Worth| Adrian Perez de Castro, Keith Packard; [notmuch] Introducing myself (inbox signed unread) +thread:XXX 2009-11-18 [1/3] Carl Worth| Israel Herraiz, Keith Packard; [notmuch] New to the list (inbox unread) +thread:XXX 2009-11-18 [1/3] Carl Worth| Jan Janak; [notmuch] What a great idea! (inbox unread) +thread:XXX 2009-11-18 [1/2] Carl Worth| Jan Janak; [notmuch] [PATCH] Older versions of install do not support -C. (inbox unread) +thread:XXX 2009-11-18 [1/3(4)] Carl Worth| Aron Griffis, Keith Packard; [notmuch] archive (inbox unread) +thread:XXX 2009-11-18 [1/2] Carl Worth| Keith Packard; [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags (inbox unread) +thread:XXX 2009-11-18 [1/7] Carl Worth| Lars Kellogg-Stedman, Mikhail Gusarov, Keith Packard; [notmuch] Working with Maildir storage? (inbox signed unread) +thread:XXX 2009-11-18 [2/5] Carl Worth| Mikhail Gusarov, Keith Packard; [notmuch] [PATCH 1/2] Close message file after parsing message headers (inbox unread) +thread:XXX 2009-11-17 [1/2] Carl Worth| Alex Botero-Lowry; [notmuch] preliminary FreeBSD support (attachment inbox unread) +EOF + +test_begin_subtest "search with saved query from config file" +query_name="test${RANDOM}" +backup_config +printf "Before:\n" > OUTPUT +notmuch search query:$query_name 2>&1 | notmuch_search_sanitize >> OUTPUT +printf "\n[query]\n${query_name} = from:cworth\n" >> notmuch-config +printf "After:\n" >> OUTPUT +notmuch search query:$query_name 2>&1 | notmuch_search_sanitize >> OUTPUT +restore_config +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "search with saved query from config file (xdg)" +query_name="test${RANDOM}" +xdg_config +printf "Before:\n" > OUTPUT +notmuch search query:$query_name 2>&1 | notmuch_search_sanitize >> OUTPUT +printf "\n[query]\n${query_name} = from:cworth\n" >> ${CONFIG_PATH} +printf "After:\n" >> OUTPUT +notmuch search query:$query_name 2>&1 | notmuch_search_sanitize >> OUTPUT +restore_config +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "search with saved query from config file (xdg + profile)" +query_name="test${RANDOM}" +xdg_config $query_name +printf "Before:\n" > OUTPUT +notmuch search query:$query_name 2>&1 | notmuch_search_sanitize >> OUTPUT +printf "\n[query]\n${query_name} = from:cworth\n" >> ${CONFIG_PATH} +printf "After:\n" >> OUTPUT +notmuch search query:$query_name 2>&1 | notmuch_search_sanitize >> OUTPUT +restore_config +test_expect_equal_file EXPECTED OUTPUT + +cat <<EOF > EXPECTED +Before: +After: +Alex Botero-Lowry <alex.boterolowry@gmail.com> +Alexander Botero-Lowry <alex.boterolowry@gmail.com> +François Boulogne <boulogne.f@gmail.com> +Jjgod Jiang <gzjjgod@gmail.com> +EOF + +test_begin_subtest "address: saved query from config file" +backup_config +query_name="test${RANDOM}" +printf "Before:\n" > OUTPUT +notmuch address --deduplicate=no --output=sender query:$query_name 2>&1 | sort >> OUTPUT +printf "\n[query]\n${query_name} = from:gmail.com\n" >> notmuch-config +printf "After:\n" >> OUTPUT +notmuch address --output=sender query:$query_name | sort >> OUTPUT +restore_config +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "address: saved query from config file (xdg)" +query_name="test${RANDOM}" +xdg_config +printf "Before:\n" > OUTPUT +notmuch address --deduplicate=no --output=sender query:$query_name 2>&1 | sort >> OUTPUT +printf "\n[query]\n${query_name} = from:gmail.com\n" >> ${CONFIG_PATH} +printf "After:\n" >> OUTPUT +notmuch address --output=sender query:$query_name | sort >> OUTPUT +restore_config +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "address: saved query from config file (xdg+profile)" +query_name="test${RANDOM}" +xdg_config $query_name +printf "Before:\n" > OUTPUT +notmuch address --deduplicate=no --output=sender query:$query_name 2>&1 | sort >> OUTPUT +printf "\n[query]\n${query_name} = from:gmail.com\n" >> ${CONFIG_PATH} +printf "After:\n" >> OUTPUT +notmuch address --output=sender query:$query_name | sort >> OUTPUT +restore_config +test_expect_equal_file EXPECTED OUTPUT + test_done diff --git a/test/T750-user-header.sh b/test/T750-user-header.sh index 204c052a..ff554b06 100755 --- a/test/T750-user-header.sh +++ b/test/T750-user-header.sh @@ -108,4 +108,22 @@ MAIL_DIR/new/04:2, EOF test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "index user header, config from file" +field_name="Test" +printf "\n[index]\nheader.${field_name} = List-Id\n" >> notmuch-config +notmuch reindex '*' +notmuch search --output=files ${field_name}:notmuch | notmuch_search_files_sanitize | sort > OUTPUT +cat <<EOF > EXPECTED +MAIL_DIR/bar/baz/05:2, +MAIL_DIR/bar/baz/23:2, +MAIL_DIR/bar/baz/24:2, +MAIL_DIR/bar/cur/20:2, +MAIL_DIR/bar/new/21:2, +MAIL_DIR/bar/new/22:2, +MAIL_DIR/foo/cur/08:2, +MAIL_DIR/foo/new/03:2, +MAIL_DIR/new/04:2, +EOF +test_expect_equal_file EXPECTED OUTPUT + test_done -- 2.30.0\r
This is intended for use in temporary code transitioning to the new configuration system. The name is chosen to avoid cluttering the notmuch_config_* namespace further with non-library functions. --- notmuch-client.h | 2 ++ notmuch-config.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/notmuch-client.h b/notmuch-client.h index e8fb0323..a026002a 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -334,6 +334,8 @@ void notmuch_config_set_search_exclude_tags (notmuch_config_t *config, const char *list[], size_t length); +const char * +_notmuch_config_get_path (notmuch_config_t *config); int notmuch_run_hook (const char *db_path, const char *hook); diff --git a/notmuch-config.c b/notmuch-config.c index 4fa274c7..0193401f 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -510,6 +510,9 @@ notmuch_config_close (notmuch_config_t *config) talloc_free (config); } +const char *_notmuch_config_get_path (notmuch_config_t *config) { + return config->filename; +} /* Save any changes made to the notmuch configuration. * * Any comments originally in the file will be preserved. -- 2.30.0
This will need some cleanup when the transition completes, and we stop passing notmuch_config_t structs to the subcommands. Unlike the general case, we open the database in the subcommand, since we don't know whether it should be opened read/write until we parse the command line arguments. Add a test to make sure passing config file on the command line is not broken by these or future config related changes. --- notmuch-show.c | 54 ++++++++++++++++++++++++---------------- test/T035-read-config.sh | 29 +++++++++++++++++++++ 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/notmuch-show.c b/notmuch-show.c index 04b90cd7..5e90c143 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -1234,6 +1234,7 @@ notmuch_show_command (notmuch_config_t *config, unused(notmuch_database_t *notmu bool entire_thread_set = false; bool single_message; bool unthreaded = FALSE; + char *status_string = NULL; notmuch_opt_desc_t options[] = { { .opt_keyword = &format, .name = "format", .keywords = @@ -1323,7 +1324,28 @@ notmuch_show_command (notmuch_config_t *config, unused(notmuch_database_t *notmu fprintf (stderr, "Warning: --include-html only implemented for format=text, format=json and format=sexp\n"); } - query_string = query_string_from_args (config, argc - opt_index, argv + opt_index); + notmuch_database_mode_t mode = NOTMUCH_DATABASE_MODE_READ_ONLY; + if (params.crypto.decrypt == NOTMUCH_DECRYPT_TRUE) + mode = NOTMUCH_DATABASE_MODE_READ_WRITE; + if (notmuch_database_open_with_config (NULL, + mode, + _notmuch_config_get_path (config), + NULL, + ¬much, + &status_string)) { + if (status_string) { + fputs (status_string, stderr); + free (status_string); + } + + return EXIT_FAILURE; + } + + config = NULL; + + notmuch_exit_if_unmatched_db_uuid (notmuch); + + query_string = query_string_from_args (notmuch, argc - opt_index, argv + opt_index); if (query_string == NULL) { fprintf (stderr, "Out of memory\n"); return EXIT_FAILURE; @@ -1334,15 +1356,6 @@ notmuch_show_command (notmuch_config_t *config, unused(notmuch_database_t *notmu return EXIT_FAILURE; } - notmuch_database_mode_t mode = NOTMUCH_DATABASE_MODE_READ_ONLY; - if (params.crypto.decrypt == NOTMUCH_DECRYPT_TRUE) - mode = NOTMUCH_DATABASE_MODE_READ_WRITE; - if (notmuch_database_open (notmuch_config_get_database_path (config), - mode, ¬much)) - return EXIT_FAILURE; - - notmuch_exit_if_unmatched_db_uuid (notmuch); - query = notmuch_query_create (notmuch, query_string); if (query == NULL) { fprintf (stderr, "Out of memory\n"); @@ -1351,27 +1364,26 @@ notmuch_show_command (notmuch_config_t *config, unused(notmuch_database_t *notmu /* Create structure printer. */ formatter = formatters[format]; - sprinter = formatter->new_sprinter (config, stdout); + sprinter = formatter->new_sprinter (notmuch, stdout); params.out_stream = g_mime_stream_stdout_new (); /* If a single message is requested we do not use search_excludes. */ if (single_message) { - ret = do_show_single (config, query, formatter, sprinter, ¶ms); + ret = do_show_single (notmuch, query, formatter, sprinter, ¶ms); } else { /* We always apply set the exclude flag. The * exclude=true|false option controls whether or not we return * threads that only match in an excluded message */ - const char **search_exclude_tags; - size_t search_exclude_tags_length; - unsigned int i; + notmuch_config_values_t *exclude_tags; notmuch_status_t status; - search_exclude_tags = notmuch_config_get_search_exclude_tags - (config, &search_exclude_tags_length); + for (exclude_tags = notmuch_config_get_values (notmuch, NOTMUCH_CONFIG_EXCLUDE_TAGS); + notmuch_config_values_valid (exclude_tags); + notmuch_config_values_move_to_next (exclude_tags)) { - for (i = 0; i < search_exclude_tags_length; i++) { - status = notmuch_query_add_tag_exclude (query, search_exclude_tags[i]); + status = notmuch_query_add_tag_exclude (query, + notmuch_config_values_get (exclude_tags)); if (status && status != NOTMUCH_STATUS_IGNORED) { print_status_query ("notmuch show", query, status); ret = -1; @@ -1385,9 +1397,9 @@ notmuch_show_command (notmuch_config_t *config, unused(notmuch_database_t *notmu } if (unthreaded) - ret = do_show_unthreaded (config, query, formatter, sprinter, ¶ms); + ret = do_show_unthreaded (notmuch, query, formatter, sprinter, ¶ms); else - ret = do_show_threaded (config, query, formatter, sprinter, ¶ms); + ret = do_show_threaded (notmuch, query, formatter, sprinter, ¶ms); } DONE: diff --git a/test/T035-read-config.sh b/test/T035-read-config.sh index 6cc4da8d..01190017 100755 --- a/test/T035-read-config.sh +++ b/test/T035-read-config.sh @@ -368,4 +368,33 @@ notmuch address --output=sender query:$query_name | sort >> OUTPUT restore_config test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "show with alternate config" +backup_database +cp notmuch-config alt-config +notmuch --config=alt-config config set search.exclude_tags foobar17 +notmuch tag -- +foobar17 '*' +output=$(notmuch --config=alt-config show '*') +restore_database +test_expect_equal "$output" "" + +test_begin_subtest "show with alternate config (xdg)" +backup_database +notmuch tag -- +foobar17 '*' +xdg_config +notmuch --config=${CONFIG_PATH} config set search.exclude_tags foobar17 +output=$(notmuch show '*') +restore_database +restore_config +test_expect_equal "$output" "" + +test_begin_subtest "show with alternate config (xdg+profile)" +backup_database +notmuch tag -- +foobar17 '*' +xdg_config foobar17 +notmuch --config=${CONFIG_PATH} config set search.exclude_tags foobar17 +output=$(notmuch show '*') +restore_database +restore_config +test_expect_equal "$output" "" + test_done -- 2.30.0
In addition to changing configuration access, change talloc context for allocation. --- notmuch-tag.c | 25 ++++++++++++--------- notmuch.c | 2 +- test/T035-read-config.sh | 48 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 12 deletions(-) diff --git a/notmuch-tag.c b/notmuch-tag.c index 205f2733..464874b4 100644 --- a/notmuch-tag.c +++ b/notmuch-tag.c @@ -187,11 +187,10 @@ tag_file (void *ctx, notmuch_database_t *notmuch, tag_op_flag_t flags, } int -notmuch_tag_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) +notmuch_tag_command (unused(notmuch_config_t *config), notmuch_database_t *notmuch, int argc, char *argv[]) { tag_op_list_t *tag_ops = NULL; char *query_string = NULL; - notmuch_database_t *notmuch; struct sigaction action; tag_op_flag_t tag_flags = TAG_FLAG_NONE; bool batch = false; @@ -200,6 +199,7 @@ notmuch_tag_command (notmuch_config_t *config, unused(notmuch_database_t *notmuc const char *input_file_name = NULL; int opt_index; int ret; + notmuch_bool_t synchronize_flags; /* Set up our handler for SIGINT */ memset (&action, 0, sizeof (struct sigaction)); @@ -240,13 +240,13 @@ notmuch_tag_command (notmuch_config_t *config, unused(notmuch_database_t *notmuc return EXIT_FAILURE; } } else { - tag_ops = tag_op_list_create (config); + tag_ops = tag_op_list_create (notmuch); if (tag_ops == NULL) { fprintf (stderr, "Out of memory.\n"); return EXIT_FAILURE; } - if (parse_tag_command_line (config, argc - opt_index, argv + opt_index, + if (parse_tag_command_line (notmuch, argc - opt_index, argv + opt_index, &query_string, tag_ops)) return EXIT_FAILURE; @@ -261,22 +261,25 @@ notmuch_tag_command (notmuch_config_t *config, unused(notmuch_database_t *notmuc } } - if (notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much)) - return EXIT_FAILURE; - notmuch_exit_if_unmatched_db_uuid (notmuch); - if (notmuch_config_get_maildir_synchronize_flags (config)) + if (print_status_database ( + "notmuch restore", + notmuch, + notmuch_config_get_bool (notmuch, NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS, + &synchronize_flags))) + return EXIT_FAILURE; + + if (synchronize_flags) tag_flags |= TAG_FLAG_MAILDIR_SYNC; if (remove_all) tag_flags |= TAG_FLAG_REMOVE_ALL; if (batch) - ret = tag_file (config, notmuch, tag_flags, input); + ret = tag_file (notmuch, notmuch, tag_flags, input); else - ret = tag_query (config, notmuch, query_string, tag_ops, tag_flags); + ret = tag_query (notmuch, notmuch, query_string, tag_ops, tag_flags); notmuch_database_destroy (notmuch); diff --git a/notmuch.c b/notmuch.c index 16504fc5..913fd312 100644 --- a/notmuch.c +++ b/notmuch.c @@ -157,7 +157,7 @@ static command_t commands[] = { "Count messages matching the search terms." }, { "reply", notmuch_reply_command, NOTMUCH_COMMAND_DATABASE_EARLY, "Construct a reply template for a set of messages." }, - { "tag", notmuch_tag_command, NOTMUCH_COMMAND_CONFIG_OPEN, + { "tag", notmuch_tag_command, NOTMUCH_COMMAND_DATABASE_EARLY | NOTMUCH_COMMAND_DATABASE_WRITE, "Add/remove tags for all messages matching the search terms." }, { "dump", notmuch_dump_command, NOTMUCH_COMMAND_DATABASE_EARLY | NOTMUCH_COMMAND_DATABASE_WRITE, "Create a plain-text dump of the tags for each message." }, diff --git a/test/T035-read-config.sh b/test/T035-read-config.sh index 01190017..c8d140b3 100755 --- a/test/T035-read-config.sh +++ b/test/T035-read-config.sh @@ -397,4 +397,52 @@ restore_database restore_config test_expect_equal "$output" "" +# reset to known state +add_email_corpus + +test_begin_subtest "tag with saved query from config file" +backup_config +query_name="test${RANDOM}" +tag_name="tag${RANDOM}" +notmuch count query:$query_name > OUTPUT +printf "\n[query]\n${query_name} = tag:inbox\n" >> notmuch-config +notmuch tag +$tag_name -- query:${query_name} +notmuch count tag:$tag_name >> OUTPUT +cat <<EOF > EXPECTED +0 +52 +EOF +restore_config +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "tag with saved query from config file (xdg)" +xdg_config +query_name="test${RANDOM}" +tag_name="tag${RANDOM}" +notmuch count query:$query_name > OUTPUT +printf "\n[query]\n${query_name} = tag:inbox\n" >> ${CONFIG_PATH} +notmuch tag +$tag_name -- query:${query_name} +notmuch count tag:$tag_name >> OUTPUT +cat <<EOF > EXPECTED +0 +52 +EOF +restore_config +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "tag with saved query from config file (xdg+profile)" +query_name="test${RANDOM}" +xdg_config ${query_name} +tag_name="tag${RANDOM}" +notmuch count query:$query_name > OUTPUT +printf "\n[query]\n${query_name} = tag:inbox\n" >> ${CONFIG_PATH} +notmuch tag +$tag_name -- query:${query_name} +notmuch count tag:$tag_name >> OUTPUT +cat <<EOF > EXPECTED +0 +52 +EOF +restore_config +test_expect_equal_file EXPECTED OUTPUT + test_done -- 2.30.0
This is a simple convenience routine to cache a configuration value without writing it to the database. --- lib/config.cc | 5 +++++ lib/notmuch-private.h | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/lib/config.cc b/lib/config.cc index d14f5422..fe67ef41 100644 --- a/lib/config.cc +++ b/lib/config.cc @@ -464,3 +464,8 @@ notmuch_config_set (notmuch_database_t *notmuch, notmuch_config_key_t key, const return notmuch_database_set_config (notmuch, _notmuch_config_key_to_string (key), val); } + +void +_notmuch_config_cache (notmuch_database_t *notmuch, notmuch_config_key_t key, const char *val) { + _notmuch_string_map_set (notmuch->config, _notmuch_config_key_to_string (key), val); +} diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 961d50cf..5e0b42e3 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -713,6 +713,10 @@ _notmuch_config_load_from_file (notmuch_database_t * db, GKeyFile *file); notmuch_status_t _notmuch_config_load_defaults (notmuch_database_t * db); + +void +_notmuch_config_cache (notmuch_database_t *db, notmuch_config_key_t key, const char* val); + NOTMUCH_END_DECLS #ifdef __cplusplus -- 2.30.0
The "back end" function takes an open notmuch database, which should know its own path (i.e. the path needs to be cached in the configuration data). --- lib/database.cc | 42 +++++++++++++++++++++++++++++++++--------- lib/notmuch.h | 12 ++++++++++++ 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/lib/database.cc b/lib/database.cc index 0f4e2ff9..650359f4 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -705,27 +705,51 @@ notmuch_database_compact (const char *path, notmuch_compact_status_cb_t status_cb, void *closure) { - void *local; - char *notmuch_path, *xapian_path, *compact_xapian_path; notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS; notmuch_database_t *notmuch = NULL; - struct stat statbuf; - bool keep_backup; char *message = NULL; - local = talloc_new (NULL); - if (! local) - return NOTMUCH_STATUS_OUT_OF_MEMORY; - ret = notmuch_database_open_verbose (path, NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much, &message); if (ret) { if (status_cb) status_cb (message, closure); - goto DONE; + return ret; } + _notmuch_config_cache (notmuch, NOTMUCH_CONFIG_DATABASE_PATH, path); + + return notmuch_database_compact_db (notmuch, + backup_path, + status_cb, + closure); +} + +notmuch_status_t +notmuch_database_compact_db (notmuch_database_t *notmuch, + const char *backup_path, + notmuch_compact_status_cb_t status_cb, + void *closure) { + void *local; + char *notmuch_path, *xapian_path, *compact_xapian_path; + const char* path; + notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS; + struct stat statbuf; + bool keep_backup; + + ret = _notmuch_database_ensure_writable (notmuch); + if (ret) + return ret; + + path = notmuch_config_get (notmuch, NOTMUCH_CONFIG_DATABASE_PATH); + if (! path) + return NOTMUCH_STATUS_PATH_ERROR; + + local = talloc_new (NULL); + if (! local) + return NOTMUCH_STATUS_OUT_OF_MEMORY; + if (! (notmuch_path = talloc_asprintf (local, "%s/%s", path, ".notmuch"))) { ret = NOTMUCH_STATUS_OUT_OF_MEMORY; goto DONE; diff --git a/lib/notmuch.h b/lib/notmuch.h index 4a513b44..4e5312ad 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -497,6 +497,18 @@ notmuch_database_compact (const char *path, notmuch_compact_status_cb_t status_cb, void *closure); +/** + * Like notmuch_database_compact, but take an open database as a + * parameter. + * + * @since libnnotmuch 5.4 (notmuch 0.32) + */ +notmuch_status_t +notmuch_database_compact_db (notmuch_database_t *database, + const char *backup_path, + notmuch_compact_status_cb_t status_cb, + void *closure); + /** * Destroy the notmuch database, closing it if necessary and freeing * all associated resources. -- 2.30.0
Switch to the newly created API function notmuch_database_compact_db, which takes the database opened in main(). --- notmuch-compact.c | 7 +++---- notmuch.c | 2 +- test/T035-read-config.sh | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/notmuch-compact.c b/notmuch-compact.c index ab2066e1..361583db 100644 --- a/notmuch-compact.c +++ b/notmuch-compact.c @@ -27,9 +27,8 @@ status_update_cb (const char *msg, unused (void *closure)) } int -notmuch_compact_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) +notmuch_compact_command (unused(notmuch_config_t *config), notmuch_database_t *notmuch, int argc, char *argv[]) { - const char *path = notmuch_config_get_database_path (config); const char *backup_path = NULL; notmuch_status_t ret; bool quiet = false; @@ -55,8 +54,8 @@ notmuch_compact_command (notmuch_config_t *config, unused(notmuch_database_t *no if (! quiet) printf ("Compacting database...\n"); - ret = notmuch_database_compact (path, backup_path, - quiet ? NULL : status_update_cb, NULL); + ret = notmuch_database_compact_db (notmuch, backup_path, + quiet ? NULL : status_update_cb, NULL); if (ret) { fprintf (stderr, "Compaction failed: %s\n", notmuch_status_to_string (ret)); return EXIT_FAILURE; diff --git a/notmuch.c b/notmuch.c index 913fd312..3a299afa 100644 --- a/notmuch.c +++ b/notmuch.c @@ -163,7 +163,7 @@ static command_t commands[] = { "Create a plain-text dump of the tags for each message." }, { "restore", notmuch_restore_command, NOTMUCH_COMMAND_DATABASE_EARLY | NOTMUCH_COMMAND_DATABASE_WRITE, "Restore the tags from the given dump file (see 'dump')." }, - { "compact", notmuch_compact_command, NOTMUCH_COMMAND_CONFIG_OPEN, + { "compact", notmuch_compact_command, NOTMUCH_COMMAND_DATABASE_EARLY | NOTMUCH_COMMAND_DATABASE_WRITE, "Compact the notmuch database." }, { "reindex", notmuch_reindex_command, NOTMUCH_COMMAND_DATABASE_EARLY | NOTMUCH_COMMAND_DATABASE_WRITE, "Re-index all messages matching the search terms." }, diff --git a/test/T035-read-config.sh b/test/T035-read-config.sh index c8d140b3..c3bff8b9 100755 --- a/test/T035-read-config.sh +++ b/test/T035-read-config.sh @@ -445,4 +445,18 @@ EOF restore_config test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "running compact (xdg)" +xdg_config +notmuch compact +output=$(notmuch count '*') +restore_config +test_expect_equal "52" "$output" + +test_begin_subtest "running compact (xdg + profile)" +xdg_config ${RANDOM} +notmuch compact +output=$(notmuch count '*') +restore_config +test_expect_equal "52" "$output" + test_done -- 2.30.0
These are needed so that the later codes line up numerically. --- bindings/python-cffi/notmuch2/_build.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bindings/python-cffi/notmuch2/_build.py b/bindings/python-cffi/notmuch2/_build.py index f269f2a1..d3bb9104 100644 --- a/bindings/python-cffi/notmuch2/_build.py +++ b/bindings/python-cffi/notmuch2/_build.py @@ -47,6 +47,9 @@ ffibuilder.cdef( NOTMUCH_STATUS_UPGRADE_REQUIRED, NOTMUCH_STATUS_PATH_ERROR, NOTMUCH_STATUS_ILLEGAL_ARGUMENT, + NOTMUCH_STATUS_MALFORMED_CRYPTO_PROTOCOL, + NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION, + NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL, NOTMUCH_STATUS_LAST_STATUS } notmuch_status_t; typedef enum { -- 2.30.0
This will be needed by (at least) the conversion of notmuch-new.c to the new config framework --- lib/config.cc | 3 +++ lib/notmuch.h | 1 + test/T590-libconfig.sh | 1 + 3 files changed, 5 insertions(+) diff --git a/lib/config.cc b/lib/config.cc index fe67ef41..4c0cfe92 100644 --- a/lib/config.cc +++ b/lib/config.cc @@ -394,6 +394,8 @@ _notmuch_config_key_to_string (notmuch_config_key_t key) { return "search.exclude_tags"; case NOTMUCH_CONFIG_NEW_TAGS: return "new.tags"; + case NOTMUCH_CONFIG_NEW_IGNORE: + return "new.ignore"; case NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS: return "maildir.synchronize_flags"; case NOTMUCH_CONFIG_PRIMARY_EMAIL: @@ -426,6 +428,7 @@ _notmuch_config_default (void *ctx, notmuch_config_key_t key) { return "inbox;unread"; case NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS: return "true"; + case NOTMUCH_CONFIG_NEW_IGNORE: case NOTMUCH_CONFIG_USER_NAME: case NOTMUCH_CONFIG_PRIMARY_EMAIL: case NOTMUCH_CONFIG_OTHER_EMAIL: diff --git a/lib/notmuch.h b/lib/notmuch.h index 4e5312ad..5ec14c38 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -2427,6 +2427,7 @@ typedef enum _notmuch_config_key { NOTMUCH_CONFIG_DATABASE_PATH = NOTMUCH_CONFIG_FIRST, NOTMUCH_CONFIG_EXCLUDE_TAGS, NOTMUCH_CONFIG_NEW_TAGS, + NOTMUCH_CONFIG_NEW_IGNORE, NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS, NOTMUCH_CONFIG_PRIMARY_EMAIL, NOTMUCH_CONFIG_OTHER_EMAIL, diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index ab439078..4edc53f1 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -335,6 +335,7 @@ cat <<'EOF' >EXPECTED MAIL_DIR inbox;unread +NULL true NULL NULL -- 2.30.0
The plan is to share code with a new database creation function that has a similar API to n_d_open_with_config. --- lib/open.cc | 50 +++++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/lib/open.cc b/lib/open.cc index ad74ccd6..6e8e0f6f 100644 --- a/lib/open.cc +++ b/lib/open.cc @@ -114,6 +114,36 @@ DONE: return status; } +static notmuch_status_t +_choose_database_path (const char *config_path, + const char *profile, + GKeyFile **key_file, + const char **database_path, + char **message) +{ + notmuch_status_t status; + + status =_load_key_file (config_path, profile, key_file); + if (status) { + *message = strdup ("Error: cannot load config file.\n"); + return status; + } + + if (! *database_path && *key_file) + *database_path = g_key_file_get_value (*key_file, "database", "path", NULL); + + if (*database_path == NULL) { + *message = strdup ("Error: Cannot open a database for a NULL path.\n"); + return NOTMUCH_STATUS_NULL_POINTER; + } + + if (*database_path[0] != '/') { + *message = strdup ("Error: Database path must be absolute.\n"); + return NOTMUCH_STATUS_PATH_ERROR; + } + return NOTMUCH_STATUS_SUCCESS; +} + notmuch_status_t notmuch_database_open_with_config (const char *database_path, notmuch_database_mode_t mode, @@ -133,26 +163,8 @@ notmuch_database_open_with_config (const char *database_path, GKeyFile *key_file = NULL; static int initialized = 0; - status = _load_key_file (config_path, profile, &key_file); - if (status) { - message = strdup ("Error: cannot load config file"); - goto DONE; - } - - if (! database_path && key_file) - database_path = g_key_file_get_value (key_file, "database", "path", NULL); - - if (database_path == NULL) { - message = strdup ("Error: Cannot open a database for a NULL path.\n"); - status = NOTMUCH_STATUS_NULL_POINTER; + if ((status = _choose_database_path (config_path, profile, &key_file, &database_path, &message))) goto DONE; - } - - if (database_path[0] != '/') { - message = strdup ("Error: Database path must be absolute.\n"); - status = NOTMUCH_STATUS_PATH_ERROR; - goto DONE; - } if (! (notmuch_path = talloc_asprintf (local, "%s/%s", database_path, ".notmuch"))) { message = strdup ("Out of memory\n"); -- 2.30.0
This will allow client code to provide more meaningful diagnostics. In particular it will enable "notmuch new" to continue suggsting the user run "notmuch setup" to create a config after "notmuch new" is transitioned to the new configuration framework. --- bindings/python-cffi/notmuch2/_build.py | 1 + bindings/python-cffi/notmuch2/_errors.py | 4 +++- lib/notmuch.h | 4 ++++ lib/open.cc | 2 +- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/bindings/python-cffi/notmuch2/_build.py b/bindings/python-cffi/notmuch2/_build.py index d3bb9104..93828627 100644 --- a/bindings/python-cffi/notmuch2/_build.py +++ b/bindings/python-cffi/notmuch2/_build.py @@ -50,6 +50,7 @@ ffibuilder.cdef( NOTMUCH_STATUS_MALFORMED_CRYPTO_PROTOCOL, NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION, NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL, + NOTMUCH_STATUS_NO_CONFIG, NOTMUCH_STATUS_LAST_STATUS } notmuch_status_t; typedef enum { diff --git a/bindings/python-cffi/notmuch2/_errors.py b/bindings/python-cffi/notmuch2/_errors.py index 13369445..c97d99cb 100644 --- a/bindings/python-cffi/notmuch2/_errors.py +++ b/bindings/python-cffi/notmuch2/_errors.py @@ -50,6 +50,8 @@ class NotmuchError(Exception): PathError, capi.lib.NOTMUCH_STATUS_ILLEGAL_ARGUMENT: IllegalArgumentError, + capi.lib.NOTMUCH_STATUS_NO_CONFIG: + NoConfigError, } return types[status] @@ -94,7 +96,7 @@ class UnsupportedOperationError(NotmuchError): pass class UpgradeRequiredError(NotmuchError): pass class PathError(NotmuchError): pass class IllegalArgumentError(NotmuchError): pass - +class NoConfigError(NotmuchError): pass class ObjectDestroyedError(NotmuchError): """The object has already been destroyed and it's memory freed. diff --git a/lib/notmuch.h b/lib/notmuch.h index 5ec14c38..4f384e58 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -208,6 +208,10 @@ typedef enum _notmuch_status { * something that notmuch doesn't know how to handle. */ NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL, + /** + * Unable to load a config file + */ + NOTMUCH_STATUS_NO_CONFIG, /** * Not an actual status value. Just a way to find out how many * valid status values there are. diff --git a/lib/open.cc b/lib/open.cc index 6e8e0f6f..06d079e4 100644 --- a/lib/open.cc +++ b/lib/open.cc @@ -106,7 +106,7 @@ _load_key_file (const char *path, *key_file = g_key_file_new (); if (! g_key_file_load_from_file (*key_file, path, G_KEY_FILE_NONE, NULL)) { - status = NOTMUCH_STATUS_FILE_ERROR; + status = NOTMUCH_STATUS_NO_CONFIG; } DONE: -- 2.30.0
This will help share code with n_d_open_with_config. --- lib/database.cc | 103 ------------------------------------------------ lib/open.cc | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 103 deletions(-) diff --git a/lib/database.cc b/lib/database.cc index 650359f4..f96ba7c0 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -453,109 +453,6 @@ notmuch_database_find_message (notmuch_database_t *notmuch, } } -notmuch_status_t -notmuch_database_create (const char *path, notmuch_database_t **database) -{ - char *status_string = NULL; - notmuch_status_t status; - - status = notmuch_database_create_verbose (path, database, - &status_string); - - if (status_string) { - fputs (status_string, stderr); - free (status_string); - } - - return status; -} - -notmuch_status_t -notmuch_database_create_verbose (const char *path, - notmuch_database_t **database, - char **status_string) -{ - notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; - notmuch_database_t *notmuch = NULL; - char *notmuch_path = NULL; - char *message = NULL; - struct stat st; - int err; - - if (path == NULL) { - message = strdup ("Error: Cannot create a database for a NULL path.\n"); - status = NOTMUCH_STATUS_NULL_POINTER; - goto DONE; - } - - if (path[0] != '/') { - message = strdup ("Error: Database path must be absolute.\n"); - status = NOTMUCH_STATUS_PATH_ERROR; - goto DONE; - } - - err = stat (path, &st); - if (err) { - IGNORE_RESULT (asprintf (&message, "Error: Cannot create database at %s: %s.\n", - path, strerror (errno))); - status = NOTMUCH_STATUS_FILE_ERROR; - goto DONE; - } - - if (! S_ISDIR (st.st_mode)) { - IGNORE_RESULT (asprintf (&message, "Error: Cannot create database at %s: " - "Not a directory.\n", - path)); - status = NOTMUCH_STATUS_FILE_ERROR; - goto DONE; - } - - notmuch_path = talloc_asprintf (NULL, "%s/%s", path, ".notmuch"); - - err = mkdir (notmuch_path, 0755); - - if (err) { - IGNORE_RESULT (asprintf (&message, "Error: Cannot create directory %s: %s.\n", - notmuch_path, strerror (errno))); - status = NOTMUCH_STATUS_FILE_ERROR; - goto DONE; - } - - status = notmuch_database_open_verbose (path, - NOTMUCH_DATABASE_MODE_READ_WRITE, - ¬much, &message); - if (status) - goto DONE; - - /* Upgrade doesn't add these feature to existing databases, but - * new databases have them. */ - notmuch->features |= NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES; - notmuch->features |= NOTMUCH_FEATURE_INDEXED_MIMETYPES; - notmuch->features |= NOTMUCH_FEATURE_UNPREFIX_BODY_ONLY; - - status = notmuch_database_upgrade (notmuch, NULL, NULL); - if (status) { - notmuch_database_close (notmuch); - notmuch = NULL; - } - - DONE: - if (notmuch_path) - talloc_free (notmuch_path); - - if (message) { - if (status_string) - *status_string = message; - else - free (message); - } - if (database) - *database = notmuch; - else - talloc_free (notmuch); - return status; -} - notmuch_status_t _notmuch_database_ensure_writable (notmuch_database_t *notmuch) { diff --git a/lib/open.cc b/lib/open.cc index 06d079e4..6046868a 100644 --- a/lib/open.cc +++ b/lib/open.cc @@ -341,3 +341,106 @@ notmuch_database_open_with_config (const char *database_path, return status; } + +notmuch_status_t +notmuch_database_create (const char *path, notmuch_database_t **database) +{ + char *status_string = NULL; + notmuch_status_t status; + + status = notmuch_database_create_verbose (path, database, + &status_string); + + if (status_string) { + fputs (status_string, stderr); + free (status_string); + } + + return status; +} + +notmuch_status_t +notmuch_database_create_verbose (const char *path, + notmuch_database_t **database, + char **status_string) +{ + notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; + notmuch_database_t *notmuch = NULL; + char *notmuch_path = NULL; + char *message = NULL; + struct stat st; + int err; + + if (path == NULL) { + message = strdup ("Error: Cannot create a database for a NULL path.\n"); + status = NOTMUCH_STATUS_NULL_POINTER; + goto DONE; + } + + if (path[0] != '/') { + message = strdup ("Error: Database path must be absolute.\n"); + status = NOTMUCH_STATUS_PATH_ERROR; + goto DONE; + } + + err = stat (path, &st); + if (err) { + IGNORE_RESULT (asprintf (&message, "Error: Cannot create database at %s: %s.\n", + path, strerror (errno))); + status = NOTMUCH_STATUS_FILE_ERROR; + goto DONE; + } + + if (! S_ISDIR (st.st_mode)) { + IGNORE_RESULT (asprintf (&message, "Error: Cannot create database at %s: " + "Not a directory.\n", + path)); + status = NOTMUCH_STATUS_FILE_ERROR; + goto DONE; + } + + notmuch_path = talloc_asprintf (NULL, "%s/%s", path, ".notmuch"); + + err = mkdir (notmuch_path, 0755); + + if (err) { + IGNORE_RESULT (asprintf (&message, "Error: Cannot create directory %s: %s.\n", + notmuch_path, strerror (errno))); + status = NOTMUCH_STATUS_FILE_ERROR; + goto DONE; + } + + status = notmuch_database_open_verbose (path, + NOTMUCH_DATABASE_MODE_READ_WRITE, + ¬much, &message); + if (status) + goto DONE; + + /* Upgrade doesn't add these feature to existing databases, but + * new databases have them. */ + notmuch->features |= NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES; + notmuch->features |= NOTMUCH_FEATURE_INDEXED_MIMETYPES; + notmuch->features |= NOTMUCH_FEATURE_UNPREFIX_BODY_ONLY; + + status = notmuch_database_upgrade (notmuch, NULL, NULL); + if (status) { + notmuch_database_close (notmuch); + notmuch = NULL; + } + + DONE: + if (notmuch_path) + talloc_free (notmuch_path); + + if (message) { + if (status_string) + *status_string = message; + else + free (message); + } + if (database) + *database = notmuch; + else + talloc_free (notmuch); + return status; +} -- 2.30.0
It is desirable to distinguish between attempting to create a database that already exists, and more fatal errors like permission problems. --- bindings/python-cffi/notmuch2/_build.py | 1 + bindings/python-cffi/notmuch2/_errors.py | 3 +++ lib/notmuch.h | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/bindings/python-cffi/notmuch2/_build.py b/bindings/python-cffi/notmuch2/_build.py index 93828627..f67b4de6 100644 --- a/bindings/python-cffi/notmuch2/_build.py +++ b/bindings/python-cffi/notmuch2/_build.py @@ -51,6 +51,7 @@ ffibuilder.cdef( NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION, NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL, NOTMUCH_STATUS_NO_CONFIG, + NOTMUCH_STATUS_DATABASE_EXISTS, NOTMUCH_STATUS_LAST_STATUS } notmuch_status_t; typedef enum { diff --git a/bindings/python-cffi/notmuch2/_errors.py b/bindings/python-cffi/notmuch2/_errors.py index c97d99cb..65064d4e 100644 --- a/bindings/python-cffi/notmuch2/_errors.py +++ b/bindings/python-cffi/notmuch2/_errors.py @@ -52,6 +52,8 @@ class NotmuchError(Exception): IllegalArgumentError, capi.lib.NOTMUCH_STATUS_NO_CONFIG: NoConfigError, + capi.lib.NOTMUCH_STATUS_DATABASE_EXISTS: + DatabaseExistsError, } return types[status] @@ -97,6 +99,7 @@ class UpgradeRequiredError(NotmuchError): pass class PathError(NotmuchError): pass class IllegalArgumentError(NotmuchError): pass class NoConfigError(NotmuchError): pass +class DatabaseExistsError(NotmuchError): pass class ObjectDestroyedError(NotmuchError): """The object has already been destroyed and it's memory freed. diff --git a/lib/notmuch.h b/lib/notmuch.h index 4f384e58..6d22d328 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -212,6 +212,10 @@ typedef enum _notmuch_status { * Unable to load a config file */ NOTMUCH_STATUS_NO_CONFIG, + /** + * Database exists, so not (re)-created + */ + NOTMUCH_STATUS_DATABASE_EXISTS, /** * Not an actual status value. Just a way to find out how many * valid status values there are. -- 2.30.0
This takes a config path parameter, and can use that to decide the new database location. --- bindings/python-cffi/tests/test_database.py | 2 +- lib/notmuch.h | 27 +++++++++++ lib/open.cc | 50 ++++++++++++--------- test/T560-lib-error.sh | 2 +- 4 files changed, 58 insertions(+), 23 deletions(-) diff --git a/bindings/python-cffi/tests/test_database.py b/bindings/python-cffi/tests/test_database.py index a2c69de6..9b3219c0 100644 --- a/bindings/python-cffi/tests/test_database.py +++ b/bindings/python-cffi/tests/test_database.py @@ -80,7 +80,7 @@ class TestCreate: db.create(tmppath) def test_create_existing(self, tmppath, db): - with pytest.raises(errors.FileError): + with pytest.raises(errors.DatabaseExistsError): dbmod.Database.create(path=tmppath) def test_close(self, db): diff --git a/lib/notmuch.h b/lib/notmuch.h index 6d22d328..5e07a01a 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -442,6 +442,33 @@ notmuch_database_open_with_config (const char *database_path, const char *profile, notmuch_database_t **database, char **error_message); +/** + * Create a new notmuch database located at 'database_path', using + * configuration in 'config_path'. + * + * For description of arguments, @see notmuch_database_open_with_config + * + * @retval NOTMUCH_STATUS_SUCCESS: Successfully created the database. + * + * @retval NOTMUCH_STATUS_DATABASE_EXISTS: Database already exists, not created + * + * @retval NOTMUCH_STATUS_OUT_OF_MEMORY: Out of memory. + * + * @retval NOTMUCH_STATUS_FILE_ERROR: An error occurred trying to open the + * database or config file (such as permission denied, or file not found, + * etc.) + * + * @retval NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred. + * + * @since libnotmuch 5.4 (notmuch 0.32) + */ + +notmuch_status_t +notmuch_database_create_with_config (const char *database_path, + const char *config_path, + const char *profile, + notmuch_database_t **database, + char **error_message); /** * Retrieve last status string for given database. diff --git a/lib/open.cc b/lib/open.cc index 6046868a..577fc88a 100644 --- a/lib/open.cc +++ b/lib/open.cc @@ -363,30 +363,32 @@ notmuch_status_t notmuch_database_create_verbose (const char *path, notmuch_database_t **database, char **status_string) +{ + return notmuch_database_create_with_config (path, "", NULL, database, status_string); +} + +notmuch_status_t +notmuch_database_create_with_config (const char *database_path, + const char *config_path, + const char *profile, + notmuch_database_t **database, + char **status_string) { notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; notmuch_database_t *notmuch = NULL; char *notmuch_path = NULL; char *message = NULL; + GKeyFile *key_file = NULL; struct stat st; int err; - if (path == NULL) { - message = strdup ("Error: Cannot create a database for a NULL path.\n"); - status = NOTMUCH_STATUS_NULL_POINTER; - goto DONE; - } - - if (path[0] != '/') { - message = strdup ("Error: Database path must be absolute.\n"); - status = NOTMUCH_STATUS_PATH_ERROR; + if ((status = _choose_database_path (config_path, profile, &key_file, &database_path, &message))) goto DONE; - } - err = stat (path, &st); + err = stat (database_path, &st); if (err) { IGNORE_RESULT (asprintf (&message, "Error: Cannot create database at %s: %s.\n", - path, strerror (errno))); + database_path, strerror (errno))); status = NOTMUCH_STATUS_FILE_ERROR; goto DONE; } @@ -394,25 +396,31 @@ notmuch_database_create_verbose (const char *path, if (! S_ISDIR (st.st_mode)) { IGNORE_RESULT (asprintf (&message, "Error: Cannot create database at %s: " "Not a directory.\n", - path)); + database_path)); status = NOTMUCH_STATUS_FILE_ERROR; goto DONE; } - notmuch_path = talloc_asprintf (NULL, "%s/%s", path, ".notmuch"); + notmuch_path = talloc_asprintf (NULL, "%s/%s", database_path, ".notmuch"); err = mkdir (notmuch_path, 0755); - if (err) { - IGNORE_RESULT (asprintf (&message, "Error: Cannot create directory %s: %s.\n", - notmuch_path, strerror (errno))); - status = NOTMUCH_STATUS_FILE_ERROR; + if (errno == EEXIST) { + status = NOTMUCH_STATUS_DATABASE_EXISTS; + } else { + IGNORE_RESULT (asprintf (&message, "Error: Cannot create directory %s: %s.\n", + notmuch_path, strerror (errno))); + status = NOTMUCH_STATUS_FILE_ERROR; + } goto DONE; } - status = notmuch_database_open_verbose (path, - NOTMUCH_DATABASE_MODE_READ_WRITE, - ¬much, &message); + /* XXX this reads the config file twice, which is a bit wasteful */ + status = notmuch_database_open_with_config (database_path, + NOTMUCH_DATABASE_MODE_READ_WRITE, + config_path, + profile, + ¬much, &message); if (status) goto DONE; diff --git a/test/T560-lib-error.sh b/test/T560-lib-error.sh index 260ac120..ade376ef 100755 --- a/test/T560-lib-error.sh +++ b/test/T560-lib-error.sh @@ -93,7 +93,7 @@ EOF cat <<'EOF' >EXPECTED == stdout == == stderr == -Error: Cannot create a database for a NULL path. +Error: Cannot open a database for a NULL path. EOF test_expect_equal_file EXPECTED OUTPUT -- 2.30.0
Move to a separate function. This is essentially just code movement. --- notmuch-new.c | 97 ++++++++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 44 deletions(-) diff --git a/notmuch-new.c b/notmuch-new.c index e0e3de25..a5c3cb54 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -1042,6 +1042,57 @@ print_results (const add_files_state_t *state) 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); + notmuch_status_t status; + char *dot_notmuch_path = talloc_asprintf (notmuch, "%s/%s", state->db_path, ".notmuch"); + + /* 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 (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, unused(notmuch_database_t *notmuch), int argc, char *argv[]) { @@ -1142,50 +1193,8 @@ notmuch_new_command (notmuch_config_t *config, unused(notmuch_database_t *notmuc 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"); - } + if (_maybe_upgrade (notmuch, &add_files_state)) + return EXIT_FAILURE; add_files_state.total_files = 0; } -- 2.30.0
In addition to the same type of changes as converting other subcommands, add the possibility of creating a database at the top level. It would probably make sense to use this for insert as well. --- notmuch-client.h | 1 + notmuch-new.c | 112 +++++++++++++++++++-------------------- notmuch.c | 50 ++++++++++++----- test/T035-read-config.sh | 14 +++++ test/T040-setup.sh | 2 +- test/T050-new.sh | 6 +-- 6 files changed, 111 insertions(+), 74 deletions(-) diff --git a/notmuch-client.h b/notmuch-client.h index a026002a..9e09c36a 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -257,6 +257,7 @@ typedef enum { NOTMUCH_COMMAND_CONFIG_CREATE = 1 << 1, NOTMUCH_COMMAND_DATABASE_EARLY = 1 << 2, NOTMUCH_COMMAND_DATABASE_WRITE = 1 << 3, + NOTMUCH_COMMAND_DATABASE_CREATE = 1 << 4, } notmuch_command_mode_t; notmuch_config_t * diff --git a/notmuch-new.c b/notmuch-new.c index a5c3cb54..0f416939 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -48,8 +48,7 @@ typedef struct { enum verbosity verbosity; bool debug; bool full_scan; - const char **new_tags; - size_t new_tags_length; + notmuch_config_values_t *new_tags; const char **ignore_verbatim; size_t ignore_verbatim_length; regex_t *ignore_regex; @@ -65,7 +64,7 @@ typedef struct { _filename_list_t *removed_directories; _filename_list_t *directory_mtimes; - bool synchronize_flags; + notmuch_bool_t synchronize_flags; } add_files_state_t; static volatile sig_atomic_t do_print_progress = 0; @@ -245,19 +244,17 @@ _special_directory (const char *entry) } static bool -_setup_ignore (notmuch_config_t *config, add_files_state_t *state) +_setup_ignore (notmuch_database_t *notmuch, add_files_state_t *state) { - const char **ignore_list, **ignore; + notmuch_config_values_t *ignore_list; int nregex = 0, nverbatim = 0; const char **verbatim = NULL; regex_t *regex = NULL; - ignore_list = notmuch_config_get_new_ignore (config, NULL); - if (! ignore_list) - return true; - - for (ignore = ignore_list; *ignore; ignore++) { - const char *s = *ignore; + 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) { @@ -276,8 +273,8 @@ _setup_ignore (notmuch_config_t *config, add_files_state_t *state) return false; } - r = talloc_strndup (config, s + 1, len - 2); - regex = talloc_realloc (config, regex, regex_t, nregex + 1); + 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); @@ -295,7 +292,7 @@ _setup_ignore (notmuch_config_t *config, add_files_state_t *state) talloc_free (r); } else { - verbatim = talloc_realloc (config, verbatim, const char *, + verbatim = talloc_realloc (notmuch, verbatim, const char *, nverbatim + 1); verbatim[nverbatim++] = s; } @@ -371,7 +368,7 @@ add_file (notmuch_database_t *notmuch, const char *filename, 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); @@ -387,13 +384,17 @@ add_file (notmuch_database_t *notmuch, const char *filename, if (state->synchronize_flags) notmuch_message_maildir_flags_to_tags (message); - for (tag = state->new_tags; *tag != NULL; tag++) { + 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); + if (strcmp ("unread", tag) != 0 || ! is_set) { + notmuch_message_add_tag (message, tag); } } @@ -962,8 +963,7 @@ remove_filename (notmuch_database_t *notmuch, /* 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) { @@ -979,7 +979,7 @@ _remove_directory (void *ctx, 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, + absolute = talloc_asprintf (notmuch, "%s/%s", path, notmuch_filenames_get (files)); status = remove_filename (notmuch, absolute, add_files_state); talloc_free (absolute); @@ -990,9 +990,9 @@ _remove_directory (void *ctx, 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, + 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; @@ -1094,9 +1094,8 @@ _maybe_upgrade (notmuch_database_t *notmuch, add_files_state_t *state) { } int -notmuch_new_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) +notmuch_new_command (unused(notmuch_config_t *config), notmuch_database_t *notmuch, int argc, char *argv[]) { - notmuch_database_t *notmuch; add_files_state_t add_files_state = { .verbosity = VERBOSITY_NORMAL, .debug = false, @@ -1105,7 +1104,6 @@ notmuch_new_command (notmuch_config_t *config, unused(notmuch_database_t *notmuc }; struct timeval tv_start; int ret = 0; - struct stat st; const char *db_path; char *dot_notmuch_path; struct sigaction action; @@ -1140,21 +1138,30 @@ notmuch_new_command (notmuch_config_t *config, unused(notmuch_database_t *notmuc 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.synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config); - db_path = notmuch_config_get_database_path (config); + 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; - if (! _setup_ignore (config, &add_files_state)) + 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; } } @@ -1165,34 +1172,21 @@ notmuch_new_command (notmuch_config_t *config, unused(notmuch_database_t *notmuc return EXIT_FAILURE; } - dot_notmuch_path = talloc_asprintf (config, "%s/%s", db_path, ".notmuch"); + dot_notmuch_path = talloc_asprintf (notmuch, "%s/%s", db_path, ".notmuch"); - if (stat (dot_notmuch_path, &st)) { - int count; + notmuch_exit_if_unmatched_db_uuid (notmuch); - count = 0; + if (notmuch_database_get_revision (notmuch, NULL) == 0) { + int count = 0; count_files (db_path, &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); - } - return EXIT_FAILURE; - } - - notmuch_exit_if_unmatched_db_uuid (notmuch); - if (_maybe_upgrade (notmuch, &add_files_state)) return EXIT_FAILURE; @@ -1223,9 +1217,9 @@ notmuch_new_command (notmuch_config_t *config, unused(notmuch_database_t *notmuc 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 ()) { @@ -1252,7 +1246,7 @@ notmuch_new_command (notmuch_config_t *config, unused(notmuch_database_t *notmuc 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); + ret = _remove_directory (notmuch, f->filename, &add_files_state); if (ret) goto DONE; if (do_print_progress) { @@ -1287,11 +1281,13 @@ notmuch_new_command (notmuch_config_t *config, unused(notmuch_database_t *notmuc 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 (db_path, "post-new"); + notmuch_database_destroy (notmuch); + if (ret || interrupted) return EXIT_FAILURE; diff --git a/notmuch.c b/notmuch.c index 3a299afa..7360e0e6 100644 --- a/notmuch.c +++ b/notmuch.c @@ -143,7 +143,8 @@ static command_t commands[] = { "Notmuch main command." }, { "setup", notmuch_setup_command, NOTMUCH_COMMAND_CONFIG_OPEN | NOTMUCH_COMMAND_CONFIG_CREATE, "Interactively set up notmuch for first use." }, - { "new", notmuch_new_command, NOTMUCH_COMMAND_CONFIG_OPEN, + { "new", notmuch_new_command, + NOTMUCH_COMMAND_DATABASE_EARLY | NOTMUCH_COMMAND_DATABASE_WRITE | NOTMUCH_COMMAND_DATABASE_CREATE, "Find and import new messages to the notmuch database." }, { "insert", notmuch_insert_command, NOTMUCH_COMMAND_DATABASE_EARLY | NOTMUCH_COMMAND_DATABASE_WRITE, "Add a new message into the maildir and notmuch database." }, @@ -504,23 +505,48 @@ main (int argc, char *argv[]) if (command->mode & NOTMUCH_COMMAND_DATABASE_EARLY) { char *status_string = NULL; notmuch_database_mode_t mode; - if (command->mode & NOTMUCH_COMMAND_DATABASE_WRITE) + notmuch_status_t status; + + if (command->mode & NOTMUCH_COMMAND_DATABASE_WRITE || + command->mode & NOTMUCH_COMMAND_DATABASE_CREATE) mode = NOTMUCH_DATABASE_MODE_READ_WRITE; else mode = NOTMUCH_DATABASE_MODE_READ_ONLY; - if (notmuch_database_open_with_config (NULL, - mode, - config_file_name, - NULL, - ¬much, - &status_string)) { - if (status_string) { - fputs (status_string, stderr); - free (status_string); + if (command->mode & NOTMUCH_COMMAND_DATABASE_CREATE) { + status = notmuch_database_create_with_config (NULL, + config_file_name, + NULL, + ¬much, + &status_string); + if (status && status != NOTMUCH_STATUS_DATABASE_EXISTS) { + if (status_string) { + fputs (status_string, stderr); + free (status_string); + } + + if (status == NOTMUCH_STATUS_NO_CONFIG) + fputs ("Try running 'notmuch setup' to create a configuration.", stderr); + + return EXIT_FAILURE; } + } - return EXIT_FAILURE; + if (notmuch == NULL) { + status = notmuch_database_open_with_config (NULL, + mode, + config_file_name, + NULL, + ¬much, + &status_string); + if (status) { + if (status_string) { + fputs (status_string, stderr); + free (status_string); + } + + return EXIT_FAILURE; + } } } else { config = notmuch_config_open (local, config_file_name, command->mode); diff --git a/test/T035-read-config.sh b/test/T035-read-config.sh index c3bff8b9..c71b70e6 100755 --- a/test/T035-read-config.sh +++ b/test/T035-read-config.sh @@ -459,4 +459,18 @@ output=$(notmuch count '*') restore_config test_expect_equal "52" "$output" +test_begin_subtest "run notmuch-new (xdg)" +xdg_config +generate_message +output=$(NOTMUCH_NEW --debug) +restore_config +test_expect_equal "$output" "Added 1 new message to the database." + +test_begin_subtest "run notmuch-new (xdg + profile)" +xdg_config ${RANDOM} +generate_message +output=$(NOTMUCH_NEW --debug) +restore_config +test_expect_equal "$output" "Added 1 new message to the database." + test_done diff --git a/test/T040-setup.sh b/test/T040-setup.sh index fbfe200a..1223ebf7 100755 --- a/test/T040-setup.sh +++ b/test/T040-setup.sh @@ -6,7 +6,7 @@ test_description='"notmuch setup"' test_begin_subtest "Notmuch new without a config suggests notmuch setup" output=$(notmuch --config=new-notmuch-config new 2>&1) test_expect_equal "$output" "\ -Configuration file new-notmuch-config not found. +Error: cannot load config file. Try running 'notmuch setup' to create a configuration." test_begin_subtest "Create a new config interactively" diff --git a/test/T050-new.sh b/test/T050-new.sh index 009b2633..882939fa 100755 --- a/test/T050-new.sh +++ b/test/T050-new.sh @@ -324,10 +324,10 @@ test_expect_equal "$output" "" OLDCONFIG=$(notmuch config get new.tags) -test_begin_subtest "Empty tags in new.tags are forbidden" +test_begin_subtest "Empty tags in new.tags are ignored" notmuch config set new.tags "foo;;bar" -output=$(NOTMUCH_NEW --debug 2>&1) -test_expect_equal "$output" "Error: tag '' in new.tags: empty tag forbidden" +output=$(NOTMUCH_NEW --quiet 2>&1) +test_expect_equal "$output" "" test_begin_subtest "Tags starting with '-' in new.tags are forbidden" notmuch config set new.tags "-foo;bar" -- 2.30.0
The hook directory configuration needs to be kept in synch with the other configuration information, so add scaffolding to support this at database opening time. --- lib/config.cc | 3 +++ lib/notmuch.h | 1 + test/T590-libconfig.sh | 1 + 3 files changed, 5 insertions(+) diff --git a/lib/config.cc b/lib/config.cc index 4c0cfe92..948751bc 100644 --- a/lib/config.cc +++ b/lib/config.cc @@ -390,6 +390,8 @@ _notmuch_config_key_to_string (notmuch_config_key_t key) { switch (key) { case NOTMUCH_CONFIG_DATABASE_PATH: return "database.path"; + case NOTMUCH_CONFIG_HOOK_DIR: + return "database.hook_dir"; case NOTMUCH_CONFIG_EXCLUDE_TAGS: return "search.exclude_tags"; case NOTMUCH_CONFIG_NEW_TAGS: @@ -428,6 +430,7 @@ _notmuch_config_default (void *ctx, notmuch_config_key_t key) { return "inbox;unread"; case NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS: return "true"; + case NOTMUCH_CONFIG_HOOK_DIR: case NOTMUCH_CONFIG_NEW_IGNORE: case NOTMUCH_CONFIG_USER_NAME: case NOTMUCH_CONFIG_PRIMARY_EMAIL: diff --git a/lib/notmuch.h b/lib/notmuch.h index 5e07a01a..5a5d99c0 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -2460,6 +2460,7 @@ notmuch_config_list_destroy (notmuch_config_list_t *config_list); typedef enum _notmuch_config_key { NOTMUCH_CONFIG_FIRST, NOTMUCH_CONFIG_DATABASE_PATH = NOTMUCH_CONFIG_FIRST, + NOTMUCH_CONFIG_HOOK_DIR, NOTMUCH_CONFIG_EXCLUDE_TAGS, NOTMUCH_CONFIG_NEW_TAGS, NOTMUCH_CONFIG_NEW_IGNORE, diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index 4edc53f1..e2050b50 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -333,6 +333,7 @@ EOF cat <<'EOF' >EXPECTED == stdout == MAIL_DIR +NULL inbox;unread NULL -- 2.30.0
This is a simple two step path search. Most error checking is deferred until running the hooks. --- lib/open.cc | 42 ++++++++++++++++++++++++++++++++++++++++++ test/T590-libconfig.sh | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/lib/open.cc b/lib/open.cc index 577fc88a..b4637ec5 100644 --- a/lib/open.cc +++ b/lib/open.cc @@ -68,6 +68,44 @@ _xdg_dir (void *ctx, profile_name); } +static notmuch_status_t +_choose_hook_dir (notmuch_database_t *notmuch, + const char *profile, + char **message) +{ + const char *config; + const char *hook_dir; + struct stat st; + int err; + + hook_dir = notmuch_config_get (notmuch, NOTMUCH_CONFIG_HOOK_DIR); + + if (hook_dir) + return NOTMUCH_STATUS_SUCCESS; + + config = _xdg_dir (notmuch, "XDG_CONFIG_HOME", ".config", profile); + if (! config) + return NOTMUCH_STATUS_PATH_ERROR; + + hook_dir = talloc_asprintf (notmuch, "%s/hooks", config); + + err = stat (hook_dir, &st); + if (err) { + if (errno == ENOENT) { + const char *database_path = notmuch_database_get_path (notmuch); + hook_dir = talloc_asprintf (notmuch, "%s/.notmuch/hooks", database_path); + } else { + IGNORE_RESULT (asprintf (message, "Error: Cannot stat %s: %s.\n", + hook_dir, strerror (errno))); + return NOTMUCH_STATUS_FILE_ERROR; + } + } + + _notmuch_config_cache (notmuch, NOTMUCH_CONFIG_HOOK_DIR, hook_dir); + + return NOTMUCH_STATUS_SUCCESS; +} + static notmuch_status_t _load_key_file (const char *path, const char *profile, @@ -301,6 +339,10 @@ notmuch_database_open_with_config (const char *database_path, if (status) goto DONE; + status = _choose_hook_dir (notmuch, profile, &message); + if (status) + goto DONE; + status = _notmuch_config_load_defaults (notmuch); if (status) goto DONE; diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index e2050b50..e7e6e08a 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -201,6 +201,37 @@ EOF test_expect_equal_file EXPECTED OUTPUT restore_database +test_begin_subtest "NOTMUCH_CONFIG_HOOK_DIR: traditional" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} %NULL% +{ + const char *val = notmuch_config_get (db, NOTMUCH_CONFIG_HOOK_DIR); + printf("database.hook_dir = %s\n", val); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +database.hook_dir = MAIL_DIR/.notmuch/hooks +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "NOTMUCH_CONFIG_HOOK_DIR: xdg" +dir="${HOME}/.config/notmuch/default/hooks" +mkdir -p $dir +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} %NULL% +{ + const char *val = notmuch_config_get (db, NOTMUCH_CONFIG_HOOK_DIR); + printf("database.hook_dir = %s\n", val); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +database.hook_dir = CWD/home/.config/notmuch/default/hooks +== stderr == +EOF +rmdir $dir +test_expect_equal_file EXPECTED OUTPUT + test_begin_subtest "notmuch_config_get_values" cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} %NULL% { @@ -333,7 +364,7 @@ EOF cat <<'EOF' >EXPECTED == stdout == MAIL_DIR -NULL +MAIL_DIR/.notmuch/hooks inbox;unread NULL -- 2.30.0
This enables support for hooks outside the database directory. It relies strongly on configuration information being usable between closing the database and destroying it. --- hooks.c | 7 +- notmuch-client.h | 2 +- notmuch-insert.c | 7 +- notmuch-new.c | 10 +-- test/T400-hooks.sh | 197 +++++++++++++++++++++++++-------------------- 5 files changed, 120 insertions(+), 103 deletions(-) diff --git a/hooks.c b/hooks.c index 59c58070..ec89b22e 100644 --- a/hooks.c +++ b/hooks.c @@ -24,14 +24,15 @@ #include <sys/wait.h> int -notmuch_run_hook (const char *db_path, const char *hook) +notmuch_run_hook (notmuch_database_t *notmuch, const char *hook) { char *hook_path; int status = 0; pid_t pid; - hook_path = talloc_asprintf (NULL, "%s/%s/%s/%s", db_path, ".notmuch", - "hooks", hook); + hook_path = talloc_asprintf (notmuch, "%s/%s", + notmuch_config_get (notmuch, NOTMUCH_CONFIG_HOOK_DIR), + hook); if (hook_path == NULL) { fprintf (stderr, "Out of memory\n"); return 1; diff --git a/notmuch-client.h b/notmuch-client.h index 9e09c36a..f60f5406 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -339,7 +339,7 @@ const char * _notmuch_config_get_path (notmuch_config_t *config); int -notmuch_run_hook (const char *db_path, const char *hook); +notmuch_run_hook (notmuch_database_t *notmuch, const char *hook); bool debugger_is_active (void); diff --git a/notmuch-insert.c b/notmuch-insert.c index e483b949..0f272e2e 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -481,7 +481,6 @@ notmuch_insert_command (unused(notmuch_config_t *config),notmuch_database_t *not notmuch_process_shared_options (argv[0]); - /* XXX TODO replace this use of DATABASE_PATH with something specific to hooks */ db_path = notmuch_config_get (notmuch, NOTMUCH_CONFIG_DATABASE_PATH); if (! db_path) @@ -570,7 +569,7 @@ notmuch_insert_command (unused(notmuch_config_t *config),notmuch_database_t *not status = add_file (notmuch, newpath, tag_ops, synchronize_flags, keep, indexing_cli_choices.opts); /* Commit changes. */ - close_status = notmuch_database_destroy (notmuch); + close_status = notmuch_database_close (notmuch); if (close_status) { /* Hold on to the first error, if any. */ if (! status) @@ -595,9 +594,11 @@ notmuch_insert_command (unused(notmuch_config_t *config),notmuch_database_t *not if (hooks && status == NOTMUCH_STATUS_SUCCESS) { /* Ignore hook failures. */ - notmuch_run_hook (db_path, "post-insert"); + notmuch_run_hook (notmuch, "post-insert"); } + notmuch_database_destroy (notmuch); + talloc_free (local); return status_to_exit (status); diff --git a/notmuch-new.c b/notmuch-new.c index 0f416939..21e66af1 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -1105,7 +1105,6 @@ notmuch_new_command (unused(notmuch_config_t *config), notmuch_database_t *notmu struct timeval tv_start; int ret = 0; const char *db_path; - char *dot_notmuch_path; struct sigaction action; _filename_node_t *f; int opt_index; @@ -1167,13 +1166,11 @@ notmuch_new_command (unused(notmuch_config_t *config), notmuch_database_t *notmu } if (hooks) { - ret = notmuch_run_hook (db_path, "pre-new"); + ret = notmuch_run_hook (notmuch, "pre-new"); if (ret) return EXIT_FAILURE; } - dot_notmuch_path = talloc_asprintf (notmuch, "%s/%s", db_path, ".notmuch"); - notmuch_exit_if_unmatched_db_uuid (notmuch); if (notmuch_database_get_revision (notmuch, NULL) == 0) { @@ -1212,9 +1209,6 @@ notmuch_new_command (unused(notmuch_config_t *config), notmuch_database_t *notmu 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 (notmuch); @@ -1284,7 +1278,7 @@ notmuch_new_command (unused(notmuch_config_t *config), notmuch_database_t *notmu notmuch_database_close (notmuch); if (hooks && ! ret && ! interrupted) - ret = notmuch_run_hook (db_path, "post-new"); + ret = notmuch_run_hook (notmuch, "post-new"); notmuch_database_destroy (notmuch); diff --git a/test/T400-hooks.sh b/test/T400-hooks.sh index 49c690eb..a3dd4c63 100755 --- a/test/T400-hooks.sh +++ b/test/T400-hooks.sh @@ -2,8 +2,6 @@ test_description='hooks' . $(dirname "$0")/test-lib.sh || exit 1 -HOOK_DIR=${MAIL_DIR}/.notmuch/hooks - create_echo_hook () { local TOKEN="${RANDOM}" mkdir -p ${HOOK_DIR} @@ -16,6 +14,7 @@ EOF } create_failing_hook () { + local HOOK_DIR=${2} mkdir -p ${HOOK_DIR} cat <<EOF >"${HOOK_DIR}/${1}" #!/bin/sh @@ -24,98 +23,120 @@ EOF chmod +x "${HOOK_DIR}/${1}" } -rm_hooks () { - rm -rf ${HOOK_DIR} -} - # add a message to generate mail dir and database add_message # create maildir structure for notmuch-insert mkdir -p "$MAIL_DIR"/{cur,new,tmp} -test_begin_subtest "pre-new is run" -rm_hooks -generate_message -create_echo_hook "pre-new" expected output -notmuch new > /dev/null -test_expect_equal_file expected output - -test_begin_subtest "post-new is run" -rm_hooks -generate_message -create_echo_hook "post-new" expected output -notmuch new > /dev/null -test_expect_equal_file expected output - -test_begin_subtest "post-insert hook is run" -rm_hooks -generate_message -create_echo_hook "post-insert" expected output -notmuch insert < "$gen_msg_filename" -test_expect_equal_file expected output - -test_begin_subtest "pre-new is run before post-new" -rm_hooks -generate_message -create_echo_hook "pre-new" pre-new.expected pre-new.output -create_echo_hook "post-new" post-new.expected post-new.output -notmuch new > /dev/null -test_expect_equal_file post-new.expected post-new.output - -test_begin_subtest "pre-new non-zero exit status (hook status)" -rm_hooks -generate_message -create_failing_hook "pre-new" -output=`notmuch new 2>&1` -test_expect_equal "$output" "Error: pre-new hook failed with status 13" - -# depends on the previous subtest leaving broken hook behind -test_begin_subtest "pre-new non-zero exit status (notmuch status)" -test_expect_code 1 "notmuch new" - -# depends on the previous subtests leaving 1 new message behind -test_begin_subtest "pre-new non-zero exit status aborts new" -rm_hooks -output=$(NOTMUCH_NEW) -test_expect_equal "$output" "Added 1 new message to the database." - -test_begin_subtest "post-new non-zero exit status (hook status)" -rm_hooks -generate_message -create_failing_hook "post-new" -NOTMUCH_NEW 2>output.stderr >output -cat output.stderr >> output -echo "Added 1 new message to the database." > expected -echo "Error: post-new hook failed with status 13" >> expected -test_expect_equal_file expected output - -# depends on the previous subtest leaving broken hook behind -test_begin_subtest "post-new non-zero exit status (notmuch status)" -test_expect_code 1 "notmuch new" - -test_begin_subtest "post-insert hook does not affect insert status" -rm_hooks -generate_message -create_failing_hook "post-insert" -test_expect_success "notmuch insert < \"$gen_msg_filename\" > /dev/null" - -test_begin_subtest "hook without executable permissions" -rm_hooks -mkdir -p ${HOOK_DIR} -cat <<EOF >"${HOOK_DIR}/pre-new" -#!/bin/sh -echo foo +for config in traditional profile explicit XDG; do + unset NOTMUCH_PROFILE + notmuch config set database.hook_dir + case $config in + traditional) + HOOK_DIR=${MAIL_DIR}/.notmuch/hooks + ;; + profile) + dir=${HOME}/.config/notmuch/other + mkdir -p ${dir} + HOOK_DIR=${dir}/hooks + cp ${NOTMUCH_CONFIG} ${dir}/config + export NOTMUCH_PROFILE=other + ;; + explicit) + HOOK_DIR=${HOME}/.notmuch-hooks + mkdir -p $HOOK_DIR + notmuch config set database.hook_dir $HOOK_DIR + ;; + XDG) + HOOK_DIR=${HOME}/.config/notmuch/default/hooks + ;; + esac + + test_begin_subtest "pre-new is run [${config}]" + rm -rf ${HOOK_DIR} + generate_message + create_echo_hook "pre-new" expected output $HOOK_DIR + notmuch new > /dev/null + test_expect_equal_file expected output + + test_begin_subtest "post-new is run [${config}]" + rm -rf ${HOOK_DIR} + generate_message + create_echo_hook "post-new" expected output $HOOK_DIR + notmuch new > /dev/null + test_expect_equal_file expected output + + test_begin_subtest "post-insert hook is run [${config}]" + rm -rf ${HOOK_DIR} + generate_message + create_echo_hook "post-insert" expected output $HOOK_DIR + notmuch insert < "$gen_msg_filename" + test_expect_equal_file expected output + + test_begin_subtest "pre-new is run before post-new [${config}]" + rm -rf ${HOOK_DIR} + generate_message + create_echo_hook "pre-new" pre-new.expected pre-new.output $HOOK_DIR + create_echo_hook "post-new" post-new.expected post-new.output $HOOK_DIR + notmuch new > /dev/null + test_expect_equal_file post-new.expected post-new.output + + test_begin_subtest "pre-new non-zero exit status (hook status) [${config}]" + rm -rf ${HOOK_DIR} + generate_message + create_failing_hook "pre-new" $HOOK_DIR + output=`notmuch new 2>&1` + test_expect_equal "$output" "Error: pre-new hook failed with status 13" + + # depends on the previous subtest leaving broken hook behind + test_begin_subtest "pre-new non-zero exit status (notmuch status) [${config}]" + test_expect_code 1 "notmuch new" + + # depends on the previous subtests leaving 1 new message behind + test_begin_subtest "pre-new non-zero exit status aborts new [${config}]" + rm -rf ${HOOK_DIR} + output=$(NOTMUCH_NEW) + test_expect_equal "$output" "Added 1 new message to the database." + + test_begin_subtest "post-new non-zero exit status (hook status) [${config}]" + rm -rf ${HOOK_DIR} + generate_message + create_failing_hook "post-new" $HOOK_DIR + NOTMUCH_NEW 2>output.stderr >output + cat output.stderr >> output + echo "Added 1 new message to the database." > expected + echo "Error: post-new hook failed with status 13" >> expected + test_expect_equal_file expected output + + # depends on the previous subtest leaving broken hook behind + test_begin_subtest "post-new non-zero exit status (notmuch status) [${config}]" + test_expect_code 1 "notmuch new" + + test_begin_subtest "post-insert hook does not affect insert status [${config}]" + rm -rf ${HOOK_DIR} + generate_message + create_failing_hook "post-insert" $HOOK_DIR + test_expect_success "notmuch insert < \"$gen_msg_filename\" > /dev/null" + + test_begin_subtest "hook without executable permissions [${config}]" + rm -rf ${HOOK_DIR} + mkdir -p ${HOOK_DIR} + cat <<EOF >"${HOOK_DIR}/pre-new" + #!/bin/sh + echo foo EOF -output=`notmuch new 2>&1` -test_expect_code 1 "notmuch new" - -test_begin_subtest "hook execution failure" -rm_hooks -mkdir -p ${HOOK_DIR} -cat <<EOF >"${HOOK_DIR}/pre-new" -no hashbang, execl fails + output=`notmuch new 2>&1` + test_expect_code 1 "notmuch new" + + test_begin_subtest "hook execution failure [${config}]" + rm -rf ${HOOK_DIR} + mkdir -p ${HOOK_DIR} + cat <<EOF >"${HOOK_DIR}/pre-new" + no hashbang, execl fails EOF -chmod +x "${HOOK_DIR}/pre-new" -test_expect_code 1 "notmuch new" + chmod +x "${HOOK_DIR}/pre-new" + test_expect_code 1 "notmuch new" + rm -rf ${HOOK_DIR} +done test_done -- 2.30.0
Remove STORED IN DATABASE discussion, describe search rules. Currently profiles seem a bit pointless. They will make more sense when they apply to databases as well. --- doc/man1/notmuch-config.rst | 43 +++++++++++++++++++++++++++++-------- doc/man5/notmuch-hooks.rst | 6 +++--- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst index 769f336a..bc597957 100644 --- a/doc/man1/notmuch-config.rst +++ b/doc/man1/notmuch-config.rst @@ -17,10 +17,6 @@ DESCRIPTION The **config** command can be used to get or set settings in the notmuch configuration file and corresponding database. -Items marked **[STORED IN DATABASE]** are only in the database. They -should not be placed in the configuration file, and should be accessed -programmatically as described in the SYNOPSIS above. - **get** The value of the specified configuration item is printed to stdout. If the item has multiple values (it is a list), each value @@ -54,6 +50,11 @@ The available configuration items are described below. Default: ``$MAILDIR`` variable if set, otherwise ``$HOME/mail``. +**database.hook_dir** + + Directory containing hooks run by notmuch commands. See + **notmuch-hooks(5)**. + **user.name** Your full name. @@ -134,7 +135,7 @@ The available configuration items are described below. Default: ``true``. -**index.decrypt** **[STORED IN DATABASE]** +**index.decrypt** Policy for decrypting encrypted messages during indexing. Must be one of: ``false``, ``auto``, ``nostash``, or ``true``. @@ -187,7 +188,7 @@ The available configuration items are described below. Default: ``auto``. -**index.header.<prefix>** **[STORED IN DATABASE]** +**index.header.<prefix>** Define the query prefix <prefix>, based on a mail header. For example ``index.header.List=List-Id`` will add a probabilistic prefix ``List:`` that searches the ``List-Id`` field. User @@ -202,7 +203,7 @@ The available configuration items are described below. (since notmuch 0.30, "compact" and "field_processor" are always included.) -**query.<name>** **[STORED IN DATABASE]** +**query.<name>** Expansion for named query called <name>. See **notmuch-search-terms(7)** for more information about named queries. @@ -214,8 +215,32 @@ The following environment variables can be used to control the behavior of notmuch. **NOTMUCH\_CONFIG** - Specifies the location of the notmuch configuration file. Notmuch - will use ${HOME}/.notmuch-config if this variable is not set. + Specifies the location of the notmuch configuration file. + +**NOTMUCH_PROFILE** + Selects among notmuch configurations. + +FILES +===== + +CONFIGURATION +------------- + +If ``NOTMUCH_CONFIG`` is unset, notmuch tries (in order) + +- ``$XDG_CONFIG_HOME/notmuch/<profile>/config`` where ``<profile>`` is + defined by ``$NOTMUCH_PROFILE`` or "default" +- ``${HOME}/.notmuch-config<profile>`` where ``<profile>`` is + ``.$NOTMUCH_PROFILE`` or "" + +Hooks +----- + +If ``database.hook_dir`` is unset, notmuch tries (in order) + +- ``$XDG_CONFIG_HOME/notmuch/<profile>/hooks`` where ``<profile>`` is + defined by ``$NOTMUCH_PROFILE`` or "default" +- ``<database.path>/.notmuch/hooks`` SEE ALSO ======== diff --git a/doc/man5/notmuch-hooks.rst b/doc/man5/notmuch-hooks.rst index de2ed0c2..c509afb3 100644 --- a/doc/man5/notmuch-hooks.rst +++ b/doc/man5/notmuch-hooks.rst @@ -5,15 +5,15 @@ notmuch-hooks SYNOPSIS ======== -$DATABASEDIR/.notmuch/hooks/* +<hook_dir>/{pre-new, post-new, post-insert} DESCRIPTION =========== Hooks are scripts (or arbitrary executables or symlinks to such) that notmuch invokes before and after certain actions. These scripts reside -in the .notmuch/hooks directory within the database directory and must -have executable permissions. +in a directory defined as described in **notmuch-config(1)**. They +must have executable permissions. The currently available hooks are described below. -- 2.30.0
Many public status values have been added without being copied to the list of private status values. --- lib/notmuch-private.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 5e0b42e3..2fbf7ab9 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -129,10 +129,22 @@ typedef enum _notmuch_private_status { NOTMUCH_PRIVATE_STATUS_OUT_OF_MEMORY = NOTMUCH_STATUS_OUT_OF_MEMORY, NOTMUCH_PRIVATE_STATUS_READ_ONLY_DATABASE = NOTMUCH_STATUS_READ_ONLY_DATABASE, NOTMUCH_PRIVATE_STATUS_XAPIAN_EXCEPTION = NOTMUCH_STATUS_XAPIAN_EXCEPTION, + NOTMUCH_PRIVATE_STATUS_FILE_ERROR = NOTMUCH_STATUS_FILE_ERROR, NOTMUCH_PRIVATE_STATUS_FILE_NOT_EMAIL = NOTMUCH_STATUS_FILE_NOT_EMAIL, NOTMUCH_PRIVATE_STATUS_NULL_POINTER = NOTMUCH_STATUS_NULL_POINTER, NOTMUCH_PRIVATE_STATUS_TAG_TOO_LONG = NOTMUCH_STATUS_TAG_TOO_LONG, NOTMUCH_PRIVATE_STATUS_UNBALANCED_FREEZE_THAW = NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW, + NOTMUCH_PRIVATE_STATUS_UNBALANCED_ATOMIC = NOTMUCH_STATUS_UNBALANCED_ATOMIC, + NOTMUCH_PRIVATE_STATUS_UNSUPPORTED_OPERATION = NOTMUCH_STATUS_UNSUPPORTED_OPERATION, + NOTMUCH_PRIVATE_STATUS_UPGRADE_REQUIRED = NOTMUCH_STATUS_UPGRADE_REQUIRED, + NOTMUCH_PRIVATE_STATUS_PATH_ERROR = NOTMUCH_STATUS_PATH_ERROR, + NOTMUCH_PRIVATE_STATUS_IGNORED = NOTMUCH_STATUS_IGNORED, + NOTMUCH_PRIVATE_STATUS_ILLEGAL_ARGUMENT = NOTMUCH_STATUS_ILLEGAL_ARGUMENT, + NOTMUCH_PRIVATE_STATUS_MALFORMED_CRYPTO_PROTOCOL = NOTMUCH_STATUS_MALFORMED_CRYPTO_PROTOCOL, + NOTMUCH_PRIVATE_STATUS_FAILED_CRYPTO_CONTEXT_CREATION = NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION, + NOTMUCH_PRIVATE_STATUS_UNKNOWN_CRYPTO_PROTOCOL = NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL, + NOTMUCH_PRIVATE_STATUS_NO_CONFIG = NOTMUCH_STATUS_NO_CONFIG, + NOTMUCH_PRIVATE_STATUS_DATABASE_EXISTS = NOTMUCH_STATUS_DATABASE_EXISTS, /* Then add our own private values. */ NOTMUCH_PRIVATE_STATUS_TERM_TOO_LONG = NOTMUCH_STATUS_LAST_STATUS, -- 2.30.0
This will need some cleanup when the transition completes, and we stop passing notmuch_config_t structs to the subcommands. Unlike the general case, we open the database in the subcommand, since we don't know whether it should be opened read/write until we parse the command line arguments. Add a test to make sure passing config file on the command line is not broken by these or future config related changes. --- notmuch-show.c | 54 ++++++++++++++++++++++++---------------- test/T035-read-config.sh | 31 +++++++++++++++++++++++ 2 files changed, 64 insertions(+), 21 deletions(-) diff --git a/notmuch-show.c b/notmuch-show.c index 04b90cd7..5e90c143 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -1234,6 +1234,7 @@ notmuch_show_command (notmuch_config_t *config, unused(notmuch_database_t *notmu bool entire_thread_set = false; bool single_message; bool unthreaded = FALSE; + char *status_string = NULL; notmuch_opt_desc_t options[] = { { .opt_keyword = &format, .name = "format", .keywords = @@ -1323,7 +1324,28 @@ notmuch_show_command (notmuch_config_t *config, unused(notmuch_database_t *notmu fprintf (stderr, "Warning: --include-html only implemented for format=text, format=json and format=sexp\n"); } - query_string = query_string_from_args (config, argc - opt_index, argv + opt_index); + notmuch_database_mode_t mode = NOTMUCH_DATABASE_MODE_READ_ONLY; + if (params.crypto.decrypt == NOTMUCH_DECRYPT_TRUE) + mode = NOTMUCH_DATABASE_MODE_READ_WRITE; + if (notmuch_database_open_with_config (NULL, + mode, + _notmuch_config_get_path (config), + NULL, + ¬much, + &status_string)) { + if (status_string) { + fputs (status_string, stderr); + free (status_string); + } + + return EXIT_FAILURE; + } + + config = NULL; + + notmuch_exit_if_unmatched_db_uuid (notmuch); + + query_string = query_string_from_args (notmuch, argc - opt_index, argv + opt_index); if (query_string == NULL) { fprintf (stderr, "Out of memory\n"); return EXIT_FAILURE; @@ -1334,15 +1356,6 @@ notmuch_show_command (notmuch_config_t *config, unused(notmuch_database_t *notmu return EXIT_FAILURE; } - notmuch_database_mode_t mode = NOTMUCH_DATABASE_MODE_READ_ONLY; - if (params.crypto.decrypt == NOTMUCH_DECRYPT_TRUE) - mode = NOTMUCH_DATABASE_MODE_READ_WRITE; - if (notmuch_database_open (notmuch_config_get_database_path (config), - mode, ¬much)) - return EXIT_FAILURE; - - notmuch_exit_if_unmatched_db_uuid (notmuch); - query = notmuch_query_create (notmuch, query_string); if (query == NULL) { fprintf (stderr, "Out of memory\n"); @@ -1351,27 +1364,26 @@ notmuch_show_command (notmuch_config_t *config, unused(notmuch_database_t *notmu /* Create structure printer. */ formatter = formatters[format]; - sprinter = formatter->new_sprinter (config, stdout); + sprinter = formatter->new_sprinter (notmuch, stdout); params.out_stream = g_mime_stream_stdout_new (); /* If a single message is requested we do not use search_excludes. */ if (single_message) { - ret = do_show_single (config, query, formatter, sprinter, ¶ms); + ret = do_show_single (notmuch, query, formatter, sprinter, ¶ms); } else { /* We always apply set the exclude flag. The * exclude=true|false option controls whether or not we return * threads that only match in an excluded message */ - const char **search_exclude_tags; - size_t search_exclude_tags_length; - unsigned int i; + notmuch_config_values_t *exclude_tags; notmuch_status_t status; - search_exclude_tags = notmuch_config_get_search_exclude_tags - (config, &search_exclude_tags_length); + for (exclude_tags = notmuch_config_get_values (notmuch, NOTMUCH_CONFIG_EXCLUDE_TAGS); + notmuch_config_values_valid (exclude_tags); + notmuch_config_values_move_to_next (exclude_tags)) { - for (i = 0; i < search_exclude_tags_length; i++) { - status = notmuch_query_add_tag_exclude (query, search_exclude_tags[i]); + status = notmuch_query_add_tag_exclude (query, + notmuch_config_values_get (exclude_tags)); if (status && status != NOTMUCH_STATUS_IGNORED) { print_status_query ("notmuch show", query, status); ret = -1; @@ -1385,9 +1397,9 @@ notmuch_show_command (notmuch_config_t *config, unused(notmuch_database_t *notmu } if (unthreaded) - ret = do_show_unthreaded (config, query, formatter, sprinter, ¶ms); + ret = do_show_unthreaded (notmuch, query, formatter, sprinter, ¶ms); else - ret = do_show_threaded (config, query, formatter, sprinter, ¶ms); + ret = do_show_threaded (notmuch, query, formatter, sprinter, ¶ms); } DONE: diff --git a/test/T035-read-config.sh b/test/T035-read-config.sh index 6cc4da8d..d015e073 100755 --- a/test/T035-read-config.sh +++ b/test/T035-read-config.sh @@ -368,4 +368,35 @@ notmuch address --output=sender query:$query_name | sort >> OUTPUT restore_config test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "show with alternate config" +backup_database +cp notmuch-config alt-config +notmuch --config=alt-config config set search.exclude_tags foobar17 +notmuch tag -- +foobar17 '*' +output=$(notmuch --config=alt-config show '*' && echo OK) +restore_database +test_expect_equal "$output" "OK" + +test_begin_subtest "show with alternate config (xdg)" +test_subtest_known_broken +backup_database +notmuch tag -- +foobar17 '*' +xdg_config +notmuch --config=${CONFIG_PATH} config set search.exclude_tags foobar17 +output=$(notmuch show '*' && echo OK) +restore_database +restore_config +test_expect_equal "$output" "OK" + +test_begin_subtest "show with alternate config (xdg+profile)" +test_subtest_known_broken +backup_database +notmuch tag -- +foobar17 '*' +xdg_config foobar17 +notmuch --config=${CONFIG_PATH} config set search.exclude_tags foobar17 +output=$(notmuch show '*' && echo OK) +restore_database +restore_config +test_expect_equal "$output" "OK" + test_done -- 2.30.0
The series at id:20210205132654.3258292-1-david@tethera.net only partially converted notmuch-show to the new merged configuration style. This meant in particular that finding configuration files via XDG conventions did not work for notmuch-show. As a side effect, publish notmuch_database_reopen as requested in id:20200213211127.nhmxnhszxsol47bi@feather.localdomain
Include the (currently unused) mode argument which will specify which mode to re-open the database in. Functionality and docs to be finalized in a followup commit. --- lib/database.cc | 3 ++- lib/message.cc | 3 ++- lib/notmuch-private.h | 3 --- lib/notmuch.h | 7 +++++++ 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/database.cc b/lib/database.cc index f96ba7c0..89865599 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -516,7 +516,8 @@ notmuch_database_close (notmuch_database_t *notmuch) } notmuch_status_t -_notmuch_database_reopen (notmuch_database_t *notmuch) +notmuch_database_reopen (notmuch_database_t *notmuch, + unused(notmuch_database_mode_t mode)) { if (_notmuch_database_mode (notmuch) != NOTMUCH_DATABASE_MODE_READ_ONLY) return NOTMUCH_STATUS_UNSUPPORTED_OPERATION; diff --git a/lib/message.cc b/lib/message.cc index fca99082..1bea90f0 100644 --- a/lib/message.cc +++ b/lib/message.cc @@ -455,7 +455,8 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message, void *field) /* all the way without an exception */ break; } catch (const Xapian::DatabaseModifiedError &error) { - notmuch_status_t status = _notmuch_database_reopen (message->notmuch); + notmuch_status_t status = notmuch_database_reopen (message->notmuch, + NOTMUCH_DATABASE_MODE_READ_ONLY); if (status != NOTMUCH_STATUS_SUCCESS) INTERNAL_ERROR ("unhandled error from notmuch_database_reopen: %s\n", notmuch_status_to_string (status)); diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 2fbf7ab9..731757cc 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -206,9 +206,6 @@ _notmuch_message_id_compressed (void *ctx, const char *message_id); notmuch_status_t _notmuch_database_ensure_writable (notmuch_database_t *notmuch); -notmuch_status_t -_notmuch_database_reopen (notmuch_database_t *notmuch); - void _notmuch_database_log (notmuch_database_t *notmuch, const char *format, ...); diff --git a/lib/notmuch.h b/lib/notmuch.h index 5a5d99c0..0c13f607 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -879,6 +879,13 @@ notmuch_database_find_message_by_filename (notmuch_database_t *notmuch, notmuch_tags_t * notmuch_database_get_all_tags (notmuch_database_t *db); +/** + * Reopen an open notmuch database. + * + */ +notmuch_status_t +notmuch_database_reopen (notmuch_database_t *db, notmuch_database_mode_t mode); + /** * Create a new query for 'database'. * -- 2.30.0
This will allow re-opening in a different mode (read/write vs. read-only) with current Xapian API. --- lib/database-private.h | 4 ++++ lib/open.cc | 19 ++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/database-private.h b/lib/database-private.h index d83cf0d0..b9d83e58 100644 --- a/lib/database-private.h +++ b/lib/database-private.h @@ -189,8 +189,12 @@ operator& (notmuch_field_flag_t a, notmuch_field_flag_t b) struct _notmuch_database { bool exception_reported; + /* Path to database parent directory and or/mail root */ char *path; + /* Path to actual database */ + char *xapian_path; + int atomic_nesting; /* true if changes have been made in this atomic section */ bool atomic_dirty; diff --git a/lib/open.cc b/lib/open.cc index b4637ec5..2576424e 100644 --- a/lib/open.cc +++ b/lib/open.cc @@ -193,7 +193,7 @@ notmuch_database_open_with_config (const char *database_path, notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; void *local = talloc_new (NULL); notmuch_database_t *notmuch = NULL; - char *notmuch_path, *xapian_path, *incompat_features; + char *notmuch_path, *incompat_features; char *message = NULL; struct stat st; int err; @@ -218,12 +218,6 @@ notmuch_database_open_with_config (const char *database_path, goto DONE; } - if (! (xapian_path = talloc_asprintf (local, "%s/%s", notmuch_path, "xapian"))) { - message = strdup ("Out of memory\n"); - status = NOTMUCH_STATUS_OUT_OF_MEMORY; - goto DONE; - } - /* Initialize the GLib type system and threads */ #if ! GLIB_CHECK_VERSION (2, 35, 1) g_type_init (); @@ -245,16 +239,23 @@ notmuch_database_open_with_config (const char *database_path, notmuch->writable_xapian_db = NULL; notmuch->atomic_nesting = 0; notmuch->view = 1; + + if (! (notmuch->xapian_path = talloc_asprintf (notmuch, "%s/%s", notmuch_path, "xapian"))) { + message = strdup ("Out of memory\n"); + status = NOTMUCH_STATUS_OUT_OF_MEMORY; + goto DONE; + } + try { std::string last_thread_id; std::string last_mod; if (mode == NOTMUCH_DATABASE_MODE_READ_WRITE) { - notmuch->writable_xapian_db = new Xapian::WritableDatabase (xapian_path, + notmuch->writable_xapian_db = new Xapian::WritableDatabase (notmuch->xapian_path, DB_ACTION); notmuch->xapian_db = notmuch->writable_xapian_db; } else { - notmuch->xapian_db = new Xapian::Database (xapian_path); + notmuch->xapian_db = new Xapian::Database (notmuch->xapian_path); } /* Check version. As of database version 3, we represent -- 2.30.0
In the future xapian will apparently support this more conveniently for the cases other than READ_ONLY => READ_ONLY --- lib/database.cc | 23 -------- lib/open.cc | 40 ++++++++++++++ test/T595-reopen.sh | 125 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 23 deletions(-) create mode 100755 test/T595-reopen.sh diff --git a/lib/database.cc b/lib/database.cc index 89865599..b231a619 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -515,29 +515,6 @@ notmuch_database_close (notmuch_database_t *notmuch) return status; } -notmuch_status_t -notmuch_database_reopen (notmuch_database_t *notmuch, - unused(notmuch_database_mode_t mode)) -{ - if (_notmuch_database_mode (notmuch) != NOTMUCH_DATABASE_MODE_READ_ONLY) - return NOTMUCH_STATUS_UNSUPPORTED_OPERATION; - - try { - notmuch->xapian_db->reopen (); - } catch (const Xapian::Error &error) { - if (! notmuch->exception_reported) { - _notmuch_database_log (notmuch, "Error: A Xapian exception reopening database: %s\n", - error.get_msg ().c_str ()); - notmuch->exception_reported = true; - } - return NOTMUCH_STATUS_XAPIAN_EXCEPTION; - } - - notmuch->view++; - - return NOTMUCH_STATUS_SUCCESS; -} - static int unlink_cb (const char *path, unused (const struct stat *sb), diff --git a/lib/open.cc b/lib/open.cc index 2576424e..a927472e 100644 --- a/lib/open.cc +++ b/lib/open.cc @@ -495,3 +495,43 @@ notmuch_database_create_with_config (const char *database_path, talloc_free (notmuch); return status; } + +notmuch_status_t +notmuch_database_reopen (notmuch_database_t *notmuch, + notmuch_database_mode_t new_mode) +{ + notmuch_database_mode_t cur_mode = _notmuch_database_mode (notmuch); + try { + if (cur_mode == new_mode && + new_mode == NOTMUCH_DATABASE_MODE_READ_ONLY) { + notmuch->xapian_db->reopen (); + } else { + notmuch->xapian_db->close (); + + delete notmuch->xapian_db; + notmuch->xapian_db = NULL; + /* no need to free the same object twice */ + notmuch->writable_xapian_db = NULL; + + if (new_mode == NOTMUCH_DATABASE_MODE_READ_WRITE) { + notmuch->writable_xapian_db = new Xapian::WritableDatabase (notmuch->xapian_path, + DB_ACTION); + notmuch->xapian_db = notmuch->writable_xapian_db; + } else { + notmuch->xapian_db = new Xapian::Database (notmuch->xapian_path, + DB_ACTION); + } + } + } catch (const Xapian::Error &error) { + if (! notmuch->exception_reported) { + _notmuch_database_log (notmuch, "Error: A Xapian exception reopening database: %s\n", + error.get_msg ().c_str ()); + notmuch->exception_reported = true; + } + return NOTMUCH_STATUS_XAPIAN_EXCEPTION; + } + + notmuch->view++; + return NOTMUCH_STATUS_SUCCESS; +} + diff --git a/test/T595-reopen.sh b/test/T595-reopen.sh new file mode 100755 index 00000000..7375e2ac --- /dev/null +++ b/test/T595-reopen.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash +test_description="library reopen API" + +. $(dirname "$0")/test-lib.sh || exit 1 + +add_email_corpus + +cat <<EOF > c_head +#include <string.h> +#include <stdlib.h> +#include <notmuch-test.h> + +int main (int argc, char** argv) +{ + notmuch_database_t *db; + char *val; + notmuch_status_t stat; + notmuch_database_mode_t mode = NOTMUCH_DATABASE_MODE_READ_ONLY; + + char *msg = NULL; + + for (int i = 1; i < argc; i++) + if (strcmp (argv[i], "%NULL%") == 0) argv[i] = NULL; + + if (argv[2] && (argv[2][0] == 'w' || argv[2][0] == 'W')) + mode = NOTMUCH_DATABASE_MODE_READ_WRITE; + + stat = notmuch_database_open_with_config (argv[1], + mode, + argv[3], + argv[4], + &db, + &msg); + if (stat != NOTMUCH_STATUS_SUCCESS) { + fprintf (stderr, "error opening database: %d %s\n", stat, msg ? msg : ""); + exit (1); + } +EOF + +cat <<EOF > c_tail + EXPECT0(notmuch_database_destroy(db)); +} +EOF + +# The sequence of tests is important here + +test_begin_subtest "notmuch_database_reopen (read=>write)" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} read ${NOTMUCH_CONFIG} +{ + EXPECT0(notmuch_database_reopen (db, NOTMUCH_DATABASE_MODE_READ_WRITE)); + EXPECT0(notmuch_database_set_config (db, "test.key1", "testvalue1")); + EXPECT0(notmuch_database_set_config (db, "test.key2", "testvalue2")); + EXPECT0(notmuch_database_get_config (db, "test.key1", &val)); + printf("test.key1 = %s\n", val); + EXPECT0(notmuch_database_get_config (db, "test.key2", &val)); + printf("test.key2 = %s\n", val); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +test.key1 = testvalue1 +test.key2 = testvalue2 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "notmuch_database_reopen (read=>read)" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} read ${NOTMUCH_CONFIG} +{ + EXPECT0(notmuch_database_reopen (db, NOTMUCH_DATABASE_MODE_READ_ONLY)); + EXPECT0(notmuch_database_get_config (db, "test.key1", &val)); + printf("test.key1 = %s\n", val); + EXPECT0(notmuch_database_get_config (db, "test.key2", &val)); + printf("test.key2 = %s\n", val); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +test.key1 = testvalue1 +test.key2 = testvalue2 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "notmuch_database_reopen (write=>read)" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} write ${NOTMUCH_CONFIG} +{ + EXPECT0(notmuch_database_reopen (db, NOTMUCH_DATABASE_MODE_READ_ONLY)); + EXPECT0(notmuch_database_get_config (db, "test.key1", &val)); + printf("test.key1 = %s\n", val); + EXPECT0(notmuch_database_get_config (db, "test.key2", &val)); + printf("test.key2 = %s\n", val); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +test.key1 = testvalue1 +test.key2 = testvalue2 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "notmuch_database_reopen (write=>write)" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} write ${NOTMUCH_CONFIG} +{ + EXPECT0(notmuch_database_reopen (db, NOTMUCH_DATABASE_MODE_READ_WRITE)); + EXPECT0(notmuch_database_set_config (db, "test.key3", "testvalue3")); + EXPECT0(notmuch_database_get_config (db, "test.key1", &val)); + printf("test.key1 = %s\n", val); + EXPECT0(notmuch_database_get_config (db, "test.key2", &val)); + printf("test.key2 = %s\n", val); + EXPECT0(notmuch_database_get_config (db, "test.key3", &val)); + printf("test.key3 = %s\n", val); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +test.key1 = testvalue1 +test.key2 = testvalue2 +test.key3 = testvalue3 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_done -- 2.30.0
In order to open the database in main() for this command, we may need to re-open it in the (possibly less common) case where crypto options require it. --- notmuch-show.c | 26 +++++++------------------- notmuch.c | 2 +- test/T035-read-config.sh | 2 -- 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/notmuch-show.c b/notmuch-show.c index c3c42caa..4a26f8ce 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -1215,9 +1215,8 @@ static const notmuch_show_format_t *formatters[] = { }; int -notmuch_show_command (notmuch_config_t *config, unused(notmuch_database_t *notmuch), int argc, char *argv[]) +notmuch_show_command (unused(notmuch_config_t *config), notmuch_database_t *notmuch, int argc, char *argv[]) { - notmuch_database_t *notmuch; notmuch_query_t *query; char *query_string; int opt_index, ret; @@ -1234,7 +1233,7 @@ notmuch_show_command (notmuch_config_t *config, unused(notmuch_database_t *notmu bool entire_thread_set = false; bool single_message; bool unthreaded = FALSE; - char *status_string = NULL; + notmuch_status_t status; notmuch_opt_desc_t options[] = { { .opt_keyword = &format, .name = "format", .keywords = @@ -1324,25 +1323,14 @@ notmuch_show_command (notmuch_config_t *config, unused(notmuch_database_t *notmu fprintf (stderr, "Warning: --include-html only implemented for format=text, format=json and format=sexp\n"); } - notmuch_database_mode_t mode = NOTMUCH_DATABASE_MODE_READ_ONLY; - if (params.crypto.decrypt == NOTMUCH_DECRYPT_TRUE) - mode = NOTMUCH_DATABASE_MODE_READ_WRITE; - if (notmuch_database_open_with_config (NULL, - mode, - _notmuch_config_get_path (config), - NULL, - ¬much, - &status_string)) { - if (status_string) { - fputs (status_string, stderr); - free (status_string); + if (params.crypto.decrypt == NOTMUCH_DECRYPT_TRUE) { + status = notmuch_database_reopen (notmuch, NOTMUCH_DATABASE_MODE_READ_WRITE); + if (status) { + fprintf (stderr, "Error reopening database for READ_WRITE: %s\n", notmuch_status_to_string (status)); + return EXIT_FAILURE; } - - return EXIT_FAILURE; } - config = NULL; - notmuch_exit_if_unmatched_db_uuid (notmuch); query_string = query_string_from_args (notmuch, argc - opt_index, argv + opt_index); diff --git a/notmuch.c b/notmuch.c index 7360e0e6..71482e43 100644 --- a/notmuch.c +++ b/notmuch.c @@ -152,7 +152,7 @@ static command_t commands[] = { "Search for messages matching the given search terms." }, { "address", notmuch_address_command, NOTMUCH_COMMAND_DATABASE_EARLY, "Get addresses from messages matching the given search terms." }, - { "show", notmuch_show_command, NOTMUCH_COMMAND_CONFIG_OPEN, + { "show", notmuch_show_command, NOTMUCH_COMMAND_DATABASE_EARLY, "Show all messages matching the search terms." }, { "count", notmuch_count_command, NOTMUCH_COMMAND_DATABASE_EARLY, "Count messages matching the search terms." }, diff --git a/test/T035-read-config.sh b/test/T035-read-config.sh index 205d1736..ac0f420b 100755 --- a/test/T035-read-config.sh +++ b/test/T035-read-config.sh @@ -378,7 +378,6 @@ restore_database test_expect_equal "$output" "OK" test_begin_subtest "show with alternate config (xdg)" -test_subtest_known_broken backup_database notmuch tag -- +foobar17 '*' xdg_config @@ -389,7 +388,6 @@ restore_config test_expect_equal "$output" "OK" test_begin_subtest "show with alternate config (xdg+profile)" -test_subtest_known_broken backup_database notmuch tag -- +foobar17 '*' xdg_config foobar17 -- 2.30.0
David Bremner <david@tethera.net> writes:
> The main difference since Tomi's review is that I made several more
> tests for configuration path searching in T035-read-config.sh, and
> moved some of the existing ones there.
I have applied this series (1-39, with the one updated patch) to
master. That's a fairly large change (a few thousand lines of new code,
including tests), so let me know if there are problems.
d