The author is the same as KyotoCabinet, so the API is pretty similar
and it was fairly easy to add.

The only difference is with compression.  KyotoCabinet allowed a
generic "c" parameter to enable compression.  Tkrzw seems to require
specifying a specific implementation, and those implementations are
enabled during the configuration and build of tkrzw.

Since mutt doesn't know what's available, I've not enabled
OPTHCACHECOMPRESS support.
---
 INSTALL      |  2 +-
 configure.ac | 35 +++++++++++++++++++++-
 hcache.c     | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 120 insertions(+), 2 deletions(-)

diff --git a/INSTALL b/INSTALL
index 445adb3f..bacd21c3 100644
--- a/INSTALL
+++ b/INSTALL
@@ -137,7 +137,7 @@ options are:
 --enable-hcache
         Enable header caching support.  If no backend library is
         specified via a --with option (e.g. --with-kyotocabinet), Mutt
-        will scan in the order: kyotocabinet, tokyocabinet, lmdb,
+        will scan in the order: tkrzw, kyotocabinet, tokyocabinet, lmdb,
         qdbm, gdbm, bdb.  To skip scanning one or more of these
         libraries, use the corresponding --without option.
 
diff --git a/configure.ac b/configure.ac
index 397e2086..074f002e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1014,6 +1014,7 @@ dnl -- start cache --
 db_found=no
 db_requested=auto
 AC_ARG_ENABLE(hcache, AS_HELP_STRING([--enable-hcache],[Enable header 
caching]))
+AC_ARG_WITH(tkrzw, AS_HELP_STRING([--with-tkrzw@<:@=DIR@:>@],[Use tkrzw hcache 
backend]))
 AC_ARG_WITH(kyotocabinet, 
AS_HELP_STRING([--with-kyotocabinet@<:@=DIR@:>@],[Use kyotocabinet hcache 
backend]))
 AC_ARG_WITH(tokyocabinet, 
AS_HELP_STRING([--with-tokyocabinet@<:@=DIR@:>@],[Use tokyocabinet hcache 
backend]))
 AC_ARG_WITH(lmdb, AS_HELP_STRING([--with-lmdb@<:@=DIR@:>@],[Use lmdb hcache 
backend]))
@@ -1032,6 +1033,15 @@ then
 
     need_md5="yes"
 
+    if test -n "$with_tkrzw" && test "$with_tkrzw" != "no"
+    then
+      if test "$db_requested" != "auto"
+      then
+        AC_MSG_ERROR([more than one header cache engine requested.])
+      else
+        db_requested=tkrzw
+      fi
+    fi
     if test -n "$with_kyotocabinet" && test "$with_kyotocabinet" != "no"
     then
       if test "$db_requested" != "auto"
@@ -1087,6 +1097,29 @@ then
       fi
     fi
 
+    dnl -- Tkrzw --
+    if test x$with_tkrzw != xno && test $db_found = no \
+            && test "$db_requested" = auto -o "$db_requested" = tkrzw
+    then
+      if test -n "$with_tkrzw" && test "$with_tkrzw" != "yes"
+      then
+        CPPFLAGS="$CPPFLAGS -I$with_tkrzw/include"
+        LDFLAGS="$LDFLAGS -L$with_tkrzw/lib"
+      fi
+
+      AC_CHECK_HEADER(tkrzw_langc.h,
+      AC_CHECK_LIB(tkrzw, tkrzw_dbm_open,
+        [MUTTLIBS="$MUTTLIBS -ltkrzw"
+         AC_DEFINE(HAVE_TKRZW, 1, [Tkrzw Support])
+         db_found=tkrzw],
+        [CPPFLAGS="$OLDCPPFLAGS"
+         LDFLAGS="$OLDLDFLAGS"]))
+      if test "$db_requested" != auto && test "$db_found" != "$db_requested"
+      then
+        AC_MSG_ERROR([Tkrzw could not be used. Check config.log for details.])
+      fi
+    fi
+
     dnl -- Kyoto Cabinet --
     if test x$with_kyotocabinet != xno && test $db_found = no \
             && test "$db_requested" = auto -o "$db_requested" = kc
@@ -1259,7 +1292,7 @@ then
 
     if test $db_found = no
     then
-        AC_MSG_ERROR([You need Kyoto Cabinet, Tokyo Cabinet, LMDB, QDBM, GDBM, 
or BDB for hcache])
+        AC_MSG_ERROR([You need Tkrzw, Kyoto Cabinet, Tokyo Cabinet, LMDB, 
QDBM, GDBM, or BDB for hcache])
     fi
 fi
 dnl -- end cache --
diff --git a/hcache.c b/hcache.c
index d006055e..dc52aba0 100644
--- a/hcache.c
+++ b/hcache.c
@@ -30,6 +30,8 @@
 #include <tcbdb.h>
 #elif HAVE_KC
 #include <kclangc.h>
+#elif HAVE_TKRZW
+#include <tkrzw_langc.h>
 #elif HAVE_GDBM
 #include <gdbm.h>
 #elif HAVE_DB4
@@ -77,6 +79,13 @@ struct header_cache
   char *folder;
   unsigned int crc;
 };
+#elif HAVE_TKRZW
+struct header_cache
+{
+  TkrzwDBM *db;
+  char *folder;
+  unsigned int crc;
+};
 #elif HAVE_GDBM
 struct header_cache
 {
@@ -841,6 +850,8 @@ mutt_hcache_fetch_raw(header_cache_t *h, const char 
*filename,
   rv = tcbdbget(h->db, mutt_b2s(path), ksize, &sp);
 #elif HAVE_KC
   rv = kcdbget(h->db, mutt_b2s(path), ksize, &sp);
+#elif HAVE_TKRZW
+  rv = tkrzw_dbm_get(h->db, mutt_b2s(path), ksize, NULL);
 #elif HAVE_GDBM
   key.dptr = path->data;
   key.dsize = ksize;
@@ -940,6 +951,8 @@ mutt_hcache_store_raw(header_cache_t *h, const char 
*filename, void *data,
   rv = tcbdbput(h->db, mutt_b2s(path), ksize, data, dlen);
 #elif HAVE_KC
   rv = kcdbset(h->db, mutt_b2s(path), ksize, data, dlen);
+#elif HAVE_TKRZW
+  rv = tkrzw_dbm_set(h->db, mutt_b2s(path), ksize, data, dlen, true);
 #elif HAVE_GDBM
   key.dptr = path->data;
   key.dsize = ksize;
@@ -1179,6 +1192,69 @@ mutt_hcache_delete(header_cache_t *h, const char 
*filename,
   return rc;
 }
 
+#elif HAVE_TKRZW
+static int
+hcache_open_tkrzw(struct header_cache *h, const char *path)
+{
+  int rc = -1;
+
+/* Options are discussed at:
+ * https://dbmx.net/tkrzw/api/classtkrzw_1_1PolyDBM.html
+ * in the OpenAdvanced method call.
+ *
+ * Unfortunately the "compression" option requires specifying a specific 
library,
+ * and mutt has no way of knowing which libraries tkrzw was built with, so we 
ignore
+ * OPTHCACHECOMPRESS.
+ */
+  h->db = tkrzw_dbm_open(path, true, "dbm=TreeDBM");
+  if (!h->db)
+  {
+    muttdbg(2, "tkrzw_dbm_open failed for %s: %s (%d)", path,
+            tkrzw_get_last_status_message(), tkrzw_get_last_status_code());
+    goto cleanup;
+  }
+
+  rc = 0;
+
+cleanup:
+  return rc;
+}
+
+void
+mutt_hcache_close(header_cache_t *h)
+{
+  if (!h)
+    return;
+
+  if (!tkrzw_dbm_close(h->db))
+    muttdbg(2, "tkrzw_dbm_close failed for %s: %s (ecode %d)", h->folder,
+            tkrzw_get_last_status_message(), tkrzw_get_last_status_code());
+  FREE(&h->folder);
+  FREE(&h);
+}
+
+int
+mutt_hcache_delete(header_cache_t *h, const char *filename,
+                   size_t (*keylen)(const char *fn))
+{
+  BUFFER *path = NULL;
+  int ksize, rc;
+
+  if (!h)
+    return -1;
+
+  path = mutt_buffer_pool_get();
+  mutt_buffer_strcpy(path, h->folder);
+  mutt_buffer_addstr(path, filename);
+
+  ksize = strlen(h->folder) + keylen(filename);
+
+  rc = tkrzw_dbm_remove(h->db, mutt_b2s(path), ksize);
+
+  mutt_buffer_pool_release(&path);
+  return rc;
+}
+
 #elif HAVE_GDBM
 static int
 hcache_open_gdbm(struct header_cache *h, const char *path)
@@ -1478,6 +1554,8 @@ mutt_hcache_open(const char *path, const char *folder, 
hcache_namer_t namer)
   hcache_open= hcache_open_tc;
 #elif HAVE_KC
   hcache_open= hcache_open_kc;
+#elif HAVE_TKRZW
+  hcache_open = hcache_open_tkrzw;
 #elif HAVE_GDBM
   hcache_open = hcache_open_gdbm;
 #elif HAVE_DB4
@@ -1603,4 +1681,11 @@ const char *mutt_hcache_backend(void)
   snprintf(backend, sizeof(backend), "kyotocabinet %s", KCVERSION);
   return backend;
 }
+#elif HAVE_TKRZW
+const char *mutt_hcache_backend(void)
+{
+  static char backend[SHORT_STRING];
+  snprintf(backend, sizeof(backend), "tkrzw %s", TKRZW_LIBRARY_VERSION);
+  return backend;
+}
 #endif
-- 
2.55.0

Reply via email to