From b929713e542bc8d0d69c0c8d87524e62633b6bda Mon Sep 17 00:00:00 2001
From: "Mirar (Pontus Hagland)" <pike@sort.mirar.org>
Date: Wed, 12 Apr 2000 13:16:31 +0200
Subject: [PATCH] glue for libmird (http://www.mirar.org/mird)

Rev: src/modules/Mird/Makefile.in:1.1
Rev: src/modules/Mird/acconfig.h:1.1
Rev: src/modules/Mird/configure.in:1.1
Rev: src/modules/Mird/mird_glue.c:1.1
Rev: src/modules/Mird/module.pmod.in:1.1
---
 .gitattributes                  |    3 +
 src/modules/Mird/Makefile.in    |   10 +
 src/modules/Mird/acconfig.h     |    1 +
 src/modules/Mird/configure.in   |   15 +
 src/modules/Mird/mird_glue.c    | 1026 +++++++++++++++++++++++++++++++
 src/modules/Mird/module.pmod.in |  352 +++++++++++
 6 files changed, 1407 insertions(+)
 create mode 100644 src/modules/Mird/Makefile.in
 create mode 100644 src/modules/Mird/acconfig.h
 create mode 100644 src/modules/Mird/configure.in
 create mode 100644 src/modules/Mird/mird_glue.c
 create mode 100644 src/modules/Mird/module.pmod.in

diff --git a/.gitattributes b/.gitattributes
index 93b07f8cff..40979671b6 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -281,6 +281,9 @@ testfont binary
 /src/modules/Math/configure.in foreign_ident
 /src/modules/Math/math_matrix.c foreign_ident
 /src/modules/Math/math_module.c foreign_ident
+/src/modules/Mird/Makefile.in foreign_ident
+/src/modules/Mird/acconfig.h foreign_ident
+/src/modules/Mird/configure.in foreign_ident
 /src/modules/Msql/Makefile.in foreign_ident
 /src/modules/Msql/configure.in foreign_ident
 /src/modules/Msql/msql_config.h.in foreign_ident
diff --git a/src/modules/Mird/Makefile.in b/src/modules/Mird/Makefile.in
new file mode 100644
index 0000000000..56d54fb50b
--- /dev/null
+++ b/src/modules/Mird/Makefile.in
@@ -0,0 +1,10 @@
+# $Id: Makefile.in,v 1.1 2000/04/12 11:16:31 mirar Exp $
+@make_variables@
+VPATH=@srcdir@:@srcdir@/../..:../..
+OBJS=mird_glue.o
+MODULE_LDFLAGS=@LDFLAGS@ @LIBS@
+
+CONFIG_HEADERS=@CONFIG_HEADERS@
+
+@dynamic_module_makefile@
+@dependencies@
diff --git a/src/modules/Mird/acconfig.h b/src/modules/Mird/acconfig.h
new file mode 100644
index 0000000000..c539959e25
--- /dev/null
+++ b/src/modules/Mird/acconfig.h
@@ -0,0 +1 @@
+/* $Id: acconfig.h,v 1.1 2000/04/12 11:16:31 mirar Exp $ */
diff --git a/src/modules/Mird/configure.in b/src/modules/Mird/configure.in
new file mode 100644
index 0000000000..2f68e2e971
--- /dev/null
+++ b/src/modules/Mird/configure.in
@@ -0,0 +1,15 @@
+# $Id: configure.in,v 1.1 2000/04/12 11:16:31 mirar Exp $
+AC_INIT(mird_glue.c)
+AC_CONFIG_HEADER(config.h)
+
+AC_MODULE_INIT()
+
+AC_CHECK_HEADERS(mird.h)
+AC_CHECK_LIB(mird,mird_sync)
+
+if test "x$ac_cv_header_mird_h" != "xyes" -o "x$ac_cv_lib_mird_mird_sync" != "xyes"; then
+    echo '*** Mird module failed to find libmird' >&2
+    echo '*** If you want Mird support, install libmird (http://www.mirar.org/mird)' >&2
+fi
+
+AC_OUTPUT(Makefile,echo FOO >stamp-h )
diff --git a/src/modules/Mird/mird_glue.c b/src/modules/Mird/mird_glue.c
new file mode 100644
index 0000000000..35ecdb4f98
--- /dev/null
+++ b/src/modules/Mird/mird_glue.c
@@ -0,0 +1,1026 @@
+#include "global.h"
+#include "config.h"
+
+#include "program.h"
+#include "module_support.h"
+#include "interpret.h"
+#include "stralloc.h"
+#include "mapping.h"
+#include "array.h"
+#include "object.h"
+
+#ifdef HAVE_MIRD_H
+#ifdef HAVE_LIBMIRD
+#define HAVE_MIRD
+#endif
+#endif
+
+#ifdef HAVE_MIRD
+
+#include "mird.h"
+
+struct program *mird_program;
+struct program *mird_transaction_program;
+struct program *mird_scanner_program;
+
+#define THISOBJ (fp->current_object)
+
+#define TRY(X) \
+   do { MIRD_RES res; if ( (res=(X)) ) pmird_exception(res); } while (0)
+
+#define LOCK(PMIRD)
+#define UNLOCK(PMIRD)
+
+/**** utilities ************************************/
+
+static void pmird_exception(MIRD_RES res)
+{
+   char *s,*d;
+   mird_describe_error(res,&s);
+   d=alloca(strlen(s)+1);
+   memcpy(d,s,strlen(s)+1);
+   error("[mird] %s\n",s);
+}
+
+static void pmird_no_database(void)
+{
+   error("database is not open\n");
+}
+
+static void pmird_tr_no_database(void)
+{
+   error("no database connected to the transaction\n");
+}
+
+static void pmird_no_transaction(void)
+{
+   error("transaction is already closed\n");
+}
+
+/**** main program *********************************/
+
+struct pmird_storage
+{
+   struct mird *db;
+};
+
+#define THIS ((struct pmird_storage*)(fp->current_storage))
+
+static void init_pmird(struct object *o)
+{
+   THIS->db=NULL;
+}
+
+static void exit_pmird(struct object *o)
+{
+   if (THIS->db)
+   {
+      mird_free_structure(THIS->db);
+      THIS->db=NULL;
+   }
+}
+
+/*
+**! module Mird
+**! submodule Glue
+**! class Mird
+**! method void create(string filename)
+**! method void create(string filename,mapping options)
+**!
+**!	<data_description type=mapping>
+**!
+**!	<elem type=string name=flags>database flags, see below</elem>
+**!
+**!     <elem type=int name=block_size>
+**!           database block size; must be even 2^n
+**!           database size is limited to 2^32 blocks, ie block_size*2^32 
+**!           (8192*2^(32-5) is approximately 1Tb)
+**!           (2048)
+**!     </elem>
+**!        
+**!     <elem type=int name=frag_bits>
+**!           this sets the number of frags in a fragmented block,
+**!           and address bits to locate them.
+**!           the number of frags in a fragmented block is equal
+**!           to 2^frag_bits-1, per default 2^5-1=32-1=31
+**!          
+**!           This steals bits from the 32 bit address used to point out
+**!           a chunk.
+**!           (5)
+**!     </elem>
+**!     
+**!     <elem type=int name=hashtrie_bits>
+**!           this is the number of bits in each hash-trie hash; 
+**!           4*2^n must be a lot less then block_size
+**!           (5)
+**!     </elem>
+**!     
+**!     <elem type=int name=cache_size>
+**!           this is the number of blocks cached from the database;
+**!           this is used for both read- and write cache.
+**!           Note that the memory usage is constant, 
+**!           approximately block_size*cache_size bytes.
+**!           (32)
+**!     </elem>
+**!     
+**!     <elem type=int name=cache_search_length>
+**!           this is the closed hash maximum search length to find
+**!           a block in the cache; note that this will result in 
+**!           linear time usage.
+**!           (8)
+**!     </elem>
+**!     
+**!     <elem type=int name=max_free_frag_blocks>
+**!           this is how many blocks with free space that is 
+**!           kept in a list to be used when a transaction
+**!           is running. The closest fit of usage of these blocks are
+**!           where the data will be stored.
+**!           (10)
+**!     </elem>
+**!     
+**!     <elem type=int name=file_mode>
+**!           this is the default mode the files are opened with;
+**!           although affected by UMASK
+**!           (0666)
+**!     </elem>
+**!     
+**!     <elem type=int name=journal_readback_n>
+**!           this is how many journal entries are read back
+**!           at once at transaction finish, cancel or copy time
+**!           bytes needed is approximately 24*this number
+**!           (200)
+**!     </elem>
+**!	</data_description>
+**! 
+**! 	<pre>flags is a string with any of these characters:
+**! 	"r" - readonly
+**! 	"n" - don't create if it doesn't exist
+**! 	"x" - exclusive (always create)
+**! 	"s" - call sync(2) after fsync
+**! 	"j" - complain if journal file is gone missing
+**! 	</pre>
+**!
+*/
+
+static void pmird_create(INT32 args)
+{
+   if (args<1)
+      SIMPLE_TOO_FEW_ARGS_ERROR("Mird",1);
+   if (sp[-args].type!=T_STRING ||
+       sp[-args].u.string->size_shift)
+      SIMPLE_BAD_ARG_ERROR("Mird",1,"8 bit string");
+
+LOCK(THIS);
+   if (THIS->db)
+      mird_free_structure(THIS->db);
+
+   TRY(mird_initialize(sp[-args].u.string->str,&(THIS->db)));
+
+/*    THIS->db->hashtrie_bits=4; */
+   
+   /* options here */
+
+   TRY(mird_open(THIS->db));
+UNLOCK(THIS);
+
+   pop_n_elems(args);
+   push_int(0);
+}
+
+/*
+**! method void close()
+**! method void destroy()
+**!	This closes the database, ie
+**!	<ol>
+**!	<li>cancels all ongoing transactions
+**!	<li>syncs the database (flushes caches,
+**!	and frees all unused blocks)
+**!	<li>destroys the database object
+**!	</ol>
+**!	Call this or destroy the database object
+**!	when you want the database closed.
+*/
+
+static void pmird_close(INT32 args)
+{
+   MIRD_RES res;
+   pop_n_elems(args);
+
+   if (THIS->db)
+   {
+LOCK(THIS);
+      res=mird_close(THIS->db);
+      if (res) mird_free_structure(THIS->db);
+      THIS->db=NULL;
+      if (res) pmird_exception(res);
+UNLOCK(THIS);
+   }
+
+   push_int(0);
+}
+
+/* 
+**! method object sync()
+**! method object sync_please()
+**!	Syncs the database; this flushes all
+**!	eventual caches and frees all unused blocks.
+**!
+**!	sync() can only be called when there is no
+**!	ongoing transactions. sync_please() sets
+**!	a marker that the database should be synced
+**!	when the last transaction is finished or cancelled,
+**!	which will eventually sync the database.
+**!	
+**!	The usage could be in a call_out-loop,
+**!	<pre>
+**!	   [...]
+**!	   call_out(sync_the_database,5*60);	
+**!	   [...]
+**!	void sync_the_database()
+**!	{
+**!	   call_out(sync_please,5*60);
+**!	   my_mird->sync_please();
+**!	}
+**!	</pre>
+**!
+**! returns the called object
+*/
+
+static void pmird_sync(INT32 args)
+{
+   pop_n_elems(args);
+
+   if (!THIS->db) pmird_no_database();
+
+   LOCK(THIS);
+   TRY(mird_sync(THIS->db));
+   UNLOCK(THIS);
+
+   ref_push_object(THISOBJ);
+}
+
+static void pmird_sync_please(INT32 args)
+{
+   pop_n_elems(args);
+
+   if (!THIS->db) pmird_no_database();
+
+   LOCK(THIS);
+   TRY(mird_sync_please(THIS->db));
+   UNLOCK(THIS);
+
+   ref_push_object(THISOBJ);
+}
+
+/*
+**! method zero|string fetch(int table_id,int|string key)
+**! returns the data value or zero_type if key wasn't found
+*/
+
+static void pmird_fetch(INT32 args)
+{
+   INT_TYPE hashkey,table_id;
+   struct pike_string *stringkey;
+   struct mird *db=THIS->db;
+   unsigned char *data;
+   mird_size_t len;
+
+   if (args<2)
+      SIMPLE_TOO_FEW_ARGS_ERROR("store",2);
+
+   if (!THIS->db) return pmird_no_transaction();
+
+   if (sp[1-args].type==T_INT)
+   {
+      get_all_args("fetch",args,"%i%i",&table_id,&hashkey);
+LOCK(THIS);
+      TRY(mird_key_lookup(db,(mird_key_t)table_id,
+			  (mird_key_t)hashkey,
+			  &data,
+			  &len));
+UNLOCK(THIS);
+   }
+   else if (sp[1-args].type==T_STRING)
+   {
+      get_all_args("fetch",args,"%i%S",&table_id,&stringkey);
+LOCK(THIS);
+      TRY(mird_s_key_lookup(db,(mird_key_t)table_id,
+			    (unsigned char*)(stringkey->str),
+			    (mird_size_t)(stringkey->len),
+			    &data,
+			    &len));
+UNLOCK(THIS);
+   }
+   else
+      SIMPLE_BAD_ARG_ERROR("fetch",2,"int|string");
+   
+   pop_n_elems(args);
+
+   if (data)
+      push_string(make_shared_binary_string(data,len));
+   else
+   {
+      push_int(0);
+      sp[-1].subtype=NUMBER_UNDEFINED;
+   }
+   mird_free(data);
+}
+
+/*
+**! method void _debug_cut()
+**!	This closes the database without
+**!	flushing or syncing. This should never be used and
+**!	exist only for testing purposes.
+*/
+
+static void pmird__debug_cut(INT32 args)
+{
+   if (THIS->db)
+   {
+      mird_free_structure(THIS->db);
+      THIS->db=NULL;
+   }
+   
+   pop_n_elems(args);
+   push_int(0);
+}
+
+/*
+**! method void _debug_check_free()
+**! method void _debug_check_free(int(0..1) silent)
+**!	This syncs the database and verifies
+**!	the database free list. It prints stuff
+**!	on stderr. It exists only for debug
+**!	purpose and has no other use.
+*/
+
+static void pmird__debug_check_free(INT32 args)
+{
+   int silent=0;
+   if (sp[-args].type==T_INT &&
+       sp[-args].u.integer) silent=1;
+   if (!THIS->db) pmird_no_database();
+   TRY(mird_sync(THIS->db));
+   mird_debug_check_free(THIS->db,silent);
+   
+   pop_n_elems(args);
+   push_int(0);
+}
+
+/**** transaction program ***************************/
+
+struct pmtr_storage
+{
+   struct mird_transaction *mtr;
+   struct object *dbobj;
+   struct pmird_storage *parent;
+};
+
+#undef THIS
+#define THIS ((struct pmtr_storage*)(fp->current_storage))
+
+static void init_pmtr(struct object *o)
+{
+   THIS->mtr=NULL;
+   THIS->dbobj=NULL;
+}
+
+static void exit_pmtr(struct object *o)
+{
+   if (THIS->mtr)
+   {
+      mird_tr_free(THIS->mtr);
+      THIS->mtr=NULL;
+   }
+   if (THIS->dbobj)
+   {
+      free_object(THIS->dbobj);
+      THIS->dbobj=NULL;
+   }
+}
+
+/*
+**! class Transaction
+**!	A transaction object is enclosing a change in the database.
+**!	It can either succeed in full or fail in full.
+**!	If some other transaction has changed the same data as
+**!	the current, the transaction closed last will fail (conflict).
+**!	
+**! method void create(Mird parent)
+**!	Creates a new transaction within the given database.
+*/
+
+static void pmtr_create(INT32 args)
+{
+   struct pmird_storage *pmird;
+
+   if (args<1)
+      SIMPLE_TOO_FEW_ARGS_ERROR("Transaction",1);
+   
+   if ( !(pmird=(struct pmird_storage*)
+	  get_storage(sp[-args].u.object,mird_program)) )
+      SIMPLE_BAD_ARG_ERROR("Transaction",1,"Mird object");
+
+   add_ref(sp[-args].u.object);
+   THIS->dbobj=sp[-args].u.object;
+   THIS->parent=pmird;
+
+   if (!pmird->db) pmird_no_database();
+
+   THIS->mtr=NULL;
+LOCK(THIS->parent);
+   TRY(mird_transaction_new(pmird->db,&(THIS->mtr)));
+UNLOCK(THIS->parent);
+
+   pop_n_elems(args);
+   push_int(0);
+}
+
+/*
+**! method void close()
+**!	closes a transaction; may cast exceptions
+**!	if there are conflicts
+*/
+
+static void pmtr_close(INT32 args)
+{
+   struct mird_transaction *mtr;
+
+   pop_n_elems(args);
+
+   if (!THIS->mtr) return pmird_no_transaction();
+   if (!THIS->mtr->db) return pmird_tr_no_database();
+
+   mtr=THIS->mtr;
+LOCK(THIS->parent);
+   TRY(mird_transaction_close(mtr));
+UNLOCK(THIS->parent);
+   THIS->mtr=NULL;
+
+   ref_push_object(THISOBJ);
+}
+
+/*
+**! method void cancel()
+**! method void destroy()
+**!	cancels (rewinds) a transaction
+*/
+
+static void pmtr_cancel(INT32 args)
+{
+   pop_n_elems(args);
+
+   if (!THIS->mtr) return pmird_no_transaction();
+   if (!THIS->mtr->db) return pmird_tr_no_database();
+
+LOCK(THIS->parent);
+   TRY(mird_transaction_cancel(THIS->mtr));
+UNLOCK(THIS->parent);
+   THIS->mtr=NULL;
+
+   ref_push_object(THISOBJ);
+}
+
+static void pmtr_destroy(INT32 args)
+{
+   pop_n_elems(args);
+
+   if (THIS->mtr &&
+       THIS->mtr->db)
+   {
+LOCK(THIS->parent);
+      TRY(mird_transaction_cancel(THIS->mtr));
+UNLOCK(THIS->parent);
+      THIS->mtr=NULL;
+   }
+   else if (THIS->mtr)
+   {
+      mird_tr_free(THIS->mtr);
+      THIS->mtr=NULL;
+   }
+
+   ref_push_object(THISOBJ);
+}
+
+/*
+**! method object resolve()
+**!	Tries to resolve a transaction; 
+**!	casts an exception if there is a conflict.
+**!	May be called more then once.
+**! returns the called object
+*/
+
+static void pmtr_resolve(INT32 args)
+{
+   pop_n_elems(args);
+
+   if (!THIS->mtr) return pmird_no_transaction();
+   if (!THIS->mtr->db) return pmird_tr_no_database();
+
+LOCK(THIS->parent);
+   TRY(mird_tr_resolve(THIS->mtr));
+UNLOCK(THIS->parent);
+
+   ref_push_object(THISOBJ);
+}
+
+/*
+**! method object store(int table_id,int|string key,string data)
+*/
+
+static void pmtr_store(INT32 args)
+{
+   INT_TYPE hashkey,table_id;
+   struct pike_string *stringkey;
+   struct pike_string *data;
+   struct mird_transaction *mtr=THIS->mtr;
+
+   if (args<3)
+      SIMPLE_TOO_FEW_ARGS_ERROR("store",3);
+
+   if (!THIS->mtr) return pmird_no_transaction();
+   if (!THIS->mtr->db) return pmird_tr_no_database();
+
+   if (sp[1-args].type==T_INT)
+   {
+      get_all_args("store",args,"%i%i%S",&table_id,&hashkey,&data);
+LOCK(THIS->parent);
+      TRY(mird_key_store(mtr,(mird_key_t)table_id,
+			 (mird_key_t)hashkey,
+			 (unsigned char*)(data->str),
+			 (mird_size_t)(data->len)));
+UNLOCK(THIS->parent);
+   }
+   else if (sp[1-args].type==T_STRING)
+   {
+      get_all_args("store",args,"%i%S%S",&table_id,&stringkey,&data);
+LOCK(THIS->parent);
+      TRY(mird_s_key_store(mtr,(mird_key_t)table_id,
+			   (unsigned char*)(stringkey->str),
+			   (mird_size_t)(stringkey->len),
+			   (unsigned char*)(data->str),
+			   (mird_size_t)(data->len)));
+UNLOCK(THIS->parent);
+   }
+   else
+      SIMPLE_BAD_ARG_ERROR("store",2,"int|string");
+   
+   pop_n_elems(args);
+   ref_push_object(THISOBJ);
+}
+
+/*
+**! method object delete(int table_id,int|string key)
+*/
+
+static void pmtr_delete(INT32 args)
+{
+   INT_TYPE hashkey,table_id;
+   struct pike_string *stringkey;
+   struct mird_transaction *mtr=THIS->mtr;
+
+   if (args<2)
+      SIMPLE_TOO_FEW_ARGS_ERROR("store",2);
+
+   if (!THIS->mtr) return pmird_no_transaction();
+   if (!THIS->mtr->db) return pmird_tr_no_database();
+
+   if (sp[1-args].type==T_INT)
+   {
+      get_all_args("delete",args,"%i%i",&table_id,&hashkey);
+LOCK(THIS->parent);
+      TRY(mird_key_store(mtr,(mird_key_t)table_id,
+			 (mird_key_t)hashkey,
+			 NULL,0));
+UNLOCK(THIS->parent);
+   }
+   else if (sp[1-args].type==T_STRING)
+   {
+      get_all_args("delete",args,"%i%S",&table_id,&stringkey);
+LOCK(THIS->parent);
+      TRY(mird_s_key_store(mtr,(mird_key_t)table_id,
+			   (unsigned char*)(stringkey->str),
+			   (mird_size_t)(stringkey->len),
+			   NULL,0));
+UNLOCK(THIS->parent);
+   }
+   else
+      SIMPLE_BAD_ARG_ERROR("delete",2,"int|string");
+   
+   pop_n_elems(args);
+   ref_push_object(THISOBJ);
+}
+
+/*
+**! method zero|string fetch(int table_id,int|string key)
+**! returns the data value or zero_type if key wasn't found
+*/
+
+static void pmtr_fetch(INT32 args)
+{
+   INT_TYPE hashkey,table_id;
+   struct pike_string *stringkey;
+   struct mird_transaction *mtr=THIS->mtr;
+   unsigned char *data;
+   mird_size_t len;
+
+   if (args<2)
+      SIMPLE_TOO_FEW_ARGS_ERROR("store",2);
+
+   if (!THIS->mtr) return pmird_no_transaction();
+   if (!THIS->mtr->db) return pmird_tr_no_database();
+
+   if (sp[1-args].type==T_INT)
+   {
+      get_all_args("fetch",args,"%i%i",&table_id,&hashkey);
+LOCK(THIS->parent);
+      TRY(mird_transaction_key_lookup(mtr,(mird_key_t)table_id,
+				      (mird_key_t)hashkey,
+				      &data,
+				      &len));
+UNLOCK(THIS->parent);
+   }
+   else if (sp[1-args].type==T_STRING)
+   {
+      get_all_args("fetch",args,"%i%S",&table_id,&stringkey);
+LOCK(THIS->parent);
+      TRY(mird_transaction_s_key_lookup(mtr,(mird_key_t)table_id,
+					(unsigned char*)(stringkey->str),
+					(mird_size_t)(stringkey->len),
+					&data,
+					&len));
+UNLOCK(THIS->parent);
+   }
+   else
+      SIMPLE_BAD_ARG_ERROR("fetch",2,"int|string");
+   
+   pop_n_elems(args);
+
+   if (data)
+      push_string(make_shared_binary_string(data,len));
+   else
+   {
+      push_int(0);
+      sp[-1].subtype=NUMBER_UNDEFINED;
+   }
+   mird_free(data);
+}
+
+/*
+**! method object new_hashkey_table(int table_id);
+**! method object new_stringkey_table(int table_id);
+**!	creates a table in the database
+*/
+
+static void pmtr_new_hashkey_table(INT32 args)
+{
+   INT_TYPE table_id;
+
+   get_all_args("new_hashkey_table",args,"%i",&table_id);
+
+   if (!THIS->mtr) return pmird_no_transaction();
+   if (!THIS->mtr->db) return pmird_tr_no_database();
+
+LOCK(THIS->parent);
+   TRY(mird_key_new_table(THIS->mtr,(mird_key_t)table_id));
+UNLOCK(THIS->parent);
+
+   pop_n_elems(args);
+   ref_push_object(THISOBJ);
+}
+
+static void pmtr_new_stringkey_table(INT32 args)
+{
+   INT_TYPE table_id;
+
+   get_all_args("new_hashkey_table",args,"%i",&table_id);
+
+   if (!THIS->mtr) return pmird_no_transaction();
+   if (!THIS->mtr->db) return pmird_tr_no_database();
+
+LOCK(THIS->parent);
+   TRY(mird_s_key_new_table(THIS->mtr,(mird_key_t)table_id));
+UNLOCK(THIS->parent);
+
+   pop_n_elems(args);
+   ref_push_object(THISOBJ);
+}
+
+/*
+**! method object delete_table(int table_id)
+**!	delets a table from the database
+**! note 
+**!	this can take some time, depending on how
+**!	much data that is in that table
+*/
+
+static void pmtr_delete_table(INT32 args)
+{
+   INT_TYPE table_id;
+
+   get_all_args("delete_table",args,"%i",&table_id);
+
+   if (!THIS->mtr) return pmird_no_transaction();
+   if (!THIS->mtr->db) return pmird_tr_no_database();
+  
+   LOCK(THIS->parent);
+   TRY(mird_delete_table(THIS->mtr,(mird_key_t)table_id));
+   UNLOCK(THIS->parent);
+}
+
+/*
+**! method object depend_table(int table_id)
+*/
+
+static void pmtr_depend_table(INT32 args)
+{
+   INT_TYPE table_id;
+
+   get_all_args("depend_table",args,"%i",&table_id);
+
+   if (!THIS->mtr) return pmird_no_transaction();
+   if (!THIS->mtr->db) return pmird_tr_no_database();
+  
+   LOCK(THIS->parent);
+   TRY(mird_depend_table(THIS->mtr,(mird_key_t)table_id));
+   UNLOCK(THIS->parent);
+}
+
+/**** hashkey table scanner ************************/
+
+#undef THIS
+#define THIS ((struct pmts_storage*)(fp->current_storage))
+
+struct pmts_storage
+{
+   struct mird_scan_result *msr;
+   struct mird_s_scan_result *mssr;
+   struct object *obj;
+   struct pmird_storage *pmird;
+   struct pmtr_storage *pmtr;
+   mird_key_t table_id;
+};
+
+static void init_pmts(struct object *o)
+{
+   THIS->msr=NULL;
+   THIS->mssr=NULL;
+   THIS->obj=NULL;
+}
+
+static void exit_pmts(struct object *o)
+{
+   if (THIS->msr) mird_free_scan_result(THIS->msr);
+   if (THIS->mssr) mird_free_s_scan_result(THIS->mssr);
+   THIS->msr=NULL;
+   THIS->mssr=NULL;
+   if (THIS->obj) free_object(THIS->obj);
+   THIS->obj=0;
+}
+
+/*
+**! class Scanner
+**!	Objects of this class is used to read off all
+**!	contents of a table in the database.
+**!
+**! method void create(Mird database,int table_id)
+**! method void create(Transaction transaction,int table_id)
+**!	Creates a new scanner object, tuned to the
+**!	given table and database or transaction.
+*/
+
+static void pmts_create(INT32 args)
+{
+   struct pmird_storage *pmird;
+   struct pmtr_storage *pmtr;
+
+   if (args<2)
+      SIMPLE_TOO_FEW_ARGS_ERROR("Scanner",2);
+
+   exit_pmts(THISOBJ);
+
+   pmird=(struct pmird_storage*)
+      get_storage(sp[-args].u.object,mird_program);
+   pmtr=(struct pmtr_storage*)
+      get_storage(sp[-args].u.object,mird_transaction_program);
+
+   if (!pmird && !pmtr)
+      SIMPLE_BAD_ARG_ERROR("Scanner",1,"Mird|Transaction");
+
+   if (sp[1-args].type!=T_INT)
+      SIMPLE_BAD_ARG_ERROR("Scanner",2,"int");
+
+   add_ref(sp[-args].u.object);
+   THIS->obj=sp[-args].u.object;
+   THIS->pmird=pmird;
+   THIS->pmtr=pmtr;
+
+   THIS->table_id=(mird_key_t)sp[1-args].u.integer;
+
+   pop_n_elems(args);
+   push_int(0);
+}
+
+/*
+**! method zero|mapping(string|int:string) read(int n)
+**!	Reads some tupels from the table the scanner
+**!	is directed against; the size of the resulting
+**!	mapping is close to this number.
+**!
+**! returns a mapping of the next (about) n tupels in the table, or zero if there is no more tupels in the table
+**!
+**! note
+**!	a buffer directly depending on this size is allocated;
+**!	it's not recommended doing a "read(0x7fffffff)".
+*/
+
+static void pmts_read(INT32 args)
+{
+   INT_TYPE n;
+   MIRD_RES res;
+   mird_size_t i;
+
+   get_all_args("read",args,"%+",&n);
+
+   if (THIS->pmird && !THIS->pmird->db) pmird_no_database();
+   if (THIS->pmtr && !THIS->pmtr->mtr) pmird_no_transaction();
+   if (THIS->pmtr && !THIS->pmtr->parent->db) pmird_tr_no_database();
+   
+   if (THIS->msr)
+   {
+      if (THIS->pmird)
+	 res=mird_table_scan(THIS->pmird->db,THIS->table_id,(mird_size_t)n,
+			     THIS->msr,&(THIS->msr));
+      else /* pmtr */
+	 res=mird_transaction_table_scan(THIS->pmtr->mtr,
+					 THIS->table_id,(mird_size_t)n,
+					 THIS->msr,&(THIS->msr));
+   }
+   else if (THIS->mssr)
+   {
+      if (THIS->pmird)
+	 res=mird_s_table_scan(THIS->pmird->db,THIS->table_id,(mird_size_t)n,
+			       THIS->mssr,&(THIS->mssr));
+      else /* pmtr */
+	 res=mird_transaction_s_table_scan(THIS->pmtr->mtr,
+					   THIS->table_id,(mird_size_t)n,
+					   THIS->mssr,&(THIS->mssr));
+   }
+   else /* chance */
+   {
+      if (THIS->pmird)
+	 res=mird_table_scan(THIS->pmird->db,THIS->table_id,(mird_size_t)n,
+			     NULL,&(THIS->msr));
+      else /* pmtr */
+	 res=mird_transaction_table_scan(THIS->pmtr->mtr,
+					 THIS->table_id,(mird_size_t)n,
+					 NULL,&(THIS->msr));
+      if (res && res->error_no==MIRDE_WRONG_TABLE) 
+      {
+	 mird_free_error(res);
+	 /* it's a string table, then */
+
+	 if (THIS->pmird)
+	    res=mird_s_table_scan(THIS->pmird->db,THIS->table_id,
+				  (mird_size_t)n,THIS->mssr,&(THIS->mssr));
+	 else /* pmtr */
+	    res=mird_transaction_s_table_scan(THIS->pmtr->mtr,
+					      THIS->table_id,(mird_size_t)n,
+					      THIS->mssr,&(THIS->mssr));
+      }
+   }
+   if (res) pmird_exception(res);
+
+   pop_n_elems(args);
+
+   if (THIS->msr)
+   {
+      for (i=0; i<THIS->msr->n; i++)
+      {
+	 push_int((INT_TYPE)(THIS->msr->tupel[i].key));
+	 push_string(make_shared_binary_string(
+	    THIS->msr->tupel[i].value,
+	    THIS->msr->tupel[i].value_len));
+      }
+      f_aggregate_mapping(THIS->msr->n*2);
+   }
+   else if (THIS->mssr)
+   {
+      for (i=0; i<THIS->mssr->n; i++)
+      {
+	 push_string(make_shared_binary_string(
+	    THIS->mssr->tupel[i].key,
+	    THIS->mssr->tupel[i].key_len));
+	 push_string(make_shared_binary_string(
+	    THIS->mssr->tupel[i].value,
+	    THIS->mssr->tupel[i].value_len));
+      }
+      f_aggregate_mapping(THIS->mssr->n*2);
+   }
+   else /* eod */
+      push_int(0);
+}
+
+/**** module stuff *********************************/
+
+void pike_module_init(void)
+{
+#if 0
+   struct program *p;
+   struct object *o;
+#endif
+
+   /* main program */
+
+   start_new_program();
+
+   ADD_STORAGE(struct pmird_storage);
+   set_init_callback(init_pmird);
+   set_exit_callback(exit_pmird);
+
+   ADD_FUNCTION("create",pmird_create,
+		tFunc(tStr tOr(tVoid,tMapping),tVoid),0);
+   ADD_FUNCTION("close",pmird_close,tFunc(tNone,tVoid),0);
+   ADD_FUNCTION("destroy",pmird_close,tFunc(tNone,tVoid),0);
+   ADD_FUNCTION("sync",pmird_sync,tFunc(tNone,tVoid),0);
+   ADD_FUNCTION("sync_please",pmird_sync_please,tFunc(tNone,tVoid),0);
+   ADD_FUNCTION("fetch",pmird_fetch,tFunc(tInt tOr(tInt,tStr),tOr(tStr,tInt0)),0);
+
+   ADD_FUNCTION("_debug_cut",pmird__debug_cut,tFunc(tNone,tVoid),0);
+   ADD_FUNCTION("_debug_check_free",pmird__debug_check_free,
+		tFunc(tNone,tVoid),0);
+
+   mird_program=end_program();
+
+   /* transaction program */
+
+   start_new_program();
+   ADD_STORAGE(struct pmtr_storage);
+   set_init_callback(init_pmtr);
+   set_exit_callback(exit_pmtr);
+
+   ADD_FUNCTION("create",pmtr_create,tFunc(tObj,tVoid),0);
+   ADD_FUNCTION("cancel",pmtr_cancel,tFunc(tNone,tVoid),0);
+   ADD_FUNCTION("destroy",pmtr_destroy,tFunc(tNone,tVoid),0);
+   ADD_FUNCTION("close",pmtr_close,tFunc(tNone,tVoid),0);
+   ADD_FUNCTION("resolve",pmtr_resolve,tFunc(tNone,tObj),0);
+
+   ADD_FUNCTION("store",pmtr_store,tFunc(tInt tOr(tInt,tStr) tStr,tObj),0);
+   ADD_FUNCTION("delete",pmtr_delete,tFunc(tInt tOr(tInt,tStr),tObj),0);
+   ADD_FUNCTION("fetch",pmtr_fetch,tFunc(tInt tOr(tInt,tStr),
+					 tOr(tStr,tInt0)),0);
+   ADD_FUNCTION("delete_table",pmtr_delete_table,tFunc(tInt,tObj),0);
+   ADD_FUNCTION("depend_table",pmtr_depend_table,tFunc(tInt,tObj),0);
+
+   ADD_FUNCTION("new_stringkey_table",pmtr_new_stringkey_table,
+		tFunc(tNone,tObj),0);
+   ADD_FUNCTION("new_hashkey_table",pmtr_new_hashkey_table,
+		tFunc(tNone,tObj),0);
+
+   mird_transaction_program=end_program();
+
+   /* scanner */
+
+   start_new_program();
+   ADD_STORAGE(struct pmts_storage);
+   set_init_callback(init_pmts);
+   set_exit_callback(exit_pmts);
+
+   ADD_FUNCTION("create",pmts_create,tFunc(tObj tInt,tVoid),0);
+   ADD_FUNCTION("read",pmts_read,tFunc(tIntPos,tMap(tOr(tInt,tStr),tStr)),0);
+
+   mird_scanner_program=end_program();
+
+   /* all done */
+
+#if 0
+   start_new_program();
+#endif
+
+   add_program_constant("Mird",mird_program,0);
+   add_program_constant("Transaction",mird_transaction_program,0);
+   add_program_constant("Scanner",mird_scanner_program,0);
+
+#if 0
+   p=end_program();
+
+   o=clone_object(p,0);
+   add_object_constant("Glue",o,0);
+   free_object(o);
+   free_program(p);
+#endif
+}
+
+void pike_module_exit(void)
+{
+   free_program(mird_program);
+   free_program(mird_transaction_program);
+   free_program(mird_scanner_program);
+}
+
+#else
+
+void pike_module_init(void) {}
+void pike_module_exit(void) {}
+
+#endif
diff --git a/src/modules/Mird/module.pmod.in b/src/modules/Mird/module.pmod.in
new file mode 100644
index 0000000000..1f1046d749
--- /dev/null
+++ b/src/modules/Mird/module.pmod.in
@@ -0,0 +1,352 @@
+import ".";
+
+class Mird
+{
+   constant Glue=@module@;
+
+   object mird;
+
+#define TABLE2ID_TABLE 1
+#define ID2TABLE_TABLE 2
+#define NEXT_TABLE "_mird_magic_next_free_table"
+
+   mapping table2id=([]); 
+   mapping id2table=([]); 
+   int next_table;
+
+//! class Mird
+//!
+//! method void create(string filename,void|mapping options)
+
+   void create(string filename,void|mapping options)
+   {
+      mird=Glue.Mird(filename,options);
+
+      mixed err=catch 
+      { 
+	 next_table=(int)mird->fetch(TABLE2ID_TABLE,NEXT_TABLE); 
+      };
+      if (err || !next_table)
+      {
+	 next_table=max(ID2TABLE_TABLE,TABLE2ID_TABLE)+1;
+	 object mtr=Glue.Transaction(mird);
+	 mtr->new_stringkey_table(TABLE2ID_TABLE);
+	 mtr->new_hashkey_table(ID2TABLE_TABLE);
+	 mtr->store(TABLE2ID_TABLE,NEXT_TABLE,(string)next_table);
+	 mtr->close();
+      }
+
+      call_out(sync_loop,60,60);
+   }
+
+//! method Table table(string name)
+
+   Table table(string name)
+   {
+      if (name==NEXT_TABLE) error("illegal name\n");
+      if (!table2id[name]) 
+      {
+	 string d=mird->fetch(TABLE2ID_TABLE,name);
+	 if (!d) error("no such table (%O)\n",name);
+	 table2id[name]=(int)d;
+      }
+      return Table(this_object(),table2id[name]);
+   }
+
+//! method table_name(int id)
+
+   string table_name(int id)
+   {
+      if (id2table[id]) return id2table[id];
+      string d=mird->fetch(ID2TABLE_TABLE,id);
+      if (!d) return 0;
+      return id2table[id]=d;
+   }
+
+//!
+//! method Table new_stringkey_table(string name)
+//! method Table new_hashkey_table(string name)
+//!
+
+   Table new_stringkey_table(string name)
+   {
+      object tr=transaction();
+      tr->new_stringkey_table(name);
+      tr->close();
+      return table(name);
+   }
+
+   Table new_hashkey_table(string name)
+   {
+      object tr=transaction();
+      tr->new_hashkey_table(name);
+      tr->close();
+      return table(name);
+   }
+
+//!
+//! method Transaction transaction()
+//! 	creates a new transaction
+
+   Transaction transaction()
+   {
+      return Transaction(this_object());
+   }
+
+//!
+//! method array(string) tables()
+//!
+
+   array(string) tables()
+   {
+      return indices(Table(this_object(),TABLE2ID_TABLE))-({NEXT_TABLE});
+   }
+
+//! method object sync()
+//! method object sync_please()
+//!	Syncs the database (syncs against disc and 
+//!	starts to reuse any space that is freed),
+//!	sync_please() does this when the last living
+//!	transaction is finished.
+//! method object sync_loop(int seconds)
+//!	Starts a call_out loop that will call sync_please
+//!	once every given interval. 0 or fewer seconds will
+//!	shut down the loop.
+//!	Default is 1 minute (60 seconds).
+
+   object sync()
+   {
+      gc(); // kill any leftover transactions
+      mird->sync();
+      return this_object();
+   }
+
+   object sync_please()
+   {
+      werror("sync %f\n",gauge {
+	 gc(); // kill any leftover transactions
+	 mird->sync_please();
+      });
+      return this_object();
+   }
+
+   object sync_loop(int seconds)
+   {
+      remove_call_out(sync_loop);
+      if (seconds<=0) return this_object();
+      if (_refs(this_object())>4) // otherwise, we're last -> close()
+	 call_out(sync_loop,seconds,seconds);
+      return sync_please();
+   }
+
+//! method void close()
+//! method void destroy()
+//!	syncs and closes the database
+
+   void close()
+   {
+      werror("close\n");
+      remove_call_out(sync_loop);
+      mird->close();
+   }
+
+   function destroy=close;
+
+//!
+//! subclass Table
+//!
+
+   class Table 
+   {
+      int table_id;
+      object parent;
+
+//! method void create(object parent,int table_id)
+      void create(object _parent,int _table_id)
+      {
+	 parent=_parent;
+	 table_id=_table_id;
+      }
+
+//! method string `[](int|string key)
+      string `[](int|string key)
+      {
+	 return parent->fetch(table_id,key);
+      }
+
+//! method string `[]=(int|string key)
+      string `[]=(int|string key,string value)
+      {
+	 if (parent->mtr)
+	 return parent->mtr->store(table_id,key,value);
+	 else
+	 {
+	    object tr=parent->transaction();
+	    tr->mtr->store(table_id,key,value);
+	    tr->close();
+	 }
+	 return value;
+      }
+
+//! method array(int|string) _indices()
+      array(int|string) _indices()
+      {
+	 array keys=({});
+	 object sc=Glue.Scanner(parent->mird,table_id);
+	 mapping m;
+	 while ( (m=sc->read(100)) ) keys+=indices(m);
+	 return keys;
+      }
+
+//! method array(int|string) _values()
+      array(int|string) _values()
+      {
+	 array values=({});
+	 object sc=Glue.Scanner(parent->mird,table_id);
+	 mapping m;
+	 while ( (m=sc->read(100)) ) values+=values(m);
+	 return values;
+      }
+
+//! method array|mapping cast("mapping"|"array")
+      array|mapping cast(string to)
+      {
+	 if (sscanf(to,"array%*s"))
+	 {
+	    array tupels=({});
+	    object sc=Glue.Scanner(parent,table_id);
+	    mapping m;
+	    while ( (m=sc->read(100)) ) tupels+=(array)m;
+	    return tupels;
+	 }
+	 if (sscanf(to,"mapping%*s"))
+	 {
+	    mapping tupels=([]);
+	    object sc=Glue.Scanner(parent,table_id);
+	    mapping m;
+	    while ( (m=sc->read(100)) ) tupels+=m;
+	    return tupels;
+	 }
+	 error("illegal argument 1 to cast\n");
+      }
+   }
+
+//!
+//! subclass Transaction
+//!
+
+   class Transaction
+   {
+      object parent;
+      object mird;
+      object mtr;
+      mapping table2id=([]);
+      int refresh_at_close=0;
+
+      void create(object _parent)
+      {
+	 parent=_parent;
+	 mird=parent->mird;
+	 mtr=Glue.Transaction(mird);
+      }
+
+//! method Table table(string name)
+//!	Creates a table object for that table,
+//!	which emulates a mapping and in which
+//!	you can make changes to the database or
+//!	do lookups.
+
+      Table table(string name)
+      {
+	 if (name==NEXT_TABLE) error("illegal name\n");
+	 int id;
+	 if (!(id=table2id[name]))
+	 {
+	    if (parent->table2id[name])
+	       id=table2id[name]=parent->table2id[name];
+	    else
+	    {
+	       string d=mtr->fetch(TABLE2ID_TABLE,name);
+	       if (!d) error("no such table (%O)\n",name);
+	       id=table2id[name]=(int)d;
+	    }
+	 }
+	 return Table(this_object(),id);
+      }
+
+//!
+//! method Table new_stringkey_table(string name)
+//! method Table new_hashkey_table(string name)
+//!	Creates a new table in the database.
+//!	A stringkey table is a mapping from string to string,
+//!	and a hashkey table is mapping from int to string.
+//!
+
+      Table new_stringkey_table(string name)
+      {
+	 string d=mird->fetch(TABLE2ID_TABLE,name);
+	 if (d) error("table already exist");
+	 int no=parent->next_table++;
+	 mtr->store(TABLE2ID_TABLE,name,(string)next_table);
+	 mtr->store(ID2TABLE_TABLE,next_table,name);
+	 mtr->store(TABLE2ID_TABLE,NEXT_TABLE,(string)next_table);
+	 mtr->new_stringkey_table(no);
+	 refresh_at_close=1;
+	 return table(name);
+      }
+
+      Table new_hashkey_table(string name)
+      {
+	 string d=mird->fetch(TABLE2ID_TABLE,name);
+	 if (d) error("table already exist");
+	 int no=parent->next_table++;
+	 mtr->store(TABLE2ID_TABLE,name,(string)next_table);
+	 mtr->store(ID2TABLE_TABLE,next_table,name);
+	 mtr->store(TABLE2ID_TABLE,NEXT_TABLE,(string)next_table);
+	 mtr->new_hashkey_table(no);
+	 refresh_at_close=1;
+	 return table(name);
+      }
+
+//!
+//! method array(string) tables()
+//! returns the names of the tables in the database.
+//!
+
+      array(string) tables()
+      {
+	 return indices(Table(this_object(),TABLE2ID_TABLE));
+      }
+
+//!
+//! method void close()
+//!	Finishes a transaction. This throws exceptions
+//!	if there were conflicts.
+//!
+
+      void close()
+      {
+	 mtr->close();
+	 if (refresh_at_close) 
+	    parent->table2id=parent->id2table=([]);
+      }
+
+//!
+//! method void cancel()
+//! method void destroy()
+//!	Cancels (rewinds) a transaction.
+//!
+
+      void cancel()
+      {
+	 mtr->cancel();
+      }
+
+      void destroy()
+      {
+	 mtr->destroy();
+      }
+   }
+}
+
+program _module_value=Mird;
-- 
GitLab