From 21af5200405f6559a6ae3cabee3d2a8b7cc9d813 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Wed, 13 Jan 2016 23:10:06 +2000 Subject: [PATCH] second round of library config API and dump --- ee/9527e90197696e0c673d01bf4700a36073967a | 688 ++++++++++++++++++++++ 1 file changed, 688 insertions(+) create mode 100644 ee/9527e90197696e0c673d01bf4700a36073967a diff --git a/ee/9527e90197696e0c673d01bf4700a36073967a b/ee/9527e90197696e0c673d01bf4700a36073967a new file mode 100644 index 000000000..f8154a55d --- /dev/null +++ b/ee/9527e90197696e0c673d01bf4700a36073967a @@ -0,0 +1,688 @@ +Return-Path: +X-Original-To: notmuch@notmuchmail.org +Delivered-To: notmuch@notmuchmail.org +Received: from localhost (localhost [127.0.0.1]) + by arlo.cworth.org (Postfix) with ESMTP id 7B4566DE0159 + for ; Tue, 12 Jan 2016 19:10:25 -0800 (PST) +X-Virus-Scanned: Debian amavisd-new at cworth.org +X-Spam-Flag: NO +X-Spam-Score: -0.31 +X-Spam-Level: +X-Spam-Status: No, score=-0.31 tagged_above=-999 required=5 tests=[AWL=0.241, + RP_MATCHES_RCVD=-0.55, SPF_PASS=-0.001] autolearn=disabled +Received: from arlo.cworth.org ([127.0.0.1]) + by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024) + with ESMTP id rAui08uTvCWO for ; + Tue, 12 Jan 2016 19:10:22 -0800 (PST) +Received: from fethera.tethera.net (fethera.tethera.net [198.245.60.197]) + by arlo.cworth.org (Postfix) with ESMTPS id C68216DE01D3 + for ; Tue, 12 Jan 2016 19:10:21 -0800 (PST) +Received: from remotemail by fethera.tethera.net with local (Exim 4.84) + (envelope-from ) id 1aJBp3-0000xw-Vb + for notmuch@notmuchmail.org; Tue, 12 Jan 2016 22:10:09 -0500 +Received: (nullmailer pid 23093 invoked by uid 1000); + Wed, 13 Jan 2016 03:10:18 -0000 +From: David Bremner +To: notmuch@notmuchmail.org +Subject: second round of library config API and dump +Date: Tue, 12 Jan 2016 23:10:06 -0400 +Message-Id: <1452654610-22864-1-git-send-email-david@tethera.net> +X-Mailer: git-send-email 2.6.4 +X-BeenThere: notmuch@notmuchmail.org +X-Mailman-Version: 2.1.20 +Precedence: list +List-Id: "Use and development of the notmuch mail system." + +List-Unsubscribe: , + +List-Archive: +List-Post: +List-Help: +List-Subscribe: , + +X-List-Received-Date: Wed, 13 Jan 2016 03:10:25 -0000 + +Based on feedback on IRC from Austin and Tomi, I've simplified the API +and no longer expose the database level prefix to the caller. + +This series does fix at least one memory ownership bug. Not a +particularly subtle one, I apparently only got half way through making +the current 'value' owned by the iterator. + +There's also a few more tests, as the API starts to settle down. + +Because some global renaming, the interdiff is somewhat noisy, but +hopefully mostly straightforward. + +diff --git a/lib/Makefile.local b/lib/Makefile.local +index ccc1e49..eb442d1 100644 +--- a/lib/Makefile.local ++++ b/lib/Makefile.local +@@ -48,7 +48,7 @@ libnotmuch_cxx_srcs = \ + $(dir)/index.cc \ + $(dir)/message.cc \ + $(dir)/query.cc \ +- $(dir)/metadata.cc \ ++ $(dir)/config.cc \ + $(dir)/thread.cc + + libnotmuch_modules := $(libnotmuch_c_srcs:.c=.o) $(libnotmuch_cxx_srcs:.cc=.o) +diff --git a/lib/metadata.cc b/lib/config.cc +similarity index 47% +rename from lib/metadata.cc +rename to lib/config.cc +index 34b46e0..4cd9314 100644 +--- a/lib/metadata.cc ++++ b/lib/config.cc +@@ -22,88 +22,36 @@ + #include "notmuch-private.h" + #include "database-private.h" + +-struct _notmuch_metadata { ++static const std::string CONFIG_PREFIX="C"; ++ ++struct _notmuch_config_list { + notmuch_database_t *notmuch; + Xapian::TermIterator *iterator; +- notmuch_metadata_class_t mclass; + char *current_key; ++ char *current_val; + }; + + static int +-_notmuch_metadata_destroy (notmuch_metadata_t *list) { ++_notmuch_config_list_destroy (notmuch_config_list_t *list) { + delete list->iterator; + return 0; + } + +-typedef struct prefix { +- notmuch_metadata_class_t mclass; +- const char *prefix; +-} prefix_t; +- +-static prefix_t METADATA_PREFIX[] = { +- {NOTMUCH_METADATA_CONFIG, "C"}, +-}; +- +-#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0])) +- +-const char * +-notmuch_metadata_prefix_string (notmuch_metadata_class_t mclass) +-{ +- unsigned int i; +- +- for (i=0; i < ARRAY_SIZE(METADATA_PREFIX); i++) { +- if (METADATA_PREFIX[0].mclass == mclass) +- return METADATA_PREFIX[i].prefix; +- } +- return NULL; +-} +- +-notmuch_status_t _make_key(void *ctx, notmuch_metadata_class_t mclass, +- const char *in, const char **out) +-{ +- const char *term; +- const char *prefix = NULL; +- +- if (!out) +- return NOTMUCH_STATUS_NULL_POINTER; +- +- prefix = notmuch_metadata_prefix_string(mclass); +- if (!prefix) +- return NOTMUCH_STATUS_UNSUPPORTED_OPERATION; +- +- term = talloc_asprintf (ctx, "%s%s", +- prefix, in); +- if (!term) +- return NOTMUCH_STATUS_OUT_OF_MEMORY; +- +- *out = term; +- return NOTMUCH_STATUS_SUCCESS; +-} +- + notmuch_status_t +-notmuch_database_set_metadata (notmuch_database_t *notmuch, +- notmuch_metadata_class_t mclass, +- const char *key, +- const char *value) ++notmuch_database_set_config (notmuch_database_t *notmuch, ++ const char *key, ++ const char *value) + { + notmuch_status_t status; + Xapian::WritableDatabase *db; +- const char *key_term = NULL; +- void *local; +- +- local = talloc_new (NULL); + + status = _notmuch_database_ensure_writable (notmuch); + if (status) +- goto DONE; +- +- status = _make_key (local, mclass, key, &key_term); +- if (status) +- goto DONE; ++ return status; + + try { + db = static_cast (notmuch->xapian_db); +- db->set_metadata (key_term, value); ++ db->set_metadata (CONFIG_PREFIX+key, value); + } catch (const Xapian::Error &error) { + status = NOTMUCH_STATUS_XAPIAN_EXCEPTION; + notmuch->exception_reported = TRUE; +@@ -112,33 +60,18 @@ notmuch_database_set_metadata (notmuch_database_t *notmuch, + error.get_msg().c_str()); + } + } +- DONE: +- talloc_free (local); +- +- return status; ++ return NOTMUCH_STATUS_SUCCESS; + } + + static notmuch_status_t + _metadata_value (notmuch_database_t *notmuch, +- notmuch_metadata_class_t mclass, + const char *key, + std::string &value) + { +- notmuch_status_t status; +- +- const char *key_term = NULL; +- void *local; +- +- local = talloc_new (NULL); +- +- status = _make_key (local, mclass, key, &key_term); +- if (status) +- goto DONE; ++ notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; + + try { +- +- value = notmuch->xapian_db->get_metadata (key_term); +- ++ value = notmuch->xapian_db->get_metadata (CONFIG_PREFIX+key); + } catch (const Xapian::Error &error) { + status = NOTMUCH_STATUS_XAPIAN_EXCEPTION; + notmuch->exception_reported = TRUE; +@@ -147,24 +80,20 @@ _metadata_value (notmuch_database_t *notmuch, + error.get_msg().c_str()); + } + } +- +- DONE: +- talloc_free (local); + return status; + } + + notmuch_status_t +-notmuch_database_get_metadata (notmuch_database_t *notmuch, +- notmuch_metadata_class_t mclass, +- const char *key, +- char **value) { ++notmuch_database_get_config (notmuch_database_t *notmuch, ++ const char *key, ++ char **value) { + std::string strval; + notmuch_status_t status; + + if (!value) + return NOTMUCH_STATUS_NULL_POINTER; + +- status = _metadata_value (notmuch, mclass, key, strval); ++ status = _metadata_value (notmuch, key, strval); + if (status) + return status; + +@@ -174,31 +103,28 @@ notmuch_database_get_metadata (notmuch_database_t *notmuch, + } + + notmuch_status_t +-notmuch_database_get_all_metadata (notmuch_database_t *notmuch, +- notmuch_metadata_class_t mclass, +- notmuch_metadata_t **out) ++notmuch_database_get_config_list (notmuch_database_t *notmuch, ++ const char *prefix, ++ notmuch_config_list_t **out) + { +- notmuch_metadata_t *list = NULL; ++ notmuch_config_list_t *list = NULL; + notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; + +- const char *prefix = notmuch_metadata_prefix_string (mclass); +- +- list = talloc (notmuch, notmuch_metadata_t); ++ list = talloc (notmuch, notmuch_config_list_t); + if (!list) { + status = NOTMUCH_STATUS_OUT_OF_MEMORY; + goto DONE; + } + +- talloc_set_destructor(list, _notmuch_metadata_destroy); ++ talloc_set_destructor(list, _notmuch_config_list_destroy); + list->iterator = new Xapian::TermIterator; + list->notmuch = notmuch; +- list->mclass = mclass; + list->current_key = NULL; ++ list->current_val = NULL; + + try { + +- *list->iterator = notmuch->xapian_db->metadata_keys_begin(); +- list->iterator->skip_to (prefix); ++ *list->iterator = notmuch->xapian_db->metadata_keys_begin(CONFIG_PREFIX + (prefix ? prefix : NULL)); + + } catch (const Xapian::Error &error) { + _notmuch_database_log (notmuch, "A Xapian exception occurred getting metadata iterator: %s.\n", +@@ -217,49 +143,52 @@ notmuch_database_get_all_metadata (notmuch_database_t *notmuch, + } + + notmuch_bool_t +-notmuch_metadata_valid (notmuch_metadata_t *metadata) ++notmuch_config_list_valid (notmuch_config_list_t *metadata) + { +- const char *prefix = notmuch_metadata_prefix_string (metadata->mclass); + if (*(metadata->iterator) == metadata->notmuch->xapian_db->metadata_keys_end()) + return FALSE; + +- return (strncmp((**(metadata->iterator)).c_str (), prefix, strlen (prefix)) == 0); ++ return TRUE; + } + + const char * +-notmuch_metadata_key (notmuch_metadata_t *metadata) ++notmuch_config_list_key (notmuch_config_list_t *list) + { +- const char *prefix = notmuch_metadata_prefix_string (metadata->mclass); ++ if (list->current_key) ++ talloc_free (list->current_key); + +- if (metadata->current_key) +- talloc_free (metadata->current_key); +- metadata->current_key = talloc_strdup (metadata, (**(metadata->iterator)).c_str () + strlen (prefix)); ++ list->current_key = talloc_strdup (list, (**(list->iterator)).c_str () + CONFIG_PREFIX.length ()); + +- return metadata->current_key; ++ return list->current_key; + } + + const char * +-notmuch_metadata_value (notmuch_metadata_t *metadata) ++notmuch_config_list_value (notmuch_config_list_t *list) + { +- const char *key = notmuch_metadata_key (metadata); +- char *val; ++ std::string strval; + notmuch_status_t status; ++ const char *key = notmuch_config_list_key (list); + +- status=notmuch_database_get_metadata (metadata->notmuch, metadata->mclass, key, &val); ++ /* TODO: better error reporting?? */ ++ status = _metadata_value (list->notmuch, key, strval); + if (status) + return NULL; + +- return val; ++ if (list->current_val) ++ talloc_free(list->current_val); ++ ++ list->current_val = talloc_strdup(list, strval.c_str ()); ++ return list->current_val; + } + + void +-notmuch_metadata_move_to_next (notmuch_metadata_t *metadata) ++notmuch_config_list_move_to_next (notmuch_config_list_t *list) + { +- (*(metadata->iterator))++; ++ (*(list->iterator))++; + } + + void +-notmuch_metadata_destroy (notmuch_metadata_t *metadata) ++notmuch_config_list_destroy (notmuch_config_list_t *list) + { +- talloc_free (metadata); ++ talloc_free (list); + } +diff --git a/lib/notmuch.h b/lib/notmuch.h +index 657a7d6..b439c88 100644 +--- a/lib/notmuch.h ++++ b/lib/notmuch.h +@@ -197,7 +197,7 @@ typedef struct _notmuch_message notmuch_message_t; + typedef struct _notmuch_tags notmuch_tags_t; + typedef struct _notmuch_directory notmuch_directory_t; + typedef struct _notmuch_filenames notmuch_filenames_t; +-typedef struct _notmuch_metadata notmuch_metadata_t; ++typedef struct _notmuch_config_list notmuch_config_list_t; + #endif /* __DOXYGEN__ */ + + /** +@@ -1830,78 +1830,69 @@ notmuch_filenames_move_to_next (notmuch_filenames_t *filenames); + void + notmuch_filenames_destroy (notmuch_filenames_t *filenames); + +-/** +- * metadata class +- */ +- +-typedef enum _notmuch_metadata_class { +- NOTMUCH_METADATA_FIRST_CLASS = 1, +- NOTMUCH_METADATA_CONFIG = 1, +- NOTMUCH_METADATA_LAST_CLASS +-} notmuch_metadata_class_t; + + /** +- * set metadata ++ * set config 'key' to 'value' + * + */ + notmuch_status_t +-notmuch_database_set_metadata (notmuch_database_t *db, notmuch_metadata_class_t mclass, const char *key, const char *value); ++notmuch_database_set_config (notmuch_database_t *db, const char *key, const char *value); + + /** +- * retrieve one metadata value ++ * retrieve config item 'key', assign to 'value' ++ * ++ * keys which have not been previously set with n_d_set_config will ++ * return an empty string. + * +- * return value is allocated by malloc and should be freed by the caller. ++ * return value is allocated by malloc and should be freed by the ++ * caller. + */ + notmuch_status_t +-notmuch_database_get_metadata (notmuch_database_t *db, notmuch_metadata_class_t mclass, const char *key, char **value); ++notmuch_database_get_config (notmuch_database_t *db, const char *key, char **value); + + /** +- * get all metadata of a given class ++ * Create an iterator for all config items with keys matching a given prefix + */ + notmuch_status_t +-notmuch_database_get_all_metadata (notmuch_database_t *db, notmuch_metadata_class_t mclass, notmuch_metadata_t **out); ++notmuch_database_get_config_list (notmuch_database_t *db, const char *prefix, notmuch_config_list_t **out); + + /** +- * Is 'metadata' iterator valid (i.e. _key, _value, _move_to_next can be called). ++ * Is 'config_list' iterator valid (i.e. _key, _value, _move_to_next can be called). + */ + notmuch_bool_t +-notmuch_metadata_valid (notmuch_metadata_t *metadata); ++notmuch_config_list_valid (notmuch_config_list_t *config_list); + + /** +- * return key for current metadata pair ++ * return key for current config pair + * + * return value is owned by the iterator, and will be destroyed by the +- * next call to notmuch_metadata_key or notmuch_metadata_destroy. ++ * next call to notmuch_config_list_key or notmuch_config_list_destroy. + */ + const char * +-notmuch_metadata_key (notmuch_metadata_t *metadata); ++notmuch_config_list_key (notmuch_config_list_t *config_list); + + /** +- * return 'value' for current metadata pair ++ * return 'value' for current config pair + * + * return value is owned by the iterator, and will be destroyed by the +- * next call to notmuch_metadata_value ++ * next call to notmuch_config_list_value or notmuch config_list_destroy + */ + const char * +-notmuch_metadata_value (notmuch_metadata_t *metadata); ++notmuch_config_list_value (notmuch_config_list_t *config_list); ++ + + /** +- * move 'metadata' iterator to the next pair ++ * move 'config_list' iterator to the next pair + */ + void +-notmuch_metadata_move_to_next (notmuch_metadata_t *metadata); ++notmuch_config_list_move_to_next (notmuch_config_list_t *config_list); + + /** +- * free any resources held by 'metadata' ++ * free any resources held by 'config_list' + */ + void +-notmuch_metadata_destroy (notmuch_metadata_t * metadata); ++notmuch_config_list_destroy (notmuch_config_list_t *config_list); + +-/** +- * convert enum to string +- */ +-const char * +-notmuch_metadata_prefix_string (notmuch_metadata_class_t mclass); + /* @} */ + + NOTMUCH_END_DECLS +diff --git a/notmuch-client.h b/notmuch-client.h +index 8ee6e0e..2dca83c 100644 +--- a/notmuch-client.h ++++ b/notmuch-client.h +@@ -445,7 +445,7 @@ typedef enum dump_formats { + + typedef enum dump_includes { + DUMP_INCLUDE_TAGS=1, +- DUMP_INCLUDE_METADATA=2, ++ DUMP_INCLUDE_CONFIG=2, + } dump_include_t; + + int +diff --git a/notmuch-dump.c b/notmuch-dump.c +index a88b5d5..4909493 100644 +--- a/notmuch-dump.c ++++ b/notmuch-dump.c +@@ -24,25 +24,21 @@ + #include + + static notmuch_status_t +-database_dump_metadata(notmuch_database_t *notmuch, gzFile output) ++database_dump_config(notmuch_database_t *notmuch, gzFile output) + { +- notmuch_metadata_class_t mclass; +- notmuch_metadata_t *meta; ++ notmuch_config_list_t *list; + notmuch_status_t status; +- +- for (mclass = NOTMUCH_METADATA_FIRST_CLASS; mclass < NOTMUCH_METADATA_LAST_CLASS; mclass++) { +- status = notmuch_database_get_all_metadata (notmuch, NOTMUCH_METADATA_CONFIG, &meta); +- if (status) +- return status; +- +- for (; notmuch_metadata_valid (meta); notmuch_metadata_move_to_next (meta)) { +- /* FIXME hexencode key and values */ +- gzprintf(output, "#@ %s %s %s\n", +- notmuch_metadata_prefix_string(mclass), +- notmuch_metadata_key (meta), notmuch_metadata_value (meta)); +- } +- notmuch_metadata_destroy (meta); ++ status = notmuch_database_get_config_list (notmuch, NULL, &list); ++ if (status) ++ return status; ++ ++ for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) { ++ /* FIXME hexencode key and values */ ++ gzprintf(output, "#@ %s %s\n", ++ notmuch_config_list_key (list), notmuch_config_list_value (list)); + } ++ notmuch_config_list_destroy (list); ++ + return NOTMUCH_STATUS_SUCCESS; + } + +@@ -55,9 +51,9 @@ database_dump_file (notmuch_database_t *notmuch, gzFile output, + notmuch_message_t *message; + notmuch_tags_t *tags; + +- if (include | DUMP_INCLUDE_METADATA) { ++ if (include | DUMP_INCLUDE_CONFIG) { + if (print_status_database ("notmuch dump", notmuch, +- database_dump_metadata(notmuch,output))) ++ database_dump_config(notmuch,output))) + return EXIT_FAILURE; + } + +@@ -267,7 +263,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[]) + { "batch-tag", DUMP_FORMAT_BATCH_TAG }, + { 0, 0 } } }, + { NOTMUCH_OPT_KEYWORD_FLAGS, &include, "include", 'i', +- (notmuch_keyword_t []){ { "metadata", DUMP_INCLUDE_METADATA }, ++ (notmuch_keyword_t []){ { "metadata", DUMP_INCLUDE_CONFIG }, + { "tags", DUMP_INCLUDE_TAGS} } }, + { NOTMUCH_OPT_STRING, &output_file_name, "output", 'o', 0 }, + { NOTMUCH_OPT_BOOLEAN, &gzip_output, "gzip", 'z', 0 }, +@@ -282,7 +278,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[]) + notmuch_process_shared_options (argv[0]); + + if (include == 0) +- include = DUMP_INCLUDE_METADATA | DUMP_INCLUDE_TAGS; ++ include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS; + + if (opt_index < argc) { + query_str = query_string_from_args (notmuch, argc - opt_index, argv + opt_index); +diff --git a/notmuch-new.c b/notmuch-new.c +index 1f8050d..fd2ff82 100644 +--- a/notmuch-new.c ++++ b/notmuch-new.c +@@ -1041,7 +1041,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[]) + } + + if (notmuch_database_dump (notmuch, backup_name, "", +- DUMP_FORMAT_BATCH_TAG, DUMP_INCLUDE_METADATA | DUMP_INCLUDE_TAGS, TRUE)) { ++ DUMP_FORMAT_BATCH_TAG, DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS, TRUE)) { + fprintf (stderr, "Backup failed. Aborting upgrade."); + return EXIT_FAILURE; + } +diff --git a/test/T590-metadata.sh b/test/T590-libconfig.sh +similarity index 35% +rename from test/T590-metadata.sh +rename to test/T590-libconfig.sh +index 45a49be..4fe6bd1 100755 +--- a/test/T590-metadata.sh ++++ b/test/T590-libconfig.sh +@@ -1,5 +1,5 @@ + #!/usr/bin/env bash +-test_description="metadata API" ++test_description="library config API" + + . ./test-lib.sh || exit 1 + +@@ -36,14 +36,14 @@ cat < c_tail + } + EOF + +-test_begin_subtest "notmuch_database_{set,get}_metadata" ++test_begin_subtest "notmuch_database_{set,get}_config" + cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} + { +- RUN(notmuch_database_set_metadata (db, NOTMUCH_METADATA_CONFIG, "testkey1", "testvalue1")); +- RUN(notmuch_database_set_metadata (db, NOTMUCH_METADATA_CONFIG, "testkey2", "testvalue2")); +- RUN(notmuch_database_get_metadata (db, NOTMUCH_METADATA_CONFIG, "testkey1", &val)); ++ RUN(notmuch_database_set_config (db, "testkey1", "testvalue1")); ++ RUN(notmuch_database_set_config (db, "testkey2", "testvalue2")); ++ RUN(notmuch_database_get_config (db, "testkey1", &val)); + printf("testkey1 = %s\n", val); +- RUN(notmuch_database_get_metadata (db, NOTMUCH_METADATA_CONFIG, "testkey2", &val)); ++ RUN(notmuch_database_get_config (db, "testkey2", &val)); + printf("testkey2 = %s\n", val); + } + EOF +@@ -55,33 +55,73 @@ testkey2 = testvalue2 + EOF + test_expect_equal_file EXPECTED OUTPUT + +-test_begin_subtest "get all metadata in one class" ++ ++test_begin_subtest "notmuch_database_get_config_list: empty list" ++cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ++{ ++ notmuch_config_list_t *list; ++ RUN(notmuch_database_get_config_list (db, "nonexistent", &list)); ++ printf("valid = %d\n", notmuch_config_list_valid (list)); ++ notmuch_config_list_destroy (list); ++} ++EOF ++cat <<'EOF' >EXPECTED ++== stdout == ++valid = 0 ++== stderr == ++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_list_t *list; ++ RUN(notmuch_database_set_config (db, "zzzafter", "afterval")); ++ RUN(notmuch_database_set_config (db, "aaabefore", "beforeval")); ++ RUN(notmuch_database_get_config_list (db, "", &list)); ++ for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) { ++ printf("%s %s\n", notmuch_config_list_key (list), notmuch_config_list_value(list)); ++ } ++ notmuch_config_list_destroy (list); ++} ++EOF ++cat <<'EOF' >EXPECTED ++== stdout == ++aaabefore beforeval ++testkey1 testvalue1 ++testkey2 testvalue2 ++zzzafter afterval ++== stderr == ++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_metadata_t *meta; +- RUN(notmuch_database_get_all_metadata (db, NOTMUCH_METADATA_CONFIG, &meta)); +- for (; notmuch_metadata_valid (meta); notmuch_metadata_move_to_next (meta)) { +- printf("key = %s\n", notmuch_metadata_key (meta)); +- printf("val = %s\n", notmuch_metadata_value (meta)); ++ notmuch_config_list_t *list; ++ RUN(notmuch_database_get_config_list (db, "testkey", &list)); ++ for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) { ++ printf("%s %s\n", notmuch_config_list_key (list), notmuch_config_list_value(list)); + } +- notmuch_metadata_destroy (meta); ++ notmuch_config_list_destroy (list); + } + EOF + cat <<'EOF' >EXPECTED + == stdout == +-key = testkey1 +-val = testvalue1 +-key = testkey2 +-val = testvalue2 ++testkey1 testvalue1 ++testkey2 testvalue2 + == stderr == + EOF + test_expect_equal_file EXPECTED OUTPUT + +-test_begin_subtest "dump metadata" ++test_begin_subtest "dump config" + notmuch dump --include=metadata >OUTPUT + cat <<'EOF' >EXPECTED +-#@ C testkey1 testvalue1 +-#@ C testkey2 testvalue2 ++#@ aaabefore beforeval ++#@ testkey1 testvalue1 ++#@ testkey2 testvalue2 ++#@ zzzafter afterval + EOF + test_expect_equal_file EXPECTED OUTPUT + -- 2.26.2