unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
* v2 Merged Config
@ 2020-12-25  0:42 David Bremner
  2020-12-25  0:42 ` [PATCH 01/24] lib: add _notmuch_string_map_set David Bremner
                   ` (23 more replies)
  0 siblings, 24 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch

This is a revised version of the series at id:20200808141653.1124111-1-david@tethera.net

It now converts everything but notmuch-setup and notmuch-config to the
new framework, which means that the distinction between configuration
stored in the database and that in an auxilary config file is mostly
gone from a user point of view.

Compared to the previous version, this one modifies
notmuch_database_{get,set}_config to use the merged configuration
cache. There is a tradeoff here between expressive power and
proliferation of APIs: now _have_ to use the merged configuration
view, and there's no easy way for a user to to distinguish what is in
the database.  Most new code is expected to use
notmuch_config_{get,set}, which takes enum keys.

I went ahead and converted the function signature of the subcommands
to take both a notmuch_config_t * and a notmuch_database_t *, one of
which is always NULL. Polymorphism C style, I guess. This mostly works
well, except for notmuch-show, where we can't determine early enough
whether to open the database for write access.

The first 10 patches are most of the groundwork. The remaining patches
are mostly converting subcommands to the new configuration framework,
with two or three more groundwork patches.

I still haven't dealt with the searching for database features
documented, but I think the existing series adds useful functionality,
and in a pinch we could release with only these changes (with
appropriate adjustements to the n_d_open_with_config documentation).


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 01/24] lib: add _notmuch_string_map_set
  2020-12-25  0:42 v2 Merged Config David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 02/24] lib: cache configuration information from database David Bremner
                   ` (22 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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      | 15 +++++++++++++++
 2 files changed, 20 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..1f3215fb 100644
--- a/lib/string-map.c
+++ b/lib/string-map.c
@@ -142,6 +142,21 @@ 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_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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 02/24] lib: cache configuration information from database
  2020-12-25  0:42 v2 Merged Config David Bremner
  2020-12-25  0:42 ` [PATCH 01/24] lib: add _notmuch_string_map_set David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 03/24] lib: add stub for notmuch_database_open_with_config David Bremner
                   ` (21 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 03/24] lib: add stub for notmuch_database_open_with_config
  2020-12-25  0:42 v2 Merged Config David Bremner
  2020-12-25  0:42 ` [PATCH 01/24] lib: add _notmuch_string_map_set David Bremner
  2020-12-25  0:42 ` [PATCH 02/24] lib: cache configuration information from database David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-26 13:13   ` David Bremner
  2020-12-25  0:42 ` [PATCH 04/24] lib/open: add support for config profiles and default locations David Bremner
                   ` (20 subsequent siblings)
  23 siblings, 1 reply; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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             | 138 ++++++++++++++++++++++++++++++--------
 lib/open.cc               |  42 ++++++++++--
 test/T562-lib-database.sh |   4 +-
 test/T590-libconfig.sh    |  39 +++++++++--
 6 files changed, 219 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..8e538cfb 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -301,52 +301,136 @@ 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
+ *
+ * - 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>)
+ *
+ * If <em>config_path</em> is NULL use the path specified
  *
- * An existing notmuch database can be identified by the presence of a
- * directory named ".notmuch" below 'path'.
+ * - 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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 04/24] lib/open: add support for config profiles and default locations
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (2 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 03/24] lib: add stub for notmuch_database_open_with_config David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 05/24] CLI: generalize notmuch_config_mode_t David Bremner
                   ` (19 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

This commit fills 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/open.cc            |  95 +++++++++++++++++++++----
 test/T590-libconfig.sh | 153 ++++++++++++++++++++++++++++++++++++++---
 2 files changed, 227 insertions(+), 21 deletions(-)

diff --git a/lib/open.cc b/lib/open.cc
index 7acaea7b..76255283 100644
--- a/lib/open.cc
+++ b/lib/open.cc
@@ -37,6 +37,82 @@ 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);
+    const char *home = getenv ("HOME");
+
+    if (! xdg_root) {
+	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 +125,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 +132,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..2986284a 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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 05/24] CLI: generalize notmuch_config_mode_t
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (3 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 04/24] lib/open: add support for config profiles and default locations David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 06/24] lib/config: add notmuch_config_key_{get,set} David Bremner
                   ` (18 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 06/24] lib/config: add notmuch_config_key_{get,set}
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (4 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 05/24] CLI: generalize notmuch_config_mode_t David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 07/24] lib/open: load default values for known configuration keys David Bremner
                   ` (17 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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 8bce7ba8..c92ffeef 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 8e538cfb..c2c71623 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -2323,6 +2323,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);
@@ -2337,6 +2342,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);
@@ -2398,6 +2404,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 2986284a..8bf43eed 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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 07/24] lib/open: load default values for known configuration keys.
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (5 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 06/24] lib/config: add notmuch_config_key_{get,set} David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 08/24] CLI: add (unused) database argument to subcommands David Bremner
                   ` (16 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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 c92ffeef..f6d9bf44 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 76255283..8cede121 100644
--- a/lib/open.cc
+++ b/lib/open.cc
@@ -288,6 +288,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 8bf43eed..0c7398a2 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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 08/24] CLI: add (unused) database argument to subcommands.
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (6 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 07/24] lib/open: load default values for known configuration keys David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 09/24] util: add strsplit_len: simplified strtok with delimiter escaping David Bremner
                   ` (15 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 09/24] util: add strsplit_len: simplified strtok with delimiter escaping
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (7 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 08/24] CLI: add (unused) database argument to subcommands David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 10/24] lib/config: add config values iterator David Bremner
                   ` (14 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 10/24] lib/config: add config values iterator
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (8 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 09/24] util: add strsplit_len: simplified strtok with delimiter escaping David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 11/24] CLI/count: switch to new configuration framework David Bremner
                   ` (13 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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 f6d9bf44..4500fe1a 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 c2c71623..3bd4aa54 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__ */
 
@@ -2454,6 +2455,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,
+					       &notmuch,
+					       &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 0c7398a2..6eaec101 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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 11/24] CLI/count: switch to new configuration framework
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (9 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 10/24] lib/config: add config values iterator David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 12/24] cli/dump: convert to new config framework David Bremner
                   ` (12 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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/T060-count.sh | 11 +++++++++++
 3 files changed, 33 insertions(+), 28 deletions(-)

diff --git a/notmuch-count.c b/notmuch-count.c
index f752ef62..321c9207 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_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, &notmuch))
-	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/T060-count.sh b/test/T060-count.sh
index a1ebf8ba..39a4dcc5 100755
--- a/test/T060-count.sh
+++ b/test/T060-count.sh
@@ -154,4 +154,15 @@ print("4: {} messages".format(query.count_messages()))
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "count with saved query from config file"
+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
+test_expect_equal_file EXPECTED OUTPUT
+
 test_done
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 12/24] cli/dump: convert to new config framework
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (10 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 11/24] CLI/count: switch to new configuration framework David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 13/24] lib: add notmuch_config_get_bool David Bremner
                   ` (11 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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/T240-dump-restore.sh | 30 +++++++++++++++++++++++++++++-
 3 files changed, 31 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, &notmuch))
-	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/T240-dump-restore.sh b/test/T240-dump-restore.sh
index 0870ff92..b3782745 100755
--- a/test/T240-dump-restore.sh
+++ b/test/T240-dump-restore.sh
@@ -82,6 +82,33 @@ test_begin_subtest "dump --output=outfile --"
 notmuch dump --output=dump-1-arg-dash.actual --
 test_expect_equal_file dump.expected dump-1-arg-dash.actual
 
+# configuration
+
+test_begin_subtest "dump with saved query from config file"
+query_name="test${RANDOM}"
+printf "Before:\n" > OUTPUT
+notmuch dump --include=tags query:$query_name >> OUTPUT
+printf "\nAfter:\n" >> OUTPUT
+cp notmuch-config old-config
+printf "\n[query]\n${query_name} = tag:signed\n" >> notmuch-config
+notmuch dump --include=tags query:$query_name >> OUTPUT
+cat <<EOF > EXPECTED
+Before:
+#notmuch-dump batch-tag:3 tags
+
+After:
+#notmuch-dump batch-tag:3 tags
++inbox +signed +unread -- id:20091118002059.067214ed@hikari
++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:20091118005040.GA25380@dottiness.seas.harvard.edu
++inbox +signed +unread -- id:87iqd9rn3l.fsf@vertex.dottedmag
++inbox +signed +unread -- id:20091117203301.GV3165@dottiness.seas.harvard.edu
++inbox +signed +unread -- id:20091117190054.GU3165@dottiness.seas.harvard.edu
+EOF
+cp old-config notmuch-config
+test_expect_equal_file EXPECTED OUTPUT
+
 # gzipped output
 
 test_begin_subtest "dump --gzip"
@@ -322,6 +349,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 +366,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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 13/24] lib: add notmuch_config_get_bool
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (11 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 12/24] cli/dump: convert to new config framework David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 14/24] CLI/restore: convert to new config framework David Bremner
                   ` (10 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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 4500fe1a..17af4b46 100644
--- a/lib/config.cc
+++ b/lib/config.cc
@@ -334,6 +334,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 3bd4aa54..b3346eb7 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -2518,6 +2518,25 @@ notmuch_config_values_move_to_next (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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 14/24] CLI/restore: convert to new config framework
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (12 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 13/24] lib: add notmuch_config_get_bool David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 15/24] CLI/insert: " David Bremner
                   ` (9 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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 +-
 2 files changed, 11 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, &notmuch))
+    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." },
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 15/24] CLI/insert: convert to new config framework.
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (13 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 14/24] CLI/restore: convert to new config framework David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 16/24] cli/reindex: convert " David Bremner
                   ` (8 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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/T070-insert.sh |  8 +++---
 3 files changed, 41 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, &notmuch);
-    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/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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 16/24] cli/reindex: convert new config framework
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (14 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 15/24] CLI/insert: " David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 17/24] CLI/reply: convert to " David Bremner
                   ` (7 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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/T700-reindex.sh | 10 ++++++++++
 3 files changed, 13 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, &notmuch))
-	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/T700-reindex.sh b/test/T700-reindex.sh
index 3d7c930d..67ee8452 100755
--- a/test/T700-reindex.sh
+++ b/test/T700-reindex.sh
@@ -75,6 +75,16 @@ notmuch reindex '*'
 notmuch dump | grep '^#=' | sort > OUTPUT
 test_expect_equal_file prop-dump OUTPUT
 
+test_begin_subtest "reindex with saved query from config file"
+query_name="test${RANDOM}"
+count1=$(notmuch count --lastmod '*' | cut -f3)
+cp notmuch-config old-config
+printf "\n[query]\n${query_name} = tag:inbox\n" >> notmuch-config
+notmuch reindex query:$query_name
+count2=$(notmuch count --lastmod '*' | cut -f3)
+cp old-config notmuch-config
+test_expect_success "test '$count2 -gt $count1'"
+
 add_email_corpus lkml
 
 test_begin_subtest "reindex of lkml corpus preserves threads"
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 17/24] CLI/reply: convert to new config framework
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (15 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 16/24] cli/reindex: convert " David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 18/24] CLI/{search,address}: convert to new configuration framework David Bremner
                   ` (6 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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/T220-reply.sh |  24 +++++++++++
 3 files changed, 74 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, &params->crypto, &node))
+	if (mime_node_open (notmuch, message, &params->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, &notmuch))
-	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, &params, format, reply_all) != 0)
+    if (do_reply (notmuch, query, &params, format, reply_all) != 0)
 	return EXIT_FAILURE;
 
     _notmuch_crypto_cleanup (&params.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/T220-reply.sh b/test/T220-reply.sh
index b6d8f42a..791b9d6e 100755
--- a/test/T220-reply.sh
+++ b/test/T220-reply.sh
@@ -58,6 +58,30 @@ On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
 > reply with CC
 OK"
 
+test_begin_subtest "reply with saved query from config file"
+query_name="test${RANDOM}"
+printf "Before:\n" > OUTPUT
+notmuch reply query:$query_name 2>&1 >> OUTPUT
+cp notmuch-config old-config
+printf "\n[query]\n${query_name} = id:${gen_msg_id}\n" >> notmuch-config
+printf "After:\n" >> OUTPUT
+notmuch reply query:$query_name >> OUTPUT
+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: <msg-003@notmuch-test-suite>
+References: <msg-003@notmuch-test-suite>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> reply with CC
+EOF
+cp old-config notmuch-config
+test_expect_equal_file EXPECTED OUTPUT
+
 test_begin_subtest "Reply from alternate address"
 add_message '[from]="Sender <sender@example.com>"' \
 	     [to]=test_suite_other@notmuchmail.org \
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 18/24] CLI/{search,address}: convert to new configuration framework
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (16 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 17/24] CLI/reply: convert to " David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 19/24] cli/config: add accessor for config file name David Bremner
                   ` (5 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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/T080-search.sh      | 35 ++++++++++++++++++++++++++
 test/T095-address.sh     | 22 ++++++++++++++++
 test/T750-user-header.sh | 18 ++++++++++++++
 5 files changed, 99 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/T080-search.sh b/test/T080-search.sh
index a3f0dead..8e82cf55 100755
--- a/test/T080-search.sh
+++ b/test/T080-search.sh
@@ -189,4 +189,39 @@ test_begin_subtest "parts do not have adjacent term positions"
 output=$(notmuch search id:termpos and '"c x"')
 test_expect_equal "$output" ""
 
+backup_database
+test_begin_subtest "search with alternate config"
+cp notmuch-config alt-config
+notmuch --config=alt-config config set search.exclude_tags foobar17
+notmuch tag -- +foobar17 '*'
+output=$(notmuch --config=alt-config count '*')
+test_expect_equal "$output" "0"
+restore_database
+
+test_begin_subtest "search with saved query from config file"
+query_name="test${RANDOM}"
+printf "Before:\n" > OUTPUT
+notmuch search query:$query_name 2>&1 | notmuch_search_sanitize >> OUTPUT
+cp notmuch-config old-config
+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
+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
+cp old-config notmuch-config
+test_expect_equal_file EXPECTED OUTPUT
+
 test_done
diff --git a/test/T095-address.sh b/test/T095-address.sh
index 817be538..ebd7c167 100755
--- a/test/T095-address.sh
+++ b/test/T095-address.sh
@@ -325,4 +325,26 @@ cat <<EOF >EXPECTED
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "saved query from config file"
+query_name="test${RANDOM}"
+printf "Before:\n" > OUTPUT
+notmuch address --deduplicate=no --output=sender query:$query_name 2>&1 | sort >> OUTPUT
+cp notmuch-config old-config
+printf "\n[query]\n${query_name} = from:foo.bar@example.com\n" >> notmuch-config
+printf "After:\n" >> OUTPUT
+notmuch address --deduplicate=no --output=sender query:$query_name | sort >> OUTPUT
+cat <<EOF > EXPECTED
+Before:
+After:
+Bar <Foo.Bar@Example.Com>
+Foo <foo.bar@example.com>
+Foo Bar <Foo.Bar@Example.Com>
+Foo Bar <foo.bar@example.com>
+Foo Bar <foo.bar@example.com>
+foo.bar@example.com
+foo.bar@example.com
+EOF
+cp old-config notmuch-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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 19/24] cli/config: add accessor for config file name
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (17 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 18/24] CLI/{search,address}: convert to new configuration framework David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 20/24] CLI/show: mostly switch show to new config framework David Bremner
                   ` (4 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 20/24] CLI/show: mostly switch show to new config framework
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (18 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 19/24] cli/config: add accessor for config file name David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 21/24] cli/tag: convert " David Bremner
                   ` (3 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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/T520-show.sh |  9 ++++++++
 2 files changed, 42 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,
+					   &notmuch,
+					   &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, &notmuch))
-	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, &params);
+	ret = do_show_single (notmuch, query, formatter, sprinter, &params);
     } 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, &params);
+	    ret = do_show_unthreaded (notmuch, query, formatter, sprinter, &params);
 	else
-	    ret = do_show_threaded (config, query, formatter, sprinter, &params);
+	    ret = do_show_threaded (notmuch, query, formatter, sprinter, &params);
     }
 
   DONE:
diff --git a/test/T520-show.sh b/test/T520-show.sh
index 16222650..e718a18c 100755
--- a/test/T520-show.sh
+++ b/test/T520-show.sh
@@ -10,4 +10,13 @@ notmuch show foo..
 exit_code=$?
 test_expect_equal 1 $exit_code
 
+backup_database
+test_begin_subtest "show with alternate config"
+cp notmuch-config alt-config
+notmuch --config=alt-config config set search.exclude_tags foobar17
+notmuch tag -- +foobar17 '*'
+output=$(notmuch --config=alt-config show '*')
+test_expect_equal "$output" ""
+restore_database
+
 test_done
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 21/24] cli/tag: convert to new config framework.
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (19 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 20/24] CLI/show: mostly switch show to new config framework David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 22/24] lib/config: add _notmuch_config_cache David Bremner
                   ` (2 subsequent siblings)
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

In addition to changing configuration access, change talloc context
for allocation.
---
 notmuch-tag.c | 25 ++++++++++++++-----------
 notmuch.c     |  2 +-
 2 files changed, 15 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, &notmuch))
-	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." },
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 22/24] lib/config: add _notmuch_config_cache
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (20 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 21/24] cli/tag: convert " David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 23/24] lib: split notmuch_database_compact David Bremner
  2020-12-25  0:42 ` [PATCH 24/24] cli/compact: convert to new configuration framework David Bremner
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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 17af4b46..99fcda1f 100644
--- a/lib/config.cc
+++ b/lib/config.cc
@@ -439,3 +439,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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 23/24] lib: split notmuch_database_compact
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (21 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 22/24] lib/config: add _notmuch_config_cache David Bremner
@ 2020-12-25  0:42 ` David Bremner
  2020-12-25  0:42 ` [PATCH 24/24] cli/compact: convert to new configuration framework David Bremner
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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,
 					 &notmuch,
 					 &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 b3346eb7..c1a1e45a 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -495,6 +495,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.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 24/24] cli/compact: convert to new configuration framework
  2020-12-25  0:42 v2 Merged Config David Bremner
                   ` (22 preceding siblings ...)
  2020-12-25  0:42 ` [PATCH 23/24] lib: split notmuch_database_compact David Bremner
@ 2020-12-25  0:42 ` David Bremner
  23 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-25  0:42 UTC (permalink / raw)
  To: notmuch; +Cc: David Bremner

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 +-
 2 files changed, 4 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." },
-- 
2.29.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH 03/24] lib: add stub for notmuch_database_open_with_config
  2020-12-25  0:42 ` [PATCH 03/24] lib: add stub for notmuch_database_open_with_config David Bremner
@ 2020-12-26 13:13   ` David Bremner
  0 siblings, 0 replies; 26+ messages in thread
From: David Bremner @ 2020-12-26 13:13 UTC (permalink / raw)
  To: notmuch

David Bremner <david@tethera.net> writes:

> +/**
> + * 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

Added:

* - 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'
> + *

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2020-12-26 13:13 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-12-25  0:42 v2 Merged Config David Bremner
2020-12-25  0:42 ` [PATCH 01/24] lib: add _notmuch_string_map_set David Bremner
2020-12-25  0:42 ` [PATCH 02/24] lib: cache configuration information from database David Bremner
2020-12-25  0:42 ` [PATCH 03/24] lib: add stub for notmuch_database_open_with_config David Bremner
2020-12-26 13:13   ` David Bremner
2020-12-25  0:42 ` [PATCH 04/24] lib/open: add support for config profiles and default locations David Bremner
2020-12-25  0:42 ` [PATCH 05/24] CLI: generalize notmuch_config_mode_t David Bremner
2020-12-25  0:42 ` [PATCH 06/24] lib/config: add notmuch_config_key_{get,set} David Bremner
2020-12-25  0:42 ` [PATCH 07/24] lib/open: load default values for known configuration keys David Bremner
2020-12-25  0:42 ` [PATCH 08/24] CLI: add (unused) database argument to subcommands David Bremner
2020-12-25  0:42 ` [PATCH 09/24] util: add strsplit_len: simplified strtok with delimiter escaping David Bremner
2020-12-25  0:42 ` [PATCH 10/24] lib/config: add config values iterator David Bremner
2020-12-25  0:42 ` [PATCH 11/24] CLI/count: switch to new configuration framework David Bremner
2020-12-25  0:42 ` [PATCH 12/24] cli/dump: convert to new config framework David Bremner
2020-12-25  0:42 ` [PATCH 13/24] lib: add notmuch_config_get_bool David Bremner
2020-12-25  0:42 ` [PATCH 14/24] CLI/restore: convert to new config framework David Bremner
2020-12-25  0:42 ` [PATCH 15/24] CLI/insert: " David Bremner
2020-12-25  0:42 ` [PATCH 16/24] cli/reindex: convert " David Bremner
2020-12-25  0:42 ` [PATCH 17/24] CLI/reply: convert to " David Bremner
2020-12-25  0:42 ` [PATCH 18/24] CLI/{search,address}: convert to new configuration framework David Bremner
2020-12-25  0:42 ` [PATCH 19/24] cli/config: add accessor for config file name David Bremner
2020-12-25  0:42 ` [PATCH 20/24] CLI/show: mostly switch show to new config framework David Bremner
2020-12-25  0:42 ` [PATCH 21/24] cli/tag: convert " David Bremner
2020-12-25  0:42 ` [PATCH 22/24] lib/config: add _notmuch_config_cache David Bremner
2020-12-25  0:42 ` [PATCH 23/24] lib: split notmuch_database_compact David Bremner
2020-12-25  0:42 ` [PATCH 24/24] cli/compact: convert to new configuration framework David Bremner

Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).