second round of library config API and dump
authorDavid Bremner <david@tethera.net>
Wed, 13 Jan 2016 03:10:06 +0000 (23:10 +2000)
committerW. Trevor King <wking@tremily.us>
Sat, 20 Aug 2016 23:20:51 +0000 (16:20 -0700)
ee/9527e90197696e0c673d01bf4700a36073967a [new file with mode: 0644]

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