Hintsdb: tidy coding for DB create
[exim.git] / src / src / hintsdb.h
index 9c2082b76711cd8fff746b1d07f55c50f434fbf6..3898b0221aa2175c44a4cc403f0b67cadc1ee327 100644 (file)
@@ -2,9 +2,10 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 2020 - 2024 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 - 2022 */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 /* This header file contains macro definitions so that a variety of DBM
 libraries can be used by Exim. Nigel Metheringham provided the original set for
@@ -15,13 +16,316 @@ from Pierre A. Humblet, so Exim could be made to work with Cygwin.
 
 For convenience, the definitions of the structures used in the various hints
 databases are also kept in this file, which is used by the maintenance
-utilities as well as the main Exim binary. */
+utilities as well as the main Exim binary.
+
+A key/value store is supported (only).  Keys are strings; values arbitrary
+binary blobs.
+
+The API is:
+  Functions:
+    exim_dbopen                O_RDONLY/O_RDWR, optionally OR'd with O_CREAT
+    exim_dbclose
+    exim_dbget
+    exim_dbput
+    exim_dbputb                (non-overwriting put)
+    exim_dbdel
+    exim_dbcreate_cursor
+    exim_dbscan                (get, and bump cursor)
+    exim_dbdelete_cursor
+    exim_datum_init
+    exim_datum_size_get
+    exim_datum_data_get
+    exim_datum_free
+  Defines:
+    EXIM_DB            access handle
+    EXIM_CURSOR                datatype for cursor
+    EXIM_DATUM         datatype for "value"
+    EXIM_DBTYPE                text for logging & debuug
+
+Selection of the shim layer implementation, and backend, is by #defines.
+
+The users of this API are:
+  hintsdb interface    dbfn.c
+  hintsdb utilities    exim_dbutil.c and exim_dbmvuild.c
+  dbmdb lookup         lookups/dbmdb,c
+  autoreply transport  transports/autoreply.c
+*/
 
 #ifndef HINTSDB_H
 #define HINTSDB_H
 
 
-#if defined(USE_TDB)
+#ifdef USE_SQLITE
+
+/* ********************* sqlite3 interface ************************ */
+
+# include <sqlite3.h>
+
+/* Basic DB type */
+# define EXIM_DB sqlite3
+
+# define EXIM_CURSOR int
+
+# /* The datum type used for queries */
+# define EXIM_DATUM blob
+
+/* Some text for messages */
+# define EXIM_DBTYPE "sqlite3"
+
+# /* Access functions */
+
+/* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */
+static inline EXIM_DB *
+exim_dbopen__(const uschar * name, const uschar * dirname, int flags,
+  unsigned mode)
+{
+EXIM_DB * dbp;
+int ret, sflags = flags & O_RDWR ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY;
+if (flags & O_CREAT) sflags |= SQLITE_OPEN_CREATE;
+if ((ret = sqlite3_open_v2(CCS name, &dbp, sflags, NULL)) == SQLITE_OK)
+  {
+  sqlite3_busy_timeout(dbp, 5000);
+  if (flags & O_CREAT)
+    ret == sqlite3_exec(dbp,
+           "CREATE TABLE IF NOT EXISTS tbl (ky TEXT PRIMARY KEY, dat BLOB);",
+           NULL, NULL, NULL);
+  }
+//else
+//  fprintf(stderr, "sqlite3_open_v2: %s\n", sqlite3_errmsg(dbp));
+return ret == SQLITE_OK ? dbp : NULL;
+}
+
+/* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
+/* note we alloc'n'copy - the caller need not do so */
+/* result has a NUL appended, but the length is as per the DB */
+
+static inline BOOL
+exim_dbget__(EXIM_DB * dbp, const uschar * s, EXIM_DATUM * res)
+{
+sqlite3_stmt * statement;
+int ret;
+
+res->len = (size_t) -1;
+/* fprintf(stderr, "exim_dbget__(%s)\n", s); */
+if ((ret = sqlite3_prepare_v2(dbp, CCS s, -1, &statement, NULL)) != SQLITE_OK)
+  {
+/* fprintf(stderr, "prepare fail: %s\n", sqlite3_errmsg(dbp)); */
+  return FALSE;
+  }
+if (sqlite3_step(statement) != SQLITE_ROW)
+  {
+/* fprintf(stderr, "step fail: %s\n", sqlite3_errmsg(dbp)); */
+  sqlite3_finalize(statement);
+  return FALSE;
+  }
+
+res->len = sqlite3_column_bytes(statement, 0);
+res->data = store_get(res->len + 1, GET_TAINTED);
+memcpy(res->data, sqlite3_column_blob(statement, 0), res->len);
+res->data[res->len] = '\0';
+/* fprintf(stderr, "res %d bytes: '%.*s'\n", (int)res->len, (int)res->len, res->data); */
+sqlite3_finalize(statement);
+return TRUE;
+}
+
+static inline BOOL
+exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
+{
+# define FMT "SELECT dat FROM tbl WHERE ky = '%.*s';"
+uschar * qry;
+int i;
+BOOL ret;
+
+# ifdef COMPILE_UTILITY
+/* fprintf(stderr, "exim_dbget(k len %d '%.*s')\n", (int)key->len, (int)key->len, key->data); */
+qry = malloc(i = snprintf(NULL, 0, FMT, (int) key->len, key->data));
+snprintf(CS qry, i, FMT, (int) key->len, key->data);
+ret = exim_dbget__(dbp, qry, res);
+free(qry);
+# else
+/* fprintf(stderr, "exim_dbget(k len %d '%.*s')\n", (int)key->len, (int)key->len, key->data); */
+qry = string_sprintf(FMT, (int) key->len, key->data);
+ret = exim_dbget__(dbp, qry, res);
+# endif
+
+return ret;
+# undef FMT
+}
+
+/**/
+# define EXIM_DBPUTB_OK  0
+# define EXIM_DBPUTB_DUP (-1)
+
+static inline int
+exim_s_dbp(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, const uschar * alt)
+{
+# define FMT "INSERT OR %s INTO tbl (ky,dat) VALUES ('%.*s', X'%.*s');"
+uschar * hex = store_get(data->len * 2, data->data), * qry;
+int res;
+
+for (const uschar * s = data->data, * t = s + data->len; s < t; s++)
+  sprintf(CS hex + 2 * (s - data->data), "%02X", *s);
+
+# ifdef COMPILE_UTILITY
+res = snprintf(NULL, 0, FMT,
+               alt, (int) key->len, key->data, (int)data->len * 2, hex);
+qry = malloc(res);
+snprintf(CS qry, res, FMT, alt, (int) key->len, key->data, (int)data->len * 2, hex);
+/* fprintf(stderr, "exim_s_dbp(%s)\n", qry); */
+res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
+free(qry);
+# else
+qry = string_sprintf(FMT, alt, (int) key->len, key->data, (int)data->len * 2, hex);
+/* fprintf(stderr, "exim_s_dbp(%s)\n", qry); */
+res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
+/* fprintf(stderr, "exim_s_dbp res %d\n", res); */
+# endif
+
+if (res != SQLITE_OK)
+  fprintf(stderr, "sqlite3_exec: %s\n", sqlite3_errmsg(dbp));
+
+return res == SQLITE_OK ? EXIM_DBPUTB_OK : EXIM_DBPUTB_DUP;
+# undef FMT
+}
+
+/* EXIM_DBPUT - returns nothing useful, assumes replace mode */
+
+static inline int
+exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
+{
+/* fprintf(stderr, "exim_dbput()\n"); */
+(void) exim_s_dbp(dbp, key, data, US"REPLACE");
+return 0;
+}
+
+/* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
+
+/* Returns from EXIM_DBPUTB */
+
+static inline int
+exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
+{
+return exim_s_dbp(dbp, key, data, US"ABORT");
+}
+
+/* EXIM_DBDEL */
+static inline int
+exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key)
+{
+# define FMT "DELETE FROM tbl WHERE ky = '%.*s';"
+uschar * qry;
+int res;
+
+# ifdef COMPILE_UTILITY
+res = snprintf(NULL, 0, FMT, (int) key->len, key->data);
+qry = malloc(res);
+snprintf(CS qry, res, FMT, (int) key->len, key->data);
+res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
+free(qry);
+# else
+qry = string_sprintf(FMT, (int) key->len, key->data);
+res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
+# endif
+
+return res;
+# undef FMT
+}
+
+
+/* EXIM_DBCREATE_CURSOR - initialize for scanning operation */
+/* Cursors are inefficiently emulated by repeating searches */
+
+static inline EXIM_CURSOR *
+exim_dbcreate_cursor(EXIM_DB * dbp)
+{
+EXIM_CURSOR * c = store_malloc(sizeof(int));
+*c = 0;
+return c;
+}
+
+/* EXIM_DBSCAN */
+/* Note that we return the (next) key, not the record value */
+static inline BOOL
+exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res, BOOL first,
+  EXIM_CURSOR * cursor)
+{
+# define FMT "SELECT ky FROM tbl ORDER BY ky LIMIT 1 OFFSET %d;"
+uschar * qry;
+int i;
+BOOL ret;
+
+# ifdef COMPILE_UTILITY
+qry = malloc(i = snprintf(NULL, 0, FMT, *cursor));
+snprintf(CS qry, i, FMT, *cursor);
+/* fprintf(stderr, "exim_dbscan(%s)\n", qry); */
+ret = exim_dbget__(dbp, qry, key);
+free(qry);
+/* fprintf(stderr, "exim_dbscan ret %c\n", ret ? 'T':'F'); */
+# else
+qry = string_sprintf(FMT, *cursor);
+/* fprintf(stderr, "exim_dbscan(%s)\n", qry); */
+ret = exim_dbget__(dbp, qry, key);
+/* fprintf(stderr, "exim_dbscan ret %c\n", ret ? 'T':'F'); */
+# endif
+if (ret) *cursor = *cursor + 1;
+return ret;
+# undef FMT
+}
+
+/* EXIM_DBDELETE_CURSOR - terminate scanning operation. */
+static inline void
+exim_dbdelete_cursor(EXIM_CURSOR * cursor)
+{ store_free(cursor); }
+
+
+/* EXIM_DBCLOSE */
+static void
+exim_dbclose__(EXIM_DB * db)
+{ sqlite3_close(db); }
+
+
+/* Datum access */
+
+static uschar *
+exim_datum_data_get(EXIM_DATUM * dp)
+{ return US dp->data; }
+static void
+exim_datum_data_set(EXIM_DATUM * dp, void * s)
+{ dp->data = s; }
+static unsigned
+exim_datum_size_get(EXIM_DATUM * dp)
+{ return dp->len; }
+static void
+exim_datum_size_set(EXIM_DATUM * dp, unsigned n)
+{ dp->len = n; }
+
+
+
+static inline void
+exim_datum_init(EXIM_DATUM * dp)
+{ dp->data = NULL; }                   /* compiler quietening */
+
+/* No free needed for a datum */
+
+static inline void
+exim_datum_free(EXIM_DATUM * dp)
+{ }
+
+/* size limit */
+
+# define EXIM_DB_RLIMIT        150
+
+
+
+
+
+
+#elif defined(USE_TDB)
+
+# if defined(USE_DB) || defined(USE_GDBM)
+#  error USE_TDB conflict with alternate definition
+# endif
 
 /* ************************* tdb interface ************************ */
 /*XXX https://manpages.org/tdb/3 mentions concurrent writes.
@@ -45,8 +349,7 @@ tdb_traverse to be called) */
 /* Access functions */
 
 /* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */
-FUNC_MAYBE_UNUSED
-static EXIM_DB *
+static inline EXIM_DB *
 exim_dbopen__(const uschar * name, const uschar * dirname, int flags,
   unsigned mode)
 {
@@ -54,8 +357,7 @@ return tdb_open(CS name, 0, TDB_DEFAULT, flags, mode);
 }
 
 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
-FUNC_MAYBE_UNUSED
-static BOOL
+static inline BOOL
 exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
 {
 *res = tdb_fetch(dbp, *key);   /* A struct arg and return!! */
@@ -63,14 +365,12 @@ return res->dptr != NULL;
 }
 
 /* EXIM_DBPUT - returns nothing useful, assumes replace mode */
-FUNC_MAYBE_UNUSED
-static int
+static inline int
 exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
 { return tdb_store(dbp, *key, *data, TDB_REPLACE); }
 
 /* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
-FUNC_MAYBE_UNUSED
-static int
+static inline int
 exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
 { return tdb_store(dbp, *key, *data, TDB_INSERT); }
 
@@ -80,14 +380,12 @@ exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
 # define EXIM_DBPUTB_DUP (-1)
 
 /* EXIM_DBDEL */
-FUNC_MAYBE_UNUSED
-static int
+static inline int
 exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key)
 { return tdb_delete(dbp, *key); }
 
 /* EXIM_DBCREATE_CURSOR - initialize for scanning operation */
-FUNC_MAYBE_UNUSED
-static EXIM_CURSOR *
+static inline EXIM_CURSOR *
 exim_dbcreate_cursor(EXIM_DB * dbp)
 {
 EXIM_CURSOR * c = store_malloc(sizeof(TDB_DATA));
@@ -98,8 +396,7 @@ return c;
 /* EXIM_DBSCAN - This is complicated because we have to free the last datum
 free() must not die when passed NULL */
 
-FUNC_MAYBE_UNUSED
-static BOOL
+static inline BOOL
 exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res, BOOL first,
   EXIM_CURSOR * cursor)
 {
@@ -110,48 +407,40 @@ return key->dptr != NULL;
 }
 
 /* EXIM_DBDELETE_CURSOR - terminate scanning operation. */
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_dbdelete_cursor(EXIM_CURSOR * cursor)
 { store_free(cursor); }
 
 /* EXIM_DBCLOSE */
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_dbclose__(EXIM_DB * db)
 { tdb_close(db); }
 
 /* Datum access */
 
-FUNC_MAYBE_UNUSED
-static uschar *
+static inline uschar *
 exim_datum_data_get(EXIM_DATUM * dp)
 { return US dp->dptr; }
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_data_set(EXIM_DATUM * dp, void * s)
 { dp->dptr = s; }
 
-FUNC_MAYBE_UNUSED
-static unsigned
+static inline unsigned
 exim_datum_size_get(EXIM_DATUM * dp)
-{ return dp->dsize; }
-FUNC_MAYBE_UNUSED
-static void
+{ return dp->len; }
+static inline void
 exim_datum_size_set(EXIM_DATUM * dp, unsigned n)
 { dp->dsize = n; }
 
 /* No initialization is needed. */
 
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_init(EXIM_DATUM * d)
 { }
 
 /* Free the stuff inside the datum. */
 
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_free(EXIM_DATUM * d)
 {
 free(d->dptr);
@@ -171,6 +460,10 @@ d->dptr = NULL;
 
 #elif defined USE_DB
 
+# if defined(USE_TDB) || defined(USE_GDBM)
+#  error USE_DB conflict with alternate definition
+# endif
+
 # include <db.h>
 
 /* 1.x did no locking
@@ -216,9 +509,13 @@ errors. This should help with debugging strange DB problems, e.g. getting "File
 exists" when you try to open a db file. The API for this function was changed
 at DB release 4.3. */
 
-static void
+static inline void
 dbfn_bdb_error_callback(const DB_ENV * dbenv, const char * pfx, const char * msg)
-{ log_write(0, LOG_MAIN, "Berkeley DB error: %s", msg); }
+{
+#ifndef MACRO_PREDEF 
+log_write(0, LOG_MAIN, "Berkeley DB error: %s", msg);
+#endif
+}
 
 
 
@@ -230,8 +527,7 @@ specified working dir, to avoid the DBCONFIG file trap. */
 
 #   define ENV_TO_DB(env) ((DB *)(((EXIM_DB *)env)->app_private))
 
-FUNC_MAYBE_UNUSED
-static EXIM_DB *
+static inline EXIM_DB *
 exim_dbopen__(const uschar * name, const uschar * dirname, int flags,
   unsigned mode)
 {
@@ -246,8 +542,10 @@ if (db_create(&b, dbp, 0) == 0)
   {
   dbp->app_private = b;
   if (b->open(b, NULL, CS name, NULL,
-             flags == O_RDONLY ? DB_UNKNOWN : DB_HASH,
-             flags == O_RDONLY ? DB_RDONLY : DB_CREATE,
+             flags & O_CREAT ? DB_HASH : DB_UNKNOWN,
+             flags & O_CREAT ? DB_CREATE
+             : flags & O_RDONLY ? DB_RDONLY
+             : 0,              /*XXX is there a writeable if exists option? */
              mode) == 0
          )
     return dbp;
@@ -259,8 +557,7 @@ return NULL;
 }
 
 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
-FUNC_MAYBE_UNUSED
-static BOOL
+static inline BOOL
 exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
 {
 DB * b = ENV_TO_DB(dbp);
@@ -268,8 +565,7 @@ return b->get(b, NULL, key, res, 0) == 0;
 }
 
 /* EXIM_DBPUT - returns nothing useful, assumes replace mode */
-FUNC_MAYBE_UNUSED
-static int
+static inline int
 exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
 {
 DB * b = ENV_TO_DB(dbp);
@@ -277,8 +573,7 @@ return b->put(b, NULL, key, data, 0);
 }
 
 /* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
-FUNC_MAYBE_UNUSED
-static int
+static inline int
 exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
 {
 DB * b = ENV_TO_DB(dbp);
@@ -291,8 +586,7 @@ return b->put(b, NULL, key, data, DB_NOOVERWRITE);
 #   define EXIM_DBPUTB_DUP DB_KEYEXIST
 
 /* EXIM_DBDEL */
-FUNC_MAYBE_UNUSED
-static int
+static inline int
 exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key)
 {
 DB * b = ENV_TO_DB(dbp);
@@ -301,8 +595,7 @@ return b->del(b, NULL, key, 0);
 
 /* EXIM_DBCREATE_CURSOR - initialize for scanning operation */
 
-FUNC_MAYBE_UNUSED
-static EXIM_CURSOR *
+static inline EXIM_CURSOR *
 exim_dbcreate_cursor(EXIM_DB * dbp)
 {
 DB * b = ENV_TO_DB(dbp);
@@ -312,8 +605,7 @@ return c;
 }
 
 /* EXIM_DBSCAN - returns TRUE if data is returned, FALSE at end */
-FUNC_MAYBE_UNUSED
-static BOOL
+static inline BOOL
 exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, BOOL first,
   EXIM_CURSOR * cursor)
 {
@@ -321,14 +613,12 @@ return cursor->c_get(cursor, key, data, first ? DB_FIRST : DB_NEXT) == 0;
 }
 
 /* EXIM_DBDELETE_CURSOR - terminate scanning operation */
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_dbdelete_cursor(EXIM_CURSOR * cursor)
 { cursor->c_close(cursor); }
 
 /* EXIM_DBCLOSE */
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_dbclose__(EXIM_DB * dbp_o)
 {
 DB_ENV * dbp = dbp_o;
@@ -339,34 +629,28 @@ dbp->close(dbp, DB_FORCESYNC);
 
 /* Datum access */
 
-FUNC_MAYBE_UNUSED
-static uschar *
+static inline uschar *
 exim_datum_data_get(EXIM_DATUM * dp)
 { return dp->data; }
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_data_set(EXIM_DATUM * dp, void * s)
 { dp->data = s; }
 
-FUNC_MAYBE_UNUSED
-static unsigned
+static inline unsigned
 exim_datum_size_get(EXIM_DATUM * dp)
 { return dp->size; }
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_size_set(EXIM_DATUM * dp, unsigned n)
 { dp->size = n; }
 
 /* The whole datum structure contains other fields that must be cleared
 before use, but we don't have to free anything after reading data. */
 
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_init(EXIM_DATUM * d)
 { memset(d, 0, sizeof(*d)); }
 
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_free(EXIM_DATUM * d)
 { }
 
@@ -386,8 +670,7 @@ exim_datum_free(EXIM_DATUM * d)
 /* Access functions */
 
 /* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */
-FUNC_MAYBE_UNUSED
-static EXIM_DB *
+static inline EXIM_DB *
 exim_dbopen__(const uschar * name, const uschar * dirname, int flags,
   unsigned mode)
 {
@@ -395,28 +678,27 @@ EXIM_DB * dbp;
 return db_create(&dbp, NULL, 0) == 0
   && (  dbp->set_errcall(dbp, dbfn_bdb_error_callback),
        dbp->open(dbp, CS name, NULL,
-         flags == O_RDONLY ? DB_UNKNOWN : DB_HASH,
-         flags == O_RDONLY ? DB_RDONLY : DB_CREATE,
+         flags & O_CREAT ? DB_HASH : DB_UNKNOWN,
+         flags & O_CREAT ? DB_CREATE
+         : flags & O_RDONLY ? DB_RDONLY
+         : 0,          /*XXX*/
          mode)
      ) == 0
   ? dbp : NULL;
 }
 
 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
-FUNC_MAYBE_UNUSED
-static BOOL
+static inline BOOL
 exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
 { return dbp->get(dbp, NULL, key, res, 0) == 0; }
 
 /* EXIM_DBPUT - returns nothing useful, assumes replace mode */
-FUNC_MAYBE_UNUSED
-static int
+static inline int
 exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
 { return dbp->put(dbp, NULL, key, data, 0); }
 
 /* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
-FUNC_MAYBE_UNUSED
-static int
+static inline int
 exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
 { return dbp->put(dbp, NULL, key, data, DB_NOOVERWRITE); }
 
@@ -426,15 +708,13 @@ exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
 #   define EXIM_DBPUTB_DUP DB_KEYEXIST
 
 /* EXIM_DBDEL */
-FUNC_MAYBE_UNUSED
-static int
+static inline int
 exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key)
 { return dbp->del(dbp, NULL, key, 0); }
 
 /* EXIM_DBCREATE_CURSOR - initialize for scanning operation */
 
-FUNC_MAYBE_UNUSED
-static EXIM_CURSOR *
+static inline EXIM_CURSOR *
 exim_dbcreate_cursor(EXIM_DB * dbp)
 {
 EXIM_CURSOR * c;
@@ -443,8 +723,7 @@ return c;
 }
 
 /* EXIM_DBSCAN - returns TRUE if data is returned, FALSE at end */
-FUNC_MAYBE_UNUSED
-static BOOL
+static inline BOOL
 exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, BOOL first,
   EXIM_CURSOR * cursor)
 {
@@ -452,47 +731,39 @@ return cursor->c_get(cursor, key, data, first ? DB_FIRST : DB_NEXT) == 0;
 }
 
 /* EXIM_DBDELETE_CURSOR - terminate scanning operation */
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_dbdelete_cursor(EXIM_CURSOR * cursor)
 { cursor->c_close(cursor); }
 
 /* EXIM_DBCLOSE */
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_dbclose__(EXIM_DB * dbp)
 { dbp->close(dbp, 0); }
 
 /* Datum access */
 
-FUNC_MAYBE_UNUSED
-static uschar *
+static inline uschar *
 exim_datum_data_get(EXIM_DATUM * dp)
 { return US dp->dptr; }
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_data_set(EXIM_DATUM * dp, void * s)
 { dp->dptr = s; }
 
-FUNC_MAYBE_UNUSED
-static uschar *
+static inline uschar *
 exim_datum_size_get(EXIM_DATUM * dp)
 { return US dp->size; }
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_size_set(EXIM_DATUM * dp, uschar * s)
 { dp->size = CS s; }
 
 /* The whole datum structure contains other fields that must be cleared
 before use, but we don't have to free anything after reading data. */
 
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_init(EXIM_DATUM * d)
 { memset(d, 0, sizeof(*d)); }
 
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_free(EXIM_DATUM * d)
 { }
 
@@ -522,6 +793,10 @@ exim_datum_free(EXIM_DATUM * d)
 #elif defined USE_GDBM
 /*XXX TODO: exim's locfile not needed */
 
+# if defined(USE_TDB) || defined(USE_DB)
+#  error USE_GDBM conflict with alternate definition
+# endif
+
 # include <gdbm.h>
 
 /* Basic DB type */
@@ -543,8 +818,7 @@ typedef struct {
 /* Access functions */
 
 /* EXIM_DBOPEN - return pointer to an EXIM_DB, NULL if failed */
-FUNC_MAYBE_UNUSED
-static EXIM_DB *
+static inline EXIM_DB *
 exim_dbopen__(const uschar * name, const uschar * dirname, int flags,
   unsigned mode)
 {
@@ -564,8 +838,7 @@ return NULL;
 }
 
 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
-FUNC_MAYBE_UNUSED
-static BOOL
+static inline BOOL
 exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
 {
 *res = gdbm_fetch(dbp->gdbm, *key);    /* A struct arg & return! */
@@ -573,14 +846,12 @@ return res->dptr != NULL;
 }
 
 /* EXIM_DBPUT - returns nothing useful, assumes replace mode */
-FUNC_MAYBE_UNUSED
-static int
+static inline int
 exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
 { return gdbm_store(dbp->gdbm, *key, *data, GDBM_REPLACE); }
 
 /* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
-FUNC_MAYBE_UNUSED
-static int
+static inline int
 exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
 { return gdbm_store(dbp->gdbm, *key, *data, GDBM_INSERT); }
 
@@ -590,20 +861,17 @@ exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
 # define EXIM_DBPUTB_DUP 1
 
 /* EXIM_DBDEL */
-FUNC_MAYBE_UNUSED
-static int
+static inline int
 exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key)
 { return gdbm_delete(dbp->gdbm, *key); }
 
 /* EXIM_DBCREATE_CURSOR - initialize for scanning operation (null) */
-FUNC_MAYBE_UNUSED
-static EXIM_CURSOR *
+static inline EXIM_CURSOR *
 exim_dbcreate_cursor(EXIM_DB * dbp)
 { return NULL; }
 
 /* EXIM_DBSCAN */
-FUNC_MAYBE_UNUSED
-static BOOL
+static inline BOOL
 exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, BOOL first,
   EXIM_CURSOR * cursor)
 {
@@ -615,14 +883,12 @@ return key->dptr != NULL;
 }
 
 /* EXIM_DBDELETE_CURSOR - terminate scanning operation (null). */
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_dbdelete_cursor(EXIM_CURSOR * cursor)
 { }
 
 /* EXIM_DBCLOSE */
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_dbclose__(EXIM_DB * dbp)
 {
 char * s;
@@ -633,38 +899,32 @@ free(dbp);
 
 /* Datum access types */
 
-FUNC_MAYBE_UNUSED
-static uschar *
+static inline uschar *
 exim_datum_data_get(EXIM_DATUM * dp)
 { return US dp->dptr; }
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_data_set(EXIM_DATUM * dp, void * s)
 { dp->dptr = s; }
 
-FUNC_MAYBE_UNUSED
-static unsigned
+static inline unsigned
 exim_datum_size_get(EXIM_DATUM * dp)
 { return dp->dsize; }
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_size_set(EXIM_DATUM * dp, unsigned n)
 { dp->dsize = n; }
 
 /* There's no clearing required before use, but we have to free the dptr
 after reading data. */
 
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_init(EXIM_DATUM * d)
 { }
 
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_free(EXIM_DATUM * d)
 { free(d->dptr); }
 
-/* size limit */
+/* size limit. GDBM is int-max limited, but we want to be less silly */
 
 # define EXIM_DB_RLIMIT        150
 
@@ -676,7 +936,7 @@ exim_datum_free(EXIM_DATUM * d)
 
 
 /* If none of USE_DB, USG_GDBM, or USE_TDB are set, the default is the NDBM
-interface */
+interface (which seems to be a wrapper for GDBM) */
 
 
 /********************* ndbm interface definitions **********************/
@@ -703,8 +963,7 @@ interface */
 a directory name; otherwise we would create the name.pag and
 name.dir files in the directory's parent. */
 
-FUNC_MAYBE_UNUSED
-static EXIM_DB *
+static inline EXIM_DB *
 exim_dbopen__(const uschar * name, const uschar * dirname, int flags,
   unsigned mode)
 {
@@ -719,8 +978,7 @@ return NULL;
 }
 
 /* EXIM_DBGET - returns TRUE if successful, FALSE otherwise */
-FUNC_MAYBE_UNUSED
-static BOOL
+static inline BOOL
 exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
 {
 *res = dbm_fetch(dbp, *key);   /* A struct arg & return! */
@@ -728,14 +986,12 @@ return res->dptr != NULL;
 }
 
 /* EXIM_DBPUT - returns nothing useful, assumes replace mode */
-FUNC_MAYBE_UNUSED
-static int
+static inline int
 exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
 { return dbm_store(dbp, *key, *data, DBM_REPLACE); }
 
 /* EXIM_DBPUTB - non-overwriting for use by dbmbuild */
-FUNC_MAYBE_UNUSED
-static int
+static inline int
 exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
 { return dbm_store(dbp, *key, *data, DBM_INSERT); }
 
@@ -745,20 +1001,17 @@ exim_dbputb(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
 # define EXIM_DBPUTB_DUP 1
 
 /* EXIM_DBDEL */
-FUNC_MAYBE_UNUSED
-static int
+static inline int
 exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key)
 { return dbm_delete(dbp, *key); }
 
 /* EXIM_DBCREATE_CURSOR - initialize for scanning operation (null) */
-FUNC_MAYBE_UNUSED
-static EXIM_CURSOR *
+static inline EXIM_CURSOR *
 exim_dbcreate_cursor(EXIM_DB * dbp)
 { return NULL; }
 
 /* EXIM_DBSCAN */
-FUNC_MAYBE_UNUSED
-static BOOL
+static inline BOOL
 exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, BOOL first,
   EXIM_CURSOR * cursor)
 {
@@ -767,47 +1020,39 @@ return key->dptr != NULL;
 }
 
 /* EXIM_DBDELETE_CURSOR - terminate scanning operation (null). */
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_dbdelete_cursor(EXIM_CURSOR * cursor)
 { }
 
 /* EXIM_DBCLOSE */
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_dbclose__(EXIM_DB * dbp)
 { dbm_close(dbp); }
 
 /* Datum access types */
 
-FUNC_MAYBE_UNUSED
-static uschar *
+static inline uschar *
 exim_datum_data_get(EXIM_DATUM * dp)
 { return US dp->dptr; }
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_data_set(EXIM_DATUM * dp, void * s)
 { dp->dptr = s; }
 
-FUNC_MAYBE_UNUSED
-static unsigned
+static inline unsigned
 exim_datum_size_get(EXIM_DATUM * dp)
 { return dp->dsize; }
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_size_set(EXIM_DATUM * dp, unsigned n)
 { dp->dsize = n; }
 
 /* There's no clearing required before use, and we don't have to free anything
 after reading data. */
 
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_init(EXIM_DATUM * d)
 { }
 
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_datum_free(EXIM_DATUM * d)
 { }
 
@@ -815,7 +1060,7 @@ exim_datum_free(EXIM_DATUM * d)
 
 # define EXIM_DB_RLIMIT        150
 
-#endif /* USE_GDBM */
+#endif /* !USE_GDBM */
 
 
 
@@ -823,27 +1068,25 @@ exim_datum_free(EXIM_DATUM * d)
 
 #if defined(COMPILE_UTILITY) || defined(MACRO_PREDEF)
 
-FUNC_MAYBE_UNUSED
-static EXIM_DB *
+static inline EXIM_DB *
 exim_dbopen(const uschar * name, const uschar * dirname, int flags,
   unsigned mode)
 {
 return exim_dbopen__(name, dirname, flags, mode);
 }
 
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_dbclose(EXIM_DB * dbp)
 { exim_dbclose__(dbp); }
 
-#else
+#else  /*  exim mainline code */
+
 /* Wrappers for open/close with debug tracing */
 
 extern void debug_printf_indent(const char *, ...);
-static BOOL is_tainted(const void *);
+static inline BOOL is_tainted(const void *);
 
-FUNC_MAYBE_UNUSED
-static EXIM_DB *
+static inline EXIM_DB *
 exim_dbopen(const uschar * name, const uschar * dirname, int flags,
   unsigned mode)
 {
@@ -867,18 +1110,19 @@ DEBUG(D_hints_lookup) debug_printf_indent("returned from EXIM_DBOPEN: %p\n", dbp
 return dbp;
 }
 
-FUNC_MAYBE_UNUSED
-static void
+static inline void
 exim_dbclose(EXIM_DB * dbp)
 {
 DEBUG(D_hints_lookup) debug_printf_indent("EXIM_DBCLOSE(%p)\n", dbp);
 exim_dbclose__(dbp);
 }
 
-# endif
+# endif                /* defined(COMPILE_UTILITY) || defined(MACRO_PREDEF) */
 
 /********************* End of dbm library definitions **********************/
 
 
 #endif /* whole file */
 /* End of hintsdb.h */
+/* vi: aw ai sw=2
+*/